/* A83T specific control bits for PHY0 */ #define PHY_CTL_VBUSVLDEXT BIT(5) #define PHY_CTL_SIDDQ BIT(3) #define PHY_CTL_H3_SIDDQ BIT(1)
/* A83T specific control bits for PHY2 HSIC */ #define SUNXI_EHCI_HS_FORCE BIT(20) #define SUNXI_HSIC_CONNECT_DET BIT(17) #define SUNXI_HSIC_CONNECT_INT BIT(16) #define SUNXI_HSIC BIT(1)
#define MAX_PHYS 4
/* * Note do not raise the debounce time, we must report Vusb high within 100ms * otherwise we get Vbus errors
*/ #define DEBOUNCE_TIME msecs_to_jiffies(50) #define POLL_TIME msecs_to_jiffies(250)
struct sun4i_usb_phy_cfg { int num_phys; int hsic_index;
u32 disc_thresh;
u32 hci_phy_ctl_clear;
u8 phyctl_offset; bool dedicated_clocks; bool phy0_dual_route; bool needs_phy2_siddq; bool siddq_in_base; bool poll_vbusen; int missing_phys;
};
/* set the address */
temp |= ((addr + i) << 8);
writel(temp, phyctl);
/* set the data bit and clear usbc bit*/
temp = readb(phyctl); if (data & 0x1)
temp |= PHYCTL_DATA; else
temp &= ~PHYCTL_DATA;
temp &= ~usbc_bit;
writeb(temp, phyctl);
ret = clk_prepare_enable(phy->clk); if (ret) return ret;
ret = clk_prepare_enable(phy->clk2); if (ret) {
clk_disable_unprepare(phy->clk); return ret;
}
ret = reset_control_deassert(phy->reset); if (ret) {
clk_disable_unprepare(phy->clk2);
clk_disable_unprepare(phy->clk); return ret;
}
/* Some PHYs on some SoCs need the help of PHY2 to work. */ if (data->cfg->needs_phy2_siddq && phy->index != 2) { struct sun4i_usb_phy *phy2 = &data->phys[2];
ret = clk_prepare_enable(phy2->clk); if (ret) {
reset_control_assert(phy->reset);
clk_disable_unprepare(phy->clk2);
clk_disable_unprepare(phy->clk); return ret;
}
ret = reset_control_deassert(phy2->reset); if (ret) {
clk_disable_unprepare(phy2->clk);
reset_control_assert(phy->reset);
clk_disable_unprepare(phy->clk2);
clk_disable_unprepare(phy->clk); return ret;
}
/* * This extra clock is just needed to access the * REG_HCI_PHY_CTL PMU register for PHY2.
*/
ret = clk_prepare_enable(phy2->clk2); if (ret) {
reset_control_assert(phy2->reset);
clk_disable_unprepare(phy2->clk);
reset_control_assert(phy->reset);
clk_disable_unprepare(phy->clk2);
clk_disable_unprepare(phy->clk); return ret;
}
if (phy2->pmu && data->cfg->hci_phy_ctl_clear) {
val = readl(phy2->pmu + REG_HCI_PHY_CTL);
val &= ~data->cfg->hci_phy_ctl_clear;
writel(val, phy2->pmu + REG_HCI_PHY_CTL);
}
clk_disable_unprepare(phy->clk2);
}
if (phy->pmu && data->cfg->hci_phy_ctl_clear) {
val = readl(phy->pmu + REG_HCI_PHY_CTL);
val &= ~data->cfg->hci_phy_ctl_clear;
writel(val, phy->pmu + REG_HCI_PHY_CTL);
}
if (data->cfg->siddq_in_base) { if (phy->index == 0) {
val = readl(data->base + data->cfg->phyctl_offset);
val |= PHY_CTL_VBUSVLDEXT;
val &= ~PHY_CTL_SIDDQ;
writel(val, data->base + data->cfg->phyctl_offset);
}
} else { /* Enable USB 45 Ohm resistor calibration */ if (phy->index == 0)
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
/* * The A31/A23/A33 companion pmics (AXP221/AXP223) do not * generate vbus change interrupts when the board is driving * vbus using the N_VBUSEN pin on the pmic, so we must poll * when using the pmic for vbus-det _and_ we're driving vbus.
*/ if (data->cfg->poll_vbusen && data->vbus_power_supply &&
data->phys[0].regulator_on) returntrue;
/* For phy0 only turn on Vbus if we don't have an ext. Vbus */ if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) &&
data->vbus_det) {
dev_warn(&_phy->dev, "External vbus detected, not enabling our own vbus\n"); return 0;
}
ret = regulator_enable(phy->vbus); if (ret) return ret;
phy->regulator_on = true;
/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */ if (phy->index == 0 && sun4i_usb_phy0_poll(data))
mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
/* * phy0 vbus typically slowly discharges, sometimes this causes the * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
*/ if (phy->index == 0 && !sun4i_usb_phy0_poll(data))
mod_delayed_work(system_wq, &data->detect, POLL_TIME);
if (id_det != data->id_det) { /* id-change, force session end if we've no vbus detection */ if (data->dr_mode == USB_DR_MODE_OTG &&
!sun4i_usb_phy0_have_vbus_det(data))
force_session_end = true;
/* When entering host mode (id = 0) force end the session now */ if (force_session_end && id_det == 0) {
sun4i_usb_phy0_set_vbus_detect(phy0, 0);
msleep(200);
sun4i_usb_phy0_set_vbus_detect(phy0, 1);
}
sun4i_usb_phy0_set_id_detect(phy0, id_det);
data->id_det = id_det;
id_notify = true;
}
if (id_notify) {
extcon_set_state_sync(data->extcon, EXTCON_USB_HOST,
!id_det); /* When leaving host mode force end the session here */ if (force_session_end && id_det == 1) {
mutex_lock(&phy0->mutex);
sun4i_usb_phy0_set_vbus_detect(phy0, 0);
msleep(1000);
sun4i_usb_phy0_set_vbus_detect(phy0, 1);
mutex_unlock(&phy0->mutex);
}
if (of_property_present(np, "usb0_vbus_power-supply")) {
data->vbus_power_supply = devm_power_supply_get_by_reference(dev, "usb0_vbus_power-supply"); if (IS_ERR(data->vbus_power_supply)) {
dev_err(dev, "Couldn't get the VBUS power supply\n"); return PTR_ERR(data->vbus_power_supply);
}
if (!data->vbus_power_supply) return -EPROBE_DEFER;
}
ret = devm_extcon_dev_register(dev, data->extcon); if (ret) {
dev_err(dev, "failed to register extcon: %d\n", ret); return ret;
}
for (i = 0; i < data->cfg->num_phys; i++) { struct sun4i_usb_phy *phy = data->phys + i; char name[32];
if (data->cfg->missing_phys & BIT(i)) continue;
snprintf(name, sizeof(name), "usb%d_vbus", i);
phy->vbus = devm_regulator_get_optional(dev, name); if (IS_ERR(phy->vbus)) { if (PTR_ERR(phy->vbus) == -EPROBE_DEFER) {
dev_err(dev, "Couldn't get regulator %s... Deferring probe\n",
name); return -EPROBE_DEFER;
}
phy->vbus = NULL;
}
if (data->cfg->dedicated_clocks)
snprintf(name, sizeof(name), "usb%d_phy", i); else
strscpy(name, "usb_phy", sizeof(name));
phy->clk = devm_clk_get(dev, name); if (IS_ERR(phy->clk)) {
dev_err(dev, "failed to get clock %s\n", name); return PTR_ERR(phy->clk);
}
/* The first PHY is always tied to OTG, and never HSIC */ if (data->cfg->hsic_index && i == data->cfg->hsic_index) { /* HSIC needs secondary clock */
snprintf(name, sizeof(name), "usb%d_hsic_12M", i);
phy->clk2 = devm_clk_get(dev, name); if (IS_ERR(phy->clk2)) {
dev_err(dev, "failed to get clock %s\n", name); return PTR_ERR(phy->clk2);
}
} else {
snprintf(name, sizeof(name), "pmu%d_clk", i);
phy->clk2 = devm_clk_get_optional(dev, name); if (IS_ERR(phy->clk2)) {
dev_err(dev, "failed to get clock %s\n", name); return PTR_ERR(phy->clk2);
}
}
snprintf(name, sizeof(name), "usb%d_reset", i);
phy->reset = devm_reset_control_get(dev, name); if (IS_ERR(phy->reset)) {
dev_err(dev, "failed to get reset %s\n", name); return PTR_ERR(phy->reset);
}
if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */
snprintf(name, sizeof(name), "pmu%d", i);
phy->pmu = devm_platform_ioremap_resource_byname(pdev, name); if (IS_ERR(phy->pmu)) return PTR_ERR(phy->pmu);
}
phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops); if (IS_ERR(phy->phy)) {
dev_err(dev, "failed to create PHY %d\n", i); return PTR_ERR(phy->phy);
}
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.