Exemplo n.º 1
0
void bcmpmu_otg_xceiv_do_srp(struct bcmpmu_otg_xceiv_data *xceiv_data)
{
#ifdef CONFIG_USB_OTG
    if (xceiv_data->otg_xceiver.xceiver.gadget && xceiv_data->otg_xceiver.xceiver.gadget->ops &&
            xceiv_data->otg_xceiver.xceiver.gadget->ops->wakeup &&
            xceiv_data->otg_enabled) {

        bool vbus_status = 0;

        bcmpmu_usb_get(xceiv_data->bcmpmu,
                       BCMPMU_USB_CTRL_GET_VBUS_STATUS, &vbus_status);

        /* Should do SRP only if Vbus is not valid */
        if (!vbus_status) {
            bcm_hsotgctrl_phy_set_non_driving(false);
            /* Do SRP */
            xceiv_data->otg_xceiver.xceiver.gadget->
            ops->wakeup(xceiv_data->otg_xceiver.xceiver.gadget);
            /* Start SRP failure timer to do ADP probes
             * if it expires
             */
            xceiv_data->otg_xceiver.srp_failure_timer.expires =
                jiffies +
                msecs_to_jiffies(T_SRP_FAILURE_MAX_IN_MS);
            add_timer(&xceiv_data->otg_xceiver.srp_failure_timer);

            /* SRP initiated. Clear the flag */
            xceiv_data->otg_xceiver.otg_srp_reqd = false;
        }
    }
#endif
}
Exemplo n.º 2
0
static void bcmpmu_otg_xceiv_chg_detect_handler(struct work_struct *work)
{
    struct bcmpmu_otg_xceiv_data *xceiv_data =
        container_of(work, struct bcmpmu_otg_xceiv_data,
                     bcm_otg_chg_detect_work);

    dev_info(xceiv_data->dev, "Charger detect event\n");

    /* Read and save USB charger type */
    bcmpmu_usb_get(xceiv_data->bcmpmu,
                   BCMPMU_USB_CTRL_GET_USB_TYPE,
                   (void *)&xceiv_data->usb_charger_type);

    if (xceiv_data->otg_enabled) {
        /* Core is already up so just set the Vbus status */
        bcm_hsotgctrl_phy_set_vbus_stat(true);

        /* Vbus is up so allow the core to connect */
        bcm_hsotgctrl_phy_set_non_driving(false);
    } else {
        bool id_default_host = false;

        id_default_host =
            bcmpmu_otg_xceiv_check_id_gnd(xceiv_data) ||
            bcmpmu_otg_xceiv_check_id_rid_a(xceiv_data);

        if (!id_default_host && xceiv_data->otg_xceiver.xceiver.gadget)
            atomic_notifier_call_chain(&xceiv_data->otg_xceiver.
                                       xceiver.notifier,
                                       USB_EVENT_VBUS, NULL);
    }
}
Exemplo n.º 3
0
bool bcmpmu_otg_xceiv_check_id_rid_a(struct bcmpmu_otg_xceiv_data
                                     *xceiv_data)
{
    unsigned int data = 0;
    bool id_rid_a = false;

    bcmpmu_usb_get(xceiv_data->bcmpmu, BCMPMU_USB_CTRL_GET_ID_VALUE, &data);
    id_rid_a = (data == PMU_USB_ID_RID_A);

    return id_rid_a;
}
Exemplo n.º 4
0
bool bcmpmu_otg_xceiv_check_id_gnd(struct bcmpmu_otg_xceiv_data
                                   *xceiv_data)
{
    unsigned int data=0;
    bool id_gnd = false;

    bcmpmu_usb_get(xceiv_data->bcmpmu, BCMPMU_USB_CTRL_GET_ID_VALUE, &data);
    id_gnd = (data == PMU_USB_ID_GROUND);

    return id_gnd;
}
Exemplo n.º 5
0
static void bcmpmu_otg_xceiv_id_change_handler(struct work_struct *work)
{
	struct bcmpmu_otg_xceiv_data *xceiv_data =
	    container_of(work, struct bcmpmu_otg_xceiv_data,
			 bcm_otg_id_status_change_work);
	unsigned int new_id;
	bool id_gnd = false;
	bool id_rid_a = false;
	bool id_rid_c = false;

	dev_info(xceiv_data->dev, "ID change detected\n");

	bcmpmu_usb_get(xceiv_data->bcmpmu,
		BCMPMU_USB_CTRL_GET_ID_VALUE,
		&new_id);

	if (xceiv_data->prev_otg_id != new_id) {
		id_gnd = bcmpmu_otg_xceiv_check_id_gnd(xceiv_data);
		id_rid_a = bcmpmu_otg_xceiv_check_id_rid_a(xceiv_data);
		id_rid_c = bcmpmu_otg_xceiv_check_id_rid_c(xceiv_data);

		bcm_hsotgctrl_phy_set_id_stat(!(id_gnd || id_rid_a));

		if (id_gnd) {
			/* If ID is gnd, we need to turn on
			 * Vbus within 200ms
			 */
			bcmpmu_otg_xceiv_set_vbus(&xceiv_data->otg_xceiver.
				    xceiver, true);
		}

		if (!id_rid_c)
			msleep(HOST_TO_PERIPHERAL_DELAY_MS);

		if (id_gnd || id_rid_a || id_rid_c) {
			bcm_hsotgctrl_phy_deinit();
			xceiv_data->otg_xceiver.xceiver.state =
				OTG_STATE_UNDEFINED;
			atomic_notifier_call_chain(&xceiv_data->
				otg_xceiver.xceiver.notifier,
				USB_EVENT_ID, NULL);
		}
	}

	/* Update local ID copy */
	xceiv_data->prev_otg_id = new_id;

}
Exemplo n.º 6
0
static int bcmpmu_otg_xceiv_vbus_notif_handler(struct notifier_block *nb,
        unsigned long value, void *data)
{
    struct bcmpmu_otg_xceiv_data *xceiv_data =
        container_of(nb, struct bcmpmu_otg_xceiv_data,
                     bcm_otg_vbus_validity_notifier);
    bool vbus_status = 0;

    if (!xceiv_data)
        return -EINVAL;

    bcmpmu_usb_get(xceiv_data->bcmpmu, BCMPMU_USB_CTRL_GET_VBUS_STATUS, &vbus_status);
    queue_work(xceiv_data->bcm_otg_work_queue, vbus_status ? &xceiv_data->bcm_otg_vbus_valid_work : &xceiv_data->bcm_otg_vbus_a_invalid_work);

    return 0;
}
static int bcmpmu_otg_xceiv_set_peripheral(struct otg_transceiver *otg,
					   struct usb_gadget *gadget)
{
	struct bcmpmu_otg_xceiv_data *xceiv_data = dev_get_drvdata(otg->dev);
	int status = 0;
	bool id_default_host = false;

	dev_dbg(xceiv_data->dev, "Setting Peripheral\n");
	otg->gadget = gadget;

	id_default_host = bcmpmu_otg_xceiv_check_id_gnd(xceiv_data) ||
		  bcmpmu_otg_xceiv_check_id_rid_a(xceiv_data);

	if (!id_default_host) {
		if (xceiv_data->otg_enabled &&
			(bcmpmu_otg_xceiv_check_id_rid_b(xceiv_data) ==
			    false)) { /* No SRP if RID_B */
			/* REVISIT. Shutdown uses sequence for lowest power
			 * and does not meet timing so don't do that in OTG mode
			 * for now. Just do SRP for ADP startup */
			bcmpmu_otg_xceiv_do_srp(xceiv_data);
		} else {
			int data;
			bcmpmu_usb_get(xceiv_data->bcmpmu,
				       BCMPMU_USB_CTRL_GET_USB_TYPE, &data);
			if ((data != PMU_USB_TYPE_SDP)
			    && (data != PMU_USB_TYPE_CDP)) {
				/* Shutdown the core */
				atomic_notifier_call_chain(&xceiv_data->
							   otg_xceiver.xceiver.
							   notifier,
							   USB_EVENT_NONE,
							   NULL);
			}
		}

	} else {
		bcm_hsotgctrl_phy_set_id_stat(false);
		/* Come up connected  */
		bcm_hsotgctrl_phy_set_non_driving(false);
	}

	return status;
}
Exemplo n.º 8
0
static void bcmpmu_otg_xceiv_id_change_handler(struct work_struct *work)
{
    struct bcmpmu_otg_xceiv_data *xceiv_data =
        container_of(work, struct bcmpmu_otg_xceiv_data,
                     bcm_otg_id_status_change_work);
    unsigned int new_id;

    bool id_gnd = false;
    bool id_rid_a = false;
    bool id_rid_c = false;

    dev_info(xceiv_data->dev, "ID change detected\n");

    bcmpmu_usb_get(xceiv_data->bcmpmu,
                   BCMPMU_USB_CTRL_GET_ID_VALUE,
                   &new_id);

    if (xceiv_data->otg_enabled) {
        /* Stop any stale ADP probe/sense attempts */
        bcm_otg_do_adp_probe(xceiv_data, false);
        bcm_otg_do_adp_sense(xceiv_data, false);

        /* Use n and n-1 comparison method */
        bcmpmu_usb_set(xceiv_data->bcmpmu,
                       BCMPMU_USB_CTRL_SET_ADP_COMP_METHOD, 1);
    }

    if ((xceiv_data->prev_otg_id != new_id) ||
            xceiv_data->otg_enabled) {
        id_gnd = bcmpmu_otg_xceiv_check_id_gnd(xceiv_data);
        id_rid_a = bcmpmu_otg_xceiv_check_id_rid_a(xceiv_data);
        id_rid_c = bcmpmu_otg_xceiv_check_id_rid_c(xceiv_data);

        bcm_hsotgctrl_phy_set_id_stat(!(id_gnd || id_rid_a));

        atomic_notifier_call_chain(&xceiv_data->otg_xceiver.
                                   xceiver.notifier, USB_EVENT_ID, NULL);
    }

    /* Update local ID copy */
    xceiv_data->prev_otg_id = new_id;

}
Exemplo n.º 9
0
static int __devinit bcmpmu_otg_xceiv_probe(struct platform_device *pdev)
{
    int error = 0;
    struct bcmpmu_otg_xceiv_data *xceiv_data;
    struct bcmpmu *bcmpmu = pdev->dev.platform_data;

    dev_info(&pdev->dev, "Probing started...\n");

    xceiv_data = kzalloc(sizeof(*xceiv_data), GFP_KERNEL);
    if (!xceiv_data) {
        dev_warn(&pdev->dev, "Memory allocation failed\n");
        return -ENOMEM;
    }

    /* REVISIT: Currently there isn't a way to obtain
     * regulator string associated with USB. Hardcode for now
     */
    xceiv_data->bcm_hsotg_regulator =
        regulator_get(NULL, "usbldo_uc");

    if (IS_ERR(xceiv_data->bcm_hsotg_regulator)) {
        dev_warn(&pdev->dev, "Failed to get regulator handle\n");
        kfree(xceiv_data);
        return -ENODEV;
    }

    /* Enable USB LDO */
    regulator_enable(xceiv_data->bcm_hsotg_regulator);
    xceiv_data->regulator_enabled = true;
    /* Give 2ms to ramp up USBLDO */
    mdelay(USBLDO_RAMP_UP_DELAY_IN_MS);
    xceiv_data->dev = &pdev->dev;
    xceiv_data->bcmpmu = bcmpmu;
    xceiv_data->otg_xceiver.xceiver.dev = xceiv_data->dev;
    xceiv_data->otg_xceiver.xceiver.label = "bcmpmu_otg_xceiv";
    xceiv_data->host = false;
    xceiv_data->vbus_enabled = false;

    /* Create a work queue for OTG work items */
    xceiv_data->bcm_otg_work_queue = create_workqueue("bcm_otg_events");
    if (xceiv_data->bcm_otg_work_queue == NULL) {
        dev_warn(&pdev->dev,
                 "BCM OTG events work queue creation failed\n");
        bcmpmu_otg_free_regulator(xceiv_data);
        kfree(xceiv_data);
        return -ENOMEM;
    }

    /* Create one work item per deferrable function */
    INIT_WORK(&xceiv_data->bcm_otg_vbus_invalid_work,
              bcmpmu_otg_xceiv_vbus_invalid_handler);
    INIT_WORK(&xceiv_data->bcm_otg_vbus_valid_work,
              bcmpmu_otg_xceiv_vbus_valid_handler);
    INIT_WORK(&xceiv_data->bcm_otg_vbus_a_invalid_work,
              bcmpmu_otg_xceiv_vbus_a_invalid_handler);
    INIT_WORK(&xceiv_data->bcm_otg_vbus_a_valid_work,
              bcmpmu_otg_xceiv_vbus_a_valid_handler);
    INIT_WORK(&xceiv_data->bcm_otg_id_status_change_work,
              bcmpmu_otg_xceiv_id_change_handler);
    INIT_WORK(&xceiv_data->bcm_otg_chg_detect_work,
              bcmpmu_otg_xceiv_chg_detect_handler);
    INIT_WORK(&xceiv_data->bcm_otg_sess_end_srp_work,
              bcmpmu_otg_xceiv_sess_end_srp_handler);
    INIT_DELAYED_WORK(&xceiv_data->bcm_otg_delayed_adp_work,
                      bcmpmu_otg_xceiv_delayed_adp_handler);

    /* Initial value for previous OTG ID value.
     * 0 means unsupported
     */
    xceiv_data->prev_otg_id = 0;

    /* Charger type not known yet */
    xceiv_data->usb_charger_type = PMU_USB_TYPE_NONE;

    xceiv_data->otg_xceiver.xceiver.state = OTG_STATE_UNDEFINED;
    xceiv_data->otg_xceiver.xceiver.set_vbus =
        bcmpmu_otg_xceiv_set_vbus;
    xceiv_data->otg_xceiver.xceiver.set_peripheral =
        bcmpmu_otg_xceiv_set_peripheral;
    xceiv_data->otg_xceiver.xceiver.set_host =
        bcmpmu_otg_xceiv_set_host;
    xceiv_data->otg_xceiver.xceiver.shutdown =
        bcmpmu_otg_xceiv_shutdown;
    xceiv_data->otg_xceiver.xceiver.init =
        bcmpmu_otg_xceiv_start;

    xceiv_data->otg_xceiver.xceiver.set_power =
        bcmpmu_otg_xceiv_set_vbus_power;

    xceiv_data->otg_xceiver.xceiver.set_delayed_adp =
        bcmpmu_otg_xceiv_set_delayed_adp;
    xceiv_data->otg_xceiver.xceiver.set_srp_reqd =
        bcmpmu_otg_xceiv_set_srp_reqd_handler;
    xceiv_data->otg_xceiver.xceiver.pullup_on =
        bcmpmu_otg_xceiv_pullup_on;
    xceiv_data->otg_xceiver.xceiver.set_otg_enable =
        bcmpmu_otg_xceiv_set_otg_enable;
    xceiv_data->otg_xceiver.xceiver.set_suspend =
        bcmpmu_otg_xceiv_set_suspend;

    ATOMIC_INIT_NOTIFIER_HEAD(&xceiv_data->otg_xceiver.xceiver.notifier);

    xceiv_data->bcm_otg_vbus_validity_notifier.notifier_call =
        bcmpmu_otg_xceiv_vbus_notif_handler;
    bcmpmu_add_notifier(BCMPMU_USB_EVENT_VBUS_VALID,
                        &xceiv_data->bcm_otg_vbus_validity_notifier);

    xceiv_data->bcm_otg_vbus_invalidity_notifier.notifier_call =
        bcmpmu_otg_xceiv_vbus_invalid_notif_handler;
    bcmpmu_add_notifier(BCMPMU_USB_EVENT_VBUS_INVALID,
                        &xceiv_data->bcm_otg_vbus_invalidity_notifier);

    bcmpmu_add_notifier(BCMPMU_USB_EVENT_SESSION_INVALID,
                        &xceiv_data->bcm_otg_vbus_validity_notifier);

    xceiv_data->bcm_otg_id_chg_notifier.notifier_call =
        bcmpmu_otg_xceiv_id_chg_notif_handler;
    bcmpmu_add_notifier(BCMPMU_USB_EVENT_ID_CHANGE,
                        &xceiv_data->bcm_otg_id_chg_notifier);

    xceiv_data->bcm_otg_chg_detection_notifier.notifier_call =
        bcmpmu_otg_xceiv_chg_detection_notif_handler;
    bcmpmu_add_notifier(BCMPMU_USB_EVENT_USB_DETECTION,
                        &xceiv_data->bcm_otg_chg_detection_notifier);


    wake_lock_init(&xceiv_data->otg_xceiver.xceiver_wake_lock, WAKE_LOCK_SUSPEND, "otg_xcvr_wakelock");

#ifdef CONFIG_USB_OTG
    init_timer(&xceiv_data->otg_xceiver.srp_failure_timer);
    xceiv_data->otg_xceiver.srp_failure_timer.data = (unsigned long)xceiv_data;
    xceiv_data->otg_xceiver.srp_failure_timer.function = bcmpmu_otg_xceiv_srp_failure_handler;

    init_timer(&xceiv_data->otg_xceiver.sess_end_srp_timer);
    xceiv_data->otg_xceiver.sess_end_srp_timer.data = (unsigned long)xceiv_data;
    xceiv_data->otg_xceiver.sess_end_srp_timer.function = bcmpmu_otg_xceiv_sess_end_srp_timer_handler;

    error = bcm_otg_adp_init(xceiv_data);
    if (error)
        goto error_attr_host;
#endif

    otg_set_transceiver(&xceiv_data->otg_xceiver.xceiver);
    local_otg_xceiver = &xceiv_data->otg_xceiver.xceiver;

    platform_set_drvdata(pdev, xceiv_data);

    error = device_create_file(&pdev->dev, &dev_attr_host);
    if (error) {
        dev_warn(&pdev->dev, "Failed to create HOST file\n");
        goto error_attr_host;;
    }

    error = device_create_file(&pdev->dev, &dev_attr_vbus);
    if (error) {
        dev_warn(&pdev->dev, "Failed to create VBUS file\n");
        goto error_attr_vbus;
    }

    error = device_create_file(&pdev->dev, &dev_attr_wake);
    if (error) {
        dev_warn(&pdev->dev, "Failed to create WAKE file\n");
        goto error_attr_wake;
    }

    /* Save original ID value */
    bcmpmu_usb_get(xceiv_data->bcmpmu,
                   BCMPMU_USB_CTRL_GET_ID_VALUE,
                   &xceiv_data->prev_otg_id);

    /* Check if we should default to A-device */
    xceiv_data->otg_xceiver.xceiver.default_a =
        bcmpmu_otg_xceiv_check_id_gnd(xceiv_data) ||
        bcmpmu_otg_xceiv_check_id_rid_a(xceiv_data);

    bcmpmu_otg_xceiv_set_def_state(xceiv_data,
                                   xceiv_data->otg_xceiver.xceiver.default_a);

    pm_runtime_set_active(&pdev->dev);
    pm_runtime_enable(&pdev->dev);

    dev_info(&pdev->dev, "Probing successful\n");

    return 0;

error_attr_wake:
    device_remove_file(xceiv_data->dev, &dev_attr_vbus);

error_attr_vbus:
    device_remove_file(xceiv_data->dev, &dev_attr_host);

error_attr_host:
    wake_lock_destroy(&xceiv_data->otg_xceiver.xceiver_wake_lock);
    destroy_workqueue(xceiv_data->bcm_otg_work_queue);
    bcmpmu_otg_free_regulator(xceiv_data);
    kfree(xceiv_data);
    return error;
}
Exemplo n.º 10
0
static int bcm59055_battery_probe(struct platform_device *pdev)
{
	int ret, usb_typ;
	struct bcm59055_power *battery_data;
	struct bcm590xx *bcm59055 = dev_get_drvdata(pdev->dev.parent);
	struct bcm590xx_battery_pdata *battery_pdata;

	pr_info("BCM59055 Battery Driver Probe\n");
	battery_pdata = bcm59055->pdata->battery_pdata;

	battery_data = kzalloc(sizeof(struct bcm59055_power), GFP_KERNEL);
	if (battery_data == NULL) {
		pr_info("%s : Failed to allocate memory for bcm59055_power\n", __func__);
		ret = -ENOMEM;
		goto err_data_alloc;
	}

	battery_data->bcm590xx = bcm59055;
	battery_data->batt_max_cap = battery_pdata->batt_max_capacity;
	battery_data->batt_min_volt = battery_pdata->batt_min_volt;
	battery_data->batt_max_volt = battery_pdata->batt_max_volt;
	battery_data->batt_technology = battery_pdata->batt_technology;
	battery_data->usb_cc = PRE_ENUM_CURRENT;
	battery_data->batt_vol = battery_pdata->batt_vol;
	battery_data->batt_adc = battery_pdata->batt_adc;
	battery_data->power_src = POWER_SUPPLY_TYPE_BATTERY;
	INIT_DELAYED_WORK(&battery_data->batt_lvl_wq, bcm59055_batt_lvl_wq);

	platform_set_drvdata(pdev, battery_data);
	pvt_data = battery_data;
	ret = bcm59055_init_charger(battery_data);
	if (ret < 0) {
		pr_info("%s Charger initialization failed. \n", __func__);
		goto err_ac_register;
	}

	if (get_batt_percentage(battery_data) < 0)
		battery_data->batt_percentage = 50;
	/* For now no FG and ADC is connected in rayboard so hardcoding the capacity */
	battery_data->batt_percentage = 50;
	battery_data->battery.name = "bcm59055-battery";
	battery_data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
	battery_data->battery.properties = bcm59055_battery_props;
	battery_data->battery.num_properties = ARRAY_SIZE(bcm59055_battery_props);
	battery_data->battery.get_property = bcm59055_battery_get_property;
	battery_data->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;

	battery_data->wall.name = "bcm59055-usb";
	battery_data->wall.type = POWER_SUPPLY_TYPE_USB;
	battery_data->wall.properties = bcm59055_usb_props;
	battery_data->wall.num_properties = ARRAY_SIZE(bcm59055_usb_props);
	battery_data->wall.get_property = bcm59055_usb_get_property;

	battery_data->usb.name = "bcm59055-wall";
	battery_data->usb.type = POWER_SUPPLY_TYPE_MAINS;
	battery_data->usb.properties = bcm59055_wall_props;
	battery_data->usb.num_properties = ARRAY_SIZE(bcm59055_wall_props);
	battery_data->usb.get_property = bcm59055_wall_get_property;

	/* Register AC power supply */
	ret = power_supply_register(&pdev->dev, &battery_data->wall);
	if (ret) {
		pr_info("%s : Failed to register WALL power supply\n", __func__);
		goto err_ac_register;
	}

	/* Register battery power supply */
	ret = power_supply_register(&pdev->dev, &battery_data->usb);
	if (ret) {
		pr_info("%s : Failed to register battery power supply\n", __func__);
		goto err_battery_register;
	}

	/* Register USB power supply */
	ret = power_supply_register(&pdev->dev, &battery_data->battery);
	if (ret) {
		pr_info("%s : Failed to register battery power supply\n", __func__);
		goto err_usb_register;
	}
	battery_data->usb_type = USB_OFFLINE;
	// Check if charger is already connected
	ret = bcm59055_get_power_supply_type(battery_data);
	if (ret != POWER_SUPPLY_TYPE_BATTERY /*&& usb_driver_init*/) {
		pr_info("%s: Charger connected %s\n", __func__,
				(ret == POWER_SUPPLY_TYPE_MAINS ? "Wall" : "USB"));
		if (ret == POWER_SUPPLY_TYPE_MAINS)
			bcm59055_power_isr(BCM59055_IRQID_INT2_CHGINS, battery_data);
		else {
			usb_typ = bcmpmu_usb_get(BCMPMU_CTRL_GET_CHARGER_TYPE, bcm59055);
			battery_data->power_src = POWER_SUPPLY_TYPE_USB;
			printk("%s: USB Type %d\n", __func__, usb_typ);
			bcm59055_battery_cb(&battery_data->nb,
					BCMPMU_USB_EVENT_CHGR_DETECTION, &usb_typ);
		}
	}
	printk("%s: pwr_src %d, usb_typ %d\n", __func__, battery_data->power_src,
			battery_data->usb_type);
	schedule_delayed_work(&battery_data->batt_lvl_wq, 0 );
	printk("%s: Probe Sucess\n", __func__);
	return 0;

err_usb_register:
	power_supply_unregister(&battery_data->usb);
err_battery_register:
	power_supply_unregister(&battery_data->wall);
err_ac_register:
	kfree(battery_data);
err_data_alloc:
	return ret;
}