/* * Use delayed workqueue to detect cable state and then * notify cable state to notifiee/platform through uevent. * After completing the booting of platform, the extcon provider * driver should notify cable state to upper layer.
*/ struct delayed_work wq_detcable;
};
/* * The below accessories has same ADC value (0x1f). * So, Device type1 is used to separate specific accessory.
*/ /* |---------|--ADC| */ /* | [7:5]|[4:0]| */
RT8973A_MUIC_ADC_USB = 0x3f, /* | 001|11111| */
};
/* Change DM_CON/DP_CON/VBUSIN switch according to cable type */ staticint rt8973a_muic_set_path(struct rt8973a_muic_info *info, unsignedint con_sw, bool attached)
{ int ret;
/* * Don't need to set h/w path according to cable type * if Auto-configuration mode of CONTROL1 register is true.
*/ if (info->auto_config) return 0;
if (!attached)
con_sw = DM_DP_SWITCH_UART;
switch (con_sw) { case DM_DP_SWITCH_OPEN: case DM_DP_SWITCH_USB: case DM_DP_SWITCH_UART:
ret = regmap_update_bits(info->regmap, RT8973A_REG_MANUAL_SW1,
RT8973A_REG_MANUAL_SW1_DP_MASK |
RT8973A_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;
}
/* Read ADC value according to external cable or button */
ret = regmap_read(info->regmap, RT8973A_REG_ADC, &adc); if (ret) {
dev_err(info->dev, "failed to read ADC register\n"); return ret;
}
cable_type = adc & RT8973A_REG_ADC_MASK;
/* Read Device 1 reigster to identify correct cable type */
ret = regmap_read(info->regmap, RT8973A_REG_DEV1, &dev1); if (ret) {
dev_err(info->dev, "failed to read DEV1 register\n"); return ret;
}
switch (event) { case RT8973A_EVENT_ATTACH:
cable_type = rt8973a_muic_get_cable_type(info);
attached = true; break; case RT8973A_EVENT_DETACH:
cable_type = prev_cable_type;
attached = false; break; case RT8973A_EVENT_OVP: case RT8973A_EVENT_OTP:
dev_warn(info->dev, "happen Over %s issue. Need to disconnect all cables\n",
event == RT8973A_EVENT_OVP ? "Voltage" : "Temperature");
cable_type = prev_cable_type;
attached = false; break; default:
dev_err(info->dev, "Cannot handle this event (event:%d)\n", event); return -EINVAL;
}
prev_cable_type = cable_type;
switch (cable_type) { case RT8973A_MUIC_ADC_OTG:
id = EXTCON_USB_HOST;
con_sw = DM_DP_SWITCH_USB; break; case RT8973A_MUIC_ADC_TA:
id = EXTCON_CHG_USB_DCP;
con_sw = DM_DP_SWITCH_OPEN; break; case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB: case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB:
id = EXTCON_JIG;
con_sw = DM_DP_SWITCH_USB; break; case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART: case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART:
id = EXTCON_JIG;
con_sw = DM_DP_SWITCH_UART; break; case RT8973A_MUIC_ADC_USB:
id = EXTCON_USB;
con_sw = DM_DP_SWITCH_USB; break; case RT8973A_MUIC_ADC_OPEN: return 0; case RT8973A_MUIC_ADC_UNKNOWN_ACC_1: case RT8973A_MUIC_ADC_UNKNOWN_ACC_2: case RT8973A_MUIC_ADC_UNKNOWN_ACC_3: case RT8973A_MUIC_ADC_UNKNOWN_ACC_4: case RT8973A_MUIC_ADC_UNKNOWN_ACC_5:
dev_warn(info->dev, "Unknown accessory type (adc:0x%x)\n", cable_type); return 0; case RT8973A_MUIC_ADC_AUDIO_SEND_END_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S1_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S2_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S3_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S4_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S5_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S6_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S7_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S8_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S9_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S10_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S11_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S12_BUTTON: case RT8973A_MUIC_ADC_AUDIO_TYPE2:
dev_warn(info->dev, "Audio device/button type (adc:0x%x)\n", cable_type); return 0; case RT8973A_MUIC_ADC_RESERVED_ACC_1: case RT8973A_MUIC_ADC_RESERVED_ACC_2: case RT8973A_MUIC_ADC_RESERVED_ACC_3: case RT8973A_MUIC_ADC_RESERVED_ACC_4: case RT8973A_MUIC_ADC_RESERVED_ACC_5: case RT8973A_MUIC_ADC_PHONE_POWERED_DEV: return 0; default:
dev_err(info->dev, "Cannot handle this cable_type (adc:0x%x)\n",
cable_type); return -EINVAL;
}
/* Change internal hardware path(DM_CON/DP_CON) */
ret = rt8973a_muic_set_path(info, con_sw, attached); if (ret < 0) return ret;
/* Change the state of external accessory */
extcon_set_state_sync(info->edev, id, attached); if (id == EXTCON_USB)
extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
attached);
return 0;
}
staticvoid rt8973a_muic_irq_work(struct work_struct *work)
{ struct rt8973a_muic_info *info = container_of(work, struct rt8973a_muic_info, irq_work); int ret = 0;
if (!info->edev) return;
mutex_lock(&info->mutex);
/* Detect attached or detached cables */ if (info->irq_attach) {
ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_ATTACH);
info->irq_attach = false;
}
if (info->irq_detach) {
ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_DETACH);
info->irq_detach = false;
}
if (info->irq_ovp) {
ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_OVP);
info->irq_ovp = false;
}
if (info->irq_otp) {
ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_OTP);
info->irq_otp = false;
}
if (ret < 0)
dev_err(info->dev, "failed to handle MUIC interrupt\n");
mutex_unlock(&info->mutex);
}
static irqreturn_t rt8973a_muic_irq_handler(int irq, void *data)
{ struct rt8973a_muic_info *info = data; int i, irq_type = -1;
for (i = 0; i < info->num_muic_irqs; i++) if (irq == info->muic_irqs[i].virq)
irq_type = info->muic_irqs[i].irq;
switch (irq_type) { case RT8973A_INT1_ATTACH:
info->irq_attach = true; break; case RT8973A_INT1_DETACH:
info->irq_detach = true; break; case RT8973A_INT1_OVP:
info->irq_ovp = true; break; case RT8973A_INT1_OTP:
info->irq_otp = true; break; case RT8973A_INT1_CHGDET: case RT8973A_INT1_DCD_T: case RT8973A_INT1_CONNECT: case RT8973A_INT1_ADC_CHG: case RT8973A_INT2_UVLO: case RT8973A_INT2_POR: case RT8973A_INT2_OTP_FET: case RT8973A_INT2_OVP_FET: case RT8973A_INT2_OCP_LATCH: case RT8973A_INT2_OCP: case RT8973A_INT2_OVP_OCP: default:
dev_dbg(info->dev, "Cannot handle this interrupt (%d)\n", irq_type); break;
}
/* Notify the state of connector cable or not */
ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_ATTACH); if (ret < 0)
dev_warn(info->dev, "failed to detect cable state\n");
}
staticvoid rt8973a_init_dev_type(struct rt8973a_muic_info *info)
{ unsignedint data, vendor_id, version_id; int i, ret;
/* To test I2C, Print version_id and vendor_id of RT8973A */
ret = regmap_read(info->regmap, RT8973A_REG_DEVICE_ID, &data); if (ret) {
dev_err(info->dev, "failed to read DEVICE_ID register: %d\n", ret); return;
}
/* Initiazle the register of RT8973A device to bring-up */ for (i = 0; i < info->num_reg_data; i++) {
u8 reg = info->reg_data[i].reg;
u8 mask = info->reg_data[i].mask;
u8 val = 0;
if (info->reg_data[i].invert)
val = ~info->reg_data[i].val; else
val = info->reg_data[i].val;
/* Check whether RT8973A is auto switching mode or not */
ret = regmap_read(info->regmap, RT8973A_REG_CONTROL1, &data); if (ret) {
dev_err(info->dev, "failed to read CONTROL1 register: %d\n", ret); return;
}
data &= RT8973A_REG_CONTROL1_AUTO_CONFIG_MASK; if (data) {
info->auto_config = true;
dev_info(info->dev, "Enable Auto-configuration for internal path\n");
}
}
staticint rt8973a_muic_i2c_probe(struct i2c_client *i2c)
{ struct device_node *np = i2c->dev.of_node; struct rt8973a_muic_info *info; int i, ret, irq_flags;
if (!np) return -EINVAL;
info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM;
i2c_set_clientdata(i2c, info);
/* Allocate extcon device */
info->edev = devm_extcon_dev_allocate(info->dev, rt8973a_extcon_cable); if (IS_ERR(info->edev)) {
dev_err(info->dev, "failed to allocate memory for extcon\n"); return -ENOMEM;
}
/* Register extcon device */
ret = devm_extcon_dev_register(info->dev, info->edev); if (ret) {
dev_err(info->dev, "failed to register extcon device\n"); return ret;
}
/* * Detect accessory after completing the initialization of platform * * - Use delayed workqueue to detect cable state and then * notify cable state to notifiee/platform through uevent. * After completing the booting of platform, the extcon provider * driver should notify cable state to upper layer.
*/
INIT_DELAYED_WORK(&info->wq_detcable, rt8973a_muic_detect_cable_wq);
queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
msecs_to_jiffies(DELAY_MS_DEFAULT));
/* Initialize RT8973A device and print vendor id and version id */
rt8973a_init_dev_type(info);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.