static int sm5502_int_init(struct sm5502_usbsw *usbsw) { struct i2c_client *client = usbsw->client; int ret; u8 intr1, intr2, val; INIT_WORK(&usbsw->work, sm5502_work_cb); ret = gpio_request(pxa_irq_to_gpio(client->irq), "sm5502 irq"); if (ret) { dev_err(&client->dev, "Unable to get gpio %d\n", client->irq); goto gpio_out; } gpio_direction_input(pxa_irq_to_gpio(client->irq)); ret = request_irq(client->irq, microusb_irq_handler, IRQF_NO_SUSPEND | IRQF_TRIGGER_FALLING, "sm5502 micro USB", usbsw); if (ret) { dev_err(&client->dev, "Unable to get IRQ %d\n", client->irq); goto out; } /* Read and Clear INTERRUPT1,2 REGS */ mutex_lock(&usbsw->mutex); read_reg(client, REG_INT1, &intr1); read_reg(client, REG_INT2, &intr2); mutex_unlock(&usbsw->mutex); if ( intr1 & OVP_EVENT_M ) { mutex_lock(&usbsw->mutex); read_reg(client, REG_CTRL, &val); mutex_unlock(&usbsw->mutex); if ( val == 0x1F ) { mutex_lock(&usbsw->mutex); sm5502_reg_init(usbsw); mutex_unlock(&usbsw->mutex); return; } } if ((usbsw->dev1 != 0 || usbsw->dev2 != 0 || usbsw->dev3 != 0) && (intr1 == 0 && intr2 == 0)) { dev_err(&client->dev, "Accs inserted but no data on int regs\n"); } return 0; gpio_out: gpio_free(pxa_irq_to_gpio(client->irq)); out: return ret; }
static ssize_t sm5502_reset(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sm5502_usbsw *usbsw = dev_get_drvdata(dev); struct i2c_client *client = usbsw->client; if (!strncmp(buf, "1", 1)) { dev_info(&client->dev, "sm5502 reset after delay 1000 msec.\n"); msleep(1000); sm5502_write_reg(client, REG_RESET, 0x01); dev_info(&client->dev, "sm5502_reset_control done!\n"); } else { dev_info(&client->dev, "sm5502_reset_control, but not reset_value!\n"); } sm5502_reg_init(usbsw); return count; }
static int __devinit sm5502_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct sm5502_usbsw *usbsw; struct device *switch_dev; int ret = 0; dev_info(&client->dev, "probe start\n"); probing = 1; /* For AT Command FactoryTest */ wakeup_source_init(&jig_suspend_wake, "JIG_UART Connect suspend wake"); usbsw = kzalloc(sizeof(struct sm5502_usbsw), GFP_KERNEL); if (!usbsw) { dev_err(&client->dev, "failed to allocate driver data\n"); return -ENOMEM; } chip = usbsw; usbsw->client = client; usbsw->pdata = client->dev.platform_data; i2c_set_clientdata(client, usbsw); mutex_init(&usbsw->mutex); /* DeskTop Dock */ usbsw->dock_dev.name = "dock"; ret = switch_dev_register(&usbsw->dock_dev); if (ret < 0) dev_err(&client->dev, "dock_dev_register error !!\n"); switch_dev = device_create(sec_class, NULL, 0, NULL, "switch"); if (device_create_file(switch_dev, &dev_attr_adc) < 0) dev_err(&client->dev, "Failed to create device file(%s)!\n", dev_attr_adc.attr.name); if (device_create_file(switch_dev, &dev_attr_usb_state) < 0) dev_err(&client->dev, "Failed to create device file(%s)!\n", dev_attr_usb_state.attr.name); if (device_create_file(switch_dev, &dev_attr_usb_sel) < 0) dev_err(&client->dev, "Failed to create device file(%s)!\n", dev_attr_usb_sel.attr.name); if (device_create_file(switch_dev, &dev_attr_uart_sel) < 0) dev_err(&client->dev, "Failed to create device file(%s)!\n", dev_attr_uart_sel.attr.name); dev_set_drvdata(switch_dev, usbsw); ret = sysfs_create_group(&client->dev.kobj, &sm5502_group); if (ret) { dev_err(&client->dev, "Creating sm5502 attribute group failed\n"); goto sm5502_probe_fail2; } usbsw->qos_idle.name = "Jig driver"; pm_qos_add_request(&usbsw->qos_idle, PM_QOS_CPUIDLE_BLOCK, PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE); ret = sm5502_reg_init(usbsw); if (ret) goto sm5502_probe_fail; /* device detection */ dev_info(&client->dev, "First Detection\n"); detect_dev_sm5502(usbsw, ATTACHED, REV_ACCE, &first_acce); ret = sm5502_int_init(usbsw); if (ret) goto sm5502_probe_fail; probing = 0; dev_info(&client->dev, "PROBE Done.\n"); return 0; sm5502_probe_fail2: if (client->irq) free_irq(client->irq, NULL); sm5502_probe_fail: i2c_set_clientdata(client, NULL); kfree(usbsw); return ret; }
/* microUSB switch IC : SM5502 - Silicon Mitus */ static void detect_dev_sm5502(struct sm5502_usbsw *usbsw, u8 intr1, u8 intr2, void *data) { struct sm5502_platform_data *pdata = usbsw->pdata; struct i2c_client *client = usbsw->client; u8 val1, val2, val3, adc, vbusin, intr1_tmp, val; int dev_classifi = 0; read_reg(client, REG_DEV_T1, &val1); read_reg(client, REG_DEV_T2, &val2); read_reg(client, REG_DEV_T3, &val3); read_reg(client, REG_ADC, &adc); read_reg(client, REG_RSV_ID1, &vbusin); /* IC Bug Case W/A */ if ( intr1 & OVP_EVENT_M ) { read_reg(client, REG_CTRL, &val); if ( val == 0x1F ) { sm5502_reg_init(usbsw); return; } } /* Detach -> Attach quickly */ if (intr1 == (ATTACHED | DETACHED)) { dev_info(&client->dev, "Bug Case 1\n"); intr1 &= ~(DETACHED); } /* Attach -> Detach quickly */ else if (intr1 & ATTACHED && probing != 1) { read_reg(client, REG_INT1, &intr1_tmp); if (intr1_tmp & DETACHED) { dev_info(&client->dev, "Bug Case 2\n"); intr1 &= ~(ATTACHED); } intr1 |= intr1_tmp; } /* Attached */ if (intr1 & ATTACHED || (intr2 & (VBUSOUT_ON | VBUSOUT_OFF) && !(intr1 & DETACHED)) || intr2 & REV_ACCE) { if (val1 & DEV_USB && vbusin & VBUSIN_VALID) { dev_classifi = CABLE_TYPE1_USB_MUIC; dev_info(&client->dev, "USB ATTACHED*****\n"); } if (val1 & DEV_CHARGER && vbusin & VBUSIN_VALID) { dev_classifi = CABLE_TYPE1_TA_MUIC; dev_info(&client->dev, "TA(DCP/CDP) ATTACHED*****\n"); } if (val1 & DEV_USB_OTG) { dev_classifi = CABLE_TYPE1_OTG_MUIC; dev_info(&client->dev, "OTG ATTACHED*****\n"); } if (val1 & DEV_CARKIT_CHG && vbusin & VBUSIN_VALID) { dev_classifi = CABLE_TYPE1_CARKIT_T1OR2_MUIC; manual_usbpath_ctrl(1); dev_info(&client->dev, "CARKIT or L USB Cable ATTACHED*****\n"); } if (val2 & DEV_JIG_UART_OFF) { if (vbusin & VBUSIN_VALID) { dev_classifi = CABLE_TYPE2_JIG_UART_OFF_VB_MUIC; dev_info(&client->dev, "JIG_UARTOFF_VB ATTACHED*****\n"); } else { dev_classifi = CABLE_TYPE2_JIG_UART_OFF_MUIC; dev_info(&client->dev, "JIG_UARTOFF ATTACHED*****\n"); } additional_vbus_int_enable(usbsw); } if (val2 & DEV_JIG_UART_ON) { if (vbusin & VBUSIN_VALID) { dev_classifi = CABLE_TYPE2_JIG_UART_ON_VB_MUIC; dev_info(&client->dev, "JIG_UARTON_VB ATTACHED*****\n"); } else { dev_classifi = CABLE_TYPE2_JIG_UART_ON_MUIC; dev_info(&client->dev, "JIG_UARTON ATTACHED*****\n"); } additional_vbus_int_enable(usbsw); } if (val2 & DEV_JIG_USB_OFF && vbusin & VBUSIN_VALID) { dev_classifi = CABLE_TYPE2_JIG_USB_OFF_MUIC; dev_info(&client->dev, "JIG_USB_OFF ATTACHED*****\n"); } if (val2 & DEV_JIG_USB_ON && vbusin & VBUSIN_VALID) { dev_classifi = CABLE_TYPE2_JIG_USB_ON_MUIC; dev_info(&client->dev, "JIG_USB_ON ATTACHED*****\n"); } if (val2 & DEV_JIG_ALL) { if (!jig_wakelock_acq) { __pm_stay_awake(&jig_suspend_wake); pm_qos_update_request(&usbsw->qos_idle, PM_QOS_CPUIDLE_BLOCK_AXI_VALUE); jig_wakelock_acq = 1; dev_info(&client->dev, "AP WakeLock for FactoryTest *****\n"); } } /* Desktop Dock Case */ if (val2 & DEV_AV) { /* Check device3 register for Dock+VBUS */ if (val3 & DEV_AV_VBUS && vbusin & VBUSIN_VALID) { dev_classifi = CABLE_TYPE3_DESKDOCK_VB_MUIC; dev_info(&client->dev, "DESKDOCK+VBUS ATTACHED*****\n"); } else { dev_classifi = CABLE_TYPE2_DESKDOCK_MUIC; dev_info(&client->dev, "DESKDOCK ATTACHED*****\n"); } additional_vbus_int_enable(usbsw); /* Dock */ switch_set_state(&usbsw->dock_dev, 1); if (jack_is_detected) sm5502_dock_audiopath_ctrl(0); else sm5502_dock_audiopath_ctrl(1); } if (val3 & DEV_U200_CHG && vbusin & VBUSIN_VALID) { dev_classifi = CABLE_TYPE3_U200CHG_MUIC; dev_info(&client->dev, "TA(U200 CHG) ATTACHED*****\n"); } if (val3 & DEV_DCD_OUT_SDP && vbusin & VBUSIN_VALID) { dev_classifi = CABLE_TYPE3_NONSTD_SDP_MUIC; dev_info(&client->dev, "TA(NON-STANDARD SDP) ATTACHED*****\n"); } /* W/A */ if (val1 == 0 && val2 == 0 && val3 == 0 && reset_count < MAX_RESET_TRIAL && probing != 1) { u8 sintm1, sintm2, sctrl, stime1, smansw1; read_reg(client, REG_INT1_MASK, &sintm1); read_reg(client, REG_INT2_MASK, &sintm2); read_reg(client, REG_TIMING1, &stime1); read_reg(client, REG_MANSW1, &smansw1); read_reg(client, REG_CTRL, &sctrl); write_reg(client, REG_RESET, IC_RESET); msleep(20); dev_info(&client->dev, "SM5502 was reset, reset_count : %d\n", reset_count); write_reg(client, REG_INT1_MASK, sintm1); write_reg(client, REG_INT2_MASK, sintm2); write_reg(client, REG_TIMING1, stime1); write_reg(client, REG_MANSW1, smansw1); write_reg(client, REG_CTRL, sctrl); reset_count++; return; } /* for Charger driver */ if (pdata->charger_cb) pdata->charger_cb(dev_classifi); if (probing == 1) *(int *)data = dev_classifi; blocking_notifier_call_chain(&usb_switch_notifier, dev_classifi, NULL); } /* Detached */ if (intr1 & DETACHED) { if (usbsw->dev1 & DEV_USB && usbsw->vbusin & VBUSIN_VALID) { dev_info(&client->dev, "USB DETACHED*****\n"); } if (usbsw->dev1 & DEV_CHARGER && usbsw->vbusin & VBUSIN_VALID) { dev_info(&client->dev, "TA(DCP/CDP) DETACHED*****\n"); } if (usbsw->dev1 & DEV_USB_OTG) { dev_info(&client->dev, "OTG DETACHED*****\n"); } if (usbsw->dev1 & DEV_CARKIT_CHG && usbsw->vbusin & VBUSIN_VALID) { manual_usbpath_ctrl(0); dev_info(&client->dev, "CARKIT or L USB Cable DETACHED*****\n"); } if (usbsw->dev2 & DEV_JIG_UART_OFF) { if (usbsw->vbusin & VBUSIN_VALID) { dev_info(&client->dev, "JIG_UARTOFF+VBUS DETACHED*****\n"); } else { dev_info(&client->dev, "JIG_UARTOFF DETACHED*****\n"); } additional_vbus_int_disable(usbsw); } if (usbsw->dev2 & DEV_JIG_UART_ON) { if (usbsw->vbusin & VBUSIN_VALID) { dev_info(&client->dev, "JIG_UARTON_VB DETACHED*****\n"); } else { dev_info(&client->dev, "JIG_UARTON DETACHED*****\n"); } additional_vbus_int_disable(usbsw); } if (usbsw->dev2 & DEV_JIG_USB_OFF && usbsw->vbusin & VBUSIN_VALID) { dev_info(&client->dev, "JIG_USB_OFF DETACHED*****\n"); } if (usbsw->dev2 & DEV_JIG_USB_ON && usbsw->vbusin & VBUSIN_VALID) { dev_info(&client->dev, "JIG_USB_ON DETACHED*****\n"); } if (usbsw->dev2 & DEV_JIG_ALL) { if (jig_wakelock_acq) { __pm_relax(&jig_suspend_wake); pm_qos_update_request(&usbsw->qos_idle, PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE); jig_wakelock_acq = 0; dev_info(&client->dev, "AP WakeLock Release *****\n"); } } if (usbsw->dev2 & DEV_AV) { /* Check device3 register for Dock+VBUS */ if (usbsw->dev3 & DEV_AV_VBUS && usbsw->vbusin & VBUSIN_VALID ) { dev_info(&client->dev, "DESKDOCK+VBUS DETTACHED*****\n"); } else { dev_info(&client->dev, "DESKDOCK DETACHED*****\n"); } additional_vbus_int_disable(usbsw); /* Dock */ switch_set_state(&usbsw->dock_dev, 0); sm5502_dock_audiopath_ctrl(0); } if (usbsw->dev3 & DEV_U200_CHG && usbsw->vbusin & VBUSIN_VALID) { dev_info(&client->dev, "TA(U200_CHG) DETTACHED*****\n"); } if (usbsw->dev3 & DEV_DCD_OUT_SDP && usbsw->vbusin & VBUSIN_VALID) { dev_info(&client->dev, "TA(NON-STANDARD SDP) DETACHED*****\n"); } /* for Charger driver */ if (pdata->charger_cb) pdata->charger_cb(CABLE_TYPE_NONE_MUIC); blocking_notifier_call_chain(&usb_switch_notifier, CABLE_TYPE_NONE_MUIC, NULL); reset_count = 0; } usbsw->dev1 = val1; usbsw->dev2 = val2; usbsw->dev3 = val3; usbsw->adc = adc; usbsw->vbusin = vbusin; return; }