extcon: sm5502: Add support for SM5504
authorStephan Gerhold <stephan@gerhold.net>
Thu, 3 Jun 2021 08:52:22 +0000 (10:52 +0200)
committerChanwoo Choi <cw00.choi@samsung.com>
Mon, 21 Jun 2021 09:28:01 +0000 (18:28 +0900)
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 <stephan@gerhold.net>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
drivers/extcon/Kconfig
drivers/extcon/extcon-sm5502.c
drivers/extcon/extcon-sm5502.h

index e3db936..c69d40a 100644 (file)
@@ -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
index 60e2d12..93da2d8 100644 (file)
@@ -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);
 };
 
@@ -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,
@@ -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)
 {
@@ -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);
@@ -365,6 +440,11 @@ 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;
@@ -372,9 +452,6 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
                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",
@@ -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;
@@ -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);
@@ -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);
index d187205..9c04315 100644 (file)
@@ -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
@@ -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)
 
@@ -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
@@ -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 */