Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp6168191imu; Mon, 21 Jan 2019 04:26:04 -0800 (PST) X-Google-Smtp-Source: ALg8bN4lf0FlZoiPAGbl5SZxqGGOMyRtQ8qjvwc2vl4NRiQPkooIliC7ab21jKqnud+ITw7iERLv X-Received: by 2002:a17:902:8c91:: with SMTP id t17mr29095825plo.119.1548073564102; Mon, 21 Jan 2019 04:26:04 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1548073564; cv=none; d=google.com; s=arc-20160816; b=bs3a/mYg2tRh6IlJAR8fSBp6c5I4b1f1Zl1EuTDtmhPs+61ZPN4D0brN5/JC4dZg4L 5HHcZ9Ek/dQzKADHUjR0qlSFSF0dsax6c0bHAKW4LaHhZ+LmhyW5n3hjhBNZpSikGi5R h17D1ja8Wa/2A+vvFx5piEqdBzSSe2vhVQgifA8Nia+zVT3kV8k4zWEbX4JI+t/AJOcm TIK25T5BYwbgClG+i9RlA9AVJiafTLEZIhvsHWuwMYEEsxzTsLtdnMp6Dggso6/WvUN5 jw4U3ZXUKSiENXcQi6plPWneY1Mjsa1tdMc17/wmk/8C5OUM738SfI5GxCOwMBWGKA9d yauA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from; bh=IayXTZY0/nS7fKcv/mTz1ywQ7tniV8wqjyEdjPa/20A=; b=Une033JmiarVODxGoqiX20iOXuwGQgv0e6qRtvOWq8V757g22DbihIm7IQRG0OwGOa fiOeW9kh9SYbYQUfdDVT8LXUK6zd/f5NhBOsADoCBVbKH7uU6Swvj64ZAg4CBJah/P+2 EaUMTZ8Na+2e7dKa07rKmXMCOVpa2OPIq995jJXpoyvFc+HPabj7vQMCl1x44zxT8Po1 uuYuW2T0WMjZ2+s/AuAbf9rWr3+tp3HtAl6RqPeNF6+8SkwYJ/FiuqvNA15ejxyW4jl/ sRCvtHDpyfERYB1dcVsoZwp3hcEOZmiEAvM7AyPTyzlR6oABSKmvEGNHc6yxMo6sPNFN 2pWQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id x10si12234380pgl.209.2019.01.21.04.25.39; Mon, 21 Jan 2019 04:26:04 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728673AbfAUMXI (ORCPT + 99 others); Mon, 21 Jan 2019 07:23:08 -0500 Received: from Mailgw01.mediatek.com ([1.203.163.78]:17313 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1728496AbfAUMXH (ORCPT ); Mon, 21 Jan 2019 07:23:07 -0500 X-UUID: d957afbfa7f7452f9b1049e90dcc75bf-20190121 X-UUID: d957afbfa7f7452f9b1049e90dcc75bf-20190121 Received: from mtkcas35.mediatek.inc [(172.27.4.250)] by mailgw01.mediatek.com (envelope-from ) (mailgw01.mediatek.com ESMTP with TLS) with ESMTP id 1538139742; Mon, 21 Jan 2019 20:22:55 +0800 Received: from mtkcas07.mediatek.inc (172.21.101.84) by MTKMBS31N1.mediatek.inc (172.27.4.69) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 21 Jan 2019 20:22:53 +0800 Received: from localhost.localdomain (10.17.3.153) by mtkcas07.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Mon, 21 Jan 2019 20:22:53 +0800 From: To: Bin Liu , Rob Herring CC: Greg Kroah-Hartman , Mark Rutland , Matthias Brugger , Alan Stern , , , , , , , , , Min Guo , Yonglong Wu Subject: [PATCH v4 6/6] usb: musb: Add support for MediaTek musb controller Date: Mon, 21 Jan 2019 20:22:31 +0800 Message-ID: <1548073351-13739-7-git-send-email-min.guo@mediatek.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1548073351-13739-1-git-send-email-min.guo@mediatek.com> References: <1548073351-13739-1-git-send-email-min.guo@mediatek.com> MIME-Version: 1.0 Content-Type: text/plain X-MTK: N Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Min Guo This adds support for MediaTek musb controller in host, peripheral and otg mode. There are some quirk of MediaTek musb controller, such as: -W1C interrupt status registers -Private data toggle registers -No dedicated DMA interrupt line Signed-off-by: Min Guo Signed-off-by: Yonglong Wu --- changes in v4: 1. no changes changes in v3: suggested by Bin: 1. Remove 'u8/u16 data' parameter in clearb/w() hooks 2. Replace musb_readb/w() with musb_clearb/w() to clear interrupts status changes in v2: suggested by Bin: 1. Add summarize of MediaTek musb controller differences in the commit log 2. Add "|| COMPILE_TEST" in Kconfig 3. Move MediaTek's private toggle registers from musb_regs.h to mediatek.c 4. Replace musb_readl() with musb_readw() to read 16bit toggle register --- drivers/usb/musb/Kconfig | 8 +- drivers/usb/musb/Makefile | 1 + drivers/usb/musb/mediatek.c | 624 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 632 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/musb/mediatek.c diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index ad08895..b72b7c1 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -115,6 +115,12 @@ config USB_MUSB_JZ4740 depends on USB_MUSB_GADGET depends on USB_OTG_BLACKLIST_HUB +config USB_MUSB_MEDIATEK + tristate "MediaTek platforms" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on NOP_USB_XCEIV + depends on GENERIC_PHY + config USB_MUSB_AM335X_CHILD tristate @@ -141,7 +147,7 @@ config USB_UX500_DMA config USB_INVENTRA_DMA bool 'Inventra' - depends on USB_MUSB_OMAP2PLUS + depends on USB_MUSB_OMAP2PLUS || USB_MUSB_MEDIATEK help Enable DMA transfers using Mentor's engine. diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 3a88c79..63d82d0 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o obj-$(CONFIG_USB_MUSB_UX500) += ux500.o obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o +obj-$(CONFIG_USB_MUSB_MEDIATEK) += mediatek.o obj-$(CONFIG_USB_MUSB_AM335X_CHILD) += musb_am335x.o diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c new file mode 100644 index 0000000..65cafa6 --- /dev/null +++ b/drivers/usb/musb/mediatek.c @@ -0,0 +1,624 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 MediaTek Inc. + * + * Author: + * Min Guo + * Yonglong Wu + */ + +#include +#include +#include +#include +#include +#include "musb_core.h" +#include "musb_dma.h" + +#define USB_L1INTS 0x00a0 +#define USB_L1INTM 0x00a4 +#define MTK_MUSB_TXFUNCADDR 0x0480 + +/* MediaTek controller toggle enable and status reg */ +#define MUSB_RXTOG 0x80 +#define MUSB_RXTOGEN 0x82 +#define MUSB_TXTOG 0x84 +#define MUSB_TXTOGEN 0x86 + +#define TX_INT_STATUS BIT(0) +#define RX_INT_STATUS BIT(1) +#define USBCOM_INT_STATUS BIT(2) +#define DMA_INT_STATUS BIT(3) + +#define DMA_INTR_STATUS_MSK GENMASK(7, 0) +#define DMA_INTR_UNMASK_SET_MSK GENMASK(31, 24) + +enum mtk_vbus_id_state { + MTK_ID_FLOAT = 1, + MTK_ID_GROUND, + MTK_VBUS_OFF, + MTK_VBUS_VALID, +}; + +struct mtk_glue { + struct device *dev; + struct musb *musb; + struct platform_device *musb_pdev; + struct platform_device *usb_phy; + struct phy *phy; + struct usb_phy *xceiv; + enum phy_mode phy_mode; + struct clk *main; + struct clk *mcu; + struct clk *univpll; + struct regulator *vbus; + struct extcon_dev *edev; + struct notifier_block vbus_nb; + struct notifier_block id_nb; +}; + +static int mtk_musb_clks_get(struct mtk_glue *glue) +{ + struct device *dev = glue->dev; + + glue->main = devm_clk_get(dev, "main"); + if (IS_ERR(glue->main)) { + dev_err(dev, "fail to get main clock\n"); + return PTR_ERR(glue->main); + } + + glue->mcu = devm_clk_get(dev, "mcu"); + if (IS_ERR(glue->mcu)) { + dev_err(dev, "fail to get mcu clock\n"); + return PTR_ERR(glue->mcu); + } + + glue->univpll = devm_clk_get(dev, "univpll"); + if (IS_ERR(glue->univpll)) { + dev_err(dev, "fail to get univpll clock\n"); + return PTR_ERR(glue->univpll); + } + + return 0; +} + +static int mtk_musb_clks_enable(struct mtk_glue *glue) +{ + int ret; + + ret = clk_prepare_enable(glue->main); + if (ret) { + dev_err(glue->dev, "failed to enable main clock\n"); + goto err_main_clk; + } + + ret = clk_prepare_enable(glue->mcu); + if (ret) { + dev_err(glue->dev, "failed to enable mcu clock\n"); + goto err_mcu_clk; + } + + ret = clk_prepare_enable(glue->univpll); + if (ret) { + dev_err(glue->dev, "failed to enable univpll clock\n"); + goto err_univpll_clk; + } + + return 0; + +err_univpll_clk: + clk_disable_unprepare(glue->mcu); +err_mcu_clk: + clk_disable_unprepare(glue->main); +err_main_clk: + return ret; +} + +static void mtk_musb_clks_disable(struct mtk_glue *glue) +{ + clk_disable_unprepare(glue->univpll); + clk_disable_unprepare(glue->mcu); + clk_disable_unprepare(glue->main); +} + +static void mtk_musb_set_vbus(struct musb *musb, int is_on) +{ + struct device *dev = musb->controller; + struct mtk_glue *glue = dev_get_drvdata(dev->parent); + int ret; + + /* vbus is optional */ + if (!glue->vbus) + return; + + dev_dbg(musb->controller, "%s, is_on=%d\r\n", __func__, is_on); + if (is_on) { + ret = regulator_enable(glue->vbus); + if (ret) { + dev_err(glue->dev, "fail to enable vbus regulator\n"); + return; + } + } else { + regulator_disable(glue->vbus); + } +} + +/* + * switch to host: -> MTK_VBUS_OFF --> MTK_ID_GROUND + * switch to device: -> MTK_ID_FLOAT --> MTK_VBUS_VALID + */ +static void mtk_musb_set_mailbox(struct mtk_glue *glue, + enum mtk_vbus_id_state status) +{ + struct musb *musb = glue->musb; + u8 devctl = 0; + + dev_dbg(glue->dev, "mailbox state(%d)\n", status); + switch (status) { + case MTK_ID_GROUND: + phy_power_on(glue->phy); + devctl = readb(musb->mregs + MUSB_DEVCTL); + musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; + mtk_musb_set_vbus(musb, 1); + glue->phy_mode = PHY_MODE_USB_HOST; + phy_set_mode(glue->phy, glue->phy_mode); + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + MUSB_HST_MODE(musb); + break; + /* + * MTK_ID_FLOAT process is the same as MTK_VBUS_VALID + * except that turn off VBUS + */ + case MTK_ID_FLOAT: + mtk_musb_set_vbus(musb, 0); + /* fall through */ + case MTK_VBUS_OFF: + musb->xceiv->otg->state = OTG_STATE_B_IDLE; + devctl &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + phy_power_off(glue->phy); + break; + case MTK_VBUS_VALID: + phy_power_on(glue->phy); + glue->phy_mode = PHY_MODE_USB_DEVICE; + phy_set_mode(glue->phy, glue->phy_mode); + MUSB_DEV_MODE(musb); + break; + default: + dev_err(glue->dev, "invalid state\n"); + } +} + +static int mtk_musb_id_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct mtk_glue *glue = container_of(nb, struct mtk_glue, id_nb); + + if (event) + mtk_musb_set_mailbox(glue, MTK_ID_GROUND); + else + mtk_musb_set_mailbox(glue, MTK_ID_FLOAT); + + return NOTIFY_DONE; +} + +static int mtk_musb_vbus_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct mtk_glue *glue = container_of(nb, struct mtk_glue, vbus_nb); + + if (event) + mtk_musb_set_mailbox(glue, MTK_VBUS_VALID); + else + mtk_musb_set_mailbox(glue, MTK_VBUS_OFF); + + return NOTIFY_DONE; +} + +static void mtk_otg_switch_init(struct mtk_glue *glue) +{ + int ret; + + /* extcon is optional */ + if (!glue->edev) + return; + + glue->vbus_nb.notifier_call = mtk_musb_vbus_notifier; + ret = devm_extcon_register_notifier(glue->dev, glue->edev, EXTCON_USB, + &glue->vbus_nb); + if (ret < 0) + dev_err(glue->dev, "failed to register notifier for USB\n"); + + glue->id_nb.notifier_call = mtk_musb_id_notifier; + ret = devm_extcon_register_notifier(glue->dev, glue->edev, + EXTCON_USB_HOST, &glue->id_nb); + if (ret < 0) + dev_err(glue->dev, "failed to register notifier for USB-HOST\n"); + + dev_dbg(glue->dev, "EXTCON_USB: %d, EXTCON_USB_HOST: %d\n", + extcon_get_state(glue->edev, EXTCON_USB), + extcon_get_state(glue->edev, EXTCON_USB_HOST)); + + /* default as host, switch to device mode if needed */ + if (extcon_get_state(glue->edev, EXTCON_USB_HOST) == false) + mtk_musb_set_mailbox(glue, MTK_ID_FLOAT); + if (extcon_get_state(glue->edev, EXTCON_USB) == true) + mtk_musb_set_mailbox(glue, MTK_VBUS_VALID); +} + +static irqreturn_t generic_interrupt(int irq, void *__hci) +{ + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + struct musb *musb = __hci; + + spin_lock_irqsave(&musb->lock, flags); + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB) & + musb_readb(musb->mregs, MUSB_INTRUSBE); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX) + & musb_readw(musb->mregs, MUSB_INTRTXE); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX) + & musb_readw(musb->mregs, MUSB_INTRRXE); + /* MediaTek controller interrupt status is W1C */ + musb_clearw(musb->mregs, MUSB_INTRRX); + musb_clearw(musb->mregs, MUSB_INTRTX); + musb_clearb(musb->mregs, MUSB_INTRUSB); + + if (musb->int_usb || musb->int_tx || musb->int_rx) + retval = musb_interrupt(musb); + + spin_unlock_irqrestore(&musb->lock, flags); + + return retval; +} + +static irqreturn_t mtk_musb_interrupt(int irq, void *dev_id) +{ + irqreturn_t retval = IRQ_NONE; + struct musb *musb = (struct musb *)dev_id; + u32 l1_ints; + + l1_ints = musb_readl(musb->mregs, USB_L1INTS) & + musb_readl(musb->mregs, USB_L1INTM); + + if (l1_ints & (TX_INT_STATUS | RX_INT_STATUS | USBCOM_INT_STATUS)) + retval = generic_interrupt(irq, musb); + +#if defined(CONFIG_USB_INVENTRA_DMA) + if (l1_ints & DMA_INT_STATUS) + retval = dma_controller_irq(irq, musb->dma_controller); +#endif + return retval; +} + +static u32 mtk_musb_busctl_offset(u8 epnum, u16 offset) +{ + return MTK_MUSB_TXFUNCADDR + offset + 8 * epnum; +} + +static u8 mtk_musb_clearb(void __iomem *addr, unsigned int offset) +{ + u8 data; + + /* W1C */ + data = musb_readb(addr, offset); + musb_writeb(addr, offset, data); + return musb_readb(addr, offset); +} + +static u16 mtk_musb_clearw(void __iomem *addr, unsigned int offset) +{ + u16 data; + + /* W1C */ + data = musb_readw(addr, offset); + musb_writew(addr, offset, data); + return musb_readw(addr, offset); +} + +static int mtk_musb_init(struct musb *musb) +{ + struct device *dev = musb->controller; + struct mtk_glue *glue = dev_get_drvdata(dev->parent); + int ret; + + glue->musb = musb; + musb->phy = glue->phy; + musb->xceiv = glue->xceiv; + musb->is_host = false; + musb->isr = mtk_musb_interrupt; + ret = phy_init(glue->phy); + if (ret) + return ret; + + ret = phy_power_on(glue->phy); + if (ret) { + phy_exit(glue->phy); + return ret; + } + + phy_set_mode(glue->phy, glue->phy_mode); + +#if defined(CONFIG_USB_INVENTRA_DMA) + musb_writel(musb->mregs, MUSB_HSDMA_INTR, + DMA_INTR_STATUS_MSK | DMA_INTR_UNMASK_SET_MSK); +#endif + musb_writel(musb->mregs, USB_L1INTM, TX_INT_STATUS | RX_INT_STATUS | + USBCOM_INT_STATUS | DMA_INT_STATUS); + return 0; +} + +static u16 mtk_musb_get_toggle(struct musb_qh *qh, int is_out) +{ + struct musb *musb = qh->hw_ep->musb; + u8 epnum = qh->hw_ep->epnum; + u16 toggle; + + if (is_out) + toggle = musb_readw(musb->mregs, MUSB_TXTOG); + else + toggle = musb_readw(musb->mregs, MUSB_RXTOG); + + return toggle & (1 << epnum); +} + +static u16 mtk_musb_set_toggle(struct musb_qh *qh, int is_out, struct urb *urb) +{ + struct musb *musb = qh->hw_ep->musb; + u8 epnum = qh->hw_ep->epnum; + u16 toggle; + + toggle = usb_gettoggle(urb->dev, qh->epnum, is_out); + + if (is_out) { + musb_writew(musb->mregs, MUSB_TXTOGEN, (1 << epnum)); + musb_writew(musb->mregs, MUSB_TXTOG, (toggle << epnum)); + } else { + musb_writew(musb->mregs, MUSB_RXTOGEN, (1 << epnum)); + musb_writew(musb->mregs, MUSB_RXTOG, (toggle << epnum)); + } + + return 0; +} + +static int mtk_musb_set_mode(struct musb *musb, u8 mode) +{ + struct device *dev = musb->controller; + struct mtk_glue *glue = dev_get_drvdata(dev->parent); + enum phy_mode new_mode; + + switch (mode) { + case MUSB_HOST: + new_mode = PHY_MODE_USB_HOST; + mtk_musb_set_vbus(musb, 1); + break; + case MUSB_PERIPHERAL: + new_mode = PHY_MODE_USB_DEVICE; + break; + case MUSB_OTG: + new_mode = PHY_MODE_USB_HOST; + break; + default: + dev_err(musb->controller->parent, + "Error requested mode not supported by this kernel\n"); + return -EINVAL; + } + if (glue->phy_mode == new_mode) + return 0; + + mtk_musb_set_mailbox(glue, MTK_ID_GROUND); + return 0; +} + +static int mtk_musb_exit(struct musb *musb) +{ + struct device *dev = musb->controller; + struct mtk_glue *glue = dev_get_drvdata(dev->parent); + + phy_power_off(glue->phy); + phy_exit(glue->phy); + mtk_musb_clks_disable(glue); + + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + return 0; +} + +static const struct musb_platform_ops mtk_musb_ops = { + .quirks = MUSB_DMA_INVENTRA, + .init = mtk_musb_init, + .get_toggle = mtk_musb_get_toggle, + .set_toggle = mtk_musb_set_toggle, + .exit = mtk_musb_exit, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create_noirq, + .dma_exit = musbhs_dma_controller_destroy, +#endif + .clearb = mtk_musb_clearb, + .clearw = mtk_musb_clearw, + .busctl_offset = mtk_musb_busctl_offset, + .set_mode = mtk_musb_set_mode, + .set_vbus = mtk_musb_set_vbus, +}; + +#define MTK_MUSB_MAX_EP_NUM 8 +#define MTK_MUSB_RAM_BITS 11 + +static struct musb_fifo_cfg mtk_musb_mode_cfg[] = { + { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 1024, }, + { .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 1024, }, + { .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 64, }, +}; + +static const struct musb_hdrc_config mtk_musb_hdrc_config = { + .fifo_cfg = mtk_musb_mode_cfg, + .fifo_cfg_size = ARRAY_SIZE(mtk_musb_mode_cfg), + .multipoint = true, + .dyn_fifo = true, + .num_eps = MTK_MUSB_MAX_EP_NUM, + .ram_bits = MTK_MUSB_RAM_BITS, +}; + +static const struct platform_device_info mtk_dev_info = { + .name = "musb-hdrc", + .id = PLATFORM_DEVID_AUTO, + .dma_mask = DMA_BIT_MASK(32), +}; + +static int mtk_musb_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata; + struct mtk_glue *glue; + struct platform_device_info pinfo; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int ret = -ENOMEM; + + glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); + if (!glue) + return -ENOMEM; + + glue->dev = dev; + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = mtk_musb_clks_get(glue); + if (ret) + return ret; + + glue->vbus = devm_regulator_get(dev, "vbus"); + if (IS_ERR(glue->vbus)) { + dev_err(dev, "fail to get vbus\n"); + return PTR_ERR(glue->vbus); + } + + pdata->config = &mtk_musb_hdrc_config; + pdata->platform_ops = &mtk_musb_ops; + if (of_property_read_bool(np, "extcon")) { + glue->edev = extcon_get_edev_by_phandle(dev, 0); + if (IS_ERR(glue->edev)) { + dev_err(dev, "fail to get extcon\n"); + return PTR_ERR(glue->edev); + } + } + + pdata->mode = usb_get_dr_mode(dev); + switch (pdata->mode) { + case USB_DR_MODE_HOST: + glue->phy_mode = PHY_MODE_USB_HOST; + break; + case USB_DR_MODE_PERIPHERAL: + glue->phy_mode = PHY_MODE_USB_DEVICE; + break; + default: + pdata->mode = USB_DR_MODE_OTG; + /* FALL THROUGH */ + case USB_DR_MODE_OTG: + glue->phy_mode = PHY_MODE_USB_OTG; + break; + } + + glue->phy = devm_of_phy_get_by_index(dev, np, 0); + if (IS_ERR(glue->phy)) { + dev_err(dev, "fail to getting phy %ld\n", + PTR_ERR(glue->phy)); + return PTR_ERR(glue->phy); + } + + glue->usb_phy = usb_phy_generic_register(); + if (IS_ERR(glue->usb_phy)) { + dev_err(dev, "fail to registering usb-phy %ld\n", + PTR_ERR(glue->usb_phy)); + return PTR_ERR(glue->usb_phy); + } + + glue->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + if (IS_ERR(glue->xceiv)) { + dev_err(dev, "fail to getting usb-phy %d\n", ret); + ret = PTR_ERR(glue->xceiv); + goto err_unregister_usb_phy; + } + + platform_set_drvdata(pdev, glue); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + ret = mtk_musb_clks_enable(glue); + if (ret) + goto err_enable_clk; + + pinfo = mtk_dev_info; + pinfo.parent = dev; + pinfo.res = pdev->resource; + pinfo.num_res = pdev->num_resources; + pinfo.data = pdata; + pinfo.size_data = sizeof(*pdata); + + glue->musb_pdev = platform_device_register_full(&pinfo); + if (IS_ERR(glue->musb_pdev)) { + ret = PTR_ERR(glue->musb_pdev); + dev_err(dev, "failed to register musb device: %d\n", ret); + goto err_device_register; + } + + if (pdata->mode == USB_DR_MODE_OTG) + mtk_otg_switch_init(glue); + + return 0; + +err_device_register: + mtk_musb_clks_disable(glue); +err_enable_clk: + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); +err_unregister_usb_phy: + usb_phy_generic_unregister(glue->usb_phy); + return ret; +} + +static int mtk_musb_remove(struct platform_device *pdev) +{ + struct mtk_glue *glue = platform_get_drvdata(pdev); + struct platform_device *usb_phy = glue->usb_phy; + + platform_device_unregister(glue->musb_pdev); + usb_phy_generic_unregister(usb_phy); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id mtk_musb_match[] = { + {.compatible = "mediatek,mtk-musb",}, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_musb_match); +#endif + +static struct platform_driver mtk_musb_driver = { + .probe = mtk_musb_probe, + .remove = mtk_musb_remove, + .driver = { + .name = "musb-mtk", + .of_match_table = of_match_ptr(mtk_musb_match), + }, +}; + +module_platform_driver(mtk_musb_driver); + +MODULE_DESCRIPTION("MediaTek MUSB Glue Layer"); +MODULE_AUTHOR("Min Guo "); +MODULE_LICENSE("GPL v2"); -- 1.9.1