diff options
-rw-r--r-- | drivers/extcon/extcon-sm5502.c | 78 | ||||
-rw-r--r-- | include/linux/extcon/sm5502.h | 23 |
2 files changed, 96 insertions, 5 deletions
diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index a32d40f..560d7dc 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -228,6 +228,61 @@ static const struct regmap_config sm5502_muic_regmap_config = { .max_register = SM5502_REG_END, }; +/* Change DM_CON/DP_CON/VBUSIN switch according to cable type */ +static int sm5502_muic_set_path(struct sm5502_muic_info *info, + unsigned int con_sw, unsigned int vbus_sw, + bool attached) +{ + int ret; + + if (!attached) { + con_sw = DM_DP_SWITCH_OPEN; + vbus_sw = VBUSIN_SWITCH_OPEN; + } + + switch (con_sw) { + case DM_DP_SWITCH_OPEN: + case DM_DP_SWITCH_USB: + case DM_DP_SWITCH_AUDIO: + case DM_DP_SWITCH_UART: + ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1, + SM5502_REG_MANUAL_SW1_DP_MASK | + SM5502_REG_MANUAL_SW1_DM_MASK, + con_sw); + if (ret < 0) { + dev_err(info->dev, + "cannot update DM_CON/DP_CON switch\n"); + return ret; + } + break; + default: + dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n", + con_sw); + return -EINVAL; + }; + + switch (vbus_sw) { + case VBUSIN_SWITCH_OPEN: + case VBUSIN_SWITCH_VBUSOUT: + case VBUSIN_SWITCH_MIC: + case VBUSIN_SWITCH_VBUSOUT_WITH_USB: + ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1, + SM5502_REG_MANUAL_SW1_VBUSIN_MASK, + vbus_sw); + if (ret < 0) { + dev_err(info->dev, + "cannot update VBUSIN switch\n"); + return ret; + } + break; + default: + dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw); + return -EINVAL; + }; + + return 0; +} + /* Return cable type of attached or detached accessories */ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) { @@ -329,7 +384,10 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND; const char **cable_names = info->edev->supported_cable; unsigned int cable_type = SM5502_MUIC_ADC_GROUND; + unsigned int con_sw = DM_DP_SWITCH_OPEN; + unsigned int vbus_sw = VBUSIN_SWITCH_OPEN; unsigned int idx = 0; + int ret; if (!cable_names) return 0; @@ -343,15 +401,19 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, switch (cable_type) { case SM5502_MUIC_ADC_OPEN_USB: - idx = EXTCON_CABLE_USB; + idx = EXTCON_CABLE_USB; + con_sw = DM_DP_SWITCH_USB; + vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB; break; case SM5502_MUIC_ADC_OPEN_TA: - idx = EXTCON_CABLE_TA; + idx = EXTCON_CABLE_TA; + con_sw = DM_DP_SWITCH_OPEN; + vbus_sw = VBUSIN_SWITCH_VBUSOUT; break; case SM5502_MUIC_ADC_OPEN_USB_OTG: - idx = EXTCON_CABLE_USB_HOST; - break; - case SM5502_MUIC_ADC_GROUND: + idx = EXTCON_CABLE_USB_HOST; + con_sw = DM_DP_SWITCH_USB; + vbus_sw = VBUSIN_SWITCH_OPEN; break; default: dev_dbg(info->dev, @@ -359,6 +421,12 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, return 0; }; + /* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */ + ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached); + if (ret < 0) + return ret; + + /* Change the state of external accessory */ extcon_set_cable_state(info->edev, cable_names[idx], attached); return 0; diff --git a/include/linux/extcon/sm5502.h b/include/linux/extcon/sm5502.h index 17bd655..030526b 100644 --- a/include/linux/extcon/sm5502.h +++ b/include/linux/extcon/sm5502.h @@ -223,6 +223,29 @@ enum sm5502_reg { #define SM5502_REG_DEV_TYPE2_TTY_MASK (0x1 << SM5502_REG_DEV_TYPE2_TTY_SHIFT) #define SM5502_REG_DEV_TYPE2_AV_CABLE_MASK (0x1 << SM5502_REG_DEV_TYPE2_AV_CABLE_SHIFT) +#define SM5502_REG_MANUAL_SW1_VBUSIN_SHIFT 0 +#define SM5502_REG_MANUAL_SW1_DP_SHIFT 2 +#define SM5502_REG_MANUAL_SW1_DM_SHIFT 5 +#define SM5502_REG_MANUAL_SW1_VBUSIN_MASK (0x3 << SM5502_REG_MANUAL_SW1_VBUSIN_SHIFT) +#define SM5502_REG_MANUAL_SW1_DP_MASK (0x7 << SM5502_REG_MANUAL_SW1_DP_SHIFT) +#define SM5502_REG_MANUAL_SW1_DM_MASK (0x7 << SM5502_REG_MANUAL_SW1_DM_SHIFT) +#define VBUSIN_SWITCH_OPEN 0x0 +#define VBUSIN_SWITCH_VBUSOUT 0x1 +#define VBUSIN_SWITCH_MIC 0x2 +#define VBUSIN_SWITCH_VBUSOUT_WITH_USB 0x3 +#define DM_DP_CON_SWITCH_OPEN 0x0 +#define DM_DP_CON_SWITCH_USB 0x1 +#define DM_DP_CON_SWITCH_AUDIO 0x2 +#define DM_DP_CON_SWITCH_UART 0x3 +#define DM_DP_SWITCH_OPEN ((DM_DP_CON_SWITCH_OPEN <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ + | (DM_DP_CON_SWITCH_OPEN <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) +#define DM_DP_SWITCH_USB ((DM_DP_CON_SWITCH_USB <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ + | (DM_DP_CON_SWITCH_USB <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) +#define DM_DP_SWITCH_AUDIO ((DM_DP_CON_SWITCH_AUDIO <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ + | (DM_DP_CON_SWITCH_AUDIO <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) +#define DM_DP_SWITCH_UART ((DM_DP_CON_SWITCH_UART <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ + | (DM_DP_CON_SWITCH_UART <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) + /* SM5502 Interrupts */ enum sm5502_irq { /* INT1 */ |