mirror of https://github.com/OpenIPC/firmware.git
553 lines
16 KiB
Diff
553 lines
16 KiB
Diff
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 <linux/module.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
+#include <linux/spinlock.h>
|
|
+#include <linux/workqueue.h>
|
|
+#include <linux/usb/phy.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/usb/gadget.h>
|
|
+#include <linux/usb/otg.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/of_gpio.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <soc/cpm.h>
|
|
+
|
|
+#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);
|