diff -drupN a/drivers/usb/phy/phy-ingenic-inno.c b/drivers/usb/phy/phy-ingenic-inno.c --- a/drivers/usb/phy/phy-ingenic-inno.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/usb/phy/phy-ingenic-inno.c 2022-06-09 05:02:34.000000000 +0300 @@ -0,0 +1,548 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define usb_phy_readb(addr) readb((addr)) +#define usb_phy_writeb(val, addr) writeb(val,(addr)) + +#define PHY_TX_HS_STRENGTH_CONF (0x40) +#define PHY_EYES_MAP_ADJ_CONF (0x60) +#define PHY_SUSPEND_LPM (0x108) + +#define PHY_RX_SQU_TRI (0x64) +#define PHY_RX_SQU_TRI_112MV (0x0) +#define PHY_RX_SQU_TRI_125MV (0x8) + +#define PHY_DETECT (0x70) +#define PHY_OTG_SESSION (0x78) + +struct ingenic_usb_phy { + struct usb_phy phy; + struct device *dev; + struct clk *gate_clk; + struct regmap *regmap; + void __iomem *base; + + /*otg phy*/ + spinlock_t phy_lock; + struct gpio_desc *gpiod_drvvbus; + struct gpio_desc *gpiod_id; + struct gpio_desc *gpiod_vbus; +#define USB_DETE_VBUS 0x1 +#define USB_DETE_ID 0x2 + unsigned int usb_dete_state; + struct delayed_work work; + + enum usb_device_speed roothub_port_speed; +}; + +static inline int inno_usbphy_read(struct usb_phy *x, u32 reg) +{ + struct ingenic_usb_phy *iphy = container_of(x, struct ingenic_usb_phy, phy); + u32 val = 0; + regmap_read(iphy->regmap, reg, &val); + return val; +} + +static inline int inno_usbphy_write(struct usb_phy *x, u32 val, u32 reg) +{ + struct ingenic_usb_phy *iphy = container_of(x, struct ingenic_usb_phy, phy); + + return regmap_write(iphy->regmap, reg, val); +} + +static inline int inno_usbphy_update_bits(struct usb_phy *x, u32 reg, u32 mask, u32 bits) +{ + struct ingenic_usb_phy *iphy = container_of(x, struct ingenic_usb_phy, phy); + + return regmap_update_bits_check(iphy->regmap, reg, mask, bits, NULL); +} + +static irqreturn_t usb_dete_irq_handler(int irq, void *data) +{ + struct ingenic_usb_phy *iphy = (struct ingenic_usb_phy *)data; + + schedule_delayed_work(&iphy->work, msecs_to_jiffies(100)); + return IRQ_HANDLED; +} + +static void usb_dete_work(struct work_struct *work) +{ + struct ingenic_usb_phy *iphy = + container_of(work, struct ingenic_usb_phy, work.work); + struct usb_otg *otg = iphy->phy.otg; + int vbus = 0, id_level = 1, usb_state = 0; + + if (iphy->gpiod_vbus) + vbus = gpiod_get_value_cansleep(iphy->gpiod_vbus); + + if (iphy->gpiod_id) + id_level = gpiod_get_value_cansleep(iphy->gpiod_id); + + if (vbus && id_level) + usb_state |= USB_DETE_VBUS; + else + usb_state &= ~USB_DETE_VBUS; + + if (id_level) + usb_state |= USB_DETE_ID; + else + usb_state &= ~USB_DETE_ID; + + if (usb_state != iphy->usb_dete_state) { + enum usb_phy_events status; + if (!(usb_state & USB_DETE_VBUS)) { + status = USB_EVENT_NONE; + otg->state = OTG_STATE_B_IDLE; + iphy->phy.last_event = status; + if (otg->gadget){ + usb_gadget_vbus_disconnect(otg->gadget); + atomic_notifier_call_chain(&iphy->phy.notifier, status, + otg->gadget); + } + } + + if (!(usb_state & USB_DETE_ID)) { + status = USB_EVENT_ID; + otg->state = OTG_STATE_A_IDLE; + iphy->phy.last_event = status; + if (otg->host) + atomic_notifier_call_chain(&iphy->phy.notifier, status, + otg->host); + } + + if (usb_state & USB_DETE_VBUS) { + status = USB_EVENT_VBUS; + otg->state = OTG_STATE_B_PERIPHERAL; + iphy->phy.last_event = status; + if (otg->gadget){ + usb_gadget_vbus_connect(otg->gadget); + atomic_notifier_call_chain(&iphy->phy.notifier, status, + otg->gadget); + } + } + iphy->usb_dete_state = usb_state; + } + return; +} + +static int iphy_init(struct usb_phy *x) +{ + struct ingenic_usb_phy *iphy = container_of(x, struct ingenic_usb_phy, phy); + u32 usbpcr, usbpcr1; + unsigned long flags; + u8 reg; + + if (!IS_ERR_OR_NULL(iphy->gate_clk)) + clk_prepare_enable(iphy->gate_clk); + + spin_lock_irqsave(&iphy->phy_lock, flags); + + usbpcr1 = inno_usbphy_read(x, CPM_USBPCR1); + usbpcr1 |= USBPCR1_DPPULLDOWN | USBPCR1_DMPULLDOWN; + inno_usbphy_write(x, usbpcr1, CPM_USBPCR1); + + usbpcr = inno_usbphy_read(x, CPM_USBPCR); + usbpcr &= ~USBPCR_IDPULLUP_MASK; +#if IS_ENABLED(CONFIG_USB_DWC2_HOST) + usbpcr |= USBPCR_USB_MODE | USBPCR_IDPULLUP_OTG; +#elif IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) + usbpcr &= ~USBPCR_USB_MODE; +#endif + inno_usbphy_write(x, usbpcr, CPM_USBPCR); + + inno_usbphy_update_bits(x, CPM_USBPCR1, USBPCR1_PORT_RST, USBPCR1_PORT_RST); + + inno_usbphy_update_bits(x, CPM_USBRDT, USBRDT_UTMI_RST, 0); + inno_usbphy_update_bits(x, CPM_USBPCR, USBPCR_POR, USBPCR_POR); + inno_usbphy_update_bits(x, CPM_SRBC, SRBC_USB_SR, SRBC_USB_SR); + udelay(5); + inno_usbphy_update_bits(x, CPM_USBPCR, USBPCR_POR, 0); + udelay(10); + inno_usbphy_update_bits(x, CPM_OPCR, OPCR_USB_SPENDN, OPCR_USB_SPENDN); + udelay(550); + inno_usbphy_update_bits(x, CPM_USBRDT, USBRDT_UTMI_RST, USBRDT_UTMI_RST); + udelay(10); + inno_usbphy_update_bits(x, CPM_SRBC, SRBC_USB_SR, 0); + + reg = usb_phy_readb(iphy->base + PHY_RX_SQU_TRI); + reg &= ~(0xf << 3); + reg |= PHY_RX_SQU_TRI_125MV << 3; + usb_phy_writeb(reg,iphy->base + PHY_RX_SQU_TRI); + + reg = usb_phy_readb(iphy->base + PHY_OTG_SESSION); + reg |=0x20; + usb_phy_writeb(reg,iphy->base + PHY_OTG_SESSION); + reg = usb_phy_readb(iphy->base + PHY_DETECT); + reg &= ~0x8; + usb_phy_writeb(reg,iphy->base + PHY_DETECT); + + spin_unlock_irqrestore(&iphy->phy_lock, flags); + return 0; +} + +static int iphy_set_suspend(struct usb_phy *x, int suspend); +static void iphy_shutdown(struct usb_phy *x) +{ + struct ingenic_usb_phy *iphy = container_of(x, struct ingenic_usb_phy, phy); + iphy_set_suspend(x, 1); + + if (!IS_ERR_OR_NULL(iphy->gate_clk)) + clk_disable_unprepare(iphy->gate_clk); +} + +static int iphy_set_vbus(struct usb_phy *x, int on) +{ + struct ingenic_usb_phy *iphy = container_of(x, struct ingenic_usb_phy, phy); + + if (!(IS_ERR_OR_NULL(iphy->gpiod_drvvbus))) { + printk("OTG VBUS %s\n", on ? "ON" : "OFF"); + gpiod_set_value(iphy->gpiod_drvvbus, on); + } + return 0; +} + +static int iphy_set_wakeup(struct usb_phy *x, bool enabled); +static int iphy_set_suspend(struct usb_phy *x, int suspend) +{ + struct ingenic_usb_phy *iphy = container_of(x, struct ingenic_usb_phy, phy); + unsigned long flags; +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) + struct usb_otg *otg = iphy->phy.otg; + enum usb_device_speed speed; + unsigned int usbrdt; + unsigned int cpm_opcr; + unsigned int cpm_clkgr; + unsigned int cpm_usbpcr1; +#endif + + spin_lock_irqsave(&iphy->phy_lock, flags); +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) + if(suspend){ + cpm_usbpcr1 = inno_usbphy_read(x, CPM_USBPCR1); + cpm_usbpcr1 &= ~(1 << 30); + inno_usbphy_write(x, cpm_usbpcr1, CPM_USBPCR1); + + usbrdt = inno_usbphy_read(x, CPM_USBRDT); + usbrdt &= ~USBRDT_RESUME_SPEED_MSK; + speed = (otg && otg->gadget) ? otg->gadget->speed : USB_SPEED_FULL; + if(speed == USB_SPEED_FULL) + usbrdt |= USBRDT_RESUME_SPEED_FULL; + if(speed == USB_SPEED_LOW) + usbrdt |= USBRDT_RESUME_SPEED_LOW; + //TODO: the function be in suspend. + //usbrdt |= USBRDT_RESUME_INTEEN; + inno_usbphy_write(x, usbrdt, CPM_USBRDT); + + usb_phy_writeb(0x8,iphy->base + PHY_SUSPEND_LPM); + + cpm_opcr =inno_usbphy_read(x,CPM_OPCR); + cpm_opcr &= ~OPCR_USB_SPENDN; + inno_usbphy_write(x,cpm_opcr,CPM_OPCR); + + udelay(10); + + cpm_opcr =inno_usbphy_read(x,CPM_OPCR); + cpm_opcr |= OPCR_USB_PHY_GATE; + inno_usbphy_write(x,cpm_opcr,CPM_OPCR); + + cpm_clkgr =inno_usbphy_read(x,CPM_CLKGR); + cpm_clkgr |= (1 << 3); + inno_usbphy_write(x,cpm_clkgr,CPM_CLKGR); + } + + if(!suspend) + iphy_set_wakeup(x, true); +#else + if (suspend) + inno_usbphy_update_bits(x, CPM_OPCR, USBPCR1_PORT_RST, 0); + + inno_usbphy_update_bits(x, CPM_OPCR, OPCR_USB_SPENDN, + suspend ? 0 : OPCR_USB_SPENDN); + if (!suspend) { + udelay(501); /*500us and 10 utmi clock*/ + inno_usbphy_update_bits(x, CPM_OPCR, USBPCR1_PORT_RST, USBPCR1_PORT_RST); + udelay(1); /*1 us*/ + inno_usbphy_update_bits(x, CPM_OPCR, USBPCR1_PORT_RST, 0); + } + udelay(1); /*1us*/ +#endif + spin_unlock_irqrestore(&iphy->phy_lock, flags); + + return 0; +} + +static int iphy_set_wakeup(struct usb_phy *x, bool enabled) +{ + struct ingenic_usb_phy *iphy = container_of(x, struct ingenic_usb_phy, phy); + struct usb_otg *otg = iphy->phy.otg; + enum usb_device_speed speed; + unsigned long flags; +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) + int timeout; + unsigned int usbrdt; + unsigned int cpm_opcr; + unsigned int cpm_clkgr; + unsigned int cpm_usbpcr1; +#endif + spin_lock_irqsave(&iphy->phy_lock, flags); + +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) + if (enabled) { + /*enable usb resume interrupt*/ + if (otg && otg->state >= OTG_STATE_A_IDLE) + speed = iphy->roothub_port_speed; + else + speed = (otg && otg->gadget) ? otg->gadget->speed : USB_SPEED_FULL; + + usbrdt = inno_usbphy_read(x, CPM_USBRDT); + if(usbrdt & (USBRDT_RESUME_STATUS)){ + usbrdt |= USBRDT_RESUME_INTERCLR; + inno_usbphy_write(x, usbrdt, CPM_USBRDT); + timeout = 100; + while(1){ + if(timeout-- < 0){ + printk("%s:%d resume interrupt clear failed\n", __func__, __LINE__); + return -EAGAIN; + } + usbrdt = inno_usbphy_read(x, CPM_USBRDT); + if(!(usbrdt & (USBRDT_RESUME_STATUS))) + break; + } + } + + cpm_opcr = inno_usbphy_read(x, CPM_OPCR); + cpm_opcr &= ~OPCR_USB_PHY_GATE; + inno_usbphy_write(x,cpm_opcr, CPM_OPCR); + + cpm_opcr = inno_usbphy_read(x, CPM_OPCR); + cpm_opcr |= OPCR_USB_SPENDN; + inno_usbphy_write(x,cpm_opcr, CPM_OPCR); + + udelay(501); + + cpm_usbpcr1 = inno_usbphy_read(x, CPM_USBPCR1); + cpm_usbpcr1 |= USBPCR1_PORT_RST; + inno_usbphy_write(x,cpm_usbpcr1, CPM_USBPCR1); + + udelay(2); + + cpm_usbpcr1 = inno_usbphy_read(x, CPM_USBPCR1); + cpm_usbpcr1 &= ~USBPCR1_PORT_RST; + inno_usbphy_write(x,cpm_usbpcr1, CPM_USBPCR1); + + udelay(1); + + cpm_clkgr = inno_usbphy_read(x, CPM_CLKGR); + cpm_clkgr &= ~(1 << 3); + inno_usbphy_write(x,cpm_clkgr, CPM_CLKGR); + + usb_phy_writeb(0x0,iphy->base + PHY_SUSPEND_LPM); + + } else { + /*disable usb resume interrupt*/ + inno_usbphy_update_bits(x, CPM_USBRDT, USBRDT_RESUME_INTEEN|USBRDT_RESUME_INTERCLR, + USBRDT_RESUME_INTERCLR); + } +#else + if (enabled) { + /*enable usb resume interrupt*/ + if (otg && otg->state >= OTG_STATE_A_IDLE) + speed = iphy->roothub_port_speed; + else + speed = (otg && otg->gadget) ? otg->gadget->speed : USB_SPEED_FULL; + inno_usbphy_update_bits(x, CPM_USBRDT, USBRDT_RESUME_INTEEN, USBRDT_RESUME_INTEEN); + } else { + /*disable usb resume interrupt*/ + inno_usbphy_update_bits(x, CPM_USBRDT, USBRDT_RESUME_INTEEN|USBRDT_RESUME_INTERCLR, + USBRDT_RESUME_INTERCLR); + } +#endif + spin_unlock_irqrestore(&iphy->phy_lock, flags); + return 0; +} + +static int iphy_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + otg->host = host; + return 0; +} + +static int iphy_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) +{ + if (!otg) + return -ENODEV; + + otg->gadget = gadget; +#ifdef CONFIG_USB_DWC2_DETECT_CONNECT + if(gadget){ + struct ingenic_usb_phy *iphy = container_of(otg->usb_phy, struct ingenic_usb_phy, phy); + iphy->usb_dete_state = ~iphy->usb_dete_state; + schedule_delayed_work(&iphy->work, msecs_to_jiffies(100)); +} +#endif + return 0; +} + +static int iphy_notify_connect(struct usb_phy *x, + enum usb_device_speed speed) +{ + struct ingenic_usb_phy *iphy = container_of(x, struct ingenic_usb_phy, phy); + + iphy->roothub_port_speed = speed; + + return 0; +} + +static int usb_phy_ingenic_probe(struct platform_device *pdev) +{ + struct ingenic_usb_phy *iphy; + struct resource *res; + int ret; + int vbus; + + iphy = (struct ingenic_usb_phy *) + devm_kzalloc(&pdev->dev, sizeof(*iphy), GFP_KERNEL); + if (!iphy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + iphy->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(iphy->base)) + return PTR_ERR(iphy->base); + + iphy->phy.init = iphy_init; + iphy->phy.shutdown = iphy_shutdown; + iphy->phy.dev = &pdev->dev; + iphy->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, NULL); + if (IS_ERR(iphy->regmap)) { + dev_err(&pdev->dev, "failed to find regmap for usb phy %ld\n", PTR_ERR(iphy->regmap)); + return PTR_ERR(iphy->regmap); + } + + spin_lock_init(&iphy->phy_lock); + + iphy->gpiod_id = devm_gpiod_get_optional(&pdev->dev,"ingenic,id-dete", GPIOD_IN); + iphy->gpiod_vbus = devm_gpiod_get_optional(&pdev->dev,"ingenic,vbus-dete", GPIOD_ASIS); + if (iphy->gpiod_id || iphy->gpiod_vbus) + INIT_DELAYED_WORK(&iphy->work, usb_dete_work); + + if (!IS_ERR_OR_NULL(iphy->gpiod_id)) { + ret = devm_request_irq(&pdev->dev, + gpiod_to_irq(iphy->gpiod_id), + usb_dete_irq_handler, + IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING|IRQF_ONESHOT, + "id_dete", + (void *)iphy); + if (ret) + return ret; + } else { + iphy->usb_dete_state |= USB_DETE_ID; + iphy->gpiod_id = NULL; + } + + if (!IS_ERR_OR_NULL(iphy->gpiod_vbus)) { + ret = devm_request_irq(&pdev->dev, + gpiod_to_irq(iphy->gpiod_vbus), + usb_dete_irq_handler, + IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING|IRQF_ONESHOT, + "vbus_dete", + (void *)iphy); + if (ret) + return ret; + vbus = gpiod_get_value_cansleep(iphy->gpiod_vbus); + if(vbus) + iphy->usb_dete_state |= USB_DETE_VBUS; + else + iphy->usb_dete_state &= ~USB_DETE_VBUS; + } else { + iphy->usb_dete_state &= ~USB_DETE_VBUS; + iphy->gpiod_vbus = NULL; + } + + iphy->gpiod_drvvbus = devm_gpiod_get_optional(&pdev->dev,"ingenic,drvvbus", GPIOD_OUT_LOW); + if (IS_ERR_OR_NULL(iphy->gpiod_drvvbus)) + iphy->gpiod_drvvbus = NULL; + iphy->phy.set_vbus = iphy_set_vbus; + iphy->phy.set_suspend = iphy_set_suspend; + iphy->phy.set_wakeup = iphy_set_wakeup; + iphy->phy.notify_connect = iphy_notify_connect; + iphy->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*iphy->phy.otg), + GFP_KERNEL); + + if (!iphy->phy.otg) + return -ENOMEM; + + iphy->phy.otg->state = OTG_STATE_UNDEFINED; + iphy->phy.otg->usb_phy = &iphy->phy; + iphy->phy.otg->set_host = iphy_set_host; + iphy->phy.otg->set_peripheral = iphy_set_peripheral; + + iphy->gate_clk = devm_clk_get(&pdev->dev, "gate_usbphy"); + if (IS_ERR_OR_NULL(iphy->gate_clk)){ + iphy->gate_clk = NULL; + dev_err(&pdev->dev, "cannot get usbphy clock !\n"); + return -ENODEV; + } + + ret = usb_add_phy_dev(&iphy->phy); + if (ret) { + dev_err(&pdev->dev, "can't register transceiver, err: %d\n", + ret); + return ret; + } + platform_set_drvdata(pdev, iphy); + pr_info("inno phy probe success\n"); + return 0; +} + +static int usb_phy_ingenic_remove(struct platform_device *pdev) +{ + struct ingenic_usb_phy *iphy = platform_get_drvdata(pdev); + + usb_remove_phy(&iphy->phy); + return 0; +} + +static const struct of_device_id of_matchs[] = { + { .compatible = "ingenic,innophy"}, + { } +}; +MODULE_DEVICE_TABLE(of, ingenic_xceiv_dt_ids); + +static struct platform_driver usb_phy_ingenic_driver = { + .probe = usb_phy_ingenic_probe, + .remove = usb_phy_ingenic_remove, + .driver = { + .name = "usb_phy", + .of_match_table = of_matchs, + }, +}; + +static int __init usb_phy_ingenic_init(void) +{ + return platform_driver_register(&usb_phy_ingenic_driver); +} +subsys_initcall(usb_phy_ingenic_init); + +static void __exit usb_phy_ingenic_exit(void) +{ + platform_driver_unregister(&usb_phy_ingenic_driver); +} +module_exit(usb_phy_ingenic_exit);