brintos

brintos / linux-shallow public Read only

0
0
Text · 23.6 KiB · c8c4b9e Raw
856 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * extcon-sm5502.c - Silicon Mitus SM5502 extcon drvier to support USB switches4 *5 * Copyright (c) 2014 Samsung Electronics Co., Ltd6 * Author: Chanwoo Choi <cw00.choi@samsung.com>7 */8 9#include <linux/err.h>10#include <linux/i2c.h>11#include <linux/interrupt.h>12#include <linux/irqdomain.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/platform_device.h>16#include <linux/regmap.h>17#include <linux/slab.h>18#include <linux/extcon-provider.h>19 20#include "extcon-sm5502.h"21 22#define	DELAY_MS_DEFAULT		17000	/* unit: millisecond */23 24struct muic_irq {25	unsigned int irq;26	const char *name;27	unsigned int virq;28};29 30struct reg_data {31	u8 reg;32	unsigned int val;33	bool invert;34};35 36struct sm5502_muic_info {37	struct device *dev;38	struct extcon_dev *edev;39 40	struct i2c_client *i2c;41	struct regmap *regmap;42 43	const struct sm5502_type *type;44	struct regmap_irq_chip_data *irq_data;45	int irq;46	bool irq_attach;47	bool irq_detach;48	struct work_struct irq_work;49 50	struct mutex mutex;51 52	/*53	 * Use delayed workqueue to detect cable state and then54	 * notify cable state to notifiee/platform through uevent.55	 * After completing the booting of platform, the extcon provider56	 * driver should notify cable state to upper layer.57	 */58	struct delayed_work wq_detcable;59};60 61struct sm5502_type {62	struct muic_irq *muic_irqs;63	unsigned int num_muic_irqs;64	const struct regmap_irq_chip *irq_chip;65 66	struct reg_data *reg_data;67	unsigned int num_reg_data;68 69	unsigned int otg_dev_type1;70	int (*parse_irq)(struct sm5502_muic_info *info, int irq_type);71};72 73/* Default value of SM5502 register to bring up MUIC device. */74static struct reg_data sm5502_reg_data[] = {75	{76		.reg = SM5502_REG_RESET,77		.val = SM5502_REG_RESET_MASK,78		.invert = true,79	}, {80		.reg = SM5502_REG_CONTROL,81		.val = SM5502_REG_CONTROL_MASK_INT_MASK,82		.invert = false,83	}, {84		.reg = SM5502_REG_INTMASK1,85		.val = SM5502_REG_INTM1_KP_MASK86			| SM5502_REG_INTM1_LKP_MASK87			| SM5502_REG_INTM1_LKR_MASK,88		.invert = true,89	}, {90		.reg = SM5502_REG_INTMASK2,91		.val = SM5502_REG_INTM2_VBUS_DET_MASK92			| SM5502_REG_INTM2_REV_ACCE_MASK93			| SM5502_REG_INTM2_ADC_CHG_MASK94			| SM5502_REG_INTM2_STUCK_KEY_MASK95			| SM5502_REG_INTM2_STUCK_KEY_RCV_MASK96			| SM5502_REG_INTM2_MHL_MASK,97		.invert = true,98	},99};100 101/* Default value of SM5504 register to bring up MUIC device. */102static struct reg_data sm5504_reg_data[] = {103	{104		.reg = SM5502_REG_RESET,105		.val = SM5502_REG_RESET_MASK,106		.invert = true,107	}, {108		.reg = SM5502_REG_INTMASK1,109		.val = SM5504_REG_INTM1_ATTACH_MASK110			| SM5504_REG_INTM1_DETACH_MASK,111		.invert = false,112	}, {113		.reg = SM5502_REG_INTMASK2,114		.val = SM5504_REG_INTM2_RID_CHG_MASK115			| SM5504_REG_INTM2_UVLO_MASK116			| SM5504_REG_INTM2_POR_MASK,117		.invert = true,118	}, {119		.reg = SM5502_REG_CONTROL,120		.val = SM5502_REG_CONTROL_MANUAL_SW_MASK121			| SM5504_REG_CONTROL_CHGTYP_MASK122			| SM5504_REG_CONTROL_USBCHDEN_MASK123			| SM5504_REG_CONTROL_ADC_EN_MASK,124		.invert = true,125	},126};127 128/* List of detectable cables */129static const unsigned int sm5502_extcon_cable[] = {130	EXTCON_USB,131	EXTCON_USB_HOST,132	EXTCON_CHG_USB_SDP,133	EXTCON_CHG_USB_DCP,134	EXTCON_NONE,135};136 137/* Define supported accessory type */138enum sm5502_muic_acc_type {139	SM5502_MUIC_ADC_GROUND = 0x0,140	SM5502_MUIC_ADC_SEND_END_BUTTON,141	SM5502_MUIC_ADC_REMOTE_S1_BUTTON,142	SM5502_MUIC_ADC_REMOTE_S2_BUTTON,143	SM5502_MUIC_ADC_REMOTE_S3_BUTTON,144	SM5502_MUIC_ADC_REMOTE_S4_BUTTON,145	SM5502_MUIC_ADC_REMOTE_S5_BUTTON,146	SM5502_MUIC_ADC_REMOTE_S6_BUTTON,147	SM5502_MUIC_ADC_REMOTE_S7_BUTTON,148	SM5502_MUIC_ADC_REMOTE_S8_BUTTON,149	SM5502_MUIC_ADC_REMOTE_S9_BUTTON,150	SM5502_MUIC_ADC_REMOTE_S10_BUTTON,151	SM5502_MUIC_ADC_REMOTE_S11_BUTTON,152	SM5502_MUIC_ADC_REMOTE_S12_BUTTON,153	SM5502_MUIC_ADC_RESERVED_ACC_1,154	SM5502_MUIC_ADC_RESERVED_ACC_2,155	SM5502_MUIC_ADC_RESERVED_ACC_3,156	SM5502_MUIC_ADC_RESERVED_ACC_4,157	SM5502_MUIC_ADC_RESERVED_ACC_5,158	SM5502_MUIC_ADC_AUDIO_TYPE2,159	SM5502_MUIC_ADC_PHONE_POWERED_DEV,160	SM5502_MUIC_ADC_TTY_CONVERTER,161	SM5502_MUIC_ADC_UART_CABLE,162	SM5502_MUIC_ADC_TYPE1_CHARGER,163	SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB,164	SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB,165	SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE,166	SM5502_MUIC_ADC_TYPE2_CHARGER,167	SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART,168	SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART,169	SM5502_MUIC_ADC_AUDIO_TYPE1,170	SM5502_MUIC_ADC_OPEN = 0x1f,171 172	/*173	 * The below accessories have same ADC value (0x1f or 0x1e).174	 * So, Device type1 is used to separate specific accessory.175	 */176							/* |---------|--ADC| */177							/* |    [7:5]|[4:0]| */178	SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e,	/* |      001|11110| */179	SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END = 0x5e,	/* |      010|11110| */180							/* |Dev Type1|--ADC| */181	SM5502_MUIC_ADC_GROUND_USB_OTG = 0x80,		/* |      100|00000| */182	SM5502_MUIC_ADC_OPEN_USB = 0x5f,		/* |      010|11111| */183	SM5502_MUIC_ADC_OPEN_TA = 0xdf,			/* |      110|11111| */184	SM5502_MUIC_ADC_OPEN_USB_OTG = 0xff,		/* |      111|11111| */185};186 187/* List of supported interrupt for SM5502 */188static struct muic_irq sm5502_muic_irqs[] = {189	{ SM5502_IRQ_INT1_ATTACH,	"muic-attach" },190	{ SM5502_IRQ_INT1_DETACH,	"muic-detach" },191	{ SM5502_IRQ_INT1_KP,		"muic-kp" },192	{ SM5502_IRQ_INT1_LKP,		"muic-lkp" },193	{ SM5502_IRQ_INT1_LKR,		"muic-lkr" },194	{ SM5502_IRQ_INT1_OVP_EVENT,	"muic-ovp-event" },195	{ SM5502_IRQ_INT1_OCP_EVENT,	"muic-ocp-event" },196	{ SM5502_IRQ_INT1_OVP_OCP_DIS,	"muic-ovp-ocp-dis" },197	{ SM5502_IRQ_INT2_VBUS_DET,	"muic-vbus-det" },198	{ SM5502_IRQ_INT2_REV_ACCE,	"muic-rev-acce" },199	{ SM5502_IRQ_INT2_ADC_CHG,	"muic-adc-chg" },200	{ SM5502_IRQ_INT2_STUCK_KEY,	"muic-stuck-key" },201	{ SM5502_IRQ_INT2_STUCK_KEY_RCV, "muic-stuck-key-rcv" },202	{ SM5502_IRQ_INT2_MHL,		"muic-mhl" },203};204 205/* Define interrupt list of SM5502 to register regmap_irq */206static const struct regmap_irq sm5502_irqs[] = {207	/* INT1 interrupts */208	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_ATTACH_MASK, },209	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_DETACH_MASK, },210	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_KP_MASK, },211	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKP_MASK, },212	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKR_MASK, },213	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_EVENT_MASK, },214	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_OCP_EVENT_MASK, },215	{ .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_OCP_DIS_MASK, },216 217	/* INT2 interrupts */218	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_VBUS_DET_MASK,},219	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_REV_ACCE_MASK, },220	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_ADC_CHG_MASK, },221	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_MASK, },222	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK, },223	{ .reg_offset = 1, .mask = SM5502_IRQ_INT2_MHL_MASK, },224};225 226static const struct regmap_irq_chip sm5502_muic_irq_chip = {227	.name			= "sm5502",228	.status_base		= SM5502_REG_INT1,229	.mask_base		= SM5502_REG_INTMASK1,230	.num_regs		= 2,231	.irqs			= sm5502_irqs,232	.num_irqs		= ARRAY_SIZE(sm5502_irqs),233};234 235/* List of supported interrupt for SM5504 */236static struct muic_irq sm5504_muic_irqs[] = {237	{ SM5504_IRQ_INT1_ATTACH,	"muic-attach" },238	{ SM5504_IRQ_INT1_DETACH,	"muic-detach" },239	{ SM5504_IRQ_INT1_CHG_DET,	"muic-chg-det" },240	{ SM5504_IRQ_INT1_DCD_OUT,	"muic-dcd-out" },241	{ SM5504_IRQ_INT1_OVP_EVENT,	"muic-ovp-event" },242	{ SM5504_IRQ_INT1_CONNECT,	"muic-connect" },243	{ SM5504_IRQ_INT1_ADC_CHG,	"muic-adc-chg" },244	{ SM5504_IRQ_INT2_RID_CHG,	"muic-rid-chg" },245	{ SM5504_IRQ_INT2_UVLO,		"muic-uvlo" },246	{ SM5504_IRQ_INT2_POR,		"muic-por" },247	{ SM5504_IRQ_INT2_OVP_FET,	"muic-ovp-fet" },248	{ SM5504_IRQ_INT2_OCP_LATCH,	"muic-ocp-latch" },249	{ SM5504_IRQ_INT2_OCP_EVENT,	"muic-ocp-event" },250	{ SM5504_IRQ_INT2_OVP_OCP_EVENT, "muic-ovp-ocp-event" },251};252 253/* Define interrupt list of SM5504 to register regmap_irq */254static const struct regmap_irq sm5504_irqs[] = {255	/* INT1 interrupts */256	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_ATTACH_MASK, },257	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_DETACH_MASK, },258	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_CHG_DET_MASK, },259	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_DCD_OUT_MASK, },260	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_OVP_MASK, },261	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_CONNECT_MASK, },262	{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_ADC_CHG_MASK, },263 264	/* INT2 interrupts */265	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_RID_CHG_MASK,},266	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_UVLO_MASK, },267	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_POR_MASK, },268	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_FET_MASK, },269	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_LATCH_MASK, },270	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_EVENT_MASK, },271	{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK, },272};273 274static const struct regmap_irq_chip sm5504_muic_irq_chip = {275	.name			= "sm5504",276	.status_base		= SM5502_REG_INT1,277	.mask_base		= SM5502_REG_INTMASK1,278	.num_regs		= 2,279	.irqs			= sm5504_irqs,280	.num_irqs		= ARRAY_SIZE(sm5504_irqs),281};282 283/* Define regmap configuration of SM5502 for I2C communication  */284static bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg)285{286	switch (reg) {287	case SM5502_REG_INTMASK1:288	case SM5502_REG_INTMASK2:289		return true;290	default:291		break;292	}293	return false;294}295 296static const struct regmap_config sm5502_muic_regmap_config = {297	.reg_bits	= 8,298	.val_bits	= 8,299	.volatile_reg	= sm5502_muic_volatile_reg,300	.max_register	= SM5502_REG_END,301};302 303/* Change DM_CON/DP_CON/VBUSIN switch according to cable type */304static int sm5502_muic_set_path(struct sm5502_muic_info *info,305				unsigned int con_sw, unsigned int vbus_sw,306				bool attached)307{308	int ret;309 310	if (!attached) {311		con_sw	= DM_DP_SWITCH_OPEN;312		vbus_sw	= VBUSIN_SWITCH_OPEN;313	}314 315	switch (con_sw) {316	case DM_DP_SWITCH_OPEN:317	case DM_DP_SWITCH_USB:318	case DM_DP_SWITCH_AUDIO:319	case DM_DP_SWITCH_UART:320		ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,321					 SM5502_REG_MANUAL_SW1_DP_MASK |322					 SM5502_REG_MANUAL_SW1_DM_MASK,323					 con_sw);324		if (ret < 0) {325			dev_err(info->dev,326				"cannot update DM_CON/DP_CON switch\n");327			return ret;328		}329		break;330	default:331		dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n",332				con_sw);333		return -EINVAL;334	}335 336	switch (vbus_sw) {337	case VBUSIN_SWITCH_OPEN:338	case VBUSIN_SWITCH_VBUSOUT:339	case VBUSIN_SWITCH_MIC:340	case VBUSIN_SWITCH_VBUSOUT_WITH_USB:341		ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,342					 SM5502_REG_MANUAL_SW1_VBUSIN_MASK,343					 vbus_sw);344		if (ret < 0) {345			dev_err(info->dev,346				"cannot update VBUSIN switch\n");347			return ret;348		}349		break;350	default:351		dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw);352		return -EINVAL;353	}354 355	return 0;356}357 358/* Return cable type of attached or detached accessories */359static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)360{361	unsigned int cable_type, adc, dev_type1;362	int ret;363 364	/* Read ADC value according to external cable or button */365	ret = regmap_read(info->regmap, SM5502_REG_ADC, &adc);366	if (ret) {367		dev_err(info->dev, "failed to read ADC register\n");368		return ret;369	}370 371	/*372	 * If ADC is SM5502_MUIC_ADC_GROUND(0x0), external cable hasn't373	 * connected with to MUIC device.374	 */375	cable_type = adc & SM5502_REG_ADC_MASK;376 377	switch (cable_type) {378	case SM5502_MUIC_ADC_GROUND:379		ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,380				  &dev_type1);381		if (ret) {382			dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");383			return ret;384		}385 386		if (dev_type1 == info->type->otg_dev_type1) {387			cable_type = SM5502_MUIC_ADC_GROUND_USB_OTG;388		} else {389			dev_dbg(info->dev,390				"cannot identify the cable type: adc(0x%x), dev_type1(0x%x)\n",391				adc, dev_type1);392			return -EINVAL;393		}394		break;395	case SM5502_MUIC_ADC_SEND_END_BUTTON:396	case SM5502_MUIC_ADC_REMOTE_S1_BUTTON:397	case SM5502_MUIC_ADC_REMOTE_S2_BUTTON:398	case SM5502_MUIC_ADC_REMOTE_S3_BUTTON:399	case SM5502_MUIC_ADC_REMOTE_S4_BUTTON:400	case SM5502_MUIC_ADC_REMOTE_S5_BUTTON:401	case SM5502_MUIC_ADC_REMOTE_S6_BUTTON:402	case SM5502_MUIC_ADC_REMOTE_S7_BUTTON:403	case SM5502_MUIC_ADC_REMOTE_S8_BUTTON:404	case SM5502_MUIC_ADC_REMOTE_S9_BUTTON:405	case SM5502_MUIC_ADC_REMOTE_S10_BUTTON:406	case SM5502_MUIC_ADC_REMOTE_S11_BUTTON:407	case SM5502_MUIC_ADC_REMOTE_S12_BUTTON:408	case SM5502_MUIC_ADC_RESERVED_ACC_1:409	case SM5502_MUIC_ADC_RESERVED_ACC_2:410	case SM5502_MUIC_ADC_RESERVED_ACC_3:411	case SM5502_MUIC_ADC_RESERVED_ACC_4:412	case SM5502_MUIC_ADC_RESERVED_ACC_5:413	case SM5502_MUIC_ADC_AUDIO_TYPE2:414	case SM5502_MUIC_ADC_PHONE_POWERED_DEV:415	case SM5502_MUIC_ADC_TTY_CONVERTER:416	case SM5502_MUIC_ADC_UART_CABLE:417	case SM5502_MUIC_ADC_TYPE1_CHARGER:418	case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB:419	case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB:420	case SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE:421	case SM5502_MUIC_ADC_TYPE2_CHARGER:422	case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART:423	case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART:424		break;425	case SM5502_MUIC_ADC_AUDIO_TYPE1:426		/*427		 * Check whether cable type is428		 * SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE429		 * or SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END430		 * by using Button event.431		 */432		break;433	case SM5502_MUIC_ADC_OPEN:434		ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,435				  &dev_type1);436		if (ret) {437			dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");438			return ret;439		}440 441		if (dev_type1 == info->type->otg_dev_type1) {442			cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG;443			break;444		}445 446		switch (dev_type1) {447		case SM5502_REG_DEV_TYPE1_USB_SDP_MASK:448			cable_type = SM5502_MUIC_ADC_OPEN_USB;449			break;450		case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK:451			cable_type = SM5502_MUIC_ADC_OPEN_TA;452			break;453		default:454			dev_dbg(info->dev,455				"cannot identify the cable type: adc(0x%x)\n",456				adc);457			return -EINVAL;458		}459		break;460	default:461		dev_err(info->dev,462			"failed to identify the cable type: adc(0x%x)\n", adc);463		return -EINVAL;464	}465 466	return cable_type;467}468 469static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,470				     bool attached)471{472	static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND;473	unsigned int cable_type = SM5502_MUIC_ADC_GROUND;474	unsigned int con_sw = DM_DP_SWITCH_OPEN;475	unsigned int vbus_sw = VBUSIN_SWITCH_OPEN;476	unsigned int id;477	int ret;478 479	/* Get the type of attached or detached cable */480	if (attached)481		cable_type = sm5502_muic_get_cable_type(info);482	else483		cable_type = prev_cable_type;484	prev_cable_type = cable_type;485 486	switch (cable_type) {487	case SM5502_MUIC_ADC_OPEN_USB:488		id	= EXTCON_USB;489		con_sw	= DM_DP_SWITCH_USB;490		vbus_sw	= VBUSIN_SWITCH_VBUSOUT_WITH_USB;491		break;492	case SM5502_MUIC_ADC_OPEN_TA:493		id	= EXTCON_CHG_USB_DCP;494		con_sw	= DM_DP_SWITCH_OPEN;495		vbus_sw	= VBUSIN_SWITCH_VBUSOUT;496		break;497	case SM5502_MUIC_ADC_GROUND_USB_OTG:498	case SM5502_MUIC_ADC_OPEN_USB_OTG:499		id	= EXTCON_USB_HOST;500		con_sw	= DM_DP_SWITCH_USB;501		vbus_sw	= VBUSIN_SWITCH_OPEN;502		break;503	default:504		dev_dbg(info->dev,505			"cannot handle this cable_type (0x%x)\n", cable_type);506		return 0;507	}508 509	/* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */510	ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached);511	if (ret < 0)512		return ret;513 514	/* Change the state of external accessory */515	extcon_set_state_sync(info->edev, id, attached);516	if (id == EXTCON_USB)517		extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,518					attached);519 520	return 0;521}522 523static void sm5502_muic_irq_work(struct work_struct *work)524{525	struct sm5502_muic_info *info = container_of(work,526			struct sm5502_muic_info, irq_work);527	int ret = 0;528 529	if (!info->edev)530		return;531 532	mutex_lock(&info->mutex);533 534	/* Detect attached or detached cables */535	if (info->irq_attach) {536		ret = sm5502_muic_cable_handler(info, true);537		info->irq_attach = false;538	}539	if (info->irq_detach) {540		ret = sm5502_muic_cable_handler(info, false);541		info->irq_detach = false;542	}543 544	if (ret < 0)545		dev_err(info->dev, "failed to handle MUIC interrupt\n");546 547	mutex_unlock(&info->mutex);548}549 550/*551 * Sets irq_attach or irq_detach in sm5502_muic_info and returns 0.552 * Returns -ESRCH if irq_type does not match registered IRQ for this dev type.553 */554static int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type)555{556	switch (irq_type) {557	case SM5502_IRQ_INT1_ATTACH:558		info->irq_attach = true;559		break;560	case SM5502_IRQ_INT1_DETACH:561		info->irq_detach = true;562		break;563	case SM5502_IRQ_INT1_KP:564	case SM5502_IRQ_INT1_LKP:565	case SM5502_IRQ_INT1_LKR:566	case SM5502_IRQ_INT1_OVP_EVENT:567	case SM5502_IRQ_INT1_OCP_EVENT:568	case SM5502_IRQ_INT1_OVP_OCP_DIS:569	case SM5502_IRQ_INT2_VBUS_DET:570	case SM5502_IRQ_INT2_REV_ACCE:571	case SM5502_IRQ_INT2_ADC_CHG:572	case SM5502_IRQ_INT2_STUCK_KEY:573	case SM5502_IRQ_INT2_STUCK_KEY_RCV:574	case SM5502_IRQ_INT2_MHL:575	default:576		break;577	}578 579	return 0;580}581 582static int sm5504_parse_irq(struct sm5502_muic_info *info, int irq_type)583{584	switch (irq_type) {585	case SM5504_IRQ_INT1_ATTACH:586		info->irq_attach = true;587		break;588	case SM5504_IRQ_INT1_DETACH:589		info->irq_detach = true;590		break;591	case SM5504_IRQ_INT1_CHG_DET:592	case SM5504_IRQ_INT1_DCD_OUT:593	case SM5504_IRQ_INT1_OVP_EVENT:594	case SM5504_IRQ_INT1_CONNECT:595	case SM5504_IRQ_INT1_ADC_CHG:596	case SM5504_IRQ_INT2_RID_CHG:597	case SM5504_IRQ_INT2_UVLO:598	case SM5504_IRQ_INT2_POR:599	case SM5504_IRQ_INT2_OVP_FET:600	case SM5504_IRQ_INT2_OCP_LATCH:601	case SM5504_IRQ_INT2_OCP_EVENT:602	case SM5504_IRQ_INT2_OVP_OCP_EVENT:603	default:604		break;605	}606 607	return 0;608}609 610static irqreturn_t sm5502_muic_irq_handler(int irq, void *data)611{612	struct sm5502_muic_info *info = data;613	int i, irq_type = -1, ret;614 615	for (i = 0; i < info->type->num_muic_irqs; i++)616		if (irq == info->type->muic_irqs[i].virq)617			irq_type = info->type->muic_irqs[i].irq;618 619	ret = info->type->parse_irq(info, irq_type);620	if (ret < 0) {621		dev_warn(info->dev, "cannot handle is interrupt:%d\n",622				    irq_type);623		return IRQ_HANDLED;624	}625	schedule_work(&info->irq_work);626 627	return IRQ_HANDLED;628}629 630static void sm5502_muic_detect_cable_wq(struct work_struct *work)631{632	struct sm5502_muic_info *info = container_of(to_delayed_work(work),633				struct sm5502_muic_info, wq_detcable);634	int ret;635 636	/* Notify the state of connector cable or not  */637	ret = sm5502_muic_cable_handler(info, true);638	if (ret < 0)639		dev_warn(info->dev, "failed to detect cable state\n");640}641 642static void sm5502_init_dev_type(struct sm5502_muic_info *info)643{644	unsigned int reg_data, vendor_id, version_id;645	int i, ret;646 647	/* To test I2C, Print version_id and vendor_id of SM5502 */648	ret = regmap_read(info->regmap, SM5502_REG_DEVICE_ID, &reg_data);649	if (ret) {650		dev_err(info->dev,651			"failed to read DEVICE_ID register: %d\n", ret);652		return;653	}654 655	vendor_id = ((reg_data & SM5502_REG_DEVICE_ID_VENDOR_MASK) >>656				SM5502_REG_DEVICE_ID_VENDOR_SHIFT);657	version_id = ((reg_data & SM5502_REG_DEVICE_ID_VERSION_MASK) >>658				SM5502_REG_DEVICE_ID_VERSION_SHIFT);659 660	dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",661			    version_id, vendor_id);662 663	/* Initiazle the register of SM5502 device to bring-up */664	for (i = 0; i < info->type->num_reg_data; i++) {665		unsigned int val = 0;666 667		if (!info->type->reg_data[i].invert)668			val |= ~info->type->reg_data[i].val;669		else670			val = info->type->reg_data[i].val;671		regmap_write(info->regmap, info->type->reg_data[i].reg, val);672	}673}674 675static int sm5022_muic_i2c_probe(struct i2c_client *i2c)676{677	struct device_node *np = i2c->dev.of_node;678	struct sm5502_muic_info *info;679	int i, ret, irq_flags;680 681	if (!np)682		return -EINVAL;683 684	info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL);685	if (!info)686		return -ENOMEM;687	i2c_set_clientdata(i2c, info);688 689	info->dev = &i2c->dev;690	info->i2c = i2c;691	info->irq = i2c->irq;692	info->type = device_get_match_data(info->dev);693	if (!info->type)694		return -EINVAL;695	if (!info->type->parse_irq) {696		dev_err(info->dev, "parse_irq missing in struct sm5502_type\n");697		return -EINVAL;698	}699 700	mutex_init(&info->mutex);701 702	INIT_WORK(&info->irq_work, sm5502_muic_irq_work);703 704	info->regmap = devm_regmap_init_i2c(i2c, &sm5502_muic_regmap_config);705	if (IS_ERR(info->regmap)) {706		ret = PTR_ERR(info->regmap);707		dev_err(info->dev, "failed to allocate register map: %d\n",708				   ret);709		return ret;710	}711 712	/* Support irq domain for SM5502 MUIC device */713	irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;714	ret = devm_regmap_add_irq_chip(info->dev, info->regmap, info->irq,715				       irq_flags, 0, info->type->irq_chip,716				       &info->irq_data);717	if (ret != 0) {718		dev_err(info->dev, "failed to request IRQ %d: %d\n",719				    info->irq, ret);720		return ret;721	}722 723	for (i = 0; i < info->type->num_muic_irqs; i++) {724		struct muic_irq *muic_irq = &info->type->muic_irqs[i];725		int virq = 0;726 727		virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq);728		if (virq <= 0)729			return -EINVAL;730		muic_irq->virq = virq;731 732		ret = devm_request_threaded_irq(info->dev, virq, NULL,733						sm5502_muic_irq_handler,734						IRQF_NO_SUSPEND | IRQF_ONESHOT,735						muic_irq->name, info);736		if (ret) {737			dev_err(info->dev,738				"failed: irq request (IRQ: %d, error :%d)\n",739				muic_irq->irq, ret);740			return ret;741		}742	}743 744	/* Allocate extcon device */745	info->edev = devm_extcon_dev_allocate(info->dev, sm5502_extcon_cable);746	if (IS_ERR(info->edev)) {747		dev_err(info->dev, "failed to allocate memory for extcon\n");748		return -ENOMEM;749	}750 751	/* Register extcon device */752	ret = devm_extcon_dev_register(info->dev, info->edev);753	if (ret) {754		dev_err(info->dev, "failed to register extcon device\n");755		return ret;756	}757 758	/*759	 * Detect accessory after completing the initialization of platform760	 *761	 * - Use delayed workqueue to detect cable state and then762	 * notify cable state to notifiee/platform through uevent.763	 * After completing the booting of platform, the extcon provider764	 * driver should notify cable state to upper layer.765	 */766	INIT_DELAYED_WORK(&info->wq_detcable, sm5502_muic_detect_cable_wq);767	queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,768			msecs_to_jiffies(DELAY_MS_DEFAULT));769 770	/* Initialize SM5502 device and print vendor id and version id */771	sm5502_init_dev_type(info);772 773	return 0;774}775 776static const struct sm5502_type sm5502_data = {777	.muic_irqs = sm5502_muic_irqs,778	.num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs),779	.irq_chip = &sm5502_muic_irq_chip,780	.reg_data = sm5502_reg_data,781	.num_reg_data = ARRAY_SIZE(sm5502_reg_data),782	.otg_dev_type1 = SM5502_REG_DEV_TYPE1_USB_OTG_MASK,783	.parse_irq = sm5502_parse_irq,784};785 786static const struct sm5502_type sm5504_data = {787	.muic_irqs = sm5504_muic_irqs,788	.num_muic_irqs = ARRAY_SIZE(sm5504_muic_irqs),789	.irq_chip = &sm5504_muic_irq_chip,790	.reg_data = sm5504_reg_data,791	.num_reg_data = ARRAY_SIZE(sm5504_reg_data),792	.otg_dev_type1 = SM5504_REG_DEV_TYPE1_USB_OTG_MASK,793	.parse_irq = sm5504_parse_irq,794};795 796static const struct of_device_id sm5502_dt_match[] = {797	{ .compatible = "siliconmitus,sm5502-muic", .data = &sm5502_data },798	{ .compatible = "siliconmitus,sm5504-muic", .data = &sm5504_data },799	{ .compatible = "siliconmitus,sm5703-muic", .data = &sm5502_data },800	{ },801};802MODULE_DEVICE_TABLE(of, sm5502_dt_match);803 804#ifdef CONFIG_PM_SLEEP805static int sm5502_muic_suspend(struct device *dev)806{807	struct i2c_client *i2c = to_i2c_client(dev);808	struct sm5502_muic_info *info = i2c_get_clientdata(i2c);809 810	enable_irq_wake(info->irq);811 812	return 0;813}814 815static int sm5502_muic_resume(struct device *dev)816{817	struct i2c_client *i2c = to_i2c_client(dev);818	struct sm5502_muic_info *info = i2c_get_clientdata(i2c);819 820	disable_irq_wake(info->irq);821 822	return 0;823}824#endif825 826static SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops,827			 sm5502_muic_suspend, sm5502_muic_resume);828 829static const struct i2c_device_id sm5502_i2c_id[] = {830	{ "sm5502", (kernel_ulong_t)&sm5502_data },831	{ "sm5504", (kernel_ulong_t)&sm5504_data },832	{ "sm5703-muic", (kernel_ulong_t)&sm5502_data },833	{ }834};835MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id);836 837static struct i2c_driver sm5502_muic_i2c_driver = {838	.driver		= {839		.name	= "sm5502",840		.pm	= &sm5502_muic_pm_ops,841		.of_match_table = sm5502_dt_match,842	},843	.probe = sm5022_muic_i2c_probe,844	.id_table = sm5502_i2c_id,845};846 847static int __init sm5502_muic_i2c_init(void)848{849	return i2c_add_driver(&sm5502_muic_i2c_driver);850}851subsys_initcall(sm5502_muic_i2c_init);852 853MODULE_DESCRIPTION("Silicon Mitus SM5502 Extcon driver");854MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");855MODULE_LICENSE("GPL");856