mbox series

[v7,00/13] Add MediaTek MT6370 PMIC support

Message ID 20220805070610.3516-1-peterwu.pub@gmail.com
Headers show
Series Add MediaTek MT6370 PMIC support | expand

Message

ChiaEn Wu Aug. 5, 2022, 7:05 a.m. UTC
From: ChiaEn Wu <chiaen_wu@richtek.com>

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.

In this series of patches, we based on Andy Shevchenko's mfd patch which
is used to adjust the Makefile order.
(https://lore.kernel.org/all/20220801114211.36267-3-andriy.shevchenko@linux.intel.com/)
Among this, we also took some changes for MT6370 drivers, revised the
MT6370 device tree files to comply with DT specifications, and revised
the wrong SoB chain in entire patches.

PS.
"[PATCH v7 06/13] dt-bindings: mfd: Add MediaTek MT6370" depends on previous
DT binding patches, so before applying this patch, please apply other DT
patches first. Thanks!

PS2.
Our MFD DT-binding depends on LED flash and LED RGB DT-bindings, but the
Kconfigs of LED flash and LED RGB depend on MFD. Due to dependency
consideration, we also submit LED flash and LED RGB with other patches
at this time.


Thank you,
ChiaEn Wu
---
Changes in v7:
- In Patch 05/13:
	- Add the second compatible string for 'mt6372'
	- Add 'mediatek,bled-exponential-mode-enable' property for enable the
	  exponential mode of backlight brightness
	- Add validation for the maximum value of 'default-brightness' and
	  'max-brightness'

- In Patch 07/13:
	- Move '#define MT6370_REG_MAXADDR' to the next line of
	  '#define MT6370_REG_CHG_MASK1'
	- Rename 'MT6370_REG_ADDRLEN' to 'MT6370_MAX_ADDRLEN'

- In Patch 08/13:
	- Revise 'devm_add_action_or_reset(dev, ...)' to one line
	- Revise 'return regmap_update_bits(...)' with using positive
	  conditional

- In Patch 09/13:
	- Add AICR(100mA ~ 350mA), ICHG(100mA ~ 800mA) macros
	- Remove 400mA AICR and 900mA ICHG macros
	- Revise using 'if-else' to 'switch-case' in mt6370_adc_read_scale()
	  where the adc channel is ibus or ibat

- In Patch 10/13:
	- Revise the method to enable/disable irq
	- Revise all 'if (ret < 0)' to 'if (ret)' after using
	  mt6370_chg_field_set/get()
	- Revise all 'OTG' text

- In Patch 11/13:
	- Add the comment for the union of 'struct mt6370_led'
	- Revise the wrong description of 'Authors'
	- Revise some typos (e.g. led --> LED)
	- Revise 'if (!fwnode_property_read_string())' to
	  'ret = fwnode_property_read_string()'
	- Replace 'memcpy(...)' with 'put_unaligned_be24()' in
	  mt6370_gen_breath_pattern()
	- Replace all 'LED_OFF' with 0
	- Remove the redundant assignment in mt6370_mc_pattern_clear()

- In Patch 12/13:
	- Fix the indentation.
	- For the well defined macro, the parenthesis is needed for input parameters.
	- Replace some dev_warn to dev_info in 'init_flash_properties'.
	- Add sentinel comment for the terminator entry of of_device_id.
	- Use priv->fled_torch_used directly.
	- Delete 0 in {}.
	- Use _uA instead of _UA in definition.
	- Refine the description.
	- Use usleep_range instead of udelay.
	- Rename config to LEDS_MT6370_FLASH.
	- Add missing ">" in copyright.
	- Change the Kconfig order

- In Patch 13/13:
	- Add support 'exponential mode' property parsing
	- Add 'return dev_err_probe()' after 'if (IS_ERR(priv->enable_gpio))'
	- Add 'mt6372' compatible string
	- Revise Kconfig help text
	- Revise update()/get() for supporting 16384 steps (MT6372)
	- Revise all shift usages form using 'ffs() and fls()' to defining the
	  _SHIFT macros.
	- Revise 'brightness ? 1 : 0' to '!!brightness' in gpiod_set_value()


Changes in v6:
- In Patch 03/13:
	- Add 'reg' property of led of multi-led to prevent checking
	  error.

- In Patch 08/13:
	- Convert tcpci as device resource managed with
	  'devm_add_action_or_reset' api.
	- Refine remvoe callback.
	- Refine the commit text from 'this commit add' to 'add'.

- In Patch 09/13:
	- Using 'struct device *dev = &pdev->dev' in probe()
	- Revise the sixth parameter of regmap_read_poll_timeout() by
	  Replacing '1000' with 'MILLI'
	- Revise the units of three macros
	- MT6370_AICR_400MA --> MT6370_AICR_400_mA
	- MT6370_ICHG_500MA --> MT6370_ICHG_500_mA
	- MT6370_ICHG_900MA --> MT6370_ICHG_900_mA

- In patch 10/13:
	- Remove the varable (*psy_desc) of struct mt6370_priv
	- Remove the deprecated usb type (POWER_SUPPLY_TYPE_USB_CDP and
	  POWER_SUPPLY_TYPE_USB_DCP)
	- Remove useless remove()
	- Revise all units from mini- to micro-
	- Revise get/set power_supply_prop (change to directly return get/set
	  regmap_field)
	- Revise probe() and use devm_add_action_or_reset() for handling of the
	  workqueue/delayed_work/mutex
	- Revise mt6370_chg_psy_desc
		- Add '.name = "mt6370-charger"'
		- Use 'static const'

- In patch 11/13:
	- Remove the 'ko' from mt6370 led Kconfig description.
	- Add both authors for Alice and ChiYuan.
	- Use pdata to distinguish the code from mt6370/71 to mt6372.
	- Instead of 'state' define, use the 'state' enum.
	- Fix the typo for 'MT6372_PMW_DUTY'.
	- For pwm_duty define, replace with bit macro - 1.
	- Refine all the labels from 'out' to 'out_unlock'.
	- Use struct 'dev' variable and 'dev_err_probe' to optimize the LOC.
	- Revise for the array initialization from {0} to {}.
	- Move into rgb folder and rename file name to 'leds-mt6370-rgb'.
	- Refine the 'comma' usage in struct/enum.

- In patch 12/13:
	- Use 'GENMASK' instead of 'BIT'.
	- Use dev_err_probe to decrease LOC.
	- Use 'dev' variable to make probe function more clean.
	- Refine the return of _mt6370_flash_brightness_set function.
	- Refine the descriptions.
	- Use mt6370_clamp() instead of clamp_align().
	- Use device resource managed API for v4l2 flash_release.


Changes in v5:
- In patch 07/13:
	- Add the comma in the last REGMAP_IRQ_REG_LINE(),
	  DEFINE_RES_IRQ_NAMED() and MFD_CELL_RES()
	- Add the prefix in the first parameter of all mfd_cell
	- Move enum and struct mt6370_info to mt6370.h
	- Remove struct device *dev in struct mt6370_info
	- Revise the description of Kconfig help text
	- Revise MODULE_DESCRIPTION()

- In patch 08/13:
	- Add comma for the last index of mt6370_reg_init.
	- Use dev_err_probe to decrease LOC.
	- Use 'dev' variable to make probe function more clean.
	- Refine kconfig text.
	- Remove both 'else' in set_vbus callback.
	- Remove comma for of_device_id if the assigned member is only one.

- In patch 09/13:
	- Replace using snprintf() with sysfs_emit() in mt6370_adc_read_label()
	- Remove macro ADC_CONV_TIME_US
	- Revise all variable ordering
	- Revise the description of Kconfig help text
	- Revise MODULE_DESCRIPTION()

- In patch 10/13:
	- Replace unsigned int type of pwr_rdy with bool in
	  mt6370_chg_set_online()
	- Remove redundant 'else' in mt6370_chg_field_get()
	- Revise 'if-else' in mt6370_chg_field_set()
	- Revise 'if' condition in mt6370_chg_enable_irq()
	- Revise all text 'otg' --> 'OTG'
	- Revise MT6370_MIVR_IBUS_TH_100_MA --> MT6370_MIVR_IBUS_TH_100_mA
	- Revise the description of Kconfig help text

- In patch 12/13:
	- Refine the coding style.
	- Use "dev" instead of "&pdev->dev".

- In patch 13/13:
	- Add missed <mod_devicetable.h>
	- Add struct device *dev in probe() to make code cleaning
	- Remove useless including header file <gpio/driver.h>, <of.h>
	- Remove useless variable uasage in mt6370_init_backlight_properties()
	- Remove redundant checking enable_gpio in mt6370_bl_update_status()
	- Remove redundant parentheses in mt6370_bl_get_brightness()
	- Revise the description of Kconfig help text
	- Revise the calculation of hys_th_steps


Changes in v4:
- In patch 02/13:
	- Add minItems of "io-channel-names"
	- Replace text "Mediatek" with "MediaTek"

- In patch 06/13:
	- Roll back all "$ref: " to v2 patch style (using "/schemas/...")

- In patch 07/13:
	- Replace text "Mediatek" with "MediaTek" in Kconfig
	- Replace "first break and then return" with "return directly"
	  in "mt6370_check_vendor_info()"
	- Add module name related description in Kconfig helptext
	- Add Copyright in the source code
	- Add header file "mt6370.h" for all "#define IRQ"
	- Adjust Makefile order of MT6370
	- Refine "bank_idx" and "bank_addr" in
	  "mt6375_regmap_read()" / "mt6375_regmap_write()"
	- Refine redundant "else if" in "mt6370_regmap_read()"

- In patch 08/13:
	- Replace text "Mediatek" with "MediaTek" in Kconfig
	- Replace "first ret=regulator_(dis/en)able and then return"
	  with "return directly" in "mt6370_tcpc_set_vbus()"
	- Replace header file <linux/of.h> with <linux/mod_devicetable.h>
	- Add Copyright in the source code
	- Add module name related description in Kconfig helptext
	- Remove header file <linux/of.h>
	- Refine all probe error by using dev_err_probe()

- In patch 09/13:
	- Replace text "Mediatek" with "MediaTek"
	- Replace all "first dev_err() and then return" with
	  "return dev_err_probe()"
	- Add Copyright in the source code
	- Add module name related description in Kconfig
	- Add unit suffix of macro "ADC_CONV_POLLING_TIME"
	- Add new macro "ADC_CONV_TIME_MS"
	- Adjust the position of include file <mediatek,mt6370_adc.h>
	- Adjust the postions between <linux/module.h> and
	   <linux/mod_devicetable.h>
	- Fix some incorrect characters

- In patch 10/13:
	- Replace text "Mediatek" with "MediaTek" in Kconfig and
	  MODULE_DESCRIPTION()
	- Replace "mt6370_chg_val_to_reg" and "mt6370_chg_reg_to_val"
	  with "linear_range" API
	- Replace "first break and then return" with "return directly"
	  in all cases of get/set power_supply_property
	- Replace all "first dev_err() and then return" with "return
	  dev_err_probe()"
	- Replace all "return IS_ERR(priv->rdev) ? PTR_ERR(priv->rdev) : 0"
	  with "PTR_ERR_OR_ZERO()"
	- Replace "priv->dev->of_node" with "dev_of_node()"
	- Add Copyright in the source code
	- Add module name related description in Kconfig helptext
	- Add proper unit of "MT6370_MIVR_IBUS_TH"
	- Add error check in "mt6370_chg_get_status"
	- Remove including <mediatek,mt6370_adc.h> header file
	- Remove redundant comma of every enum terminator line
	- Remove unwanted blank lines
	- Remove the useless label (toggle_cfo_exit:)
	- Remove using atomic
	- Remove using of_match_ptr()
	- Fix some incorrect characters
	- Fix updating wrong bits when using ena_gpiod of OTG regulator
	- Adjust the probe order in probe()

- In patch 11/13:
	- Replace text "Mediatek" with "MediaTek" in Kconfig
	- Replace text "const" with "constant" in Kconfig
	- Add Copyright in the source code

- In patch 12/13:
	- Replace text "Mediatek" with "MediaTek" in Kconfig
	- Add Copyright in the source code

- In patch 13/13:
	- Replace text "Mediatek" with "MediaTek" in Kconfig
	- Add Copyright in the source code
	- Revise the comment of "PWM HYS STEPS"


Changes in v3:
- Remove ADC ABI file, which is added in v2 Patch 7
- In patch 02/14:
	- Add items and remove maxItems of io-channels
	- Add io-channel-names and describe each item
	- Add "unevaluatedProperties: false" in "usb-otg-vbus-regulator"
	- Rename "enable-gpio" to "enable-gpios" in "usb-otg-vbus-regulator"

- In patch 03/14:
	- Use leds-class-multicolor.yaml instead of common.yaml.
	- Split multi-led and led node.
	- Add subdevice "led" in "multi-led".

- In patch 04/14:
	- Remove the description of enum.

- In patch 05/14:
	- Rename "mediatek,bled-pwm-hys-input-threshold-steps" to
	  "mediatek,bled-pwm-hys-input-th-steps"
	- Refine "bled-pwm-hys-input-th-steps", "bled-ovp-microvolt",
	  "bled-ocp-microamp" enum values

- In patch 06/14:
	- Use " in entire patchset
	- Refine ADC description
	- Rename "enable-gpio" to "enable-gpios" in "regualtor"

- In patch 07/14:
	- Refine Kconfig help text
	- Refine error message of unknown vendor ID in
	  mt6370_check_vendor_info()
	- Refine return value handling of mt6370_regmap_read()
	- Refine all probe error by using dev_err_probe()
	- Refine "bank_idx" and "bank_addr" in mt6370_regmap_read() and
	  mt6370_regmap_write()
	- Add "#define VENID*" and drop the comments in
	  mt6370_check_vendor_info()
	- Drop "MFD" in MODULE_DESCRIPTION()

- In patch 09/14:
	- Refine Kconfig help text

- In patch 10/14:
	- Refine Kconfig help text
	- Refine all channel value in read_scale()
		a. current: uA --> mA
		b. voltage: uV --> mV
		c. temperature: degrees Celsius --> milli degrees Celsius
	- Add "default:" condition of switch statement in read_scale() and read_raw()
	- Add error message for reading ADC register failed
	- Add the comment for adc_lock
	- Add <linux/mod_devicetable.h> header file for struct of_device_id
	- Replace "adc" text with "ADC" in all of the error messages

- In patch 12/14:
	- Refine the grammer of the Kconfig.
	- Change reg mode to the const current mode.

- In patch 14/14:
	- Refine bool properties parsing (pwm-enable, ovp-shutdown, ocp-shutdown) in DT
	  parsing function
	- Refine u32 and u8 properties parsing (pwm-hys-input-th-steps, ovp-microvolt,
	  ocp-microamp), from using register value to using actual value
	- Refine error string of "channle-use" parsing failed
	- Refine Kconfig help text


Changes in v2:
- In patch 01/15:
	- Add "unevaluatedProperties: false".
	- Delete "DT bindings".
	- Refine the description to fit in 80 columns.
	- Skip the connector description.

- In patch 02/15:
	- Refine items description of interrupt-name
	- Rename "usb-otg-vbus" to "usb-otg-vbus-regulator"
	- Add constraint properties for ADC

- In patch 03/15:
	- Skip not useful description of "^(multi-)?led@[0-3]$"
	  and reg.
	- Due to the dependency, remove the mention of mfd
	  document directory.
	- Delete Soft-start property. In design aspect, we think
	  soft-restart should always be enabled, our new chip
	  has deleted the related setting register , also, we don’t
	  allow user adjust this parameter in this chip.
	- Refine the commit message.

- In patch 04/15:
	- Skip not useful description of "^led@[0-1]$" and reg.
	- Add apace after '#'.
	- Refine the commit message.

- In patch 05/15:
	- Remove "binding documentation" in subject title
	- Refine description of mt6370 backlight binding
	  document
	- Refine properties name(bled-pwm-hys-input-bit,
	  bled-ovp-microvolt, bled-ocp-microamp) and their
	  description

- In patch 06/15:
	- Refine ADC and Regulator descriptions
	- Refine include header usage in example
	- Refine node name to generic node name("pmic@34")
	- Refine led example indentation
	- Refine license of mediatek,mt6370_adc.h
	- Rename the dts example from IRQ define to number.
	- Remove mediatek,mt6370.h

- In patch 07/15:
	- Add ABI documentation for mt6370 non-standard ADC
	  sysfs interfaces.

- In patch 08/15:
	- Add all IRQ define into mt6370.c.
	- Refine include header usage

- In patch 09/15:
	- No changes.

- In patch 10/15:
	- Use 'gpiod_get_from_of_node' to replace
	  'fwnode_gpiod_get_index'.

- In patch 11/15:
	- Refine Kconfig mt6370 help text
	- Refine mask&shift to FIELD_PREP()
	- Refine mutex lock name ("lock" -> "adc_lock")
	- Refine mt6370_adc_read_scale()
	- Refine mt6370_adc_read_offset()
	- Refine mt6370_channel_labels[] by using enum to index
	  chan spec
	- Refine MT6370_ADC_CHAN()
	- Refine indio_dev->name
	- Remove useless include header files

- In patch 12/15:
	- Refine mt6370_chg_otg_rdesc.of_match
	  ("mt6370,otg-vbus" -> "usb-otg-vbus-regulator") to match
	  DT binding

- In patch 13/15:
	- Refine Kconfig description.
	- Remove include "linux/of.h" and use
	  "linux/mod_devicetable.h".
	- Place a comma for the last element of the const
	  unsigned int array.
	- Add a comment line for the mutex 'lock'.
	- In probe function, use 'dev_err_probe' in some
	  judgement to reduce the LOC.
	- Refine include header usage.
	  BIT/GENMASK -> linux/bits.h
	  FIELD_GET -> linux/bitfield.h

- In patch 14/15:
	- Add blank line.
	- Replace container_of() with to_mt6370_led() .
	- Refine description of ramping.
	- Refine the mt6370_init_common_properties function.
	- Refine the probe return.

- In patch 15/15:
	- Refine MT6370 help text in Kconfig
	- Refine DT Parse function
	- Remove useless enum
	- Add comment for 6372 backward compatible in
	  bl_update_status() and
	  check_vendor_info()
	- Using dev_err_probe(); insteads dev_err()&return; in
	  the probe()


Alice Chen (2):
  dt-bindings: leds: Add MediaTek MT6370 flashlight
  leds: flash: mt6370: Add MediaTek MT6370 flashlight support

ChiYuan Huang (7):
  dt-bindings: usb: Add MediaTek MT6370 TCPC
  dt-bindings: leds: mt6370: Add MediaTek MT6370 current sink type LED
    indicator
  dt-bindings: backlight: Add MediaTek MT6370 backlight
  dt-bindings: mfd: Add MediaTek MT6370
  mfd: mt6370: Add MediaTek MT6370 support
  usb: typec: tcpci_mt6370: Add MediaTek MT6370 tcpci driver
  leds: rgb: mt6370: Add MediaTek MT6370 current sink type LED Indicator
    support

ChiaEn Wu (4):
  dt-bindings: power: supply: Add MediaTek MT6370 Charger
  iio: adc: mt6370: Add MediaTek MT6370 support
  power: supply: mt6370: Add MediaTek MT6370 charger driver
  video: backlight: mt6370: Add MediaTek MT6370 support

 .../leds/backlight/mediatek,mt6370-backlight.yaml  |  121 +++
 .../bindings/leds/mediatek,mt6370-flashlight.yaml  |   41 +
 .../bindings/leds/mediatek,mt6370-indicator.yaml   |   81 ++
 .../devicetree/bindings/mfd/mediatek,mt6370.yaml   |  280 ++++++
 .../power/supply/mediatek,mt6370-charger.yaml      |   88 ++
 .../bindings/usb/mediatek,mt6370-tcpc.yaml         |   36 +
 drivers/iio/adc/Kconfig                            |   12 +
 drivers/iio/adc/Makefile                           |    1 +
 drivers/iio/adc/mt6370-adc.c                       |  305 ++++++
 drivers/leds/flash/Kconfig                         |   12 +
 drivers/leds/flash/Makefile                        |    1 +
 drivers/leds/flash/leds-mt6370-flash.c             |  632 ++++++++++++
 drivers/leds/rgb/Kconfig                           |   13 +
 drivers/leds/rgb/Makefile                          |    1 +
 drivers/leds/rgb/leds-mt6370-rgb.c                 | 1022 ++++++++++++++++++++
 drivers/mfd/Kconfig                                |   16 +
 drivers/mfd/Makefile                               |    1 +
 drivers/mfd/mt6370.c                               |  312 ++++++
 drivers/mfd/mt6370.h                               |   99 ++
 drivers/power/supply/Kconfig                       |   14 +
 drivers/power/supply/Makefile                      |    1 +
 drivers/power/supply/mt6370-charger.c              |  965 ++++++++++++++++++
 drivers/usb/typec/tcpm/Kconfig                     |   11 +
 drivers/usb/typec/tcpm/Makefile                    |    1 +
 drivers/usb/typec/tcpm/tcpci_mt6370.c              |  207 ++++
 drivers/video/backlight/Kconfig                    |   13 +
 drivers/video/backlight/Makefile                   |    1 +
 drivers/video/backlight/mt6370-backlight.c         |  351 +++++++
 include/dt-bindings/iio/adc/mediatek,mt6370_adc.h  |   18 +
 29 files changed, 4656 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/rgb/leds-mt6370-rgb.c
 create mode 100644 drivers/mfd/mt6370.c
 create mode 100644 drivers/mfd/mt6370.h
 create mode 100644 drivers/power/supply/mt6370-charger.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

Comments

Andy Shevchenko Aug. 5, 2022, 10:50 a.m. UTC | #1
On Fri, Aug 5, 2022 at 9:07 AM ChiaEn Wu <peterwu.pub@gmail.com> wrote:
>
> From: ChiYuan Huang <cy_huang@richtek.com>
>
> The MediaTek 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.
>
> Add a support for the Type-C & Power Delivery controller in
> MediaTek MT6370 IC.

Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>

> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
> Reviewed-by: Guenter Roeck <linux@roeck-us.net>
> Signed-off-by: ChiYuan Huang <cy_huang@richtek.com>
> Signed-off-by: ChiaEn Wu <chiaen_wu@richtek.com>
> ---
>
> v7
> - Revise 'devm_add_action_or_reset(dev, ...)' to one line
> - Revise 'return regmap_update_bits(...)' with using positive
>   conditional
> ---
>  drivers/usb/typec/tcpm/Kconfig        |  11 ++
>  drivers/usb/typec/tcpm/Makefile       |   1 +
>  drivers/usb/typec/tcpm/tcpci_mt6370.c | 207 ++++++++++++++++++++++++++++++++++
>  3 files changed, 219 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 073fd2e..e6b88ca 100644
> --- a/drivers/usb/typec/tcpm/Kconfig
> +++ b/drivers/usb/typec/tcpm/Kconfig
> @@ -35,6 +35,17 @@ 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.
> +
> +         This driver can also be built as a module. The module
> +         will be called "tcpci_mt6370".
> +
>  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 7d499f3..906d9dc 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 0000000..c5bb201
> --- /dev/null
> +++ b/drivers/usb/typec/tcpm/tcpci_mt6370.c
> @@ -0,0 +1,207 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2022 Richtek Technology Corp.
> + *
> + * Author: ChiYuan Huang <cy_huang@richtek.com>
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_wakeup.h>
> +#include <linux/pm_wakeirq.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/usb/tcpci.h>
> +#include <linux/usb/tcpm.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;
> +};
> +
> +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)
> +               return regmap_write(data->regmap, TCPC_FAULT_CTRL, 0x80);
> +
> +       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 ? 0 : MT6370_AUTOIDLE_MASK);
> +}
> +
> +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)
> +               return regulator_disable(priv->vbus);
> +
> +       if (!ret && source)
> +               return regulator_enable(priv->vbus);
> +
> +       return 0;
> +}
> +
> +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)
> +               return dev_err_probe(priv->dev, -ENODEV,
> +                                    "Vendor ID not correct 0x%02x\n", vid);
> +
> +       return 0;
> +}
> +
> +static void mt6370_unregister_tcpci_port(void *tcpci)
> +{
> +       tcpci_unregister_port(tcpci);
> +}
> +
> +static int mt6370_tcpc_probe(struct platform_device *pdev)
> +{
> +       struct mt6370_priv *priv;
> +       struct device *dev = &pdev->dev;
> +       int irq, ret;
> +
> +       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +
> +       priv->dev = dev;
> +
> +       priv->tcpci_data.regmap = dev_get_regmap(dev->parent, NULL);
> +       if (!priv->tcpci_data.regmap)
> +               return dev_err_probe(dev, -ENODEV, "Failed to init regmap\n");
> +
> +       ret = mt6370_check_vendor_info(priv);
> +       if (ret)
> +               return ret;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0)
> +               return dev_err_probe(dev, irq, "Failed to get irq\n");
> +
> +       /* 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(dev, "vbus");
> +       if (!IS_ERR(priv->vbus))
> +               priv->tcpci_data.set_vbus = mt6370_tcpc_set_vbus;
> +
> +       priv->tcpci = tcpci_register_port(dev, &priv->tcpci_data);
> +       if (IS_ERR(priv->tcpci))
> +               return dev_err_probe(dev, PTR_ERR(priv->tcpci),
> +                                    "Failed to register tcpci port\n");
> +
> +       ret = devm_add_action_or_reset(dev, mt6370_unregister_tcpci_port, priv->tcpci);
> +       if (ret)
> +               return ret;
> +
> +       ret = devm_request_threaded_irq(dev, irq, NULL, mt6370_irq_handler,
> +                                       IRQF_ONESHOT, dev_name(dev), priv);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Failed to allocate irq\n");
> +
> +       device_init_wakeup(dev, true);
> +       dev_pm_set_wake_irq(dev, irq);
> +
> +       return 0;
> +}
> +
> +static int mt6370_tcpc_remove(struct platform_device *pdev)
> +{
> +       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");
> --
> 2.7.4
>
Andy Shevchenko Aug. 5, 2022, 10:57 a.m. UTC | #2
On Fri, Aug 5, 2022 at 9:07 AM ChiaEn Wu <peterwu.pub@gmail.com> wrote:
>
> From: ChiaEn Wu <chiaen_wu@richtek.com>
>
> MediaTek MT6370 is a SubPMIC consisting of a single cell battery charger
> with ADC monitoring, RGB LEDs, dual channel flashlight, WLED backlight
> driver, display bias voltage supply, one general purpose LDO, and the
> USB Type-C & PD controller complies with the latest USB Type-C and PD
> standards.
>
> Add a support for the MediaTek MT6370 Charger driver. The charger module
> of MT6370 supports High-Accuracy Voltage/Current Regulation,
> Average Input Current Regulation, Battery Temperature Sensing,
> Over-Temperature Protection, DPDM Detection for BC1.2.

On Fri, Aug 5, 2022 at 9:07 AM ChiaEn Wu <peterwu.pub@gmail.com> wrote:
>
> From: ChiYuan Huang <cy_huang@richtek.com>
>
> The MediaTek 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.
>
> Add a support for the Type-C & Power Delivery controller in
> MediaTek MT6370 IC.

FWIW,
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>

> Signed-off-by: ChiaEn Wu <chiaen_wu@richtek.com>
> ---
>
> v7
> - Revise the method to enable/disable irq
> - Revise all 'if (ret < 0)' to 'if (ret)' after using
>   mt6370_chg_field_set/get()
> - Revise all 'OTG' text
> ---
>  drivers/power/supply/Kconfig          |  14 +
>  drivers/power/supply/Makefile         |   1 +
>  drivers/power/supply/mt6370-charger.c | 965 ++++++++++++++++++++++++++++++++++
>  3 files changed, 980 insertions(+)
>  create mode 100644 drivers/power/supply/mt6370-charger.c
>
> diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
> index 1aa8323..591deb8 100644
> --- a/drivers/power/supply/Kconfig
> +++ b/drivers/power/supply/Kconfig
> @@ -619,6 +619,20 @@ 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.
> +
> +         This driver can also be built as a module. If so, the module
> +         will be called "mt6370-charger".
> +
>  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 7f02f36..8c95276 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 0000000..42fddc3
> --- /dev/null
> +++ b/drivers/power/supply/mt6370-charger.c
> @@ -0,0 +1,965 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2022 Richtek Technology Corp.
> + *
> + * Author: ChiaEn Wu <chiaen_wu@richtek.com>
> + */
> +
> +#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/linear_range.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_100_mA     100000
> +#define MT6370_ADC_CHAN_IBUS           5
> +#define MT6370_ADC_CHAN_MAX            9
> +
> +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_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
> +};
> +
> +enum mt6370_irq {
> +       MT6370_IRQ_ATTACH_I = 0,
> +       MT6370_IRQ_UVP_D_EVT,
> +       MT6370_IRQ_MIVR,
> +       MT6370_IRQ_MAX
> +};
> +
> +struct mt6370_priv {
> +       struct device *dev;
> +       struct iio_channel *iio_adcs;
> +       struct mutex attach_lock;
> +       struct power_supply *psy;
> +       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;
> +       unsigned int irq_nums[MT6370_IRQ_MAX];
> +       int 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_field {
> +       const char *name;
> +       const struct linear_range *range;
> +       struct reg_field field;
> +};
> +
> +enum {
> +       MT6370_RANGE_F_IAICR = 0,
> +       MT6370_RANGE_F_VOREG,
> +       MT6370_RANGE_F_VMIVR,
> +       MT6370_RANGE_F_ICHG,
> +       MT6370_RANGE_F_IPREC,
> +       MT6370_RANGE_F_IEOC,
> +       MT6370_RANGE_F_MAX
> +};
> +
> +#define MT6370_CHG_LINEAR_RANGE(_rfd, _min, _min_sel, _max_sel, _step) \
> +[_rfd] = {                                                             \
> +       .min = _min,                                                    \
> +       .min_sel = _min_sel,                                            \
> +       .max_sel = _max_sel,                                            \
> +       .step = _step,                                                  \
> +}
> +
> +static const struct linear_range mt6370_chg_ranges[MT6370_RANGE_F_MAX] = {
> +       MT6370_CHG_LINEAR_RANGE(MT6370_RANGE_F_IAICR, 100000, 0x0, 0x3F, 50000),
> +       MT6370_CHG_LINEAR_RANGE(MT6370_RANGE_F_VOREG, 3900000, 0x0, 0x51, 10000),
> +       MT6370_CHG_LINEAR_RANGE(MT6370_RANGE_F_VMIVR, 3900000, 0x0, 0x5F, 100000),
> +       MT6370_CHG_LINEAR_RANGE(MT6370_RANGE_F_ICHG, 900000, 0x08, 0x31, 100000),
> +       MT6370_CHG_LINEAR_RANGE(MT6370_RANGE_F_IPREC, 100000, 0x0, 0x0F, 50000),
> +       MT6370_CHG_LINEAR_RANGE(MT6370_RANGE_F_IEOC, 100000, 0x0, 0x0F, 50000),
> +};
> +
> +#define MT6370_CHG_FIELD(_fd, _reg, _lsb, _msb)                                \
> +[_fd] = {                                                              \
> +       .name = #_fd,                                                   \
> +       .range = NULL,                                                  \
> +       .field = REG_FIELD(_reg, _lsb, _msb),                           \
> +}
> +
> +#define MT6370_CHG_FIELD_RANGE(_fd, _reg, _lsb, _msb)                  \
> +[_fd] = {                                                              \
> +       .name = #_fd,                                                   \
> +       .range = &mt6370_chg_ranges[MT6370_RANGE_##_fd],                \
> +       .field = REG_FIELD(_reg, _lsb, _msb),                           \
> +}
> +
> +static const struct mt6370_chg_field mt6370_chg_fields[F_MAX] = {
> +       MT6370_CHG_FIELD(F_IINLMTSEL, MT6370_REG_CHG_CTRL2, 2, 3),
> +       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_RANGE(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_RANGE(F_VOREG, MT6370_REG_CHG_CTRL4, 1, 7),
> +       MT6370_CHG_FIELD_RANGE(F_VMIVR, MT6370_REG_CHG_CTRL6, 1, 7),
> +       MT6370_CHG_FIELD_RANGE(F_ICHG, MT6370_REG_CHG_CTRL7, 2, 7),
> +       MT6370_CHG_FIELD_RANGE(F_IPREC, MT6370_REG_CHG_CTRL8, 0, 3),
> +       MT6370_CHG_FIELD_RANGE(F_IEOC, MT6370_REG_CHG_CTRL9, 4, 7),
> +       MT6370_CHG_FIELD(F_USBCHGEN, MT6370_REG_DEVICE_TYPE, 7, 7),
> +       MT6370_CHG_FIELD(F_USB_STAT, MT6370_REG_USB_STATUS1, 4, 6),
> +       MT6370_CHG_FIELD(F_CHGDET, MT6370_REG_USB_STATUS1, 3, 3),
> +       MT6370_CHG_FIELD(F_CHG_STAT, MT6370_REG_CHG_STAT, 6, 7),
> +       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 int mt6370_chg_field_get(struct mt6370_priv *priv,
> +                                      enum mt6370_chg_reg_field fd,
> +                                      unsigned int *val)
> +{
> +       int ret;
> +       unsigned int reg_val;
> +
> +       ret = regmap_field_read(priv->rmap_fields[fd], &reg_val);
> +       if (ret)
> +               return ret;
> +
> +       if (mt6370_chg_fields[fd].range)
> +               return linear_range_get_value(mt6370_chg_fields[fd].range,
> +                                              reg_val, val);
> +
> +       *val = reg_val;
> +       return 0;
> +}
> +
> +static inline int mt6370_chg_field_set(struct mt6370_priv *priv,
> +                                      enum mt6370_chg_reg_field fd,
> +                                      unsigned int val)
> +{
> +       int ret;
> +       bool f;
> +       const struct linear_range *r;
> +
> +       if (mt6370_chg_fields[fd].range) {
> +               r = mt6370_chg_fields[fd].range;
> +
> +               if (fd == F_VMIVR) {
> +                       ret = linear_range_get_selector_high(r, val, &val, &f);
> +                       if (ret)
> +                               val = r->max_sel;
> +               } else {
> +                       linear_range_get_selector_within(r, val, &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;
> +
> +       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)) {
> +               rcfg->ena_gpiod = NULL;
> +               return 0;
> +       }
> +
> +       return regmap_update_bits(priv->regmap, MT6370_REG_CHG_CTRL1,
> +                                 MT6370_OTG_PIN_EN_MASK,
> +                                 MT6370_OTG_PIN_EN_MASK);
> +}
> +
> +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;
> +       bool rpt_psy = false;
> +       unsigned int attach, usb_stat;
> +
> +       mutex_lock(&priv->attach_lock);
> +       attach = 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) {
> +                       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)
> +                       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:
> +       case MT6370_USB_STAT_SDP_NSTD:
> +               priv->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
> +               break;
> +       case MT6370_USB_STAT_DCP:
> +               priv->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP;
> +               break;
> +       case MT6370_USB_STAT_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_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 int mt6370_chg_toggle_cfo(struct mt6370_priv *priv)
> +{
> +       int ret;
> +       unsigned int fl_strobe;
> +
> +       /* check if flash led in strobe mode */
> +       ret = mt6370_chg_field_get(priv, F_FL_STROBE, &fl_strobe);
> +       if (ret) {
> +               dev_err(priv->dev, "Failed to get FL_STROBE_EN\n");
> +               return ret;
> +       }
> +
> +       if (fl_strobe) {
> +               dev_err(priv->dev, "Flash led is still in strobe mode\n");
> +               return ret;
> +       }
> +
> +       /* cfo off */
> +       ret = mt6370_chg_field_set(priv, F_CFO_EN, 0);
> +       if (ret) {
> +               dev_err(priv->dev, "Failed to disable CFO_EN\n");
> +               return ret;
> +       }
> +
> +       /* cfo on */
> +       ret = mt6370_chg_field_set(priv, F_CFO_EN, 1);
> +       if (ret)
> +               dev_err(priv->dev, "Failed to enable CFO_EN\n");
> +
> +       return ret;
> +}
> +
> +static int mt6370_chg_read_adc_chan(struct mt6370_priv *priv, unsigned int chan,
> +                                   int *val)
> +{
> +       int ret;
> +
> +       if (chan >= MT6370_ADC_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;
> +       unsigned int mivr_stat, ibus;
> +
> +       ret = mt6370_chg_field_get(priv, F_CHG_MIVR_STAT, &mivr_stat);
> +       if (ret) {
> +               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_ADC_CHAN_IBUS, &ibus);
> +       if (ret) {
> +               dev_err(priv->dev, "Failed to get ibus\n");
> +               goto mivr_handler_out;
> +       }
> +
> +       if (ibus < MT6370_MIVR_IBUS_TH_100_mA) {
> +               ret = mt6370_chg_toggle_cfo(priv);
> +               if (ret)
> +                       dev_err(priv->dev, "Failed to toggle cfo\n");
> +       }
> +
> +mivr_handler_out:
> +       enable_irq(priv->irq_nums[MT6370_IRQ_MIVR]);
> +       pm_relax(priv->dev);
> +}
> +
> +static void mt6370_chg_pwr_rdy_check(struct mt6370_priv *priv)
> +{
> +       int ret;
> +       unsigned int 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) {
> +               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) {
> +               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 = !!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;
> +       unsigned int chg_stat;
> +       union power_supply_propval online;
> +
> +       ret = power_supply_get_property(priv->psy, POWER_SUPPLY_PROP_ONLINE,
> +                                       &online);
> +       if (ret) {
> +               dev_err(priv->dev, "Failed to get online status\n");
> +               return ret;
> +       }
> +
> +       if (!online.intval) {
> +               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
> +               return 0;
> +       }
> +
> +       ret = mt6370_chg_field_get(priv, F_CHG_STAT, &chg_stat);
> +       if (ret)
> +               return ret;
> +
> +       switch (chg_stat) {
> +       case MT6370_CHG_STAT_READY:
> +       case MT6370_CHG_STAT_FAULT:
> +               val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
> +               return ret;
> +       case MT6370_CHG_STAT_CHARGE_IN_PROGRESS:
> +               val->intval = POWER_SUPPLY_STATUS_CHARGING;
> +               return ret;
> +       case MT6370_CHG_STAT_DONE:
> +               val->intval = POWER_SUPPLY_STATUS_FULL;
> +               return ret;
> +       default:
> +               val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
> +               return ret;
> +       }
> +}
> +
> +static int mt6370_chg_get_charge_type(struct mt6370_priv *priv,
> +                                     union power_supply_propval *val)
> +{
> +       int type, ret;
> +       unsigned int chg_stat, vbat_lvl;
> +
> +       ret = mt6370_chg_field_get(priv, F_CHG_STAT, &chg_stat);
> +       if (ret)
> +               return ret;
> +
> +       ret = mt6370_chg_field_get(priv, F_VBAT_LVL, &vbat_lvl);
> +       if (ret)
> +               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_set_online(struct mt6370_priv *priv,
> +                                const union power_supply_propval *val)
> +{
> +       bool pwr_rdy = !!val->intval;
> +
> +       mutex_lock(&priv->attach_lock);
> +       if (pwr_rdy == !!priv->attach) {
> +               dev_err(priv->dev, "pwr_rdy is same(%d)\n", pwr_rdy);
> +               mutex_unlock(&priv->attach_lock);
> +               return 0;
> +       }
> +
> +       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_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);
> +
> +       switch (psp) {
> +       case POWER_SUPPLY_PROP_ONLINE:
> +               return mt6370_chg_get_online(priv, val);
> +       case POWER_SUPPLY_PROP_STATUS:
> +               return mt6370_chg_get_status(priv, val);
> +       case POWER_SUPPLY_PROP_CHARGE_TYPE:
> +               return mt6370_chg_get_charge_type(priv, val);
> +       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
> +               return mt6370_chg_field_get(priv, F_ICHG, &val->intval);
> +       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
> +               val->intval = linear_range_get_max_value(&mt6370_chg_ranges[MT6370_RANGE_F_ICHG]);
> +               return 0;
> +       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
> +               return mt6370_chg_field_get(priv, F_VOREG, &val->intval);
> +       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
> +               val->intval = linear_range_get_max_value(&mt6370_chg_ranges[MT6370_RANGE_F_VOREG]);
> +               return 0;
> +       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> +               return mt6370_chg_field_get(priv, F_IAICR, &val->intval);
> +       case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
> +               return mt6370_chg_field_get(priv, F_VMIVR, &val->intval);
> +       case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
> +               return mt6370_chg_field_get(priv, F_IPREC, &val->intval);
> +       case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
> +               return mt6370_chg_field_get(priv, F_IEOC, &val->intval);
> +       case POWER_SUPPLY_PROP_USB_TYPE:
> +               val->intval = priv->psy_usb_type;
> +               return 0;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +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);
> +
> +       switch (psp) {
> +       case POWER_SUPPLY_PROP_ONLINE:
> +               return mt6370_chg_set_online(priv, val);
> +       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
> +               return mt6370_chg_field_set(priv, F_ICHG, val->intval);
> +       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
> +               return mt6370_chg_field_set(priv, F_VOREG, val->intval);
> +       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> +               return mt6370_chg_field_set(priv, F_IAICR, val->intval);
> +       case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
> +               return mt6370_chg_field_set(priv, F_VMIVR, val->intval);
> +       case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
> +               return mt6370_chg_field_set(priv, F_IPREC, val->intval);
> +       case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
> +               return mt6370_chg_field_set(priv, F_IEOC, val->intval);
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +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_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 const struct power_supply_desc mt6370_chg_psy_desc = {
> +       .name = "mt6370-charger",
> +       .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 u32 mt6370_chg_otg_oc_ma[] = {
> +       500000, 700000, 1100000, 1300000, 1800000, 2100000, 2400000,
> +};
> +
> +static const struct regulator_desc mt6370_chg_otg_rdesc = {
> +       .of_match = "usb-otg-vbus-regulator",
> +       .of_parse_cb = mt6370_chg_otg_of_parse_cb,
> +       .name = "mt6370-usb-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]))
> +                       return dev_err_probe(priv->dev,
> +                                       PTR_ERR(priv->rmap_fields[i]),
> +                                       "Failed to allocate regmapfield[%s]\n",
> +                                       fds[i].name);
> +       }
> +
> +       return 0;
> +}
> +
> +static int mt6370_chg_init_setting(struct mt6370_priv *priv)
> +{
> +       int ret;
> +
> +       /* Disable usb_chg_en */
> +       ret = mt6370_chg_field_set(priv, F_USBCHGEN, 0);
> +       if (ret) {
> +               dev_err(priv->dev, "Failed to disable usb_chg_en\n");
> +               return ret;
> +       }
> +
> +       /* ICHG/IEOC Workaround, ICHG can not be set less than 900mA */
> +       ret = mt6370_chg_field_set(priv, F_ICHG, 900000);
> +       if (ret) {
> +               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) {
> +               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 PTR_ERR_OR_ZERO(priv->rdev);
> +}
> +
> +static int mt6370_chg_init_psy(struct mt6370_priv *priv)
> +{
> +       struct power_supply_config cfg = {
> +               .drv_data = priv,
> +               .of_node = dev_of_node(priv->dev),
> +       };
> +
> +       priv->psy = devm_power_supply_register(priv->dev, &mt6370_chg_psy_desc,
> +                                              &cfg);
> +
> +       return PTR_ERR_OR_ZERO(priv->psy);
> +}
> +
> +static void mt6370_chg_destroy_attach_lock(void *data)
> +{
> +       struct mutex *attach_lock = data;
> +
> +       mutex_destroy(attach_lock);
> +}
> +
> +static void mt6370_chg_destroy_wq(void *data)
> +{
> +       struct workqueue_struct *wq = data;
> +
> +       flush_workqueue(wq);
> +       destroy_workqueue(wq);
> +}
> +
> +static void mt6370_chg_cancel_mivr_dwork(void *data)
> +{
> +       struct delayed_work *mivr_dwork = data;
> +
> +       cancel_delayed_work_sync(mivr_dwork);
> +}
> +
> +static irqreturn_t mt6370_attach_i_handler(int irq, void *data)
> +{
> +       struct mt6370_priv *priv = data;
> +       unsigned int otg_en;
> +       int ret;
> +
> +       /* Check in OTG mode or not */
> +       ret = mt6370_chg_field_get(priv, F_BOOST_STAT, &otg_en);
> +       if (ret) {
> +               dev_err(priv->dev, "Failed to get OTG state\n");
> +               return IRQ_NONE;
> +       }
> +
> +       if (otg_en)
> +               return IRQ_HANDLED;
> +
> +       mutex_lock(&priv->attach_lock);
> +       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);
> +       disable_irq_nosync(priv->irq_nums[MT6370_IRQ_MIVR]);
> +       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)
> +                       return dev_err_probe(priv->dev, ret,
> +                                            "Failed to get irq %s\n",
> +                                            mt6370_chg_irqs[i].name);
> +
> +               priv->irq_nums[i] = 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)
> +                       return dev_err_probe(priv->dev, ret,
> +                                            "Failed to request irq %s\n",
> +                                            mt6370_chg_irqs[i].name);
> +       }
> +
> +       return 0;
> +}
> +
> +static int mt6370_chg_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct mt6370_priv *priv;
> +       int ret;
> +
> +       priv = devm_kzalloc(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)
> +               return dev_err_probe(dev, -ENODEV, "Failed to get regmap\n");
> +
> +       ret = mt6370_chg_init_rmap_fields(priv);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Failed to init regmap fields\n");
> +
> +       platform_set_drvdata(pdev, priv);
> +
> +       priv->iio_adcs = devm_iio_channel_get_all(priv->dev);
> +       if (IS_ERR(priv->iio_adcs))
> +               return dev_err_probe(dev, PTR_ERR(priv->iio_adcs),
> +                                    "Failed to get iio adc\n");
> +
> +       ret = mt6370_chg_init_otg_regulator(priv);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Failed to init OTG regulator\n");
> +
> +       ret = mt6370_chg_init_psy(priv);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Failed to init psy\n");
> +
> +       mutex_init(&priv->attach_lock);
> +       ret = devm_add_action_or_reset(dev, mt6370_chg_destroy_attach_lock,
> +                                      &priv->attach_lock);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Failed to init attach lock\n");
> +
> +       priv->attach = MT6370_ATTACH_STAT_DETACH;
> +
> +       priv->wq = create_singlethread_workqueue(dev_name(priv->dev));
> +       if (IS_ERR(priv->wq))
> +               return dev_err_probe(dev, PTR_ERR(priv->wq),
> +                                    "Failed to create workqueue\n");
> +
> +       ret = devm_add_action_or_reset(dev, mt6370_chg_destroy_wq, priv->wq);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Failed to init wq\n");
> +
> +       INIT_WORK(&priv->bc12_work, mt6370_chg_bc12_work_func);
> +       INIT_DELAYED_WORK(&priv->mivr_dwork, mt6370_chg_mivr_dwork_func);
> +       ret = devm_add_action_or_reset(dev, mt6370_chg_cancel_mivr_dwork,
> +                                      &priv->mivr_dwork);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "Failed to init mivr dwork\n");
> +
> +       ret = mt6370_chg_init_setting(priv);
> +       if (ret)
> +               return dev_err_probe(dev, ret,
> +                                    "Failed to init mt6370 charger setting\n");
> +
> +       ret = mt6370_chg_init_irq(priv);
> +       if (ret)
> +               return ret;
> +
> +       mt6370_chg_pwr_rdy_check(priv);
> +
> +       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,
> +       .driver = {
> +               .name = "mt6370-charger",
> +               .of_match_table = 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");
> --
> 2.7.4
>