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;
}
Example #2
0
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;
}