Beispiel #1
0
static int isp1704_charger_remove(struct platform_device *pdev)
{
	struct isp1704_charger *isp = platform_get_drvdata(pdev);

	usb_unregister_notifier(isp->phy, &isp->nb);
	power_supply_unregister(isp->psy);
	isp1704_charger_set_power(isp, 0);

	return 0;
}
Beispiel #2
0
static int __devexit isp1704_charger_remove(struct platform_device *pdev)
{
	struct isp1704_charger *isp = platform_get_drvdata(pdev);

	otg_unregister_notifier(isp->otg, &isp->nb);
	power_supply_unregister(&isp->psy);
	otg_put_transceiver(isp->otg);
	isp1704_charger_set_power(isp, 0);
	kfree(isp);

	return 0;
}
Beispiel #3
0
static int isp1704_charger_probe(struct platform_device *pdev)
{
	struct isp1704_charger	*isp;
	int			ret = -ENODEV;
	struct power_supply_config psy_cfg = {};

	struct isp1704_charger_data *pdata = dev_get_platdata(&pdev->dev);
	struct device_node *np = pdev->dev.of_node;

	if (np) {
		int gpio = of_get_named_gpio(np, "nxp,enable-gpio", 0);

		if (gpio < 0) {
			dev_err(&pdev->dev, "missing DT GPIO nxp,enable-gpio\n");
			return gpio;
		}

		pdata = devm_kzalloc(&pdev->dev,
			sizeof(struct isp1704_charger_data), GFP_KERNEL);
		if (!pdata) {
			ret = -ENOMEM;
			goto fail0;
		}
		pdata->enable_gpio = gpio;

		dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio);

		ret = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio,
					GPIOF_OUT_INIT_HIGH, "isp1704_reset");
		if (ret) {
			dev_err(&pdev->dev, "gpio request failed\n");
			goto fail0;
		}
	}

	if (!pdata) {
		dev_err(&pdev->dev, "missing platform data!\n");
		return -ENODEV;
	}


	isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
	if (!isp)
		return -ENOMEM;

	if (np)
		isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
	else
		isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);

	if (IS_ERR(isp->phy)) {
		ret = PTR_ERR(isp->phy);
		dev_err(&pdev->dev, "usb_get_phy failed\n");
		goto fail0;
	}

	isp->dev = &pdev->dev;
	platform_set_drvdata(pdev, isp);

	isp1704_charger_set_power(isp, 1);

	ret = isp1704_test_ulpi(isp);
	if (ret < 0) {
		dev_err(&pdev->dev, "isp1704_test_ulpi failed\n");
		goto fail1;
	}

	isp->psy_desc.name		= "isp1704";
	isp->psy_desc.type		= POWER_SUPPLY_TYPE_USB;
	isp->psy_desc.properties	= power_props;
	isp->psy_desc.num_properties	= ARRAY_SIZE(power_props);
	isp->psy_desc.get_property	= isp1704_charger_get_property;

	psy_cfg.drv_data		= isp;

	isp->psy = power_supply_register(isp->dev, &isp->psy_desc, &psy_cfg);
	if (IS_ERR(isp->psy)) {
		ret = PTR_ERR(isp->psy);
		dev_err(&pdev->dev, "power_supply_register failed\n");
		goto fail1;
	}

	/*
	 * REVISIT: using work in order to allow the usb notifications to be
	 * made atomically in the future.
	 */
	INIT_WORK(&isp->work, isp1704_charger_work);

	isp->nb.notifier_call = isp1704_notifier_call;

	ret = usb_register_notifier(isp->phy, &isp->nb);
	if (ret) {
		dev_err(&pdev->dev, "usb_register_notifier failed\n");
		goto fail2;
	}

	dev_info(isp->dev, "registered with product id %s\n", isp->model);

	/*
	 * Taking over the D+ pullup.
	 *
	 * FIXME: The device will be disconnected if it was already
	 * enumerated. The charger driver should be always loaded before any
	 * gadget is loaded.
	 */
	if (isp->phy->otg->gadget)
		usb_gadget_disconnect(isp->phy->otg->gadget);

	if (isp->phy->last_event == USB_EVENT_NONE)
		isp1704_charger_set_power(isp, 0);

	/* Detect charger if VBUS is valid (the cable was already plugged). */
	if (isp->phy->last_event == USB_EVENT_VBUS &&
			!isp->phy->otg->default_a)
		schedule_work(&isp->work);

	return 0;
fail2:
	power_supply_unregister(isp->psy);
fail1:
	isp1704_charger_set_power(isp, 0);
fail0:
	dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);

	return ret;
}
Beispiel #4
0
static void isp1704_charger_work(struct work_struct *data)
{
	struct isp1704_charger	*isp =
		container_of(data, struct isp1704_charger, work);
	static DEFINE_MUTEX(lock);

	mutex_lock(&lock);

	switch (isp->phy->last_event) {
	case USB_EVENT_VBUS:
		/* do not call wall charger detection more times */
		if (!isp->present) {
			isp->online = true;
			isp->present = 1;
			isp1704_charger_set_power(isp, 1);

			/* detect wall charger */
			if (isp1704_charger_detect_dcp(isp)) {
				isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP;
				isp->current_max = 1800;
			} else {
				isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
				isp->current_max = 500;
			}

			/* enable data pullups */
			if (isp->phy->otg->gadget)
				usb_gadget_connect(isp->phy->otg->gadget);
		}

		if (isp->psy_desc.type != POWER_SUPPLY_TYPE_USB_DCP) {
			/*
			 * Only 500mA here or high speed chirp
			 * handshaking may break
			 */
			if (isp->current_max > 500)
				isp->current_max = 500;

			if (isp->current_max > 100)
				isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP;
		}
		break;
	case USB_EVENT_NONE:
		isp->online = false;
		isp->present = 0;
		isp->current_max = 0;
		isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;

		/*
		 * Disable data pullups. We need to prevent the controller from
		 * enumerating.
		 *
		 * FIXME: This is here to allow charger detection with Host/HUB
		 * chargers. The pullups may be enabled elsewhere, so this can
		 * not be the final solution.
		 */
		if (isp->phy->otg->gadget)
			usb_gadget_disconnect(isp->phy->otg->gadget);

		isp1704_charger_set_power(isp, 0);
		break;
	default:
		goto out;
	}

	power_supply_changed(isp->psy);
out:
	mutex_unlock(&lock);
}
static int __devinit isp1704_charger_probe(struct platform_device *pdev)
{
	struct isp1704_charger	*isp;
	int			ret = -ENODEV;

	isp = kzalloc(sizeof *isp, GFP_KERNEL);
	if (!isp)
		return -ENOMEM;

	isp->phy = usb_get_phy();
	if (!isp->phy)
		goto fail0;

	isp->dev = &pdev->dev;
	platform_set_drvdata(pdev, isp);

	isp1704_charger_set_power(isp, 1);

	ret = isp1704_test_ulpi(isp);
	if (ret < 0)
		goto fail1;

	isp->psy.name		= "isp1704";
	isp->psy.type		= POWER_SUPPLY_TYPE_USB;
	isp->psy.properties	= power_props;
	isp->psy.num_properties	= ARRAY_SIZE(power_props);
	isp->psy.get_property	= isp1704_charger_get_property;

	ret = power_supply_register(isp->dev, &isp->psy);
	if (ret)
		goto fail1;

	/*
	 * REVISIT: using work in order to allow the usb notifications to be
	 * made atomically in the future.
	 */
	INIT_WORK(&isp->work, isp1704_charger_work);

	isp->nb.notifier_call = isp1704_notifier_call;

	ret = usb_register_notifier(isp->phy, &isp->nb);
	if (ret)
		goto fail2;

	dev_info(isp->dev, "registered with product id %s\n", isp->model);

	/*
	 * Taking over the D+ pullup.
	 *
	 * FIXME: The device will be disconnected if it was already
	 * enumerated. The charger driver should be always loaded before any
	 * gadget is loaded.
	 */
	if (isp->phy->otg->gadget)
		usb_gadget_disconnect(isp->phy->otg->gadget);

	/* Detect charger if VBUS is valid (the cable was already plugged). */
	ret = isp1704_read(isp, ULPI_USB_INT_STS);
	isp1704_charger_set_power(isp, 0);
	if ((ret & ULPI_INT_VBUS_VALID) && !isp->phy->otg->default_a) {
		isp->event = USB_EVENT_VBUS;
		schedule_work(&isp->work);
	}

	return 0;
fail2:
	power_supply_unregister(&isp->psy);
fail1:
	usb_put_phy(isp->phy);
fail0:
	kfree(isp);

	dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);

	isp1704_charger_set_power(isp, 0);
	return ret;
}
static void isp1704_charger_work(struct work_struct *data)
{
	int			detect;
	unsigned long		event;
	unsigned		power;
	struct isp1704_charger	*isp =
		container_of(data, struct isp1704_charger, work);
	static DEFINE_MUTEX(lock);

	event = isp->event;
	power = isp->max_power;

	mutex_lock(&lock);

	if (event != USB_EVENT_NONE)
		isp1704_charger_set_power(isp, 1);

	switch (event) {
	case USB_EVENT_VBUS:
		isp->online = true;

		/* detect charger */
		detect = isp1704_charger_detect(isp);

		if (detect) {
			isp->present = detect;
			isp->psy.type = isp1704_charger_type(isp);
		}

		switch (isp->psy.type) {
		case POWER_SUPPLY_TYPE_USB_DCP:
			isp->current_max = 1800;
			break;
		case POWER_SUPPLY_TYPE_USB_CDP:
			/*
			 * Only 500mA here or high speed chirp
			 * handshaking may break
			 */
			isp->current_max = 500;
			/* FALLTHROUGH */
		case POWER_SUPPLY_TYPE_USB:
		default:
			/* enable data pullups */
			if (isp->phy->otg->gadget)
				usb_gadget_connect(isp->phy->otg->gadget);
		}
		break;
	case USB_EVENT_NONE:
		isp->online = false;
		isp->current_max = 0;
		isp->present = 0;
		isp->current_max = 0;
		isp->psy.type = POWER_SUPPLY_TYPE_USB;

		/*
		 * Disable data pullups. We need to prevent the controller from
		 * enumerating.
		 *
		 * FIXME: This is here to allow charger detection with Host/HUB
		 * chargers. The pullups may be enabled elsewhere, so this can
		 * not be the final solution.
		 */
		if (isp->phy->otg->gadget)
			usb_gadget_disconnect(isp->phy->otg->gadget);

		isp1704_charger_set_power(isp, 0);
		break;
	case USB_EVENT_ENUMERATED:
		if (isp->present)
			isp->current_max = 1800;
		else
			isp->current_max = power;
		break;
	default:
		goto out;
	}

	power_supply_changed(&isp->psy);
out:
	mutex_unlock(&lock);
}