Lines Matching +full:sun8i +full:- +full:a23 +full:- +full:usb +full:- +full:clk
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Allwinner sun4i USB phy driver
5 * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com>
15 #include <linux/clk.h>
18 #include <linux/extcon-provider.h>
30 #include <linux/phy/phy-sun4i-usb.h>
36 #include <linux/usb/of.h>
136 struct clk *clk; member
137 struct clk *clk2;
158 container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
166 iscr = readl(data->base + REG_ISCR); in sun4i_usb_phy0_update_iscr()
169 writel(iscr, data->base + REG_ISCR); in sun4i_usb_phy0_update_iscr()
196 u32 temp, usbc_bit = BIT(phy->index * 2); in sun4i_usb_phy_write()
197 void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset; in sun4i_usb_phy_write()
201 spin_lock_irqsave(&phy_data->reg_lock, flags); in sun4i_usb_phy_write()
203 if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) { in sun4i_usb_phy_write()
239 spin_unlock_irqrestore(&phy_data->reg_lock, flags); in sun4i_usb_phy_write()
247 if (!phy->pmu) in sun4i_usb_phy_passby()
254 if (phy_data->cfg->type == sun8i_a83t_phy && phy->index == 2) in sun4i_usb_phy_passby()
258 reg_value = readl(phy->pmu); in sun4i_usb_phy_passby()
265 writel(reg_value, phy->pmu); in sun4i_usb_phy_passby()
275 ret = clk_prepare_enable(phy->clk); in sun4i_usb_phy_init()
279 ret = clk_prepare_enable(phy->clk2); in sun4i_usb_phy_init()
281 clk_disable_unprepare(phy->clk); in sun4i_usb_phy_init()
285 ret = reset_control_deassert(phy->reset); in sun4i_usb_phy_init()
287 clk_disable_unprepare(phy->clk2); in sun4i_usb_phy_init()
288 clk_disable_unprepare(phy->clk); in sun4i_usb_phy_init()
292 if (phy->pmu && data->cfg->hci_phy_ctl_clear) { in sun4i_usb_phy_init()
293 val = readl(phy->pmu + REG_HCI_PHY_CTL); in sun4i_usb_phy_init()
294 val &= ~data->cfg->hci_phy_ctl_clear; in sun4i_usb_phy_init()
295 writel(val, phy->pmu + REG_HCI_PHY_CTL); in sun4i_usb_phy_init()
298 if (data->cfg->type == sun8i_a83t_phy || in sun4i_usb_phy_init()
299 data->cfg->type == sun50i_h6_phy) { in sun4i_usb_phy_init()
300 if (phy->index == 0) { in sun4i_usb_phy_init()
301 val = readl(data->base + data->cfg->phyctl_offset); in sun4i_usb_phy_init()
304 writel(val, data->base + data->cfg->phyctl_offset); in sun4i_usb_phy_init()
307 /* Enable USB 45 Ohm resistor calibration */ in sun4i_usb_phy_init()
308 if (phy->index == 0) in sun4i_usb_phy_init()
316 data->cfg->disc_thresh, 2); in sun4i_usb_phy_init()
321 if (phy->index == 0) { in sun4i_usb_phy_init()
322 data->phy0_init = true; in sun4i_usb_phy_init()
324 /* Enable pull-ups */ in sun4i_usb_phy_init()
329 data->id_det = -1; in sun4i_usb_phy_init()
330 data->vbus_det = -1; in sun4i_usb_phy_init()
331 queue_delayed_work(system_wq, &data->detect, 0); in sun4i_usb_phy_init()
342 if (phy->index == 0) { in sun4i_usb_phy_exit()
343 if (data->cfg->type == sun8i_a83t_phy || in sun4i_usb_phy_exit()
344 data->cfg->type == sun50i_h6_phy) { in sun4i_usb_phy_exit()
345 void __iomem *phyctl = data->base + in sun4i_usb_phy_exit()
346 data->cfg->phyctl_offset; in sun4i_usb_phy_exit()
351 /* Disable pull-ups */ in sun4i_usb_phy_exit()
354 data->phy0_init = false; in sun4i_usb_phy_exit()
358 reset_control_assert(phy->reset); in sun4i_usb_phy_exit()
359 clk_disable_unprepare(phy->clk2); in sun4i_usb_phy_exit()
360 clk_disable_unprepare(phy->clk); in sun4i_usb_phy_exit()
367 switch (data->dr_mode) { in sun4i_usb_phy0_get_id_det()
369 if (data->id_det_gpio) in sun4i_usb_phy0_get_id_det()
370 return gpiod_get_value_cansleep(data->id_det_gpio); in sun4i_usb_phy0_get_id_det()
383 if (data->vbus_det_gpio) in sun4i_usb_phy0_get_vbus_det()
384 return gpiod_get_value_cansleep(data->vbus_det_gpio); in sun4i_usb_phy0_get_vbus_det()
386 if (data->vbus_power_supply) { in sun4i_usb_phy0_get_vbus_det()
390 r = power_supply_get_property(data->vbus_power_supply, in sun4i_usb_phy0_get_vbus_det()
402 return data->vbus_det_gpio || data->vbus_power_supply; in sun4i_usb_phy0_have_vbus_det()
407 if ((data->id_det_gpio && data->id_det_irq <= 0) || in sun4i_usb_phy0_poll()
408 (data->vbus_det_gpio && data->vbus_det_irq <= 0)) in sun4i_usb_phy0_poll()
412 * The A31/A23/A33 companion pmics (AXP221/AXP223) do not in sun4i_usb_phy0_poll()
415 * when using the pmic for vbus-det _and_ we're driving vbus. in sun4i_usb_phy0_poll()
417 if ((data->cfg->type == sun6i_a31_phy || in sun4i_usb_phy0_poll()
418 data->cfg->type == sun8i_a33_phy) && in sun4i_usb_phy0_poll()
419 data->vbus_power_supply && data->phys[0].regulator_on) in sun4i_usb_phy0_poll()
431 if (!phy->vbus || phy->regulator_on) in sun4i_usb_phy_power_on()
435 if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) && in sun4i_usb_phy_power_on()
436 data->vbus_det) { in sun4i_usb_phy_power_on()
437 dev_warn(&_phy->dev, "External vbus detected, not enabling our own vbus\n"); in sun4i_usb_phy_power_on()
441 ret = regulator_enable(phy->vbus); in sun4i_usb_phy_power_on()
445 phy->regulator_on = true; in sun4i_usb_phy_power_on()
448 if (phy->index == 0 && sun4i_usb_phy0_poll(data)) in sun4i_usb_phy_power_on()
449 mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); in sun4i_usb_phy_power_on()
459 if (!phy->vbus || !phy->regulator_on) in sun4i_usb_phy_power_off()
462 regulator_disable(phy->vbus); in sun4i_usb_phy_power_off()
463 phy->regulator_on = false; in sun4i_usb_phy_power_off()
469 if (phy->index == 0 && !sun4i_usb_phy0_poll(data)) in sun4i_usb_phy_power_off()
470 mod_delayed_work(system_wq, &data->detect, POLL_TIME); in sun4i_usb_phy_power_off()
482 if (phy->index != 0) { in sun4i_usb_phy_set_mode()
485 return -EINVAL; in sun4i_usb_phy_set_mode()
499 return -EINVAL; in sun4i_usb_phy_set_mode()
502 if (new_mode != data->dr_mode) { in sun4i_usb_phy_set_mode()
503 dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode); in sun4i_usb_phy_set_mode()
504 data->dr_mode = new_mode; in sun4i_usb_phy_set_mode()
507 data->id_det = -1; /* Force reprocessing of id */ in sun4i_usb_phy_set_mode()
508 data->force_session_end = true; in sun4i_usb_phy_set_mode()
509 queue_delayed_work(system_wq, &data->detect, 0); in sun4i_usb_phy_set_mode()
535 regval = readl(data->base + REG_PHY_OTGCTL); in sun4i_usb_phy0_reroute()
543 writel(regval, data->base + REG_PHY_OTGCTL); in sun4i_usb_phy0_reroute()
550 struct phy *phy0 = data->phys[0].phy; in sun4i_usb_phy0_id_vbus_det_scan()
562 mutex_lock(&phy0->mutex); in sun4i_usb_phy0_id_vbus_det_scan()
564 if (!data->phy0_init) { in sun4i_usb_phy0_id_vbus_det_scan()
565 mutex_unlock(&phy0->mutex); in sun4i_usb_phy0_id_vbus_det_scan()
569 force_session_end = data->force_session_end; in sun4i_usb_phy0_id_vbus_det_scan()
570 data->force_session_end = false; in sun4i_usb_phy0_id_vbus_det_scan()
572 if (id_det != data->id_det) { in sun4i_usb_phy0_id_vbus_det_scan()
573 /* id-change, force session end if we've no vbus detection */ in sun4i_usb_phy0_id_vbus_det_scan()
574 if (data->dr_mode == USB_DR_MODE_OTG && in sun4i_usb_phy0_id_vbus_det_scan()
585 data->id_det = id_det; in sun4i_usb_phy0_id_vbus_det_scan()
589 if (vbus_det != data->vbus_det) { in sun4i_usb_phy0_id_vbus_det_scan()
591 data->vbus_det = vbus_det; in sun4i_usb_phy0_id_vbus_det_scan()
595 mutex_unlock(&phy0->mutex); in sun4i_usb_phy0_id_vbus_det_scan()
598 extcon_set_state_sync(data->extcon, EXTCON_USB_HOST, in sun4i_usb_phy0_id_vbus_det_scan()
602 mutex_lock(&phy0->mutex); in sun4i_usb_phy0_id_vbus_det_scan()
606 mutex_unlock(&phy0->mutex); in sun4i_usb_phy0_id_vbus_det_scan()
612 /* Re-route PHY0 if necessary */ in sun4i_usb_phy0_id_vbus_det_scan()
613 if (data->cfg->phy0_dual_route) in sun4i_usb_phy0_id_vbus_det_scan()
618 extcon_set_state_sync(data->extcon, EXTCON_USB, vbus_det); in sun4i_usb_phy0_id_vbus_det_scan()
621 queue_delayed_work(system_wq, &data->detect, POLL_TIME); in sun4i_usb_phy0_id_vbus_det_scan()
629 mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); in sun4i_usb_phy0_id_vbus_det_irq()
642 if (val == PSY_EVENT_PROP_CHANGED && psy == data->vbus_power_supply) in sun4i_usb_phy0_vbus_notify()
643 mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); in sun4i_usb_phy0_vbus_notify()
653 if (args->args[0] >= data->cfg->num_phys) in sun4i_usb_phy_xlate()
654 return ERR_PTR(-ENODEV); in sun4i_usb_phy_xlate()
656 if (data->cfg->missing_phys & BIT(args->args[0])) in sun4i_usb_phy_xlate()
657 return ERR_PTR(-ENODEV); in sun4i_usb_phy_xlate()
659 return data->phys[args->args[0]].phy; in sun4i_usb_phy_xlate()
664 struct device *dev = &pdev->dev; in sun4i_usb_phy_remove()
667 if (data->vbus_power_nb_registered) in sun4i_usb_phy_remove()
668 power_supply_unreg_notifier(&data->vbus_power_nb); in sun4i_usb_phy_remove()
669 if (data->id_det_irq > 0) in sun4i_usb_phy_remove()
670 devm_free_irq(dev, data->id_det_irq, data); in sun4i_usb_phy_remove()
671 if (data->vbus_det_irq > 0) in sun4i_usb_phy_remove()
672 devm_free_irq(dev, data->vbus_det_irq, data); in sun4i_usb_phy_remove()
674 cancel_delayed_work_sync(&data->detect); in sun4i_usb_phy_remove()
688 struct device *dev = &pdev->dev; in sun4i_usb_phy_probe()
689 struct device_node *np = dev->of_node; in sun4i_usb_phy_probe()
695 return -ENOMEM; in sun4i_usb_phy_probe()
697 spin_lock_init(&data->reg_lock); in sun4i_usb_phy_probe()
698 INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan); in sun4i_usb_phy_probe()
700 data->cfg = of_device_get_match_data(dev); in sun4i_usb_phy_probe()
701 if (!data->cfg) in sun4i_usb_phy_probe()
702 return -EINVAL; in sun4i_usb_phy_probe()
704 data->base = devm_platform_ioremap_resource_byname(pdev, "phy_ctrl"); in sun4i_usb_phy_probe()
705 if (IS_ERR(data->base)) in sun4i_usb_phy_probe()
706 return PTR_ERR(data->base); in sun4i_usb_phy_probe()
708 data->id_det_gpio = devm_gpiod_get_optional(dev, "usb0_id_det", in sun4i_usb_phy_probe()
710 if (IS_ERR(data->id_det_gpio)) { in sun4i_usb_phy_probe()
712 return PTR_ERR(data->id_det_gpio); in sun4i_usb_phy_probe()
715 data->vbus_det_gpio = devm_gpiod_get_optional(dev, "usb0_vbus_det", in sun4i_usb_phy_probe()
717 if (IS_ERR(data->vbus_det_gpio)) { in sun4i_usb_phy_probe()
719 return PTR_ERR(data->vbus_det_gpio); in sun4i_usb_phy_probe()
722 if (of_find_property(np, "usb0_vbus_power-supply", NULL)) { in sun4i_usb_phy_probe()
723 data->vbus_power_supply = devm_power_supply_get_by_phandle(dev, in sun4i_usb_phy_probe()
724 "usb0_vbus_power-supply"); in sun4i_usb_phy_probe()
725 if (IS_ERR(data->vbus_power_supply)) { in sun4i_usb_phy_probe()
727 return PTR_ERR(data->vbus_power_supply); in sun4i_usb_phy_probe()
730 if (!data->vbus_power_supply) in sun4i_usb_phy_probe()
731 return -EPROBE_DEFER; in sun4i_usb_phy_probe()
734 data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0); in sun4i_usb_phy_probe()
736 data->extcon = devm_extcon_dev_allocate(dev, sun4i_usb_phy0_cable); in sun4i_usb_phy_probe()
737 if (IS_ERR(data->extcon)) { in sun4i_usb_phy_probe()
739 return PTR_ERR(data->extcon); in sun4i_usb_phy_probe()
742 ret = devm_extcon_dev_register(dev, data->extcon); in sun4i_usb_phy_probe()
748 for (i = 0; i < data->cfg->num_phys; i++) { in sun4i_usb_phy_probe()
749 struct sun4i_usb_phy *phy = data->phys + i; in sun4i_usb_phy_probe()
752 if (data->cfg->missing_phys & BIT(i)) in sun4i_usb_phy_probe()
755 snprintf(name, sizeof(name), "usb%d_vbus", i); in sun4i_usb_phy_probe()
756 phy->vbus = devm_regulator_get_optional(dev, name); in sun4i_usb_phy_probe()
757 if (IS_ERR(phy->vbus)) { in sun4i_usb_phy_probe()
758 if (PTR_ERR(phy->vbus) == -EPROBE_DEFER) { in sun4i_usb_phy_probe()
762 return -EPROBE_DEFER; in sun4i_usb_phy_probe()
765 phy->vbus = NULL; in sun4i_usb_phy_probe()
768 if (data->cfg->dedicated_clocks) in sun4i_usb_phy_probe()
769 snprintf(name, sizeof(name), "usb%d_phy", i); in sun4i_usb_phy_probe()
773 phy->clk = devm_clk_get(dev, name); in sun4i_usb_phy_probe()
774 if (IS_ERR(phy->clk)) { in sun4i_usb_phy_probe()
776 return PTR_ERR(phy->clk); in sun4i_usb_phy_probe()
780 if (data->cfg->hsic_index && i == data->cfg->hsic_index) { in sun4i_usb_phy_probe()
782 snprintf(name, sizeof(name), "usb%d_hsic_12M", i); in sun4i_usb_phy_probe()
783 phy->clk2 = devm_clk_get(dev, name); in sun4i_usb_phy_probe()
784 if (IS_ERR(phy->clk2)) { in sun4i_usb_phy_probe()
786 return PTR_ERR(phy->clk2); in sun4i_usb_phy_probe()
790 snprintf(name, sizeof(name), "usb%d_reset", i); in sun4i_usb_phy_probe()
791 phy->reset = devm_reset_control_get(dev, name); in sun4i_usb_phy_probe()
792 if (IS_ERR(phy->reset)) { in sun4i_usb_phy_probe()
794 return PTR_ERR(phy->reset); in sun4i_usb_phy_probe()
797 if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */ in sun4i_usb_phy_probe()
799 phy->pmu = devm_platform_ioremap_resource_byname(pdev, name); in sun4i_usb_phy_probe()
800 if (IS_ERR(phy->pmu)) in sun4i_usb_phy_probe()
801 return PTR_ERR(phy->pmu); in sun4i_usb_phy_probe()
804 phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops); in sun4i_usb_phy_probe()
805 if (IS_ERR(phy->phy)) { in sun4i_usb_phy_probe()
807 return PTR_ERR(phy->phy); in sun4i_usb_phy_probe()
810 phy->index = i; in sun4i_usb_phy_probe()
811 phy_set_drvdata(phy->phy, &data->phys[i]); in sun4i_usb_phy_probe()
814 data->id_det_irq = gpiod_to_irq(data->id_det_gpio); in sun4i_usb_phy_probe()
815 if (data->id_det_irq > 0) { in sun4i_usb_phy_probe()
816 ret = devm_request_irq(dev, data->id_det_irq, in sun4i_usb_phy_probe()
819 "usb0-id-det", data); in sun4i_usb_phy_probe()
821 dev_err(dev, "Err requesting id-det-irq: %d\n", ret); in sun4i_usb_phy_probe()
826 data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio); in sun4i_usb_phy_probe()
827 if (data->vbus_det_irq > 0) { in sun4i_usb_phy_probe()
828 ret = devm_request_irq(dev, data->vbus_det_irq, in sun4i_usb_phy_probe()
831 "usb0-vbus-det", data); in sun4i_usb_phy_probe()
833 dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret); in sun4i_usb_phy_probe()
834 data->vbus_det_irq = -1; in sun4i_usb_phy_probe()
840 if (data->vbus_power_supply) { in sun4i_usb_phy_probe()
841 data->vbus_power_nb.notifier_call = sun4i_usb_phy0_vbus_notify; in sun4i_usb_phy_probe()
842 data->vbus_power_nb.priority = 0; in sun4i_usb_phy_probe()
843 ret = power_supply_reg_notifier(&data->vbus_power_nb); in sun4i_usb_phy_probe()
848 data->vbus_power_nb_registered = true; in sun4i_usb_phy_probe()
977 { .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg },
978 { .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg },
979 { .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg },
980 { .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg },
981 { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg },
982 { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
983 { .compatible = "allwinner,sun8i-a83t-usb-phy", .data = &sun8i_a83t_cfg },
984 { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
985 { .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg },
986 { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg },
987 { .compatible = "allwinner,sun20i-d1-usb-phy", .data = &sun20i_d1_cfg },
988 { .compatible = "allwinner,sun50i-a64-usb-phy",
990 { .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg },
1000 .name = "sun4i-usb-phy",
1005 MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");