Skip to content

Commit

Permalink
extcon: sm5502: Add support for SM5504
Browse files Browse the repository at this point in the history
SM5504 is another MUIC from Silicon Mitus that is fairly similar
to SM5502. They seem to use the same register set, but:

  - SM5504 has some additional bits in SM5502_REG_CONTROL
  - SM5504 has a quite different set of interrupts
  - SM5504 reports USB OTG as dev_type1 = BIT(0) instead of BIT(7)

Overall it's minor and we can support this by defining a separate
struct sm5502_type for SM5504.

Signed-off-by: Stephan Gerhold <[email protected]>
Signed-off-by: Chanwoo Choi <[email protected]>
  • Loading branch information
stephan-gh authored and chanwoochoi committed Jun 21, 2021
1 parent f33c056 commit d97c0ff
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 8 deletions.
2 changes: 1 addition & 1 deletion drivers/extcon/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ config EXTCON_RT8973A
from abnormal high input voltage (up to 28V).

config EXTCON_SM5502
tristate "Silicon Mitus SM5502 EXTCON support"
tristate "Silicon Mitus SM5502/SM5504 EXTCON support"
depends on I2C
select IRQ_DOMAIN
select REGMAP_I2C
Expand Down
132 changes: 125 additions & 7 deletions drivers/extcon/extcon-sm5502.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ struct sm5502_type {
struct reg_data *reg_data;
unsigned int num_reg_data;

unsigned int otg_dev_type1;
int (*parse_irq)(struct sm5502_muic_info *info, int irq_type);
};

Expand Down Expand Up @@ -97,6 +98,33 @@ static struct reg_data sm5502_reg_data[] = {
},
};

/* Default value of SM5504 register to bring up MUIC device. */
static struct reg_data sm5504_reg_data[] = {
{
.reg = SM5502_REG_RESET,
.val = SM5502_REG_RESET_MASK,
.invert = true,
}, {
.reg = SM5502_REG_INTMASK1,
.val = SM5504_REG_INTM1_ATTACH_MASK
| SM5504_REG_INTM1_DETACH_MASK,
.invert = false,
}, {
.reg = SM5502_REG_INTMASK2,
.val = SM5504_REG_INTM2_RID_CHG_MASK
| SM5504_REG_INTM2_UVLO_MASK
| SM5504_REG_INTM2_POR_MASK,
.invert = true,
}, {
.reg = SM5502_REG_CONTROL,
.val = SM5502_REG_CONTROL_MANUAL_SW_MASK
| SM5504_REG_CONTROL_CHGTYP_MASK
| SM5504_REG_CONTROL_USBCHDEN_MASK
| SM5504_REG_CONTROL_ADC_EN_MASK,
.invert = true,
},
};

/* List of detectable cables */
static const unsigned int sm5502_extcon_cable[] = {
EXTCON_USB,
Expand Down Expand Up @@ -205,6 +233,55 @@ static const struct regmap_irq_chip sm5502_muic_irq_chip = {
.num_irqs = ARRAY_SIZE(sm5502_irqs),
};

/* List of supported interrupt for SM5504 */
static struct muic_irq sm5504_muic_irqs[] = {
{ SM5504_IRQ_INT1_ATTACH, "muic-attach" },
{ SM5504_IRQ_INT1_DETACH, "muic-detach" },
{ SM5504_IRQ_INT1_CHG_DET, "muic-chg-det" },
{ SM5504_IRQ_INT1_DCD_OUT, "muic-dcd-out" },
{ SM5504_IRQ_INT1_OVP_EVENT, "muic-ovp-event" },
{ SM5504_IRQ_INT1_CONNECT, "muic-connect" },
{ SM5504_IRQ_INT1_ADC_CHG, "muic-adc-chg" },
{ SM5504_IRQ_INT2_RID_CHG, "muic-rid-chg" },
{ SM5504_IRQ_INT2_UVLO, "muic-uvlo" },
{ SM5504_IRQ_INT2_POR, "muic-por" },
{ SM5504_IRQ_INT2_OVP_FET, "muic-ovp-fet" },
{ SM5504_IRQ_INT2_OCP_LATCH, "muic-ocp-latch" },
{ SM5504_IRQ_INT2_OCP_EVENT, "muic-ocp-event" },
{ SM5504_IRQ_INT2_OVP_OCP_EVENT, "muic-ovp-ocp-event" },
};

/* Define interrupt list of SM5504 to register regmap_irq */
static const struct regmap_irq sm5504_irqs[] = {
/* INT1 interrupts */
{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_ATTACH_MASK, },
{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_DETACH_MASK, },
{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_CHG_DET_MASK, },
{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_DCD_OUT_MASK, },
{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_OVP_MASK, },
{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_CONNECT_MASK, },
{ .reg_offset = 0, .mask = SM5504_IRQ_INT1_ADC_CHG_MASK, },

/* INT2 interrupts */
{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_RID_CHG_MASK,},
{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_UVLO_MASK, },
{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_POR_MASK, },
{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_FET_MASK, },
{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_LATCH_MASK, },
{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_EVENT_MASK, },
{ .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK, },
};

static const struct regmap_irq_chip sm5504_muic_irq_chip = {
.name = "sm5504",
.status_base = SM5502_REG_INT1,
.mask_base = SM5502_REG_INTMASK1,
.mask_invert = false,
.num_regs = 2,
.irqs = sm5504_irqs,
.num_irqs = ARRAY_SIZE(sm5504_irqs),
};

/* Define regmap configuration of SM5502 for I2C communication */
static bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg)
{
Expand Down Expand Up @@ -308,11 +385,9 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
return ret;
}

switch (dev_type1) {
case SM5502_REG_DEV_TYPE1_USB_OTG_MASK:
if (dev_type1 == info->type->otg_dev_type1) {
cable_type = SM5502_MUIC_ADC_GROUND_USB_OTG;
break;
default:
} else {
dev_dbg(info->dev,
"cannot identify the cable type: adc(0x%x), dev_type1(0x%x)\n",
adc, dev_type1);
Expand Down Expand Up @@ -365,16 +440,18 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
return ret;
}

if (dev_type1 == info->type->otg_dev_type1) {
cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG;
break;
}

switch (dev_type1) {
case SM5502_REG_DEV_TYPE1_USB_SDP_MASK:
cable_type = SM5502_MUIC_ADC_OPEN_USB;
break;
case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK:
cable_type = SM5502_MUIC_ADC_OPEN_TA;
break;
case SM5502_REG_DEV_TYPE1_USB_OTG_MASK:
cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG;
break;
default:
dev_dbg(info->dev,
"cannot identify the cable type: adc(0x%x)\n",
Expand Down Expand Up @@ -504,6 +581,34 @@ static int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type)
return 0;
}

static int sm5504_parse_irq(struct sm5502_muic_info *info, int irq_type)
{
switch (irq_type) {
case SM5504_IRQ_INT1_ATTACH:
info->irq_attach = true;
break;
case SM5504_IRQ_INT1_DETACH:
info->irq_detach = true;
break;
case SM5504_IRQ_INT1_CHG_DET:
case SM5504_IRQ_INT1_DCD_OUT:
case SM5504_IRQ_INT1_OVP_EVENT:
case SM5504_IRQ_INT1_CONNECT:
case SM5504_IRQ_INT1_ADC_CHG:
case SM5504_IRQ_INT2_RID_CHG:
case SM5504_IRQ_INT2_UVLO:
case SM5504_IRQ_INT2_POR:
case SM5504_IRQ_INT2_OVP_FET:
case SM5504_IRQ_INT2_OCP_LATCH:
case SM5504_IRQ_INT2_OCP_EVENT:
case SM5504_IRQ_INT2_OVP_OCP_EVENT:
default:
break;
}

return 0;
}

static irqreturn_t sm5502_muic_irq_handler(int irq, void *data)
{
struct sm5502_muic_info *info = data;
Expand Down Expand Up @@ -676,11 +781,23 @@ static const struct sm5502_type sm5502_data = {
.irq_chip = &sm5502_muic_irq_chip,
.reg_data = sm5502_reg_data,
.num_reg_data = ARRAY_SIZE(sm5502_reg_data),
.otg_dev_type1 = SM5502_REG_DEV_TYPE1_USB_OTG_MASK,
.parse_irq = sm5502_parse_irq,
};

static const struct sm5502_type sm5504_data = {
.muic_irqs = sm5504_muic_irqs,
.num_muic_irqs = ARRAY_SIZE(sm5504_muic_irqs),
.irq_chip = &sm5504_muic_irq_chip,
.reg_data = sm5504_reg_data,
.num_reg_data = ARRAY_SIZE(sm5504_reg_data),
.otg_dev_type1 = SM5504_REG_DEV_TYPE1_USB_OTG_MASK,
.parse_irq = sm5504_parse_irq,
};

static const struct of_device_id sm5502_dt_match[] = {
{ .compatible = "siliconmitus,sm5502-muic", .data = &sm5502_data },
{ .compatible = "siliconmitus,sm5504-muic", .data = &sm5504_data },
{ },
};
MODULE_DEVICE_TABLE(of, sm5502_dt_match);
Expand Down Expand Up @@ -712,6 +829,7 @@ static SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops,

static const struct i2c_device_id sm5502_i2c_id[] = {
{ "sm5502", (kernel_ulong_t)&sm5502_data },
{ "sm5504", (kernel_ulong_t)&sm5504_data },
{ }
};
MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id);
Expand Down
78 changes: 78 additions & 0 deletions drivers/extcon/extcon-sm5502.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ enum sm5502_reg {
#define SM5502_REG_CONTROL_RAW_DATA_MASK (0x1 << SM5502_REG_CONTROL_RAW_DATA_SHIFT)
#define SM5502_REG_CONTROL_SW_OPEN_MASK (0x1 << SM5502_REG_CONTROL_SW_OPEN_SHIFT)

#define SM5504_REG_CONTROL_CHGTYP_SHIFT 5
#define SM5504_REG_CONTROL_USBCHDEN_SHIFT 6
#define SM5504_REG_CONTROL_ADC_EN_SHIFT 7
#define SM5504_REG_CONTROL_CHGTYP_MASK (0x1 << SM5504_REG_CONTROL_CHGTYP_SHIFT)
#define SM5504_REG_CONTROL_USBCHDEN_MASK (0x1 << SM5504_REG_CONTROL_USBCHDEN_SHIFT)
#define SM5504_REG_CONTROL_ADC_EN_MASK (0x1 << SM5504_REG_CONTROL_ADC_EN_SHIFT)

#define SM5502_REG_INTM1_ATTACH_SHIFT 0
#define SM5502_REG_INTM1_DETACH_SHIFT 1
#define SM5502_REG_INTM1_KP_SHIFT 2
Expand Down Expand Up @@ -119,6 +126,36 @@ enum sm5502_reg {
#define SM5502_REG_INTM2_STUCK_KEY_RCV_MASK (0x1 << SM5502_REG_INTM2_STUCK_KEY_RCV_SHIFT)
#define SM5502_REG_INTM2_MHL_MASK (0x1 << SM5502_REG_INTM2_MHL_SHIFT)

#define SM5504_REG_INTM1_ATTACH_SHIFT 0
#define SM5504_REG_INTM1_DETACH_SHIFT 1
#define SM5504_REG_INTM1_CHG_DET_SHIFT 2
#define SM5504_REG_INTM1_DCD_OUT_SHIFT 3
#define SM5504_REG_INTM1_OVP_EVENT_SHIFT 4
#define SM5504_REG_INTM1_CONNECT_SHIFT 5
#define SM5504_REG_INTM1_ADC_CHG_SHIFT 6
#define SM5504_REG_INTM1_ATTACH_MASK (0x1 << SM5504_REG_INTM1_ATTACH_SHIFT)
#define SM5504_REG_INTM1_DETACH_MASK (0x1 << SM5504_REG_INTM1_DETACH_SHIFT)
#define SM5504_REG_INTM1_CHG_DET_MASK (0x1 << SM5504_REG_INTM1_CHG_DET_SHIFT)
#define SM5504_REG_INTM1_DCD_OUT_MASK (0x1 << SM5504_REG_INTM1_DCD_OUT_SHIFT)
#define SM5504_REG_INTM1_OVP_EVENT_MASK (0x1 << SM5504_REG_INTM1_OVP_EVENT_SHIFT)
#define SM5504_REG_INTM1_CONNECT_MASK (0x1 << SM5504_REG_INTM1_CONNECT_SHIFT)
#define SM5504_REG_INTM1_ADC_CHG_MASK (0x1 << SM5504_REG_INTM1_ADC_CHG_SHIFT)

#define SM5504_REG_INTM2_RID_CHG_SHIFT 0
#define SM5504_REG_INTM2_UVLO_SHIFT 1
#define SM5504_REG_INTM2_POR_SHIFT 2
#define SM5504_REG_INTM2_OVP_FET_SHIFT 4
#define SM5504_REG_INTM2_OCP_LATCH_SHIFT 5
#define SM5504_REG_INTM2_OCP_EVENT_SHIFT 6
#define SM5504_REG_INTM2_OVP_OCP_EVENT_SHIFT 7
#define SM5504_REG_INTM2_RID_CHG_MASK (0x1 << SM5504_REG_INTM2_RID_CHG_SHIFT)
#define SM5504_REG_INTM2_UVLO_MASK (0x1 << SM5504_REG_INTM2_UVLO_SHIFT)
#define SM5504_REG_INTM2_POR_MASK (0x1 << SM5504_REG_INTM2_POR_SHIFT)
#define SM5504_REG_INTM2_OVP_FET_MASK (0x1 << SM5504_REG_INTM2_OVP_FET_SHIFT)
#define SM5504_REG_INTM2_OCP_LATCH_MASK (0x1 << SM5504_REG_INTM2_OCP_LATCH_SHIFT)
#define SM5504_REG_INTM2_OCP_EVENT_MASK (0x1 << SM5504_REG_INTM2_OCP_EVENT_SHIFT)
#define SM5504_REG_INTM2_OVP_OCP_EVENT_MASK (0x1 << SM5504_REG_INTM2_OVP_OCP_EVENT_SHIFT)

#define SM5502_REG_ADC_SHIFT 0
#define SM5502_REG_ADC_MASK (0x1f << SM5502_REG_ADC_SHIFT)

Expand Down Expand Up @@ -195,6 +232,9 @@ enum sm5502_reg {
#define SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK (0x1 << SM5502_REG_DEV_TYPE1_DEDICATED_CHG_SHIFT)
#define SM5502_REG_DEV_TYPE1_USB_OTG_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_OTG_SHIFT)

#define SM5504_REG_DEV_TYPE1_USB_OTG_SHIFT 0
#define SM5504_REG_DEV_TYPE1_USB_OTG_MASK (0x1 << SM5504_REG_DEV_TYPE1_USB_OTG_SHIFT)

#define SM5502_REG_DEV_TYPE2_JIG_USB_ON_SHIFT 0
#define SM5502_REG_DEV_TYPE2_JIG_USB_OFF_SHIFT 1
#define SM5502_REG_DEV_TYPE2_JIG_UART_ON_SHIFT 2
Expand Down Expand Up @@ -273,4 +313,42 @@ enum sm5502_irq {
#define SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK BIT(4)
#define SM5502_IRQ_INT2_MHL_MASK BIT(5)

/* SM5504 Interrupts */
enum sm5504_irq {
/* INT1 */
SM5504_IRQ_INT1_ATTACH,
SM5504_IRQ_INT1_DETACH,
SM5504_IRQ_INT1_CHG_DET,
SM5504_IRQ_INT1_DCD_OUT,
SM5504_IRQ_INT1_OVP_EVENT,
SM5504_IRQ_INT1_CONNECT,
SM5504_IRQ_INT1_ADC_CHG,

/* INT2 */
SM5504_IRQ_INT2_RID_CHG,
SM5504_IRQ_INT2_UVLO,
SM5504_IRQ_INT2_POR,
SM5504_IRQ_INT2_OVP_FET,
SM5504_IRQ_INT2_OCP_LATCH,
SM5504_IRQ_INT2_OCP_EVENT,
SM5504_IRQ_INT2_OVP_OCP_EVENT,

SM5504_IRQ_NUM,
};

#define SM5504_IRQ_INT1_ATTACH_MASK BIT(0)
#define SM5504_IRQ_INT1_DETACH_MASK BIT(1)
#define SM5504_IRQ_INT1_CHG_DET_MASK BIT(2)
#define SM5504_IRQ_INT1_DCD_OUT_MASK BIT(3)
#define SM5504_IRQ_INT1_OVP_MASK BIT(4)
#define SM5504_IRQ_INT1_CONNECT_MASK BIT(5)
#define SM5504_IRQ_INT1_ADC_CHG_MASK BIT(6)
#define SM5504_IRQ_INT2_RID_CHG_MASK BIT(0)
#define SM5504_IRQ_INT2_UVLO_MASK BIT(1)
#define SM5504_IRQ_INT2_POR_MASK BIT(2)
#define SM5504_IRQ_INT2_OVP_FET_MASK BIT(4)
#define SM5504_IRQ_INT2_OCP_LATCH_MASK BIT(5)
#define SM5504_IRQ_INT2_OCP_EVENT_MASK BIT(6)
#define SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK BIT(7)

#endif /* __LINUX_EXTCON_SM5502_H */

0 comments on commit d97c0ff

Please sign in to comment.