/* ehci-msm-hsic.c - HSUSB Host Controller Driver Implementation * * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. * * Partly derived from ehci-fsl.c and ehci-hcd.c * Copyright (c) 2000-2004 by David Brownell * Copyright (c) 2005 MontaVista Software * * All source code in this file is licensed under the following license except * where indicated. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, you can find it at http://www.fsf.org */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hbm.c" #define MSM_USB_BASE (hcd->regs) #define USB_REG_START_OFFSET 0x90 #define USB_REG_END_OFFSET 0x324 static struct workqueue_struct *ehci_wq; struct ehci_timer { #define GPT_LD(p) ((p) & 0x00FFFFFF) u32 gptimer0_ld; #define GPT_RUN BIT(31) #define GPT_RESET BIT(30) #define GPT_MODE BIT(24) #define GPT_CNT(p) ((p) & 0x00FFFFFF) u32 gptimer0_ctrl; u32 gptimer1_ld; u32 gptimer1_ctrl; }; struct msm_hsic_hcd { struct ehci_hcd ehci; spinlock_t wakeup_lock; struct device *dev; struct clk *ahb_clk; struct clk *core_clk; struct clk *alt_core_clk; struct clk *phy_clk; struct clk *cal_clk; struct clk *inactivity_clk; struct regulator *hsic_vddcx; struct regulator *hsic_gdsc; atomic_t async_int; atomic_t in_lpm; struct wake_lock wlock; int peripheral_status_irq; int wakeup_irq; bool wakeup_irq_enabled; int async_irq; uint32_t async_int_cnt; atomic_t pm_usage_cnt; uint32_t bus_perf_client; uint32_t wakeup_int_cnt; enum usb_vdd_type vdd_type; struct work_struct bus_vote_w; bool bus_vote; /* gp timer */ struct ehci_timer __iomem *timer; struct completion gpt0_completion; struct completion rt_completion; int resume_status; int resume_again; int bus_reset; int reset_again; struct pm_qos_request pm_qos_req_dma; unsigned enable_hbm:1; }; struct msm_hsic_hcd *__mehci; static bool debug_bus_voting_enabled = true; static u64 ehci_msm_hsic_dma_mask = DMA_BIT_MASK(32); static struct platform_driver ehci_msm_hsic_driver; static unsigned int enable_payload_log = 1; module_param(enable_payload_log, uint, S_IRUGO | S_IWUSR); static unsigned int enable_dbg_log = 1; module_param(enable_dbg_log, uint, S_IRUGO | S_IWUSR); /*by default log ep0 and efs sync ep*/ static unsigned int ep_addr_rxdbg_mask = 9; module_param(ep_addr_rxdbg_mask, uint, S_IRUGO | S_IWUSR); static unsigned int ep_addr_txdbg_mask = 9; module_param(ep_addr_txdbg_mask, uint, S_IRUGO | S_IWUSR); static unsigned int dbg_qh_addr; module_param(dbg_qh_addr, uint, S_IRUGO | S_IWUSR); /* Maximum debug message length */ #define DBG_MSG_LEN 128UL /* Maximum number of messages */ #define DBG_MAX_MSG 256UL #define TIME_BUF_LEN 20 #define HEX_DUMP_LEN 72 enum event_type { EVENT_UNDEF = -1, URB_SUBMIT, URB_COMPLETE, EVENT_NONE, }; #define EVENT_STR_LEN 5 static enum event_type str_to_event(const char *name) { if (!strncasecmp("S", name, EVENT_STR_LEN)) return URB_SUBMIT; if (!strncasecmp("C", name, EVENT_STR_LEN)) return URB_COMPLETE; if (!strncasecmp("", name, EVENT_STR_LEN)) return EVENT_NONE; return EVENT_UNDEF; } /*log ep0 activity*/ static struct { char (buf[DBG_MAX_MSG])[DBG_MSG_LEN]; /* buffer */ unsigned idx; /* index */ rwlock_t lck; /* lock */ } dbg_hsic_ctrl = { .idx = 0, .lck = __RW_LOCK_UNLOCKED(lck) }; static struct { char (buf[DBG_MAX_MSG])[DBG_MSG_LEN]; /* buffer */ unsigned idx; /* index */ rwlock_t lck; /* lock */ } dbg_hsic_data = { .idx = 0, .lck = __RW_LOCK_UNLOCKED(lck) }; /** * dbg_inc: increments debug event index * @idx: buffer index */ static void dbg_inc(unsigned *idx) { *idx = (*idx + 1) & (DBG_MAX_MSG-1); } /*get_timestamp - returns time of day in us */ static char *get_timestamp(char *tbuf) { unsigned long long t; unsigned long nanosec_rem; t = cpu_clock(smp_processor_id()); nanosec_rem = do_div(t, 1000000000)/1000; scnprintf(tbuf, TIME_BUF_LEN, "[%5lu.%06lu] ", (unsigned long)t, nanosec_rem); return tbuf; } static int allow_dbg_log(int ep_addr) { int dir, num; dir = ep_addr & USB_DIR_IN ? USB_DIR_IN : USB_DIR_OUT; num = ep_addr & ~USB_DIR_IN; num = 1 << num; if ((dir == USB_DIR_IN) && (num & ep_addr_rxdbg_mask)) return 1; if ((dir == USB_DIR_OUT) && (num & ep_addr_txdbg_mask)) return 1; return 0; } static char *get_hex_data(char *dbuf, struct urb *urb, int event, int status) { int ep_addr = urb->ep->desc.bEndpointAddress; char *ubuf = urb->transfer_buffer; size_t len = event ? \ urb->actual_length : urb->transfer_buffer_length; if (status == -EINPROGRESS) status = 0; /*Only dump ep in completions and epout submissions*/ if (len && !status && (((ep_addr & USB_DIR_IN) && event) || (!(ep_addr & USB_DIR_IN) && !event))) { if (len >= 32) len = 32; hex_dump_to_buffer(ubuf, len, 32, 4, dbuf, HEX_DUMP_LEN, 0); } else { dbuf = ""; } return dbuf; } static void dbg_log_event(struct urb *urb, char * event, unsigned extra) { unsigned long flags; int ep_addr; char tbuf[TIME_BUF_LEN]; char dbuf[HEX_DUMP_LEN]; if (!enable_dbg_log) return; if (!urb) { write_lock_irqsave(&dbg_hsic_ctrl.lck, flags); scnprintf(dbg_hsic_ctrl.buf[dbg_hsic_ctrl.idx], DBG_MSG_LEN, "%s: %s : %u", get_timestamp(tbuf), event, extra); dbg_inc(&dbg_hsic_ctrl.idx); write_unlock_irqrestore(&dbg_hsic_ctrl.lck, flags); return; } ep_addr = urb->ep->desc.bEndpointAddress; if (!allow_dbg_log(ep_addr)) return; if ((ep_addr & 0x0f) == 0x0) { /*submit event*/ if (!str_to_event(event)) { write_lock_irqsave(&dbg_hsic_ctrl.lck, flags); scnprintf(dbg_hsic_ctrl.buf[dbg_hsic_ctrl.idx], DBG_MSG_LEN, "%s: [%s : %p]:[%s] " "%02x %02x %04x %04x %04x %u %d", get_timestamp(tbuf), event, urb, (ep_addr & USB_DIR_IN) ? "in" : "out", urb->setup_packet[0], urb->setup_packet[1], (urb->setup_packet[3] << 8) | urb->setup_packet[2], (urb->setup_packet[5] << 8) | urb->setup_packet[4], (urb->setup_packet[7] << 8) | urb->setup_packet[6], urb->transfer_buffer_length, extra); dbg_inc(&dbg_hsic_ctrl.idx); write_unlock_irqrestore(&dbg_hsic_ctrl.lck, flags); } else { write_lock_irqsave(&dbg_hsic_ctrl.lck, flags); scnprintf(dbg_hsic_ctrl.buf[dbg_hsic_ctrl.idx], DBG_MSG_LEN, "%s: [%s : %p]:[%s] %u %d", get_timestamp(tbuf), event, urb, (ep_addr & USB_DIR_IN) ? "in" : "out", urb->actual_length, extra); dbg_inc(&dbg_hsic_ctrl.idx); write_unlock_irqrestore(&dbg_hsic_ctrl.lck, flags); } } else { write_lock_irqsave(&dbg_hsic_data.lck, flags); scnprintf(dbg_hsic_data.buf[dbg_hsic_data.idx], DBG_MSG_LEN, "%s: [%s : %p]:ep%d[%s] %u %d %s", get_timestamp(tbuf), event, urb, ep_addr & 0x0f, (ep_addr & USB_DIR_IN) ? "in" : "out", str_to_event(event) ? urb->actual_length : urb->transfer_buffer_length, extra, enable_payload_log ? get_hex_data(dbuf, urb, str_to_event(event), extra) : ""); dbg_inc(&dbg_hsic_data.idx); write_unlock_irqrestore(&dbg_hsic_data.lck, flags); } } static inline struct msm_hsic_hcd *hcd_to_hsic(struct usb_hcd *hcd) { return (struct msm_hsic_hcd *) (hcd->hcd_priv); } static inline struct usb_hcd *hsic_to_hcd(struct msm_hsic_hcd *mehci) { return container_of((void *) mehci, struct usb_hcd, hcd_priv); } static void dbg_msm_qh(struct ehci_hcd *ehci, struct ehci_qh *qh) { struct ehci_qh_hw *hw = qh->hw; pr_info("EP:%x qh %p info %x %x c_qtd-%08x\n", (hw->hw_info1 & 0xF00) >> 8, qh, hw->hw_info1, hw->hw_info2, hw->hw_current); pr_info("overlay n%08x %08x t-%08x nak-%x c_err %x\n", hw->hw_qtd_next, hw->hw_alt_next, hw->hw_token, (hw->hw_alt_next & 0xE)>>1, (hw->hw_token & 0xC00) >> 10); } static void dump_msm_qhs(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_qh *qh, *qh_next; unsigned long flags; int i; for (i = 0; i < 5; i++) { pr_info("Dump QHs, iteration:%d\n", i); spin_lock_irqsave(&ehci->lock, flags); qh_next = ehci->async->qh_next.qh; while (qh_next) { qh = qh_next; qh_next = qh->qh_next.qh; if (!dbg_qh_addr || ((struct ehci_qh *)dbg_qh_addr) == qh) dbg_msm_qh(ehci, qh); } spin_unlock_irqrestore(&ehci->lock, flags); usleep_range(5000, 10000); } } static void dump_hsic_regs(struct usb_hcd *hcd) { int i; struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); if (atomic_read(&mehci->in_lpm)) return; for (i = USB_REG_START_OFFSET; i <= USB_REG_END_OFFSET; i += 0x10) pr_info("%p: %08x\t%08x\t%08x\t%08x\n", hcd->regs + i, readl_relaxed(hcd->regs + i), readl_relaxed(hcd->regs + i + 4), readl_relaxed(hcd->regs + i + 8), readl_relaxed(hcd->regs + i + 0xc)); } #define ULPI_IO_TIMEOUT_USEC (10 * 1000) #define USB_PHY_VDD_DIG_VOL_NONE 0 /*uV */ #define USB_PHY_VDD_DIG_VOL_MIN 945000 /* uV */ #define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */ #define HSIC_DBG1_REG 0x38 static const int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = { { /* VDD_CX CORNER Voting */ [VDD_NONE] = RPM_VREG_CORNER_NONE, [VDD_MIN] = RPM_VREG_CORNER_NOMINAL, [VDD_MAX] = RPM_VREG_CORNER_HIGH, }, { /* VDD_CX Voltage Voting */ [VDD_NONE] = USB_PHY_VDD_DIG_VOL_NONE, [VDD_MIN] = USB_PHY_VDD_DIG_VOL_MIN, [VDD_MAX] = USB_PHY_VDD_DIG_VOL_MAX, }, }; static int msm_hsic_init_vddcx(struct msm_hsic_hcd *mehci, int init) { int ret = 0; int none_vol, min_vol, max_vol; if (!mehci->hsic_vddcx) { mehci->vdd_type = VDDCX_CORNER; mehci->hsic_vddcx = devm_regulator_get(mehci->dev, "hsic_vdd_dig"); if (IS_ERR(mehci->hsic_vddcx)) { mehci->hsic_vddcx = devm_regulator_get(mehci->dev, "HSIC_VDDCX"); if (IS_ERR(mehci->hsic_vddcx)) { dev_err(mehci->dev, "unable to get hsic vddcx\n"); return PTR_ERR(mehci->hsic_vddcx); } mehci->vdd_type = VDDCX; } } none_vol = vdd_val[mehci->vdd_type][VDD_NONE]; min_vol = vdd_val[mehci->vdd_type][VDD_MIN]; max_vol = vdd_val[mehci->vdd_type][VDD_MAX]; if (!init) goto disable_reg; ret = regulator_set_voltage(mehci->hsic_vddcx, min_vol, max_vol); if (ret) { dev_err(mehci->dev, "unable to set the voltage" "for hsic vddcx\n"); return ret; } ret = regulator_enable(mehci->hsic_vddcx); if (ret) { dev_err(mehci->dev, "unable to enable hsic vddcx\n"); goto reg_enable_err; } return 0; disable_reg: regulator_disable(mehci->hsic_vddcx); reg_enable_err: regulator_set_voltage(mehci->hsic_vddcx, none_vol, max_vol); return ret; } /* Global Distributed Switch Controller (GDSC) init */ static int msm_hsic_init_gdsc(struct msm_hsic_hcd *mehci, int init) { int ret = 0; if (IS_ERR(mehci->hsic_gdsc)) return 0; if (!mehci->hsic_gdsc) { mehci->hsic_gdsc = devm_regulator_get(mehci->dev, "HSIC_GDSC"); if (IS_ERR(mehci->hsic_gdsc)) return 0; } if (init) { ret = regulator_enable(mehci->hsic_gdsc); if (ret) { dev_err(mehci->dev, "unable to enable hsic gdsc\n"); return ret; } } else { regulator_disable(mehci->hsic_gdsc); } return 0; } static int __maybe_unused ulpi_read(struct msm_hsic_hcd *mehci, u32 reg) { struct usb_hcd *hcd = hsic_to_hcd(mehci); int cnt = 0; /* initiate read operation */ writel_relaxed(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), USB_ULPI_VIEWPORT); /* wait for completion */ while (cnt < ULPI_IO_TIMEOUT_USEC) { if (!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN)) break; udelay(1); cnt++; } if (cnt >= ULPI_IO_TIMEOUT_USEC) { dev_err(mehci->dev, "ulpi_read: timeout ULPI_VIEWPORT: %08x\n", readl_relaxed(USB_ULPI_VIEWPORT)); dev_err(mehci->dev, "PORTSC: %08x USBCMD: %08x FRINDEX: %08x\n", readl_relaxed(USB_PORTSC), readl_relaxed(USB_USBCMD), readl_relaxed(USB_FRINDEX)); /*frame counter increments afte 125us*/ udelay(130); dev_err(mehci->dev, "ulpi_read: FRINDEX: %08x\n", readl_relaxed(USB_FRINDEX)); return -ETIMEDOUT; } return ULPI_DATA_READ(readl_relaxed(USB_ULPI_VIEWPORT)); } static int ulpi_write(struct msm_hsic_hcd *mehci, u32 val, u32 reg) { struct usb_hcd *hcd = hsic_to_hcd(mehci); int cnt = 0; /* initiate write operation */ writel_relaxed(ULPI_RUN | ULPI_WRITE | ULPI_ADDR(reg) | ULPI_DATA(val), USB_ULPI_VIEWPORT); /* wait for completion */ while (cnt < ULPI_IO_TIMEOUT_USEC) { if (!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN)) break; udelay(1); cnt++; } if (cnt >= ULPI_IO_TIMEOUT_USEC) { dev_err(mehci->dev, "ulpi_write: timeout ULPI_VIEWPORT: %08x\n", readl_relaxed(USB_ULPI_VIEWPORT)); dev_err(mehci->dev, "PORTSC: %08x USBCMD: %08x FRINDEX: %08x\n", readl_relaxed(USB_PORTSC), readl_relaxed(USB_USBCMD), readl_relaxed(USB_FRINDEX)); /*frame counter increments afte 125us*/ udelay(130); dev_err(mehci->dev, "ulpi_write: FRINDEX: %08x\n", readl_relaxed(USB_FRINDEX)); return -ETIMEDOUT; } return 0; } static int msm_hsic_config_gpios(struct msm_hsic_hcd *mehci, int gpio_en) { int rc = 0; struct msm_hsic_host_platform_data *pdata; static int gpio_status; pdata = mehci->dev->platform_data; if (!pdata || !pdata->strobe || !pdata->data) return rc; if (gpio_status == gpio_en) return 0; gpio_status = gpio_en; if (!gpio_en) goto free_gpio; rc = gpio_request(pdata->strobe, "HSIC_STROBE_GPIO"); if (rc < 0) { dev_err(mehci->dev, "gpio request failed for HSIC STROBE\n"); return rc; } rc = gpio_request(pdata->data, "HSIC_DATA_GPIO"); if (rc < 0) { dev_err(mehci->dev, "gpio request failed for HSIC DATA\n"); goto free_strobe; } return 0; free_gpio: gpio_free(pdata->data); free_strobe: gpio_free(pdata->strobe); return rc; } static void msm_hsic_clk_reset(struct msm_hsic_hcd *mehci) { int ret; /* alt_core_clk exists in targets that do not use asynchronous reset */ if (!IS_ERR(mehci->alt_core_clk)) { ret = clk_reset(mehci->core_clk, CLK_RESET_ASSERT); if (ret) { dev_err(mehci->dev, "hsic clk assert failed:%d\n", ret); return; } /* Since a hw bug, turn off the clock before complete reset */ clk_disable(mehci->core_clk); ret = clk_reset(mehci->core_clk, CLK_RESET_DEASSERT); if (ret) dev_err(mehci->dev, "hsic clk deassert failed:%d\n", ret); usleep_range(10000, 12000); clk_enable(mehci->core_clk); } else { /* Using asynchronous block reset to the hardware */ clk_disable_unprepare(mehci->core_clk); clk_disable_unprepare(mehci->phy_clk); clk_disable_unprepare(mehci->cal_clk); clk_disable_unprepare(mehci->ahb_clk); if (!IS_ERR(mehci->inactivity_clk)) clk_disable_unprepare(mehci->inactivity_clk); ret = clk_reset(mehci->core_clk, CLK_RESET_ASSERT); if (ret) { dev_err(mehci->dev, "hsic clk assert failed:%d\n", ret); return; } usleep_range(10000, 12000); ret = clk_reset(mehci->core_clk, CLK_RESET_DEASSERT); if (ret) dev_err(mehci->dev, "hsic clk deassert failed:%d\n", ret); /* * Required delay between the deassertion and * clock enablement. */ ndelay(200); clk_prepare_enable(mehci->core_clk); clk_prepare_enable(mehci->phy_clk); clk_prepare_enable(mehci->cal_clk); clk_prepare_enable(mehci->ahb_clk); if (!IS_ERR(mehci->inactivity_clk)) clk_prepare_enable(mehci->inactivity_clk); } } #define HSIC_STROBE_GPIO_PAD_CTL (MSM_TLMM_BASE+0x20C0) #define HSIC_DATA_GPIO_PAD_CTL (MSM_TLMM_BASE+0x20C4) #define HSIC_CAL_PAD_CTL (MSM_TLMM_BASE+0x20C8) #define HSIC_LV_MODE 0x04 #define HSIC_PAD_CALIBRATION 0xA8 #define HSIC_GPIO_PAD_VAL 0x0A0AAA10 #define LINK_RESET_TIMEOUT_USEC (250 * 1000) static void msm_hsic_phy_reset(struct msm_hsic_hcd *mehci) { struct usb_hcd *hcd = hsic_to_hcd(mehci); msm_hsic_clk_reset(mehci); /* select ulpi phy */ writel_relaxed(0x80000000, USB_PORTSC); mb(); } static int msm_hsic_start(struct msm_hsic_hcd *mehci) { struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; int ret; void __iomem *reg; if (pdata && pdata->resume_gpio) { ret = gpio_request(pdata->resume_gpio, "HSIC_RESUME_GPIO"); if (ret < 0) { dev_err(mehci->dev, "gpio req failed for hsic resume:%d\n", ret); pdata->resume_gpio = 0; } } /* HSIC init sequence when HSIC signals (Strobe/Data) are routed via GPIOs */ if (pdata && pdata->strobe && pdata->data) { if (!pdata->ignore_cal_pad_config) { /* Enable LV_MODE in HSIC_CAL_PAD_CTL register */ writel_relaxed(HSIC_LV_MODE, HSIC_CAL_PAD_CTL); mb(); } /*set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ ulpi_write(mehci, 0xFF, 0x33); /* Enable periodic IO calibration in HSIC_CFG register */ ulpi_write(mehci, HSIC_PAD_CALIBRATION, 0x30); /* Configure GPIO pins for HSIC functionality mode */ ret = msm_hsic_config_gpios(mehci, 1); if (ret) { dev_err(mehci->dev, " gpio configuarion failed\n"); goto free_resume_gpio; } if (pdata->strobe_pad_offset) { /* Set CORE_CTL_EN in STROBE GPIO PAD_CTL register */ reg = MSM_TLMM_BASE + pdata->strobe_pad_offset; writel_relaxed(readl_relaxed(reg) | 0x2000000, reg); } else { /* Set LV_MODE=0x1 and DCC=0x2 in STROBE GPIO PAD_CTL */ reg = HSIC_STROBE_GPIO_PAD_CTL; writel_relaxed(HSIC_GPIO_PAD_VAL, reg); } if (pdata->data_pad_offset) { /* Set CORE_CTL_EN in HSIC_DATA GPIO PAD_CTL register */ reg = MSM_TLMM_BASE + pdata->data_pad_offset; writel_relaxed(readl_relaxed(reg) | 0x2000000, reg); } else { /* Set LV_MODE=0x1 and DCC=0x2 in STROBE GPIO PAD_CTL */ reg = HSIC_DATA_GPIO_PAD_CTL; writel_relaxed(HSIC_GPIO_PAD_VAL, reg); } mb(); /* Enable HSIC mode in HSIC_CFG register */ ulpi_write(mehci, 0x01, 0x31); } else { /* HSIC init sequence when HSIC signals (Strobe/Data) are routed via dedicated I/O */ /* programmable length of connect signaling (33.2ns) */ ret = ulpi_write(mehci, 3, HSIC_DBG1_REG); if (ret) { pr_err("%s: Unable to program length of connect " "signaling\n", __func__); } /*set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ ulpi_write(mehci, 0xFF, 0x33); /* Enable HSIC mode in HSIC_CFG register */ ulpi_write(mehci, 0xA9, 0x30); } /*disable auto resume*/ ulpi_write(mehci, ULPI_IFC_CTRL_AUTORESUME, ULPI_CLR(ULPI_IFC_CTRL)); return 0; free_resume_gpio: if (pdata && pdata->resume_gpio) gpio_free(pdata->resume_gpio); return ret; } #define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000) #define PHY_RESUME_TIMEOUT_USEC (100 * 1000) #ifdef CONFIG_PM_SLEEP static int msm_hsic_reset(struct msm_hsic_hcd *mehci) { /* reset HSIC phy */ msm_hsic_phy_reset(mehci); /* HSIC init procedure (caliberation) */ return msm_hsic_start(mehci); } static int msm_hsic_suspend(struct msm_hsic_hcd *mehci) { struct usb_hcd *hcd = hsic_to_hcd(mehci); int cnt = 0, ret; u32 val; int none_vol, max_vol; struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; if (atomic_read(&mehci->in_lpm)) { dev_dbg(mehci->dev, "%s called in lpm\n", __func__); return 0; } disable_irq(hcd->irq); /* make sure we don't race against a remote wakeup */ if (test_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags) || readl_relaxed(USB_PORTSC) & PORT_RESUME) { dev_dbg(mehci->dev, "wakeup pending, aborting suspend\n"); enable_irq(hcd->irq); return -EBUSY; } if (pdata->consider_ipa_handshake) { dev_dbg(mehci->dev, "%s:Wait for resources release\n", __func__); if (!msm_bam_hsic_lpm_ok()) { dev_dbg(mehci->dev, "%s:Prod+Cons not released\n", __func__); enable_irq(hcd->irq); return -EBUSY; } dev_dbg(mehci->dev, "%s:Prod+Cons resources released\n", __func__); } /* * PHY may take some time or even fail to enter into low power * mode (LPM). Hence poll for 500 msec and reset the PHY and link * in failure case. */ val = readl_relaxed(USB_PORTSC); val &= ~PORT_RWC_BITS; val |= PORTSC_PHCD; writel_relaxed(val, USB_PORTSC); while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD) break; udelay(1); cnt++; } if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { dev_err(mehci->dev, "Unable to suspend PHY\n"); msm_hsic_config_gpios(mehci, 0); msm_hsic_reset(mehci); } /* * PHY has capability to generate interrupt asynchronously in low * power mode (LPM). This interrupt is level triggered. So USB IRQ * line must be disabled till async interrupt enable bit is cleared * in USBCMD register. Assert STP (ULPI interface STOP signal) to * block data communication from PHY. Enable asynchronous interrupt * only when wakeup gpio IRQ is not present. */ if (mehci->wakeup_irq) writel_relaxed(readl_relaxed(USB_USBCMD) | ULPI_STP_CTRL, USB_USBCMD); else writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); /* * Ensure that hardware is put in low power mode before * clocks are turned OFF and VDD is allowed to minimize. */ mb(); clk_disable_unprepare(mehci->core_clk); clk_disable_unprepare(mehci->phy_clk); clk_disable_unprepare(mehci->cal_clk); clk_disable_unprepare(mehci->ahb_clk); if (!IS_ERR(mehci->inactivity_clk)) clk_disable_unprepare(mehci->inactivity_clk); none_vol = vdd_val[mehci->vdd_type][VDD_NONE]; max_vol = vdd_val[mehci->vdd_type][VDD_MAX]; ret = regulator_set_voltage(mehci->hsic_vddcx, none_vol, max_vol); if (ret < 0) dev_err(mehci->dev, "unable to set vddcx voltage for VDD MIN\n"); if (mehci->bus_perf_client && debug_bus_voting_enabled) { mehci->bus_vote = false; queue_work(ehci_wq, &mehci->bus_vote_w); } atomic_set(&mehci->in_lpm, 1); enable_irq(hcd->irq); if (mehci->wakeup_irq) { mehci->wakeup_irq_enabled = 1; enable_irq_wake(mehci->wakeup_irq); enable_irq(mehci->wakeup_irq); } if (pdata && pdata->standalone_latency) pm_qos_update_request(&mehci->pm_qos_req_dma, PM_QOS_DEFAULT_VALUE); wake_unlock(&mehci->wlock); dev_info(mehci->dev, "HSIC-USB in low power mode\n"); return 0; } static int msm_hsic_resume(struct msm_hsic_hcd *mehci) { struct usb_hcd *hcd = hsic_to_hcd(mehci); int cnt = 0, ret; unsigned temp; int min_vol, max_vol; unsigned long flags; struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; if (!atomic_read(&mehci->in_lpm)) { dev_dbg(mehci->dev, "%s called in !in_lpm\n", __func__); return 0; } if (pdata->consider_ipa_handshake) { dev_dbg(mehci->dev, "%s:Wait for producer resource\n", __func__); msm_bam_wait_for_hsic_prod_granted(); dev_dbg(mehci->dev, "%s:Producer resource obtained\n", __func__); } /* Handles race with Async interrupt */ disable_irq(hcd->irq); if (pdata && pdata->standalone_latency) pm_qos_update_request(&mehci->pm_qos_req_dma, pdata->standalone_latency + 1); if (mehci->wakeup_irq) { spin_lock_irqsave(&mehci->wakeup_lock, flags); if (mehci->wakeup_irq_enabled) { disable_irq_wake(mehci->wakeup_irq); disable_irq_nosync(mehci->wakeup_irq); mehci->wakeup_irq_enabled = 0; } spin_unlock_irqrestore(&mehci->wakeup_lock, flags); } wake_lock(&mehci->wlock); if (mehci->bus_perf_client && debug_bus_voting_enabled) { mehci->bus_vote = true; queue_work(ehci_wq, &mehci->bus_vote_w); } min_vol = vdd_val[mehci->vdd_type][VDD_MIN]; max_vol = vdd_val[mehci->vdd_type][VDD_MAX]; ret = regulator_set_voltage(mehci->hsic_vddcx, min_vol, max_vol); if (ret < 0) dev_err(mehci->dev, "unable to set nominal vddcx voltage (no VDD MIN)\n"); clk_prepare_enable(mehci->core_clk); clk_prepare_enable(mehci->phy_clk); clk_prepare_enable(mehci->cal_clk); clk_prepare_enable(mehci->ahb_clk); if (!IS_ERR(mehci->inactivity_clk)) clk_prepare_enable(mehci->inactivity_clk); temp = readl_relaxed(USB_USBCMD); temp &= ~ASYNC_INTR_CTRL; temp &= ~ULPI_STP_CTRL; writel_relaxed(temp, USB_USBCMD); if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) goto skip_phy_resume; temp = readl_relaxed(USB_PORTSC); temp &= ~(PORT_RWC_BITS | PORTSC_PHCD); writel_relaxed(temp, USB_PORTSC); while (cnt < PHY_RESUME_TIMEOUT_USEC) { if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD) && (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE)) break; udelay(1); cnt++; } if (cnt >= PHY_RESUME_TIMEOUT_USEC) { /* * This is a fatal error. Reset the link and * PHY to make hsic working. */ dev_err(mehci->dev, "Unable to resume USB. Reset the hsic\n"); msm_hsic_config_gpios(mehci, 0); msm_hsic_reset(mehci); } skip_phy_resume: /* Check HSIC host pipe state before resume. Pipe should be empty. */ if (pdata->consider_ipa_handshake) { if (!msm_bam_hsic_host_pipe_empty()) { dev_err(mehci->dev, "Data pending without resumed.\n"); BUG_ON(1); } } usb_hcd_resume_root_hub(hcd); atomic_set(&mehci->in_lpm, 0); if (atomic_read(&mehci->async_int)) { atomic_set(&mehci->async_int, 0); pm_runtime_put_noidle(mehci->dev); enable_irq(hcd->irq); } if (atomic_read(&mehci->pm_usage_cnt)) { atomic_set(&mehci->pm_usage_cnt, 0); pm_runtime_put_noidle(mehci->dev); } enable_irq(hcd->irq); dev_info(mehci->dev, "HSIC-USB exited from low power mode\n"); if (pdata->consider_ipa_handshake) { dev_dbg(mehci->dev, "%s:Notify usb bam on resume complete\n", __func__); msm_bam_hsic_notify_on_resume(); } return 0; } #endif static void ehci_hsic_bus_vote_w(struct work_struct *w) { struct msm_hsic_hcd *mehci = container_of(w, struct msm_hsic_hcd, bus_vote_w); int ret; ret = msm_bus_scale_client_update_request(mehci->bus_perf_client, mehci->bus_vote); if (ret) dev_err(mehci->dev, "%s: Failed to vote for bus bandwidth %d\n", __func__, ret); } static int msm_hsic_reset_done(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); u32 __iomem *status_reg = &ehci->regs->port_status[0]; int ret; ehci_writel(ehci, ehci_readl(ehci, status_reg) & ~(PORT_RWC_BITS | PORT_RESET), status_reg); ret = handshake(ehci, status_reg, PORT_RESET, 0, 1 * 1000); if (ret) pr_err("reset handshake failed in %s\n", __func__); else ehci_writel(ehci, ehci_readl(ehci, &ehci->regs->command) | CMD_RUN, &ehci->regs->command); return ret; } #define STS_GPTIMER0_INTERRUPT BIT(24) static irqreturn_t msm_hsic_irq(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); u32 status; int ret; if (atomic_read(&mehci->in_lpm)) { dev_dbg(mehci->dev, "phy async intr\n"); dbg_log_event(NULL, "Async IRQ", 0); ret = pm_runtime_get(mehci->dev); if ((ret == 1) || (ret == -EINPROGRESS)) { pm_runtime_put_noidle(mehci->dev); } else { disable_irq_nosync(hcd->irq); atomic_set(&mehci->async_int, 1); } return IRQ_HANDLED; } status = ehci_readl(ehci, &ehci->regs->status); if (status & STS_GPTIMER0_INTERRUPT) { int timeleft; dbg_log_event(NULL, "FPR: gpt0_isr", mehci->bus_reset); timeleft = GPT_CNT(ehci_readl(ehci, &mehci->timer->gptimer1_ctrl)); if (timeleft) { if (mehci->bus_reset) { ret = msm_hsic_reset_done(hcd); if (ret) { mehci->reset_again = 1; dbg_log_event(NULL, "RESET: fail", 0); } } else { ehci_writel(ehci, ehci_readl(ehci, &ehci->regs->command) | CMD_RUN, &ehci->regs->command); } } else { if (mehci->bus_reset) mehci->reset_again = 1; else mehci->resume_again = 1; } dbg_log_event(NULL, "FPR: timeleft", timeleft); complete(&mehci->gpt0_completion); ehci_writel(ehci, STS_GPTIMER0_INTERRUPT, &ehci->regs->status); } return ehci_irq(hcd); } static int ehci_hsic_reset(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; int retval; u32 temp; mehci->timer = USB_HS_GPTIMER_BASE; ehci->caps = USB_CAPLENGTH; ehci->regs = USB_CAPLENGTH + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); /* cache the data to minimize the chip reads*/ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; ehci->sbrn = HCD_USB2; retval = ehci_halt(ehci); if (retval) return retval; /* data structure init */ retval = ehci_init(hcd); if (retval) return retval; retval = ehci_reset(ehci); if (retval) return retval; /* bursts of unspecified length. */ writel_relaxed(0, USB_AHBBURST); /* Use the AHB transactor and configure async bridge bypass */ #define MSM_USB_ASYNC_BRIDGE_BYPASS BIT(31) if (pdata->ahb_async_bridge_bypass) writel_relaxed(0x08 | MSM_USB_ASYNC_BRIDGE_BYPASS, USB_AHBMODE); else writel_relaxed(0x08, USB_AHBMODE); if (pdata->dis_internal_clk_gating) { temp = readl_relaxed(USB_GENCONFIG2); temp &= ~GENCFG2_SYS_CLK_HOST_DEV_GATE_EN; writel_relaxed(temp, USB_GENCONFIG2); } /* Disable streaming mode and select host mode */ writel_relaxed(0x13, USB_USBMODE); ehci_port_power(ehci, 1); return 0; } #ifdef CONFIG_PM #define RESET_RETRY_LIMIT 3 #define RESET_SIGNAL_TIME_SOF_USEC (50 * 1000) #define RESET_SIGNAL_TIME_USEC (20 * 1000) static void ehci_hsic_reset_sof_bug_handler(struct usb_hcd *hcd, u32 val) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; u32 __iomem *status_reg = &ehci->regs->port_status[0]; u32 cmd; unsigned long flags; int retries = 0, ret, cnt = RESET_SIGNAL_TIME_USEC; if (pdata && pdata->swfi_latency) pm_qos_update_request(&mehci->pm_qos_req_dma, pdata->swfi_latency + 1); mehci->bus_reset = 1; /* Halt the controller */ cmd = ehci_readl(ehci, &ehci->regs->command); cmd &= ~CMD_RUN; ehci_writel(ehci, cmd, &ehci->regs->command); ret = handshake(ehci, &ehci->regs->status, STS_HALT, STS_HALT, 16 * 125); if (ret) { pr_err("halt handshake fatal error\n"); dbg_log_event(NULL, "HALT: fatal", 0); goto fail; } retry: retries++; dbg_log_event(NULL, "RESET: start", retries); pr_debug("reset begin %d\n", retries); mehci->reset_again = 0; spin_lock_irqsave(&ehci->lock, flags); ehci_writel(ehci, val, status_reg); ehci_writel(ehci, GPT_LD(RESET_SIGNAL_TIME_USEC - 1), &mehci->timer->gptimer0_ld); ehci_writel(ehci, GPT_RESET | GPT_RUN, &mehci->timer->gptimer0_ctrl); ehci_writel(ehci, INTR_MASK | STS_GPTIMER0_INTERRUPT, &ehci->regs->intr_enable); ehci_writel(ehci, GPT_LD(RESET_SIGNAL_TIME_SOF_USEC - 1), &mehci->timer->gptimer1_ld); ehci_writel(ehci, GPT_RESET | GPT_RUN, &mehci->timer->gptimer1_ctrl); spin_unlock_irqrestore(&ehci->lock, flags); wait_for_completion(&mehci->gpt0_completion); if (!mehci->reset_again) goto done; if (handshake(ehci, status_reg, PORT_RESET, 0, 10 * 1000)) { pr_err("reset handshake fatal error\n"); dbg_log_event(NULL, "RESET: fatal", retries); goto fail; } if (retries < RESET_RETRY_LIMIT) goto retry; /* complete reset in tight loop */ pr_info("RESET in tight loop\n"); dbg_log_event(NULL, "RESET: tight", 0); spin_lock_irqsave(&ehci->lock, flags); ehci_writel(ehci, val, status_reg); while (cnt--) udelay(1); ret = msm_hsic_reset_done(hcd); spin_unlock_irqrestore(&ehci->lock, flags); if (ret) { pr_err("RESET in tight loop failed\n"); dbg_log_event(NULL, "RESET: tight failed", 0); goto fail; } done: dbg_log_event(NULL, "RESET: done", retries); pr_debug("reset completed\n"); fail: mehci->bus_reset = 0; if (pdata && pdata->swfi_latency) pm_qos_update_request(&mehci->pm_qos_req_dma, PM_QOS_DEFAULT_VALUE); } static int ehci_hsic_bus_suspend(struct usb_hcd *hcd) { struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); if (!(readl_relaxed(USB_PORTSC) & PORT_PE)) { dbg_log_event(NULL, "RH suspend attempt failed", 0); dev_dbg(mehci->dev, "%s:port is not enabled skip suspend\n", __func__); return -EAGAIN; } dbg_log_event(NULL, "Suspend RH", 0); return ehci_bus_suspend(hcd); } #define RESUME_RETRY_LIMIT 3 #define RESUME_SIGNAL_TIME_USEC (21 * 1000) #define RESUME_SIGNAL_TIME_SOF_USEC (23 * 1000) static int msm_hsic_resume_thread(void *data) { struct msm_hsic_hcd *mehci = data; struct usb_hcd *hcd = hsic_to_hcd(mehci); struct ehci_hcd *ehci = hcd_to_ehci(hcd); u32 temp; unsigned long resume_needed = 0; int retry_cnt = 0; int tight_resume = 0; struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; dbg_log_event(NULL, "Resume RH", 0); /* keep delay between bus states */ if (time_before(jiffies, ehci->next_statechange)) usleep_range(5000, 5000); spin_lock_irq(&ehci->lock); if (!HCD_HW_ACCESSIBLE(hcd)) { spin_unlock_irq(&ehci->lock); mehci->resume_status = -ESHUTDOWN; complete(&mehci->rt_completion); return 0; } if (unlikely(ehci->debug)) { if (!dbgp_reset_prep()) ehci->debug = NULL; else dbgp_external_startup(); } /* at least some APM implementations will try to deliver * IRQs right away, so delay them until we're ready. */ ehci_writel(ehci, 0, &ehci->regs->intr_enable); /* re-init operational registers */ ehci_writel(ehci, 0, &ehci->regs->segment); ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next); /*CMD_RUN will be set after, PORT_RESUME gets cleared*/ if (ehci->resume_sof_bug) ehci->command &= ~CMD_RUN; /* restore CMD_RUN, framelist size, and irq threshold */ ehci_writel(ehci, ehci->command, &ehci->regs->command); /* manually resume the ports we suspended during bus_suspend() */ resume_again: if (retry_cnt >= RESUME_RETRY_LIMIT) { pr_info("retry count(%d) reached max, resume in tight loop\n", retry_cnt); tight_resume = 1; } temp = ehci_readl(ehci, &ehci->regs->port_status[0]); temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); if (test_bit(0, &ehci->bus_suspended) && (temp & PORT_SUSPEND)) { temp |= PORT_RESUME; set_bit(0, &resume_needed); } dbg_log_event(NULL, "FPR: Set", temp); ehci_writel(ehci, temp, &ehci->regs->port_status[0]); /* HSIC controller has a h/w bug due to which it can try to send SOFs * (start of frames) during port resume resulting in phy lockup. HSIC hw * controller in MSM clears FPR bit after driving the resume signal for * 20ms. Workaround is to stop SOFs before driving resume and then start * sending SOFs immediately. Need to send SOFs within 3ms of resume * completion otherwise peripheral may enter undefined state. As * usleep_range does not gurantee exact sleep time, GPTimer is used to * to time the resume sequence. If driver exceeds allowable time SOFs, * repeat the resume process. */ if (ehci->resume_sof_bug && resume_needed) { if (!tight_resume) { mehci->resume_again = 0; ehci_writel(ehci, GPT_LD(RESUME_SIGNAL_TIME_USEC - 1), &mehci->timer->gptimer0_ld); ehci_writel(ehci, GPT_RESET | GPT_RUN, &mehci->timer->gptimer0_ctrl); ehci_writel(ehci, INTR_MASK | STS_GPTIMER0_INTERRUPT, &ehci->regs->intr_enable); ehci_writel(ehci, GPT_LD( RESUME_SIGNAL_TIME_SOF_USEC - 1), &mehci->timer->gptimer1_ld); ehci_writel(ehci, GPT_RESET | GPT_RUN, &mehci->timer->gptimer1_ctrl); spin_unlock_irq(&ehci->lock); if (pdata && pdata->swfi_latency) pm_qos_update_request(&mehci->pm_qos_req_dma, pdata->swfi_latency + 1); wait_for_completion(&mehci->gpt0_completion); if (pdata && pdata->standalone_latency) pm_qos_update_request(&mehci->pm_qos_req_dma, pdata->standalone_latency + 1); spin_lock_irq(&ehci->lock); } else { dbg_log_event(NULL, "FPR: Tightloop", 0); /* do the resume in a tight loop */ handshake(ehci, &ehci->regs->port_status[0], PORT_RESUME, 0, 22 * 1000); ehci_writel(ehci, ehci_readl(ehci, &ehci->regs->command) | CMD_RUN, &ehci->regs->command); } if (mehci->resume_again) { int temp; dbg_log_event(NULL, "FPR: Re-Resume", retry_cnt); pr_info("FPR: retry count: %d\n", retry_cnt); spin_unlock_irq(&ehci->lock); temp = ehci_readl(ehci, &ehci->regs->port_status[0]); temp &= ~PORT_RWC_BITS; temp |= PORT_SUSPEND; ehci_writel(ehci, temp, &ehci->regs->port_status[0]); /* Keep the bus idle for 5ms so that peripheral * can detect and initiate suspend */ usleep_range(5000, 5000); dbg_log_event(NULL, "FPR: RResume", ehci_readl(ehci, &ehci->regs->port_status[0])); spin_lock_irq(&ehci->lock); mehci->resume_again = 0; retry_cnt++; goto resume_again; } } dbg_log_event(NULL, "FPR: RT-Done", 0); mehci->resume_status = 1; spin_unlock_irq(&ehci->lock); complete(&mehci->rt_completion); return 0; } static int ehci_hsic_bus_resume(struct usb_hcd *hcd) { struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); struct ehci_hcd *ehci = hcd_to_ehci(hcd); u32 temp; struct task_struct *resume_thread = NULL; struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; if (pdata->resume_gpio) gpio_direction_output(pdata->resume_gpio, 1); if (!mehci->ehci.resume_sof_bug) { ehci_bus_resume(hcd); } else { mehci->resume_status = 0; resume_thread = kthread_run(msm_hsic_resume_thread, mehci, "hsic_resume_thread"); if (IS_ERR(resume_thread)) { pr_err("Error creating resume thread:%lu\n", PTR_ERR(resume_thread)); return PTR_ERR(resume_thread); } wait_for_completion(&mehci->rt_completion); if (mehci->resume_status < 0) return mehci->resume_status; dbg_log_event(NULL, "FPR: Wokeup", 0); spin_lock_irq(&ehci->lock); (void) ehci_readl(ehci, &ehci->regs->command); temp = 0; if (ehci->async->qh_next.qh) temp |= CMD_ASE; if (ehci->periodic_sched) temp |= CMD_PSE; if (temp) { ehci->command |= temp; ehci_writel(ehci, ehci->command, &ehci->regs->command); } ehci->next_statechange = jiffies + msecs_to_jiffies(5); hcd->state = HC_STATE_RUNNING; ehci->rh_state = EHCI_RH_RUNNING; ehci->command |= CMD_RUN; /* Now we can safely re-enable irqs */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); spin_unlock_irq(&ehci->lock); } if (pdata->resume_gpio) gpio_direction_output(pdata->resume_gpio, 0); return 0; } #else #define ehci_hsic_bus_suspend NULL #define ehci_hsic_bus_resume NULL #endif /* CONFIG_PM */ static void ehci_msm_set_autosuspend_delay(struct usb_device *dev) { if (!dev->parent) /*for root hub no delay*/ pm_runtime_set_autosuspend_delay(&dev->dev, 0); else pm_runtime_set_autosuspend_delay(&dev->dev, 200); } static int ehci_msm_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); struct usb_host_bam_type *usb_host_bam = (struct usb_host_bam_type *)urb->priv_data; if (usb_host_bam && mehci && mehci->enable_hbm) return hbm_urb_enqueue(hcd, urb, mem_flags); return ehci_urb_enqueue(hcd, urb, mem_flags); } static struct hc_driver msm_hsic_driver = { .description = hcd_name, .product_desc = "Qualcomm EHCI Host Controller using HSIC", .hcd_priv_size = sizeof(struct msm_hsic_hcd), /* * generic hardware linkage */ .irq = msm_hsic_irq, .flags = HCD_USB2 | HCD_MEMORY | HCD_OLD_ENUM, .reset = ehci_hsic_reset, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, /* * managing i/o requests and associated device resources */ .urb_enqueue = ehci_msm_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, .endpoint_reset = ehci_endpoint_reset, .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, /* * scheduling support */ .get_frame_number = ehci_get_frame, /* * root hub support */ .hub_status_data = ehci_hub_status_data, .hub_control = ehci_hub_control, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, /* * PM support */ .bus_suspend = ehci_hsic_bus_suspend, .bus_resume = ehci_hsic_bus_resume, .log_urb = dbg_log_event, .dump_regs = dump_hsic_regs, .set_autosuspend_delay = ehci_msm_set_autosuspend_delay, .reset_sof_bug_handler = ehci_hsic_reset_sof_bug_handler, }; static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init) { int ret = 0; if (!init) goto put_clocks; /*core_clk is required for LINK protocol engine *clock rate appropriately set by target specific clock driver */ mehci->core_clk = clk_get(mehci->dev, "core_clk"); if (IS_ERR(mehci->core_clk)) { dev_err(mehci->dev, "failed to get core_clk\n"); ret = PTR_ERR(mehci->core_clk); return ret; } /* alt_core_clk is for LINK to be used during PHY RESET in * targets on which link does NOT use asynchronous reset methodology. * clock rate appropriately set by target specific clock driver */ mehci->alt_core_clk = clk_get(mehci->dev, "alt_core_clk"); if (IS_ERR(mehci->alt_core_clk)) dev_dbg(mehci->dev, "failed to get alt_core_clk\n"); /* phy_clk is required for HSIC PHY operation * clock rate appropriately set by target specific clock driver */ mehci->phy_clk = clk_get(mehci->dev, "phy_clk"); if (IS_ERR(mehci->phy_clk)) { dev_err(mehci->dev, "failed to get phy_clk\n"); ret = PTR_ERR(mehci->phy_clk); goto put_alt_core_clk; } /* 10MHz cal_clk is required for calibration of I/O pads */ mehci->cal_clk = clk_get(mehci->dev, "cal_clk"); if (IS_ERR(mehci->cal_clk)) { dev_err(mehci->dev, "failed to get cal_clk\n"); ret = PTR_ERR(mehci->cal_clk); goto put_phy_clk; } /* ahb_clk is required for data transfers */ mehci->ahb_clk = clk_get(mehci->dev, "iface_clk"); if (IS_ERR(mehci->ahb_clk)) { dev_err(mehci->dev, "failed to get iface_clk\n"); ret = PTR_ERR(mehci->ahb_clk); goto put_cal_clk; } /* * Inactivity_clk is required for hsic bam inactivity timer. * This clock is not compulsory and is defined in clock lookup * only for targets that need to use the inactivity timer feature. */ mehci->inactivity_clk = clk_get(mehci->dev, "inactivity_clk"); if (IS_ERR(mehci->inactivity_clk)) dev_dbg(mehci->dev, "failed to get inactivity_clk\n"); clk_prepare_enable(mehci->core_clk); clk_prepare_enable(mehci->phy_clk); clk_prepare_enable(mehci->cal_clk); clk_prepare_enable(mehci->ahb_clk); if (!IS_ERR(mehci->inactivity_clk)) clk_prepare_enable(mehci->inactivity_clk); return 0; put_clocks: if (!atomic_read(&mehci->in_lpm)) { clk_disable_unprepare(mehci->core_clk); clk_disable_unprepare(mehci->phy_clk); clk_disable_unprepare(mehci->cal_clk); clk_disable_unprepare(mehci->ahb_clk); if (!IS_ERR(mehci->inactivity_clk)) clk_disable_unprepare(mehci->inactivity_clk); } if (!IS_ERR(mehci->inactivity_clk)) clk_put(mehci->inactivity_clk); clk_put(mehci->ahb_clk); put_cal_clk: clk_put(mehci->cal_clk); put_phy_clk: clk_put(mehci->phy_clk); put_alt_core_clk: if (!IS_ERR(mehci->alt_core_clk)) clk_put(mehci->alt_core_clk); clk_put(mehci->core_clk); return ret; } static irqreturn_t hsic_peripheral_status_change(int irq, void *dev_id) { struct msm_hsic_hcd *mehci = dev_id; pr_debug("%s: mehci:%p dev_id:%p\n", __func__, mehci, dev_id); if (mehci) msm_hsic_config_gpios(mehci, 0); return IRQ_HANDLED; } static irqreturn_t msm_hsic_wakeup_irq(int irq, void *data) { struct msm_hsic_hcd *mehci = data; int ret; if (irq == mehci->async_irq) { mehci->async_int_cnt++; dbg_log_event(NULL, "Remote Wakeup (ASYNC) IRQ", mehci->async_int_cnt); } else { mehci->wakeup_int_cnt++; dbg_log_event(NULL, "Remote Wakeup IRQ", mehci->wakeup_int_cnt); } dev_dbg(mehci->dev, "%s: hsic remote wakeup interrupt %d cnt: %u, %u\n", __func__, irq, mehci->wakeup_int_cnt, mehci->async_int_cnt); wake_lock(&mehci->wlock); if (mehci->wakeup_irq) { spin_lock(&mehci->wakeup_lock); if (mehci->wakeup_irq_enabled) { mehci->wakeup_irq_enabled = 0; disable_irq_wake(irq); disable_irq_nosync(irq); } spin_unlock(&mehci->wakeup_lock); } if (!atomic_read(&mehci->pm_usage_cnt)) { ret = pm_runtime_get(mehci->dev); /* * HSIC runtime resume can race with us. * if we are active (ret == 1) or resuming * (ret == -EINPROGRESS), decrement the * PM usage counter before returning. */ if ((ret == 1) || (ret == -EINPROGRESS)) pm_runtime_put_noidle(mehci->dev); else atomic_set(&mehci->pm_usage_cnt, 1); } return IRQ_HANDLED; } static int ehci_hsic_msm_bus_show(struct seq_file *s, void *unused) { if (debug_bus_voting_enabled) seq_printf(s, "enabled\n"); else seq_printf(s, "disabled\n"); return 0; } static int ehci_hsic_msm_bus_open(struct inode *inode, struct file *file) { return single_open(file, ehci_hsic_msm_bus_show, inode->i_private); } static ssize_t ehci_hsic_msm_bus_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { char buf[8]; int ret; struct seq_file *s = file->private_data; struct msm_hsic_hcd *mehci = s->private; memset(buf, 0x00, sizeof(buf)); if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; if (!strncmp(buf, "enable", 6)) { /* Do not vote here. Let hsic driver decide when to vote */ debug_bus_voting_enabled = true; } else { debug_bus_voting_enabled = false; if (mehci->bus_perf_client) { ret = msm_bus_scale_client_update_request( mehci->bus_perf_client, 0); if (ret) dev_err(mehci->dev, "%s: Failed to devote " "for bus bw %d\n", __func__, ret); } } return count; } const struct file_operations ehci_hsic_msm_bus_fops = { .open = ehci_hsic_msm_bus_open, .read = seq_read, .write = ehci_hsic_msm_bus_write, .llseek = seq_lseek, .release = single_release, }; static int ehci_hsic_msm_wakeup_cnt_show(struct seq_file *s, void *unused) { struct msm_hsic_hcd *mehci = s->private; seq_printf(s, "%u\n", mehci->wakeup_int_cnt); return 0; } static int ehci_hsic_msm_wakeup_cnt_open(struct inode *inode, struct file *f) { return single_open(f, ehci_hsic_msm_wakeup_cnt_show, inode->i_private); } const struct file_operations ehci_hsic_msm_wakeup_cnt_fops = { .open = ehci_hsic_msm_wakeup_cnt_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int ehci_hsic_msm_data_events_show(struct seq_file *s, void *unused) { unsigned long flags; unsigned i; read_lock_irqsave(&dbg_hsic_data.lck, flags); i = dbg_hsic_data.idx; for (dbg_inc(&i); i != dbg_hsic_data.idx; dbg_inc(&i)) { if (!strnlen(dbg_hsic_data.buf[i], DBG_MSG_LEN)) continue; seq_printf(s, "%s\n", dbg_hsic_data.buf[i]); } read_unlock_irqrestore(&dbg_hsic_data.lck, flags); return 0; } static int ehci_hsic_msm_data_events_open(struct inode *inode, struct file *f) { return single_open(f, ehci_hsic_msm_data_events_show, inode->i_private); } const struct file_operations ehci_hsic_msm_dbg_data_fops = { .open = ehci_hsic_msm_data_events_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int ehci_hsic_msm_ctrl_events_show(struct seq_file *s, void *unused) { unsigned long flags; unsigned i; read_lock_irqsave(&dbg_hsic_ctrl.lck, flags); i = dbg_hsic_ctrl.idx; for (dbg_inc(&i); i != dbg_hsic_ctrl.idx; dbg_inc(&i)) { if (!strnlen(dbg_hsic_ctrl.buf[i], DBG_MSG_LEN)) continue; seq_printf(s, "%s\n", dbg_hsic_ctrl.buf[i]); } read_unlock_irqrestore(&dbg_hsic_ctrl.lck, flags); return 0; } static int ehci_hsic_msm_ctrl_events_open(struct inode *inode, struct file *f) { return single_open(f, ehci_hsic_msm_ctrl_events_show, inode->i_private); } const struct file_operations ehci_hsic_msm_dbg_ctrl_fops = { .open = ehci_hsic_msm_ctrl_events_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int ehci_hsic_msm_qh_show(struct seq_file *s, void *unused) { struct msm_hsic_hcd *mehci = s->private; dump_msm_qhs(hsic_to_hcd(mehci)); return 0; } static int ehci_hsic_msm_qh_open(struct inode *inode, struct file *f) { return single_open(f, ehci_hsic_msm_qh_show, inode->i_private); } const struct file_operations ehci_hsic_msm_qh_fops = { .open = ehci_hsic_msm_qh_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static struct dentry *ehci_hsic_msm_dbg_root; static int ehci_hsic_msm_debugfs_init(struct msm_hsic_hcd *mehci) { struct dentry *ehci_hsic_msm_dentry; ehci_hsic_msm_dbg_root = debugfs_create_dir("ehci_hsic_msm_dbg", NULL); if (!ehci_hsic_msm_dbg_root || IS_ERR(ehci_hsic_msm_dbg_root)) return -ENODEV; ehci_hsic_msm_dentry = debugfs_create_file("bus_voting", S_IRUGO | S_IWUSR, ehci_hsic_msm_dbg_root, mehci, &ehci_hsic_msm_bus_fops); if (!ehci_hsic_msm_dentry) { debugfs_remove_recursive(ehci_hsic_msm_dbg_root); return -ENODEV; } ehci_hsic_msm_dentry = debugfs_create_file("wakeup_cnt", S_IRUGO, ehci_hsic_msm_dbg_root, mehci, &ehci_hsic_msm_wakeup_cnt_fops); if (!ehci_hsic_msm_dentry) { debugfs_remove_recursive(ehci_hsic_msm_dbg_root); return -ENODEV; } ehci_hsic_msm_dentry = debugfs_create_file("show_ctrl_events", S_IRUGO, ehci_hsic_msm_dbg_root, mehci, &ehci_hsic_msm_dbg_ctrl_fops); if (!ehci_hsic_msm_dentry) { debugfs_remove_recursive(ehci_hsic_msm_dbg_root); return -ENODEV; } ehci_hsic_msm_dentry = debugfs_create_file("show_data_events", S_IRUGO, ehci_hsic_msm_dbg_root, mehci, &ehci_hsic_msm_dbg_data_fops); if (!ehci_hsic_msm_dentry) { debugfs_remove_recursive(ehci_hsic_msm_dbg_root); return -ENODEV; } ehci_hsic_msm_dentry = debugfs_create_file("show_hsic_qh", S_IRUGO, ehci_hsic_msm_dbg_root, mehci, &ehci_hsic_msm_qh_fops); if (!ehci_hsic_msm_dentry) { debugfs_remove_recursive(ehci_hsic_msm_dbg_root); return -ENODEV; } return 0; } static void ehci_hsic_msm_debugfs_cleanup(void) { debugfs_remove_recursive(ehci_hsic_msm_dbg_root); } struct msm_hsic_host_platform_data *msm_hsic_dt_to_pdata( struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; struct msm_hsic_host_platform_data *pdata; int res_gpio; pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { dev_err(&pdev->dev, "unable to allocate platform data\n"); return NULL; } res_gpio = of_get_named_gpio(node, "hsic,strobe-gpio", 0); if (res_gpio < 0) res_gpio = 0; pdata->strobe = res_gpio; res_gpio = of_get_named_gpio(node, "hsic,data-gpio", 0); if (res_gpio < 0) res_gpio = 0; pdata->data = res_gpio; res_gpio = of_get_named_gpio(node, "hsic,resume-gpio", 0); if (res_gpio < 0) res_gpio = 0; pdata->resume_gpio = res_gpio; pdata->phy_sof_workaround = of_property_read_bool(node, "qcom,phy-sof-workaround"); pdata->phy_susp_sof_workaround = of_property_read_bool(node, "qcom,phy-susp-sof-workaround"); pdata->phy_reset_sof_workaround = of_property_read_bool(node, "qcom,phy-reset-sof-workaround"); pdata->dis_internal_clk_gating = of_property_read_bool(node, "qcom,disable-internal-clk-gating"); pdata->ignore_cal_pad_config = of_property_read_bool(node, "hsic,ignore-cal-pad-config"); of_property_read_u32(node, "hsic,strobe-pad-offset", &pdata->strobe_pad_offset); of_property_read_u32(node, "hsic,data-pad-offset", &pdata->data_pad_offset); pdata->bus_scale_table = msm_bus_cl_get_pdata(pdev); pdata->pool_64_bit_align = of_property_read_bool(node, "qcom,pool-64-bit-align"); pdata->enable_hbm = of_property_read_bool(node, "qcom,enable-hbm"); pdata->disable_park_mode = (of_property_read_bool(node, "qcom,disable-park-mode")); pdata->consider_ipa_handshake = (of_property_read_bool(node, "hsic,consider-ipa-handshake")); pdata->ahb_async_bridge_bypass = of_property_read_bool(node, "qcom,ahb-async-bridge-bypass"); pdata->disable_cerr = of_property_read_bool(node, "hsic,disable-cerr"); return pdata; } static int __devinit ehci_hsic_msm_probe(struct platform_device *pdev) { struct usb_hcd *hcd; struct resource *res; struct msm_hsic_hcd *mehci; struct msm_hsic_host_platform_data *pdata; unsigned long wakeup_irq_flags = 0; int ret; dev_dbg(&pdev->dev, "ehci_msm-hsic probe\n"); if (pdev->dev.of_node) { dev_dbg(&pdev->dev, "device tree enabled\n"); pdev->dev.platform_data = msm_hsic_dt_to_pdata(pdev); dev_set_name(&pdev->dev, ehci_msm_hsic_driver.driver.name); } else { /* explicitly pass wakeup_irq flag for !DT */ wakeup_irq_flags = IRQF_TRIGGER_HIGH; } if (!pdev->dev.platform_data) dev_dbg(&pdev->dev, "No platform data given\n"); if (!pdev->dev.dma_mask) pdev->dev.dma_mask = &ehci_msm_hsic_dma_mask; if (!pdev->dev.coherent_dma_mask) pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); /* After parent device's probe is executed, it will be put in suspend * mode. When child device's probe is called, driver core is not * resuming parent device due to which parent will be in suspend even * though child is active. Hence resume the parent device explicitly. */ if (pdev->dev.parent) pm_runtime_get_sync(pdev->dev.parent); hcd = usb_create_hcd(&msm_hsic_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Unable to create HCD\n"); return -ENOMEM; } hcd_to_bus(hcd)->skip_resume = true; hcd->irq = platform_get_irq(pdev, 0); if (hcd->irq < 0) { dev_err(&pdev->dev, "Unable to get IRQ resource\n"); ret = hcd->irq; goto put_hcd; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "Unable to get memory resource\n"); ret = -ENODEV; goto put_hcd; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) { dev_err(&pdev->dev, "ioremap failed\n"); ret = -ENOMEM; goto put_hcd; } mehci = hcd_to_hsic(hcd); mehci->dev = &pdev->dev; pdata = mehci->dev->platform_data; spin_lock_init(&mehci->wakeup_lock); if (pdata->phy_sof_workaround) { /* Enable ALL workarounds related to PHY SOF bugs */ mehci->ehci.susp_sof_bug = 1; mehci->ehci.reset_sof_bug = 1; mehci->ehci.resume_sof_bug = 1; } else { if (pdata->phy_susp_sof_workaround) /* SUSP SOF hardware bug exists */ mehci->ehci.susp_sof_bug = 1; if (pdata->phy_reset_sof_workaround) /* RESET SOF hardware bug exists */ mehci->ehci.reset_sof_bug = 1; } mehci->ehci.pool_64_bit_align = pdata->pool_64_bit_align; mehci->enable_hbm = pdata->enable_hbm; if (pdata) { mehci->ehci.log2_irq_thresh = pdata->log2_irq_thresh; mehci->ehci.disable_cerr = pdata->disable_cerr; } ret = msm_hsic_init_gdsc(mehci, 1); if (ret) { dev_err(&pdev->dev, "unable to initialize GDSC\n"); ret = -ENODEV; goto put_hcd; } res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "peripheral_status_irq"); if (res) mehci->peripheral_status_irq = res->start; res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "wakeup"); if (res) { mehci->wakeup_irq = res->start; dev_dbg(mehci->dev, "wakeup_irq: %d\n", mehci->wakeup_irq); } ret = msm_hsic_init_clocks(mehci, 1); if (ret) { dev_err(&pdev->dev, "unable to initialize clocks\n"); ret = -ENODEV; goto unmap; } ret = msm_hsic_init_vddcx(mehci, 1); if (ret) { dev_err(&pdev->dev, "unable to initialize VDDCX\n"); ret = -ENODEV; goto deinit_clocks; } init_completion(&mehci->rt_completion); init_completion(&mehci->gpt0_completion); msm_hsic_phy_reset(mehci); ehci_wq = create_singlethread_workqueue("ehci_wq"); if (!ehci_wq) { dev_err(&pdev->dev, "unable to create workqueue\n"); ret = -ENOMEM; goto deinit_vddcx; } INIT_WORK(&mehci->bus_vote_w, ehci_hsic_bus_vote_w); ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); if (ret) { dev_err(&pdev->dev, "unable to register HCD\n"); goto destroy_wq; } ret = msm_hsic_start(mehci); if (ret) { dev_err(&pdev->dev, "unable to initialize PHY\n"); goto destroy_wq; } device_init_wakeup(&pdev->dev, 1); wake_lock_init(&mehci->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev)); wake_lock(&mehci->wlock); if (mehci->peripheral_status_irq) { ret = request_threaded_irq(mehci->peripheral_status_irq, NULL, hsic_peripheral_status_change, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, "hsic_peripheral_status", mehci); if (ret) dev_err(&pdev->dev, "%s:request_irq:%d failed:%d", __func__, mehci->peripheral_status_irq, ret); } /* configure wakeup irq */ if (mehci->wakeup_irq) { /* In case if wakeup gpio is pulled high at this point * remote wakeup interrupt fires right after request_irq. * Remote wake up interrupt only needs to be enabled when * HSIC bus goes to suspend. */ irq_set_status_flags(mehci->wakeup_irq, IRQ_NOAUTOEN); ret = request_irq(mehci->wakeup_irq, msm_hsic_wakeup_irq, wakeup_irq_flags, "msm_hsic_wakeup", mehci); if (ret) { dev_err(&pdev->dev, "request_irq(%d) failed: %d\n", mehci->wakeup_irq, ret); mehci->wakeup_irq = 0; } } mehci->async_irq = platform_get_irq_byname(pdev, "async_irq"); if (mehci->async_irq < 0) { dev_dbg(&pdev->dev, "platform_get_irq for async_int failed\n"); mehci->async_irq = 0; } else { ret = request_irq(mehci->async_irq, msm_hsic_wakeup_irq, IRQF_TRIGGER_RISING, "msm_hsic_async", mehci); if (ret) { dev_err(&pdev->dev, "request irq failed (ASYNC INT)\n"); mehci->async_irq = 0; } else if (!mehci->wakeup_irq) { /* Async IRQ is used only in absence of dedicated irq */ enable_irq_wake(mehci->async_irq); } } ret = ehci_hsic_msm_debugfs_init(mehci); if (ret) dev_dbg(&pdev->dev, "mode debugfs file is" "not available\n"); if (pdata && pdata->bus_scale_table) { mehci->bus_perf_client = msm_bus_scale_register_client(pdata->bus_scale_table); /* Configure BUS performance parameters for MAX bandwidth */ if (mehci->bus_perf_client) { mehci->bus_vote = true; queue_work(ehci_wq, &mehci->bus_vote_w); } else { dev_err(&pdev->dev, "%s: Failed to register BUS " "scaling client!!\n", __func__); } } __mehci = mehci; if (pdata && pdata->standalone_latency) pm_qos_add_request(&mehci->pm_qos_req_dma, PM_QOS_CPU_DMA_LATENCY, pdata->standalone_latency + 1); /* * This pdev->dev is assigned parent of root-hub by USB core, * hence, runtime framework automatically calls this driver's * runtime APIs based on root-hub's state. */ pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); /* Decrement the parent device's counter after probe. * As child is active, parent will not be put into * suspend mode. */ if (pdev->dev.parent) pm_runtime_put_sync(pdev->dev.parent); if (mehci->enable_hbm) { hbm_init(hcd, pdata->disable_park_mode); msm_bam_hsic_reset(); } if (pdata && pdata->consider_ipa_handshake) msm_bam_set_hsic_host_dev(&pdev->dev); return 0; destroy_wq: destroy_workqueue(ehci_wq); deinit_vddcx: msm_hsic_init_vddcx(mehci, 0); msm_hsic_init_gdsc(mehci, 0); deinit_clocks: msm_hsic_init_clocks(mehci, 0); unmap: iounmap(hcd->regs); put_hcd: usb_put_hcd(hcd); return ret; } static int __devexit ehci_hsic_msm_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; if (pdata && pdata->consider_ipa_handshake) msm_bam_set_hsic_host_dev(NULL); /* If the device was removed no need to call pm_runtime_disable */ if (pdev->dev.power.power_state.event != PM_EVENT_INVALID) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); if (mehci->enable_hbm) hbm_uninit(); /* Remove the HCD prior to releasing our resources. */ usb_remove_hcd(hcd); if (pdata && pdata->standalone_latency) pm_qos_remove_request(&mehci->pm_qos_req_dma); if (mehci->peripheral_status_irq) free_irq(mehci->peripheral_status_irq, mehci); if (mehci->wakeup_irq) { if (mehci->wakeup_irq_enabled) disable_irq_wake(mehci->wakeup_irq); free_irq(mehci->wakeup_irq, mehci); } if (mehci->async_irq) { /* Async IRQ is used only in absence of dedicated wakeup irq */ if (!mehci->wakeup_irq) disable_irq_wake(mehci->async_irq); free_irq(mehci->async_irq, mehci); } /* * If the update request is called after unregister, the request will * fail. Results are undefined if unregister is called in the middle of * update request. */ mehci->bus_vote = false; cancel_work_sync(&mehci->bus_vote_w); if (mehci->bus_perf_client) msm_bus_scale_unregister_client(mehci->bus_perf_client); ehci_hsic_msm_debugfs_cleanup(); device_init_wakeup(&pdev->dev, 0); destroy_workqueue(ehci_wq); msm_hsic_config_gpios(mehci, 0); if (pdata && pdata->resume_gpio) gpio_free(pdata->resume_gpio); msm_hsic_init_vddcx(mehci, 0); msm_hsic_init_gdsc(mehci, 0); msm_hsic_init_clocks(mehci, 0); wake_lock_destroy(&mehci->wlock); iounmap(hcd->regs); usb_put_hcd(hcd); return 0; } #ifdef CONFIG_PM_SLEEP static int msm_hsic_pm_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); dev_dbg(dev, "ehci-msm-hsic PM suspend\n"); dbg_log_event(NULL, "PM Suspend", 0); if (!atomic_read(&mehci->in_lpm)) { dev_info(dev, "abort suspend\n"); dbg_log_event(NULL, "PM Suspend abort", 0); return -EBUSY; } if (device_may_wakeup(dev) && !mehci->async_irq) enable_irq_wake(hcd->irq); return 0; } static int msm_hsic_pm_suspend_noirq(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); if (atomic_read(&mehci->async_int)) { dev_dbg(dev, "suspend_noirq: Aborting due to pending interrupt\n"); return -EBUSY; } return 0; } static int msm_hsic_pm_resume(struct device *dev) { int ret; struct usb_hcd *hcd = dev_get_drvdata(dev); struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); dev_dbg(dev, "ehci-msm-hsic PM resume\n"); dbg_log_event(NULL, "PM Resume", 0); if (device_may_wakeup(dev) && !mehci->async_irq) disable_irq_wake(hcd->irq); /* * Keep HSIC in Low Power Mode if system is resumed * by any other wakeup source. HSIC is resumed later * when remote wakeup is received or interface driver * start I/O. */ if (!atomic_read(&mehci->pm_usage_cnt) && !atomic_read(&mehci->async_int) && pm_runtime_suspended(dev)) return 0; ret = msm_hsic_resume(mehci); if (ret) return ret; /* Bring the device to full powered state upon system resume */ pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); return 0; } #endif #ifdef CONFIG_PM_RUNTIME static int msm_hsic_runtime_idle(struct device *dev) { dev_dbg(dev, "EHCI runtime idle\n"); return 0; } static int msm_hsic_runtime_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); dev_dbg(dev, "EHCI runtime suspend\n"); dbg_log_event(NULL, "Run Time PM Suspend", 0); return msm_hsic_suspend(mehci); } static int msm_hsic_runtime_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); dev_dbg(dev, "EHCI runtime resume\n"); dbg_log_event(NULL, "Run Time PM Resume", 0); return msm_hsic_resume(mehci); } #endif #ifdef CONFIG_PM static const struct dev_pm_ops msm_hsic_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(msm_hsic_pm_suspend, msm_hsic_pm_resume) .suspend_noirq = msm_hsic_pm_suspend_noirq, SET_RUNTIME_PM_OPS(msm_hsic_runtime_suspend, msm_hsic_runtime_resume, msm_hsic_runtime_idle) }; #endif static const struct of_device_id hsic_host_dt_match[] = { { .compatible = "qcom,hsic-host", }, {} }; static struct platform_driver ehci_msm_hsic_driver = { .probe = ehci_hsic_msm_probe, .remove = __devexit_p(ehci_hsic_msm_remove), .driver = { .name = "msm_hsic_host", #ifdef CONFIG_PM .pm = &msm_hsic_dev_pm_ops, #endif .of_match_table = hsic_host_dt_match, }, };