162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * extcon-sm5502.c - Silicon Mitus SM5502 extcon drvier to support USB switches
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2014 Samsung Electronics Co., Ltd
662306a36Sopenharmony_ci * Author: Chanwoo Choi <cw00.choi@samsung.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/err.h>
1062306a36Sopenharmony_ci#include <linux/i2c.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/irqdomain.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/regmap.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/extcon-provider.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "extcon-sm5502.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define	DELAY_MS_DEFAULT		17000	/* unit: millisecond */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct muic_irq {
2562306a36Sopenharmony_ci	unsigned int irq;
2662306a36Sopenharmony_ci	const char *name;
2762306a36Sopenharmony_ci	unsigned int virq;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct reg_data {
3162306a36Sopenharmony_ci	u8 reg;
3262306a36Sopenharmony_ci	unsigned int val;
3362306a36Sopenharmony_ci	bool invert;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct sm5502_muic_info {
3762306a36Sopenharmony_ci	struct device *dev;
3862306a36Sopenharmony_ci	struct extcon_dev *edev;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	struct i2c_client *i2c;
4162306a36Sopenharmony_ci	struct regmap *regmap;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	const struct sm5502_type *type;
4462306a36Sopenharmony_ci	struct regmap_irq_chip_data *irq_data;
4562306a36Sopenharmony_ci	int irq;
4662306a36Sopenharmony_ci	bool irq_attach;
4762306a36Sopenharmony_ci	bool irq_detach;
4862306a36Sopenharmony_ci	struct work_struct irq_work;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	struct mutex mutex;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/*
5362306a36Sopenharmony_ci	 * Use delayed workqueue to detect cable state and then
5462306a36Sopenharmony_ci	 * notify cable state to notifiee/platform through uevent.
5562306a36Sopenharmony_ci	 * After completing the booting of platform, the extcon provider
5662306a36Sopenharmony_ci	 * driver should notify cable state to upper layer.
5762306a36Sopenharmony_ci	 */
5862306a36Sopenharmony_ci	struct delayed_work wq_detcable;
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct sm5502_type {
6262306a36Sopenharmony_ci	struct muic_irq *muic_irqs;
6362306a36Sopenharmony_ci	unsigned int num_muic_irqs;
6462306a36Sopenharmony_ci	const struct regmap_irq_chip *irq_chip;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	struct reg_data *reg_data;
6762306a36Sopenharmony_ci	unsigned int num_reg_data;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	unsigned int otg_dev_type1;
7062306a36Sopenharmony_ci	int (*parse_irq)(struct sm5502_muic_info *info, int irq_type);
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* Default value of SM5502 register to bring up MUIC device. */
7462306a36Sopenharmony_cistatic struct reg_data sm5502_reg_data[] = {
7562306a36Sopenharmony_ci	{
7662306a36Sopenharmony_ci		.reg = SM5502_REG_RESET,
7762306a36Sopenharmony_ci		.val = SM5502_REG_RESET_MASK,
7862306a36Sopenharmony_ci		.invert = true,
7962306a36Sopenharmony_ci	}, {
8062306a36Sopenharmony_ci		.reg = SM5502_REG_CONTROL,
8162306a36Sopenharmony_ci		.val = SM5502_REG_CONTROL_MASK_INT_MASK,
8262306a36Sopenharmony_ci		.invert = false,
8362306a36Sopenharmony_ci	}, {
8462306a36Sopenharmony_ci		.reg = SM5502_REG_INTMASK1,
8562306a36Sopenharmony_ci		.val = SM5502_REG_INTM1_KP_MASK
8662306a36Sopenharmony_ci			| SM5502_REG_INTM1_LKP_MASK
8762306a36Sopenharmony_ci			| SM5502_REG_INTM1_LKR_MASK,
8862306a36Sopenharmony_ci		.invert = true,
8962306a36Sopenharmony_ci	}, {
9062306a36Sopenharmony_ci		.reg = SM5502_REG_INTMASK2,
9162306a36Sopenharmony_ci		.val = SM5502_REG_INTM2_VBUS_DET_MASK
9262306a36Sopenharmony_ci			| SM5502_REG_INTM2_REV_ACCE_MASK
9362306a36Sopenharmony_ci			| SM5502_REG_INTM2_ADC_CHG_MASK
9462306a36Sopenharmony_ci			| SM5502_REG_INTM2_STUCK_KEY_MASK
9562306a36Sopenharmony_ci			| SM5502_REG_INTM2_STUCK_KEY_RCV_MASK
9662306a36Sopenharmony_ci			| SM5502_REG_INTM2_MHL_MASK,
9762306a36Sopenharmony_ci		.invert = true,
9862306a36Sopenharmony_ci	},
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/* Default value of SM5504 register to bring up MUIC device. */
10262306a36Sopenharmony_cistatic struct reg_data sm5504_reg_data[] = {
10362306a36Sopenharmony_ci	{
10462306a36Sopenharmony_ci		.reg = SM5502_REG_RESET,
10562306a36Sopenharmony_ci		.val = SM5502_REG_RESET_MASK,
10662306a36Sopenharmony_ci		.invert = true,
10762306a36Sopenharmony_ci	}, {
10862306a36Sopenharmony_ci		.reg = SM5502_REG_INTMASK1,
10962306a36Sopenharmony_ci		.val = SM5504_REG_INTM1_ATTACH_MASK
11062306a36Sopenharmony_ci			| SM5504_REG_INTM1_DETACH_MASK,
11162306a36Sopenharmony_ci		.invert = false,
11262306a36Sopenharmony_ci	}, {
11362306a36Sopenharmony_ci		.reg = SM5502_REG_INTMASK2,
11462306a36Sopenharmony_ci		.val = SM5504_REG_INTM2_RID_CHG_MASK
11562306a36Sopenharmony_ci			| SM5504_REG_INTM2_UVLO_MASK
11662306a36Sopenharmony_ci			| SM5504_REG_INTM2_POR_MASK,
11762306a36Sopenharmony_ci		.invert = true,
11862306a36Sopenharmony_ci	}, {
11962306a36Sopenharmony_ci		.reg = SM5502_REG_CONTROL,
12062306a36Sopenharmony_ci		.val = SM5502_REG_CONTROL_MANUAL_SW_MASK
12162306a36Sopenharmony_ci			| SM5504_REG_CONTROL_CHGTYP_MASK
12262306a36Sopenharmony_ci			| SM5504_REG_CONTROL_USBCHDEN_MASK
12362306a36Sopenharmony_ci			| SM5504_REG_CONTROL_ADC_EN_MASK,
12462306a36Sopenharmony_ci		.invert = true,
12562306a36Sopenharmony_ci	},
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/* List of detectable cables */
12962306a36Sopenharmony_cistatic const unsigned int sm5502_extcon_cable[] = {
13062306a36Sopenharmony_ci	EXTCON_USB,
13162306a36Sopenharmony_ci	EXTCON_USB_HOST,
13262306a36Sopenharmony_ci	EXTCON_CHG_USB_SDP,
13362306a36Sopenharmony_ci	EXTCON_CHG_USB_DCP,
13462306a36Sopenharmony_ci	EXTCON_NONE,
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/* Define supported accessory type */
13862306a36Sopenharmony_cienum sm5502_muic_acc_type {
13962306a36Sopenharmony_ci	SM5502_MUIC_ADC_GROUND = 0x0,
14062306a36Sopenharmony_ci	SM5502_MUIC_ADC_SEND_END_BUTTON,
14162306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S1_BUTTON,
14262306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S2_BUTTON,
14362306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S3_BUTTON,
14462306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S4_BUTTON,
14562306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S5_BUTTON,
14662306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S6_BUTTON,
14762306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S7_BUTTON,
14862306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S8_BUTTON,
14962306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S9_BUTTON,
15062306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S10_BUTTON,
15162306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S11_BUTTON,
15262306a36Sopenharmony_ci	SM5502_MUIC_ADC_REMOTE_S12_BUTTON,
15362306a36Sopenharmony_ci	SM5502_MUIC_ADC_RESERVED_ACC_1,
15462306a36Sopenharmony_ci	SM5502_MUIC_ADC_RESERVED_ACC_2,
15562306a36Sopenharmony_ci	SM5502_MUIC_ADC_RESERVED_ACC_3,
15662306a36Sopenharmony_ci	SM5502_MUIC_ADC_RESERVED_ACC_4,
15762306a36Sopenharmony_ci	SM5502_MUIC_ADC_RESERVED_ACC_5,
15862306a36Sopenharmony_ci	SM5502_MUIC_ADC_AUDIO_TYPE2,
15962306a36Sopenharmony_ci	SM5502_MUIC_ADC_PHONE_POWERED_DEV,
16062306a36Sopenharmony_ci	SM5502_MUIC_ADC_TTY_CONVERTER,
16162306a36Sopenharmony_ci	SM5502_MUIC_ADC_UART_CABLE,
16262306a36Sopenharmony_ci	SM5502_MUIC_ADC_TYPE1_CHARGER,
16362306a36Sopenharmony_ci	SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB,
16462306a36Sopenharmony_ci	SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB,
16562306a36Sopenharmony_ci	SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE,
16662306a36Sopenharmony_ci	SM5502_MUIC_ADC_TYPE2_CHARGER,
16762306a36Sopenharmony_ci	SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART,
16862306a36Sopenharmony_ci	SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART,
16962306a36Sopenharmony_ci	SM5502_MUIC_ADC_AUDIO_TYPE1,
17062306a36Sopenharmony_ci	SM5502_MUIC_ADC_OPEN = 0x1f,
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/*
17362306a36Sopenharmony_ci	 * The below accessories have same ADC value (0x1f or 0x1e).
17462306a36Sopenharmony_ci	 * So, Device type1 is used to separate specific accessory.
17562306a36Sopenharmony_ci	 */
17662306a36Sopenharmony_ci							/* |---------|--ADC| */
17762306a36Sopenharmony_ci							/* |    [7:5]|[4:0]| */
17862306a36Sopenharmony_ci	SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e,	/* |      001|11110| */
17962306a36Sopenharmony_ci	SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END = 0x5e,	/* |      010|11110| */
18062306a36Sopenharmony_ci							/* |Dev Type1|--ADC| */
18162306a36Sopenharmony_ci	SM5502_MUIC_ADC_GROUND_USB_OTG = 0x80,		/* |      100|00000| */
18262306a36Sopenharmony_ci	SM5502_MUIC_ADC_OPEN_USB = 0x5f,		/* |      010|11111| */
18362306a36Sopenharmony_ci	SM5502_MUIC_ADC_OPEN_TA = 0xdf,			/* |      110|11111| */
18462306a36Sopenharmony_ci	SM5502_MUIC_ADC_OPEN_USB_OTG = 0xff,		/* |      111|11111| */
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/* List of supported interrupt for SM5502 */
18862306a36Sopenharmony_cistatic struct muic_irq sm5502_muic_irqs[] = {
18962306a36Sopenharmony_ci	{ SM5502_IRQ_INT1_ATTACH,	"muic-attach" },
19062306a36Sopenharmony_ci	{ SM5502_IRQ_INT1_DETACH,	"muic-detach" },
19162306a36Sopenharmony_ci	{ SM5502_IRQ_INT1_KP,		"muic-kp" },
19262306a36Sopenharmony_ci	{ SM5502_IRQ_INT1_LKP,		"muic-lkp" },
19362306a36Sopenharmony_ci	{ SM5502_IRQ_INT1_LKR,		"muic-lkr" },
19462306a36Sopenharmony_ci	{ SM5502_IRQ_INT1_OVP_EVENT,	"muic-ovp-event" },
19562306a36Sopenharmony_ci	{ SM5502_IRQ_INT1_OCP_EVENT,	"muic-ocp-event" },
19662306a36Sopenharmony_ci	{ SM5502_IRQ_INT1_OVP_OCP_DIS,	"muic-ovp-ocp-dis" },
19762306a36Sopenharmony_ci	{ SM5502_IRQ_INT2_VBUS_DET,	"muic-vbus-det" },
19862306a36Sopenharmony_ci	{ SM5502_IRQ_INT2_REV_ACCE,	"muic-rev-acce" },
19962306a36Sopenharmony_ci	{ SM5502_IRQ_INT2_ADC_CHG,	"muic-adc-chg" },
20062306a36Sopenharmony_ci	{ SM5502_IRQ_INT2_STUCK_KEY,	"muic-stuck-key" },
20162306a36Sopenharmony_ci	{ SM5502_IRQ_INT2_STUCK_KEY_RCV, "muic-stuck-key-rcv" },
20262306a36Sopenharmony_ci	{ SM5502_IRQ_INT2_MHL,		"muic-mhl" },
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/* Define interrupt list of SM5502 to register regmap_irq */
20662306a36Sopenharmony_cistatic const struct regmap_irq sm5502_irqs[] = {
20762306a36Sopenharmony_ci	/* INT1 interrupts */
20862306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_ATTACH_MASK, },
20962306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_DETACH_MASK, },
21062306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_KP_MASK, },
21162306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKP_MASK, },
21262306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKR_MASK, },
21362306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_EVENT_MASK, },
21462306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_OCP_EVENT_MASK, },
21562306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_OCP_DIS_MASK, },
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* INT2 interrupts */
21862306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_VBUS_DET_MASK,},
21962306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_REV_ACCE_MASK, },
22062306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_ADC_CHG_MASK, },
22162306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_MASK, },
22262306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK, },
22362306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_MHL_MASK, },
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic const struct regmap_irq_chip sm5502_muic_irq_chip = {
22762306a36Sopenharmony_ci	.name			= "sm5502",
22862306a36Sopenharmony_ci	.status_base		= SM5502_REG_INT1,
22962306a36Sopenharmony_ci	.mask_base		= SM5502_REG_INTMASK1,
23062306a36Sopenharmony_ci	.num_regs		= 2,
23162306a36Sopenharmony_ci	.irqs			= sm5502_irqs,
23262306a36Sopenharmony_ci	.num_irqs		= ARRAY_SIZE(sm5502_irqs),
23362306a36Sopenharmony_ci};
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci/* List of supported interrupt for SM5504 */
23662306a36Sopenharmony_cistatic struct muic_irq sm5504_muic_irqs[] = {
23762306a36Sopenharmony_ci	{ SM5504_IRQ_INT1_ATTACH,	"muic-attach" },
23862306a36Sopenharmony_ci	{ SM5504_IRQ_INT1_DETACH,	"muic-detach" },
23962306a36Sopenharmony_ci	{ SM5504_IRQ_INT1_CHG_DET,	"muic-chg-det" },
24062306a36Sopenharmony_ci	{ SM5504_IRQ_INT1_DCD_OUT,	"muic-dcd-out" },
24162306a36Sopenharmony_ci	{ SM5504_IRQ_INT1_OVP_EVENT,	"muic-ovp-event" },
24262306a36Sopenharmony_ci	{ SM5504_IRQ_INT1_CONNECT,	"muic-connect" },
24362306a36Sopenharmony_ci	{ SM5504_IRQ_INT1_ADC_CHG,	"muic-adc-chg" },
24462306a36Sopenharmony_ci	{ SM5504_IRQ_INT2_RID_CHG,	"muic-rid-chg" },
24562306a36Sopenharmony_ci	{ SM5504_IRQ_INT2_UVLO,		"muic-uvlo" },
24662306a36Sopenharmony_ci	{ SM5504_IRQ_INT2_POR,		"muic-por" },
24762306a36Sopenharmony_ci	{ SM5504_IRQ_INT2_OVP_FET,	"muic-ovp-fet" },
24862306a36Sopenharmony_ci	{ SM5504_IRQ_INT2_OCP_LATCH,	"muic-ocp-latch" },
24962306a36Sopenharmony_ci	{ SM5504_IRQ_INT2_OCP_EVENT,	"muic-ocp-event" },
25062306a36Sopenharmony_ci	{ SM5504_IRQ_INT2_OVP_OCP_EVENT, "muic-ovp-ocp-event" },
25162306a36Sopenharmony_ci};
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/* Define interrupt list of SM5504 to register regmap_irq */
25462306a36Sopenharmony_cistatic const struct regmap_irq sm5504_irqs[] = {
25562306a36Sopenharmony_ci	/* INT1 interrupts */
25662306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_ATTACH_MASK, },
25762306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_DETACH_MASK, },
25862306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_CHG_DET_MASK, },
25962306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_DCD_OUT_MASK, },
26062306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_OVP_MASK, },
26162306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_CONNECT_MASK, },
26262306a36Sopenharmony_ci	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_ADC_CHG_MASK, },
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* INT2 interrupts */
26562306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_RID_CHG_MASK,},
26662306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_UVLO_MASK, },
26762306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_POR_MASK, },
26862306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_FET_MASK, },
26962306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_LATCH_MASK, },
27062306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_EVENT_MASK, },
27162306a36Sopenharmony_ci	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK, },
27262306a36Sopenharmony_ci};
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic const struct regmap_irq_chip sm5504_muic_irq_chip = {
27562306a36Sopenharmony_ci	.name			= "sm5504",
27662306a36Sopenharmony_ci	.status_base		= SM5502_REG_INT1,
27762306a36Sopenharmony_ci	.mask_base		= SM5502_REG_INTMASK1,
27862306a36Sopenharmony_ci	.num_regs		= 2,
27962306a36Sopenharmony_ci	.irqs			= sm5504_irqs,
28062306a36Sopenharmony_ci	.num_irqs		= ARRAY_SIZE(sm5504_irqs),
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/* Define regmap configuration of SM5502 for I2C communication  */
28462306a36Sopenharmony_cistatic bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	switch (reg) {
28762306a36Sopenharmony_ci	case SM5502_REG_INTMASK1:
28862306a36Sopenharmony_ci	case SM5502_REG_INTMASK2:
28962306a36Sopenharmony_ci		return true;
29062306a36Sopenharmony_ci	default:
29162306a36Sopenharmony_ci		break;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci	return false;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic const struct regmap_config sm5502_muic_regmap_config = {
29762306a36Sopenharmony_ci	.reg_bits	= 8,
29862306a36Sopenharmony_ci	.val_bits	= 8,
29962306a36Sopenharmony_ci	.volatile_reg	= sm5502_muic_volatile_reg,
30062306a36Sopenharmony_ci	.max_register	= SM5502_REG_END,
30162306a36Sopenharmony_ci};
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/* Change DM_CON/DP_CON/VBUSIN switch according to cable type */
30462306a36Sopenharmony_cistatic int sm5502_muic_set_path(struct sm5502_muic_info *info,
30562306a36Sopenharmony_ci				unsigned int con_sw, unsigned int vbus_sw,
30662306a36Sopenharmony_ci				bool attached)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	int ret;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (!attached) {
31162306a36Sopenharmony_ci		con_sw	= DM_DP_SWITCH_OPEN;
31262306a36Sopenharmony_ci		vbus_sw	= VBUSIN_SWITCH_OPEN;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	switch (con_sw) {
31662306a36Sopenharmony_ci	case DM_DP_SWITCH_OPEN:
31762306a36Sopenharmony_ci	case DM_DP_SWITCH_USB:
31862306a36Sopenharmony_ci	case DM_DP_SWITCH_AUDIO:
31962306a36Sopenharmony_ci	case DM_DP_SWITCH_UART:
32062306a36Sopenharmony_ci		ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,
32162306a36Sopenharmony_ci					 SM5502_REG_MANUAL_SW1_DP_MASK |
32262306a36Sopenharmony_ci					 SM5502_REG_MANUAL_SW1_DM_MASK,
32362306a36Sopenharmony_ci					 con_sw);
32462306a36Sopenharmony_ci		if (ret < 0) {
32562306a36Sopenharmony_ci			dev_err(info->dev,
32662306a36Sopenharmony_ci				"cannot update DM_CON/DP_CON switch\n");
32762306a36Sopenharmony_ci			return ret;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci		break;
33062306a36Sopenharmony_ci	default:
33162306a36Sopenharmony_ci		dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n",
33262306a36Sopenharmony_ci				con_sw);
33362306a36Sopenharmony_ci		return -EINVAL;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	switch (vbus_sw) {
33762306a36Sopenharmony_ci	case VBUSIN_SWITCH_OPEN:
33862306a36Sopenharmony_ci	case VBUSIN_SWITCH_VBUSOUT:
33962306a36Sopenharmony_ci	case VBUSIN_SWITCH_MIC:
34062306a36Sopenharmony_ci	case VBUSIN_SWITCH_VBUSOUT_WITH_USB:
34162306a36Sopenharmony_ci		ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,
34262306a36Sopenharmony_ci					 SM5502_REG_MANUAL_SW1_VBUSIN_MASK,
34362306a36Sopenharmony_ci					 vbus_sw);
34462306a36Sopenharmony_ci		if (ret < 0) {
34562306a36Sopenharmony_ci			dev_err(info->dev,
34662306a36Sopenharmony_ci				"cannot update VBUSIN switch\n");
34762306a36Sopenharmony_ci			return ret;
34862306a36Sopenharmony_ci		}
34962306a36Sopenharmony_ci		break;
35062306a36Sopenharmony_ci	default:
35162306a36Sopenharmony_ci		dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw);
35262306a36Sopenharmony_ci		return -EINVAL;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci/* Return cable type of attached or detached accessories */
35962306a36Sopenharmony_cistatic unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	unsigned int cable_type, adc, dev_type1;
36262306a36Sopenharmony_ci	int ret;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Read ADC value according to external cable or button */
36562306a36Sopenharmony_ci	ret = regmap_read(info->regmap, SM5502_REG_ADC, &adc);
36662306a36Sopenharmony_ci	if (ret) {
36762306a36Sopenharmony_ci		dev_err(info->dev, "failed to read ADC register\n");
36862306a36Sopenharmony_ci		return ret;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/*
37262306a36Sopenharmony_ci	 * If ADC is SM5502_MUIC_ADC_GROUND(0x0), external cable hasn't
37362306a36Sopenharmony_ci	 * connected with to MUIC device.
37462306a36Sopenharmony_ci	 */
37562306a36Sopenharmony_ci	cable_type = adc & SM5502_REG_ADC_MASK;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	switch (cable_type) {
37862306a36Sopenharmony_ci	case SM5502_MUIC_ADC_GROUND:
37962306a36Sopenharmony_ci		ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,
38062306a36Sopenharmony_ci				  &dev_type1);
38162306a36Sopenharmony_ci		if (ret) {
38262306a36Sopenharmony_ci			dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");
38362306a36Sopenharmony_ci			return ret;
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		if (dev_type1 == info->type->otg_dev_type1) {
38762306a36Sopenharmony_ci			cable_type = SM5502_MUIC_ADC_GROUND_USB_OTG;
38862306a36Sopenharmony_ci		} else {
38962306a36Sopenharmony_ci			dev_dbg(info->dev,
39062306a36Sopenharmony_ci				"cannot identify the cable type: adc(0x%x), dev_type1(0x%x)\n",
39162306a36Sopenharmony_ci				adc, dev_type1);
39262306a36Sopenharmony_ci			return -EINVAL;
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci		break;
39562306a36Sopenharmony_ci	case SM5502_MUIC_ADC_SEND_END_BUTTON:
39662306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S1_BUTTON:
39762306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S2_BUTTON:
39862306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S3_BUTTON:
39962306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S4_BUTTON:
40062306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S5_BUTTON:
40162306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S6_BUTTON:
40262306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S7_BUTTON:
40362306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S8_BUTTON:
40462306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S9_BUTTON:
40562306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S10_BUTTON:
40662306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S11_BUTTON:
40762306a36Sopenharmony_ci	case SM5502_MUIC_ADC_REMOTE_S12_BUTTON:
40862306a36Sopenharmony_ci	case SM5502_MUIC_ADC_RESERVED_ACC_1:
40962306a36Sopenharmony_ci	case SM5502_MUIC_ADC_RESERVED_ACC_2:
41062306a36Sopenharmony_ci	case SM5502_MUIC_ADC_RESERVED_ACC_3:
41162306a36Sopenharmony_ci	case SM5502_MUIC_ADC_RESERVED_ACC_4:
41262306a36Sopenharmony_ci	case SM5502_MUIC_ADC_RESERVED_ACC_5:
41362306a36Sopenharmony_ci	case SM5502_MUIC_ADC_AUDIO_TYPE2:
41462306a36Sopenharmony_ci	case SM5502_MUIC_ADC_PHONE_POWERED_DEV:
41562306a36Sopenharmony_ci	case SM5502_MUIC_ADC_TTY_CONVERTER:
41662306a36Sopenharmony_ci	case SM5502_MUIC_ADC_UART_CABLE:
41762306a36Sopenharmony_ci	case SM5502_MUIC_ADC_TYPE1_CHARGER:
41862306a36Sopenharmony_ci	case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB:
41962306a36Sopenharmony_ci	case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB:
42062306a36Sopenharmony_ci	case SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE:
42162306a36Sopenharmony_ci	case SM5502_MUIC_ADC_TYPE2_CHARGER:
42262306a36Sopenharmony_ci	case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART:
42362306a36Sopenharmony_ci	case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART:
42462306a36Sopenharmony_ci		break;
42562306a36Sopenharmony_ci	case SM5502_MUIC_ADC_AUDIO_TYPE1:
42662306a36Sopenharmony_ci		/*
42762306a36Sopenharmony_ci		 * Check whether cable type is
42862306a36Sopenharmony_ci		 * SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE
42962306a36Sopenharmony_ci		 * or SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END
43062306a36Sopenharmony_ci		 * by using Button event.
43162306a36Sopenharmony_ci		 */
43262306a36Sopenharmony_ci		break;
43362306a36Sopenharmony_ci	case SM5502_MUIC_ADC_OPEN:
43462306a36Sopenharmony_ci		ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,
43562306a36Sopenharmony_ci				  &dev_type1);
43662306a36Sopenharmony_ci		if (ret) {
43762306a36Sopenharmony_ci			dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");
43862306a36Sopenharmony_ci			return ret;
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		if (dev_type1 == info->type->otg_dev_type1) {
44262306a36Sopenharmony_ci			cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG;
44362306a36Sopenharmony_ci			break;
44462306a36Sopenharmony_ci		}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci		switch (dev_type1) {
44762306a36Sopenharmony_ci		case SM5502_REG_DEV_TYPE1_USB_SDP_MASK:
44862306a36Sopenharmony_ci			cable_type = SM5502_MUIC_ADC_OPEN_USB;
44962306a36Sopenharmony_ci			break;
45062306a36Sopenharmony_ci		case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK:
45162306a36Sopenharmony_ci			cable_type = SM5502_MUIC_ADC_OPEN_TA;
45262306a36Sopenharmony_ci			break;
45362306a36Sopenharmony_ci		default:
45462306a36Sopenharmony_ci			dev_dbg(info->dev,
45562306a36Sopenharmony_ci				"cannot identify the cable type: adc(0x%x)\n",
45662306a36Sopenharmony_ci				adc);
45762306a36Sopenharmony_ci			return -EINVAL;
45862306a36Sopenharmony_ci		}
45962306a36Sopenharmony_ci		break;
46062306a36Sopenharmony_ci	default:
46162306a36Sopenharmony_ci		dev_err(info->dev,
46262306a36Sopenharmony_ci			"failed to identify the cable type: adc(0x%x)\n", adc);
46362306a36Sopenharmony_ci		return -EINVAL;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	return cable_type;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
47062306a36Sopenharmony_ci				     bool attached)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND;
47362306a36Sopenharmony_ci	unsigned int cable_type = SM5502_MUIC_ADC_GROUND;
47462306a36Sopenharmony_ci	unsigned int con_sw = DM_DP_SWITCH_OPEN;
47562306a36Sopenharmony_ci	unsigned int vbus_sw = VBUSIN_SWITCH_OPEN;
47662306a36Sopenharmony_ci	unsigned int id;
47762306a36Sopenharmony_ci	int ret;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* Get the type of attached or detached cable */
48062306a36Sopenharmony_ci	if (attached)
48162306a36Sopenharmony_ci		cable_type = sm5502_muic_get_cable_type(info);
48262306a36Sopenharmony_ci	else
48362306a36Sopenharmony_ci		cable_type = prev_cable_type;
48462306a36Sopenharmony_ci	prev_cable_type = cable_type;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	switch (cable_type) {
48762306a36Sopenharmony_ci	case SM5502_MUIC_ADC_OPEN_USB:
48862306a36Sopenharmony_ci		id	= EXTCON_USB;
48962306a36Sopenharmony_ci		con_sw	= DM_DP_SWITCH_USB;
49062306a36Sopenharmony_ci		vbus_sw	= VBUSIN_SWITCH_VBUSOUT_WITH_USB;
49162306a36Sopenharmony_ci		break;
49262306a36Sopenharmony_ci	case SM5502_MUIC_ADC_OPEN_TA:
49362306a36Sopenharmony_ci		id	= EXTCON_CHG_USB_DCP;
49462306a36Sopenharmony_ci		con_sw	= DM_DP_SWITCH_OPEN;
49562306a36Sopenharmony_ci		vbus_sw	= VBUSIN_SWITCH_VBUSOUT;
49662306a36Sopenharmony_ci		break;
49762306a36Sopenharmony_ci	case SM5502_MUIC_ADC_GROUND_USB_OTG:
49862306a36Sopenharmony_ci	case SM5502_MUIC_ADC_OPEN_USB_OTG:
49962306a36Sopenharmony_ci		id	= EXTCON_USB_HOST;
50062306a36Sopenharmony_ci		con_sw	= DM_DP_SWITCH_USB;
50162306a36Sopenharmony_ci		vbus_sw	= VBUSIN_SWITCH_OPEN;
50262306a36Sopenharmony_ci		break;
50362306a36Sopenharmony_ci	default:
50462306a36Sopenharmony_ci		dev_dbg(info->dev,
50562306a36Sopenharmony_ci			"cannot handle this cable_type (0x%x)\n", cable_type);
50662306a36Sopenharmony_ci		return 0;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */
51062306a36Sopenharmony_ci	ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached);
51162306a36Sopenharmony_ci	if (ret < 0)
51262306a36Sopenharmony_ci		return ret;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* Change the state of external accessory */
51562306a36Sopenharmony_ci	extcon_set_state_sync(info->edev, id, attached);
51662306a36Sopenharmony_ci	if (id == EXTCON_USB)
51762306a36Sopenharmony_ci		extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
51862306a36Sopenharmony_ci					attached);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	return 0;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic void sm5502_muic_irq_work(struct work_struct *work)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct sm5502_muic_info *info = container_of(work,
52662306a36Sopenharmony_ci			struct sm5502_muic_info, irq_work);
52762306a36Sopenharmony_ci	int ret = 0;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (!info->edev)
53062306a36Sopenharmony_ci		return;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	mutex_lock(&info->mutex);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* Detect attached or detached cables */
53562306a36Sopenharmony_ci	if (info->irq_attach) {
53662306a36Sopenharmony_ci		ret = sm5502_muic_cable_handler(info, true);
53762306a36Sopenharmony_ci		info->irq_attach = false;
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci	if (info->irq_detach) {
54062306a36Sopenharmony_ci		ret = sm5502_muic_cable_handler(info, false);
54162306a36Sopenharmony_ci		info->irq_detach = false;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (ret < 0)
54562306a36Sopenharmony_ci		dev_err(info->dev, "failed to handle MUIC interrupt\n");
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	mutex_unlock(&info->mutex);
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci/*
55162306a36Sopenharmony_ci * Sets irq_attach or irq_detach in sm5502_muic_info and returns 0.
55262306a36Sopenharmony_ci * Returns -ESRCH if irq_type does not match registered IRQ for this dev type.
55362306a36Sopenharmony_ci */
55462306a36Sopenharmony_cistatic int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	switch (irq_type) {
55762306a36Sopenharmony_ci	case SM5502_IRQ_INT1_ATTACH:
55862306a36Sopenharmony_ci		info->irq_attach = true;
55962306a36Sopenharmony_ci		break;
56062306a36Sopenharmony_ci	case SM5502_IRQ_INT1_DETACH:
56162306a36Sopenharmony_ci		info->irq_detach = true;
56262306a36Sopenharmony_ci		break;
56362306a36Sopenharmony_ci	case SM5502_IRQ_INT1_KP:
56462306a36Sopenharmony_ci	case SM5502_IRQ_INT1_LKP:
56562306a36Sopenharmony_ci	case SM5502_IRQ_INT1_LKR:
56662306a36Sopenharmony_ci	case SM5502_IRQ_INT1_OVP_EVENT:
56762306a36Sopenharmony_ci	case SM5502_IRQ_INT1_OCP_EVENT:
56862306a36Sopenharmony_ci	case SM5502_IRQ_INT1_OVP_OCP_DIS:
56962306a36Sopenharmony_ci	case SM5502_IRQ_INT2_VBUS_DET:
57062306a36Sopenharmony_ci	case SM5502_IRQ_INT2_REV_ACCE:
57162306a36Sopenharmony_ci	case SM5502_IRQ_INT2_ADC_CHG:
57262306a36Sopenharmony_ci	case SM5502_IRQ_INT2_STUCK_KEY:
57362306a36Sopenharmony_ci	case SM5502_IRQ_INT2_STUCK_KEY_RCV:
57462306a36Sopenharmony_ci	case SM5502_IRQ_INT2_MHL:
57562306a36Sopenharmony_ci	default:
57662306a36Sopenharmony_ci		break;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return 0;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic int sm5504_parse_irq(struct sm5502_muic_info *info, int irq_type)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	switch (irq_type) {
58562306a36Sopenharmony_ci	case SM5504_IRQ_INT1_ATTACH:
58662306a36Sopenharmony_ci		info->irq_attach = true;
58762306a36Sopenharmony_ci		break;
58862306a36Sopenharmony_ci	case SM5504_IRQ_INT1_DETACH:
58962306a36Sopenharmony_ci		info->irq_detach = true;
59062306a36Sopenharmony_ci		break;
59162306a36Sopenharmony_ci	case SM5504_IRQ_INT1_CHG_DET:
59262306a36Sopenharmony_ci	case SM5504_IRQ_INT1_DCD_OUT:
59362306a36Sopenharmony_ci	case SM5504_IRQ_INT1_OVP_EVENT:
59462306a36Sopenharmony_ci	case SM5504_IRQ_INT1_CONNECT:
59562306a36Sopenharmony_ci	case SM5504_IRQ_INT1_ADC_CHG:
59662306a36Sopenharmony_ci	case SM5504_IRQ_INT2_RID_CHG:
59762306a36Sopenharmony_ci	case SM5504_IRQ_INT2_UVLO:
59862306a36Sopenharmony_ci	case SM5504_IRQ_INT2_POR:
59962306a36Sopenharmony_ci	case SM5504_IRQ_INT2_OVP_FET:
60062306a36Sopenharmony_ci	case SM5504_IRQ_INT2_OCP_LATCH:
60162306a36Sopenharmony_ci	case SM5504_IRQ_INT2_OCP_EVENT:
60262306a36Sopenharmony_ci	case SM5504_IRQ_INT2_OVP_OCP_EVENT:
60362306a36Sopenharmony_ci	default:
60462306a36Sopenharmony_ci		break;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	return 0;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic irqreturn_t sm5502_muic_irq_handler(int irq, void *data)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct sm5502_muic_info *info = data;
61362306a36Sopenharmony_ci	int i, irq_type = -1, ret;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	for (i = 0; i < info->type->num_muic_irqs; i++)
61662306a36Sopenharmony_ci		if (irq == info->type->muic_irqs[i].virq)
61762306a36Sopenharmony_ci			irq_type = info->type->muic_irqs[i].irq;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	ret = info->type->parse_irq(info, irq_type);
62062306a36Sopenharmony_ci	if (ret < 0) {
62162306a36Sopenharmony_ci		dev_warn(info->dev, "cannot handle is interrupt:%d\n",
62262306a36Sopenharmony_ci				    irq_type);
62362306a36Sopenharmony_ci		return IRQ_HANDLED;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci	schedule_work(&info->irq_work);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	return IRQ_HANDLED;
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic void sm5502_muic_detect_cable_wq(struct work_struct *work)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	struct sm5502_muic_info *info = container_of(to_delayed_work(work),
63362306a36Sopenharmony_ci				struct sm5502_muic_info, wq_detcable);
63462306a36Sopenharmony_ci	int ret;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/* Notify the state of connector cable or not  */
63762306a36Sopenharmony_ci	ret = sm5502_muic_cable_handler(info, true);
63862306a36Sopenharmony_ci	if (ret < 0)
63962306a36Sopenharmony_ci		dev_warn(info->dev, "failed to detect cable state\n");
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic void sm5502_init_dev_type(struct sm5502_muic_info *info)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	unsigned int reg_data, vendor_id, version_id;
64562306a36Sopenharmony_ci	int i, ret;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/* To test I2C, Print version_id and vendor_id of SM5502 */
64862306a36Sopenharmony_ci	ret = regmap_read(info->regmap, SM5502_REG_DEVICE_ID, &reg_data);
64962306a36Sopenharmony_ci	if (ret) {
65062306a36Sopenharmony_ci		dev_err(info->dev,
65162306a36Sopenharmony_ci			"failed to read DEVICE_ID register: %d\n", ret);
65262306a36Sopenharmony_ci		return;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	vendor_id = ((reg_data & SM5502_REG_DEVICE_ID_VENDOR_MASK) >>
65662306a36Sopenharmony_ci				SM5502_REG_DEVICE_ID_VENDOR_SHIFT);
65762306a36Sopenharmony_ci	version_id = ((reg_data & SM5502_REG_DEVICE_ID_VERSION_MASK) >>
65862306a36Sopenharmony_ci				SM5502_REG_DEVICE_ID_VERSION_SHIFT);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",
66162306a36Sopenharmony_ci			    version_id, vendor_id);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* Initiazle the register of SM5502 device to bring-up */
66462306a36Sopenharmony_ci	for (i = 0; i < info->type->num_reg_data; i++) {
66562306a36Sopenharmony_ci		unsigned int val = 0;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		if (!info->type->reg_data[i].invert)
66862306a36Sopenharmony_ci			val |= ~info->type->reg_data[i].val;
66962306a36Sopenharmony_ci		else
67062306a36Sopenharmony_ci			val = info->type->reg_data[i].val;
67162306a36Sopenharmony_ci		regmap_write(info->regmap, info->type->reg_data[i].reg, val);
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic int sm5022_muic_i2c_probe(struct i2c_client *i2c)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct device_node *np = i2c->dev.of_node;
67862306a36Sopenharmony_ci	struct sm5502_muic_info *info;
67962306a36Sopenharmony_ci	int i, ret, irq_flags;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (!np)
68262306a36Sopenharmony_ci		return -EINVAL;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL);
68562306a36Sopenharmony_ci	if (!info)
68662306a36Sopenharmony_ci		return -ENOMEM;
68762306a36Sopenharmony_ci	i2c_set_clientdata(i2c, info);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	info->dev = &i2c->dev;
69062306a36Sopenharmony_ci	info->i2c = i2c;
69162306a36Sopenharmony_ci	info->irq = i2c->irq;
69262306a36Sopenharmony_ci	info->type = device_get_match_data(info->dev);
69362306a36Sopenharmony_ci	if (!info->type)
69462306a36Sopenharmony_ci		return -EINVAL;
69562306a36Sopenharmony_ci	if (!info->type->parse_irq) {
69662306a36Sopenharmony_ci		dev_err(info->dev, "parse_irq missing in struct sm5502_type\n");
69762306a36Sopenharmony_ci		return -EINVAL;
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	mutex_init(&info->mutex);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	INIT_WORK(&info->irq_work, sm5502_muic_irq_work);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	info->regmap = devm_regmap_init_i2c(i2c, &sm5502_muic_regmap_config);
70562306a36Sopenharmony_ci	if (IS_ERR(info->regmap)) {
70662306a36Sopenharmony_ci		ret = PTR_ERR(info->regmap);
70762306a36Sopenharmony_ci		dev_err(info->dev, "failed to allocate register map: %d\n",
70862306a36Sopenharmony_ci				   ret);
70962306a36Sopenharmony_ci		return ret;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/* Support irq domain for SM5502 MUIC device */
71362306a36Sopenharmony_ci	irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;
71462306a36Sopenharmony_ci	ret = devm_regmap_add_irq_chip(info->dev, info->regmap, info->irq,
71562306a36Sopenharmony_ci				       irq_flags, 0, info->type->irq_chip,
71662306a36Sopenharmony_ci				       &info->irq_data);
71762306a36Sopenharmony_ci	if (ret != 0) {
71862306a36Sopenharmony_ci		dev_err(info->dev, "failed to request IRQ %d: %d\n",
71962306a36Sopenharmony_ci				    info->irq, ret);
72062306a36Sopenharmony_ci		return ret;
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	for (i = 0; i < info->type->num_muic_irqs; i++) {
72462306a36Sopenharmony_ci		struct muic_irq *muic_irq = &info->type->muic_irqs[i];
72562306a36Sopenharmony_ci		int virq = 0;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci		virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq);
72862306a36Sopenharmony_ci		if (virq <= 0)
72962306a36Sopenharmony_ci			return -EINVAL;
73062306a36Sopenharmony_ci		muic_irq->virq = virq;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		ret = devm_request_threaded_irq(info->dev, virq, NULL,
73362306a36Sopenharmony_ci						sm5502_muic_irq_handler,
73462306a36Sopenharmony_ci						IRQF_NO_SUSPEND | IRQF_ONESHOT,
73562306a36Sopenharmony_ci						muic_irq->name, info);
73662306a36Sopenharmony_ci		if (ret) {
73762306a36Sopenharmony_ci			dev_err(info->dev,
73862306a36Sopenharmony_ci				"failed: irq request (IRQ: %d, error :%d)\n",
73962306a36Sopenharmony_ci				muic_irq->irq, ret);
74062306a36Sopenharmony_ci			return ret;
74162306a36Sopenharmony_ci		}
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/* Allocate extcon device */
74562306a36Sopenharmony_ci	info->edev = devm_extcon_dev_allocate(info->dev, sm5502_extcon_cable);
74662306a36Sopenharmony_ci	if (IS_ERR(info->edev)) {
74762306a36Sopenharmony_ci		dev_err(info->dev, "failed to allocate memory for extcon\n");
74862306a36Sopenharmony_ci		return -ENOMEM;
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	/* Register extcon device */
75262306a36Sopenharmony_ci	ret = devm_extcon_dev_register(info->dev, info->edev);
75362306a36Sopenharmony_ci	if (ret) {
75462306a36Sopenharmony_ci		dev_err(info->dev, "failed to register extcon device\n");
75562306a36Sopenharmony_ci		return ret;
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	/*
75962306a36Sopenharmony_ci	 * Detect accessory after completing the initialization of platform
76062306a36Sopenharmony_ci	 *
76162306a36Sopenharmony_ci	 * - Use delayed workqueue to detect cable state and then
76262306a36Sopenharmony_ci	 * notify cable state to notifiee/platform through uevent.
76362306a36Sopenharmony_ci	 * After completing the booting of platform, the extcon provider
76462306a36Sopenharmony_ci	 * driver should notify cable state to upper layer.
76562306a36Sopenharmony_ci	 */
76662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&info->wq_detcable, sm5502_muic_detect_cable_wq);
76762306a36Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
76862306a36Sopenharmony_ci			msecs_to_jiffies(DELAY_MS_DEFAULT));
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	/* Initialize SM5502 device and print vendor id and version id */
77162306a36Sopenharmony_ci	sm5502_init_dev_type(info);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	return 0;
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic const struct sm5502_type sm5502_data = {
77762306a36Sopenharmony_ci	.muic_irqs = sm5502_muic_irqs,
77862306a36Sopenharmony_ci	.num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs),
77962306a36Sopenharmony_ci	.irq_chip = &sm5502_muic_irq_chip,
78062306a36Sopenharmony_ci	.reg_data = sm5502_reg_data,
78162306a36Sopenharmony_ci	.num_reg_data = ARRAY_SIZE(sm5502_reg_data),
78262306a36Sopenharmony_ci	.otg_dev_type1 = SM5502_REG_DEV_TYPE1_USB_OTG_MASK,
78362306a36Sopenharmony_ci	.parse_irq = sm5502_parse_irq,
78462306a36Sopenharmony_ci};
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic const struct sm5502_type sm5504_data = {
78762306a36Sopenharmony_ci	.muic_irqs = sm5504_muic_irqs,
78862306a36Sopenharmony_ci	.num_muic_irqs = ARRAY_SIZE(sm5504_muic_irqs),
78962306a36Sopenharmony_ci	.irq_chip = &sm5504_muic_irq_chip,
79062306a36Sopenharmony_ci	.reg_data = sm5504_reg_data,
79162306a36Sopenharmony_ci	.num_reg_data = ARRAY_SIZE(sm5504_reg_data),
79262306a36Sopenharmony_ci	.otg_dev_type1 = SM5504_REG_DEV_TYPE1_USB_OTG_MASK,
79362306a36Sopenharmony_ci	.parse_irq = sm5504_parse_irq,
79462306a36Sopenharmony_ci};
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic const struct of_device_id sm5502_dt_match[] = {
79762306a36Sopenharmony_ci	{ .compatible = "siliconmitus,sm5502-muic", .data = &sm5502_data },
79862306a36Sopenharmony_ci	{ .compatible = "siliconmitus,sm5504-muic", .data = &sm5504_data },
79962306a36Sopenharmony_ci	{ .compatible = "siliconmitus,sm5703-muic", .data = &sm5502_data },
80062306a36Sopenharmony_ci	{ },
80162306a36Sopenharmony_ci};
80262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sm5502_dt_match);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
80562306a36Sopenharmony_cistatic int sm5502_muic_suspend(struct device *dev)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	struct i2c_client *i2c = to_i2c_client(dev);
80862306a36Sopenharmony_ci	struct sm5502_muic_info *info = i2c_get_clientdata(i2c);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	enable_irq_wake(info->irq);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	return 0;
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic int sm5502_muic_resume(struct device *dev)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct i2c_client *i2c = to_i2c_client(dev);
81862306a36Sopenharmony_ci	struct sm5502_muic_info *info = i2c_get_clientdata(i2c);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	disable_irq_wake(info->irq);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return 0;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci#endif
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops,
82762306a36Sopenharmony_ci			 sm5502_muic_suspend, sm5502_muic_resume);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic const struct i2c_device_id sm5502_i2c_id[] = {
83062306a36Sopenharmony_ci	{ "sm5502", (kernel_ulong_t)&sm5502_data },
83162306a36Sopenharmony_ci	{ "sm5504", (kernel_ulong_t)&sm5504_data },
83262306a36Sopenharmony_ci	{ "sm5703-muic", (kernel_ulong_t)&sm5502_data },
83362306a36Sopenharmony_ci	{ }
83462306a36Sopenharmony_ci};
83562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, sm5502_i2c_id);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_cistatic struct i2c_driver sm5502_muic_i2c_driver = {
83862306a36Sopenharmony_ci	.driver		= {
83962306a36Sopenharmony_ci		.name	= "sm5502",
84062306a36Sopenharmony_ci		.pm	= &sm5502_muic_pm_ops,
84162306a36Sopenharmony_ci		.of_match_table = sm5502_dt_match,
84262306a36Sopenharmony_ci	},
84362306a36Sopenharmony_ci	.probe = sm5022_muic_i2c_probe,
84462306a36Sopenharmony_ci	.id_table = sm5502_i2c_id,
84562306a36Sopenharmony_ci};
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic int __init sm5502_muic_i2c_init(void)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci	return i2c_add_driver(&sm5502_muic_i2c_driver);
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_cisubsys_initcall(sm5502_muic_i2c_init);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ciMODULE_DESCRIPTION("Silicon Mitus SM5502 Extcon driver");
85462306a36Sopenharmony_ciMODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
85562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
856