From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.0 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_PASS,UNPARSEABLE_RELAY, URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 61490C43387 for ; Tue, 15 Jan 2019 01:44:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 24AC2206B7 for ; Tue, 15 Jan 2019 01:44:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727802AbfAOBoU (ORCPT ); Mon, 14 Jan 2019 20:44:20 -0500 Received: from mailgw02.mediatek.com ([1.203.163.81]:54481 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1727736AbfAOBoT (ORCPT ); Mon, 14 Jan 2019 20:44:19 -0500 X-UUID: 8fcc09923070495b9f2d8837e35eda1e-20190115 X-UUID: 8fcc09923070495b9f2d8837e35eda1e-20190115 Received: from mtkcas34.mediatek.inc [(172.27.4.250)] by mailgw02.mediatek.com (envelope-from ) (mailgw01.mediatek.com ESMTP with TLS) with ESMTP id 817362210; Tue, 15 Jan 2019 09:44:04 +0800 Received: from mtkcas09.mediatek.inc (172.21.101.178) by MTKMBS31N2.mediatek.inc (172.27.4.87) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 15 Jan 2019 09:44:02 +0800 Received: from localhost.localdomain (10.17.3.153) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 15 Jan 2019 09:44:01 +0800 From: To: Bin Liu , Rob Herring CC: Greg Kroah-Hartman , Mark Rutland , Matthias Brugger , Alan Stern , , , , , , , Min Guo , Yonglong Wu Subject: [PATCH v2 4/4] usb: musb: Add support for MediaTek musb controller Date: Tue, 15 Jan 2019 09:43:46 +0800 Message-ID: <1547516626-5084-5-git-send-email-min.guo@mediatek.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1547516626-5084-1-git-send-email-min.guo@mediatek.com> References: <1547516626-5084-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 --- drivers/usb/musb/Kconfig | 8 +- drivers/usb/musb/Makefile | 1 + drivers/usb/musb/mediatek.c | 617 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/musb/musb_core.c | 69 +++++ drivers/usb/musb/musb_core.h | 9 + drivers/usb/musb/musb_dma.h | 9 + drivers/usb/musb/musb_host.c | 26 +- drivers/usb/musb/musb_io.h | 6 + drivers/usb/musb/musbhsdma.c | 55 ++-- 9 files changed, 762 insertions(+), 38 deletions(-) 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..7221989 --- /dev/null +++ b/drivers/usb/musb/mediatek.c @@ -0,0 +1,617 @@ +// 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->int_rx); + musb_clearw(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb_clearb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + + 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 void mtk_musb_clearb(void __iomem *addr, unsigned int offset, u8 data) +{ + /* W1C */ + musb_writeb(addr, offset, data); +} + +static void mtk_musb_clearw(void __iomem *addr, unsigned int offset, u16 data) +{ + /* W1C */ + musb_writew(addr, offset, data); +} + +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_in) +{ + struct musb *musb = qh->hw_ep->musb; + u8 epnum = qh->hw_ep->epnum; + u16 toggle; + + if (is_in) + toggle = musb_readw(musb->mregs, MUSB_RXTOG); + else + toggle = musb_readw(musb->mregs, MUSB_TXTOG); + + return toggle & (1 << epnum); +} + +static u16 mtk_musb_set_toggle(struct musb_qh *qh, int is_in, struct urb *urb) +{ + struct musb *musb = qh->hw_ep->musb; + u8 epnum = qh->hw_ep->epnum; + u16 toggle = 0; + + toggle = usb_gettoggle(urb->dev, qh->epnum, !is_in); + + if (is_in) { + musb_writew(musb->mregs, MUSB_RXTOGEN, (1 << epnum)); + musb_writew(musb->mregs, MUSB_RXTOG, (toggle << epnum)); + } else { + musb_writew(musb->mregs, MUSB_TXTOGEN, (1 << epnum)); + musb_writew(musb->mregs, MUSB_TXTOG, (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); + + dev_info(dev, "USB probe done!\n"); + 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"); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b7d5627..2c0d102 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -260,6 +260,11 @@ static void musb_default_writeb(void __iomem *addr, unsigned offset, u8 data) __raw_writeb(data, addr + offset); } +static void +musb_default_clearb(void __iomem *addr, unsigned int offset, u8 data) +{ +} + static u16 musb_default_readw(const void __iomem *addr, unsigned offset) { u16 data = __raw_readw(addr + offset); @@ -274,6 +279,43 @@ static void musb_default_writew(void __iomem *addr, unsigned offset, u16 data) __raw_writew(data, addr + offset); } +static void +musb_default_clearw(void __iomem *addr, unsigned int offset, u16 data) +{ +} + +static u16 musb_default_get_toggle(struct musb_qh *qh, int is_in) +{ + void __iomem *epio = qh->hw_ep->regs; + u16 csr; + + if (is_in) + csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE; + else + csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE; + + return csr; +} + +static u16 musb_default_set_toggle(struct musb_qh *qh, int is_in, + struct urb *urb) +{ + u16 csr = 0; + u16 toggle = 0; + + toggle = usb_gettoggle(urb->dev, qh->epnum, !is_in); + + if (is_in) + csr = toggle ? (MUSB_RXCSR_H_WR_DATATOGGLE + | MUSB_RXCSR_H_DATATOGGLE) : 0; + else + csr |= toggle ? (MUSB_TXCSR_H_WR_DATATOGGLE + | MUSB_TXCSR_H_DATATOGGLE) + : MUSB_TXCSR_CLRDATATOG; + + return csr; +} + /* * Load an endpoint's FIFO */ @@ -370,12 +412,18 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data); EXPORT_SYMBOL_GPL(musb_writeb); +void (*musb_clearb)(void __iomem *addr, unsigned int offset, u8 data); +EXPORT_SYMBOL_GPL(musb_clearb); + u16 (*musb_readw)(const void __iomem *addr, unsigned offset); EXPORT_SYMBOL_GPL(musb_readw); void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data); EXPORT_SYMBOL_GPL(musb_writew); +void (*musb_clearw)(void __iomem *addr, unsigned int offset, u16 data); +EXPORT_SYMBOL_GPL(musb_clearw); + u32 musb_readl(const void __iomem *addr, unsigned offset) { u32 data = __raw_readl(addr + offset); @@ -1028,6 +1076,11 @@ static void musb_disable_interrupts(struct musb *musb) temp = musb_readb(mbase, MUSB_INTRUSB); temp = musb_readw(mbase, MUSB_INTRTX); temp = musb_readw(mbase, MUSB_INTRRX); + + /* some platform needs clear pending interrupts by manual */ + musb_clearb(mbase, MUSB_INTRUSB, musb_readb(mbase, MUSB_INTRUSB)); + musb_clearw(mbase, MUSB_INTRRX, musb_readw(mbase, MUSB_INTRRX)); + musb_clearw(mbase, MUSB_INTRTX, musb_readw(mbase, MUSB_INTRTX)); } static void musb_enable_interrupts(struct musb *musb) @@ -2192,6 +2245,8 @@ static void musb_deassert_reset(struct work_struct *work) musb_writeb = musb_default_writeb; musb_readw = musb_default_readw; musb_writew = musb_default_writew; + musb_clearb = musb_default_clearb; + musb_clearw = musb_default_clearw; /* The musb_platform_init() call: * - adjusts musb->mregs @@ -2252,10 +2307,14 @@ static void musb_deassert_reset(struct work_struct *work) musb_readb = musb->ops->readb; if (musb->ops->writeb) musb_writeb = musb->ops->writeb; + if (musb->ops->clearb) + musb_clearb = musb->ops->clearb; if (musb->ops->readw) musb_readw = musb->ops->readw; if (musb->ops->writew) musb_writew = musb->ops->writew; + if (musb->ops->clearw) + musb_clearw = musb->ops->clearw; #ifndef CONFIG_MUSB_PIO_ONLY if (!musb->ops->dma_init || !musb->ops->dma_exit) { @@ -2277,6 +2336,16 @@ static void musb_deassert_reset(struct work_struct *work) else musb->io.write_fifo = musb_default_write_fifo; + if (musb->ops->get_toggle) + musb->io.get_toggle = musb->ops->get_toggle; + else + musb->io.get_toggle = musb_default_get_toggle; + + if (musb->ops->set_toggle) + musb->io.set_toggle = musb->ops->set_toggle; + else + musb->io.set_toggle = musb_default_set_toggle; + if (!musb->xceiv->io_ops) { musb->xceiv->io_dev = musb->controller; musb->xceiv->io_priv = musb->mregs; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 04203b7..71dca80 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -27,6 +27,7 @@ struct musb; struct musb_hw_ep; struct musb_ep; +struct musb_qh; /* Helper defines for struct musb->hwvers */ #define MUSB_HWVERS_MAJOR(x) ((x >> 10) & 0x1f) @@ -119,10 +120,14 @@ enum musb_g_ep0_state { * @fifo_offset: returns the fifo offset * @readb: read 8 bits * @writeb: write 8 bits + * @clearb: clear 8 bits * @readw: read 16 bits * @writew: write 16 bits + * @clearw: clear 16 bits * @read_fifo: reads the fifo * @write_fifo: writes to fifo + * @get_toggle: platform specific get toggle function + * @set_toggle: platform specific set toggle function * @dma_init: platform specific dma init function * @dma_exit: platform specific dma exit function * @init: turns on clocks, sets up platform-specific registers, etc @@ -163,10 +168,14 @@ struct musb_platform_ops { u32 (*busctl_offset)(u8 epnum, u16 offset); u8 (*readb)(const void __iomem *addr, unsigned offset); void (*writeb)(void __iomem *addr, unsigned offset, u8 data); + void (*clearb)(void __iomem *addr, unsigned int offset, u8 data); u16 (*readw)(const void __iomem *addr, unsigned offset); void (*writew)(void __iomem *addr, unsigned offset, u16 data); + void (*clearw)(void __iomem *addr, unsigned int offset, u16 data); void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); + u16 (*get_toggle)(struct musb_qh *qh, int is_in); + u16 (*set_toggle)(struct musb_qh *qh, int is_in, struct urb *urb); struct dma_controller * (*dma_init) (struct musb *musb, void __iomem *base); void (*dma_exit)(struct dma_controller *c); diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 8f60271..05103ea 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -35,6 +35,12 @@ * whether shared with the Inventra core or separate. */ +#define MUSB_HSDMA_BASE 0x200 +#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) +#define MUSB_HSDMA_CONTROL 0x4 +#define MUSB_HSDMA_ADDRESS 0x8 +#define MUSB_HSDMA_COUNT 0xc + #define DMA_ADDR_INVALID (~(dma_addr_t)0) #ifdef CONFIG_MUSB_PIO_ONLY @@ -191,6 +197,9 @@ static inline void musb_dma_controller_destroy(struct dma_controller *d) { } extern struct dma_controller * musbhs_dma_controller_create(struct musb *musb, void __iomem *base); extern void musbhs_dma_controller_destroy(struct dma_controller *c); +extern struct dma_controller * +musbhs_dma_controller_create_noirq(struct musb *musb, void __iomem *base); +extern irqreturn_t dma_controller_irq(int irq, void *private_data); extern struct dma_controller * tusb_dma_controller_create(struct musb *musb, void __iomem *base); diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 16d0ba4..ba66f44 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -290,39 +290,23 @@ static void musb_giveback(struct musb *musb, struct urb *urb, int status) static inline void musb_save_toggle(struct musb_qh *qh, int is_in, struct urb *urb) { - void __iomem *epio = qh->hw_ep->regs; - u16 csr; + struct musb *musb = qh->hw_ep->musb; + u16 csr; /* * FIXME: the current Mentor DMA code seems to have * problems getting toggle correct. */ - - if (is_in) - csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE; - else - csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE; - + csr = musb->io.get_toggle(qh, is_in); usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0); } static inline u16 musb_set_toggle(struct musb_qh *qh, int is_in, struct urb *urb) { - u16 csr = 0; - u16 toggle = 0; - - toggle = usb_gettoggle(urb->dev, qh->epnum, !is_in); - - if (is_in) - csr = toggle ? (MUSB_RXCSR_H_WR_DATATOGGLE - | MUSB_RXCSR_H_DATATOGGLE) : 0; - else - csr = toggle ? (MUSB_TXCSR_H_WR_DATATOGGLE - | MUSB_TXCSR_H_DATATOGGLE) - : MUSB_TXCSR_CLRDATATOG; + struct musb *musb = qh->hw_ep->musb; - return csr; + return musb->io.set_toggle(qh, is_in, urb); } /* diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 8058a58..9bae09b 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -22,6 +22,8 @@ * @read_fifo: platform specific function to read fifo * @write_fifo: platform specific function to write fifo * @busctl_offset: platform specific function to get busctl offset + * @get_toggle: platform specific function to get toggle + * @set_toggle: platform specific function to set toggle */ struct musb_io { u32 (*ep_offset)(u8 epnum, u16 offset); @@ -30,13 +32,17 @@ struct musb_io { void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); u32 (*busctl_offset)(u8 epnum, u16 offset); + u16 (*get_toggle)(struct musb_qh *qh, int is_in); + u16 (*set_toggle)(struct musb_qh *qh, int is_in, struct urb *urb); }; /* Do not add new entries here, add them the struct musb_io instead */ extern u8 (*musb_readb)(const void __iomem *addr, unsigned offset); extern void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data); +extern void (*musb_clearb)(void __iomem *addr, unsigned int offset, u8 data); extern u16 (*musb_readw)(const void __iomem *addr, unsigned offset); extern void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data); +extern void (*musb_clearw)(void __iomem *addr, unsigned int offset, u16 data); extern u32 musb_readl(const void __iomem *addr, unsigned offset); extern void musb_writel(void __iomem *addr, unsigned offset, u32 data); diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index a688f7f..b05fe68 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -10,12 +10,7 @@ #include #include #include "musb_core.h" - -#define MUSB_HSDMA_BASE 0x200 -#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) -#define MUSB_HSDMA_CONTROL 0x4 -#define MUSB_HSDMA_ADDRESS 0x8 -#define MUSB_HSDMA_COUNT 0xc +#include "musb_dma.h" #define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \ (MUSB_HSDMA_BASE + (_bchannel << 4) + _offset) @@ -268,7 +263,7 @@ static int dma_channel_abort(struct dma_channel *channel) return 0; } -static irqreturn_t dma_controller_irq(int irq, void *private_data) +irqreturn_t dma_controller_irq(int irq, void *private_data) { struct musb_dma_controller *controller = private_data; struct musb *musb = controller->private_data; @@ -291,6 +286,9 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR); + /* some platform needs clear pending interrupts by manual */ + musb_clearb(musb->mregs, MUSB_HSDMA_INTR, int_hsdma); + if (!int_hsdma) { musb_dbg(musb, "spurious DMA irq"); @@ -382,6 +380,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) spin_unlock_irqrestore(&musb->lock, flags); return retval; } +EXPORT_SYMBOL_GPL(dma_controller_irq); void musbhs_dma_controller_destroy(struct dma_controller *c) { @@ -397,18 +396,10 @@ void musbhs_dma_controller_destroy(struct dma_controller *c) } EXPORT_SYMBOL_GPL(musbhs_dma_controller_destroy); -struct dma_controller *musbhs_dma_controller_create(struct musb *musb, +static struct musb_dma_controller *dma_controller_alloc(struct musb *musb, void __iomem *base) { struct musb_dma_controller *controller; - struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); - int irq = platform_get_irq_byname(pdev, "dma"); - - if (irq <= 0) { - dev_err(dev, "No DMA interrupt line!\n"); - return NULL; - } controller = kzalloc(sizeof(*controller), GFP_KERNEL); if (!controller) @@ -422,6 +413,25 @@ struct dma_controller *musbhs_dma_controller_create(struct musb *musb, controller->controller.channel_release = dma_channel_release; controller->controller.channel_program = dma_channel_program; controller->controller.channel_abort = dma_channel_abort; + return controller; +} + +struct dma_controller *musbhs_dma_controller_create(struct musb *musb, + void __iomem *base) +{ + struct musb_dma_controller *controller; + struct device *dev = musb->controller; + struct platform_device *pdev = to_platform_device(dev); + int irq = platform_get_irq_byname(pdev, "dma"); + + if (irq <= 0) { + dev_err(dev, "No DMA interrupt line!\n"); + return NULL; + } + + controller = dma_controller_alloc(musb, base); + if (!controller) + return NULL; if (request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), &controller->controller)) { @@ -436,3 +446,16 @@ struct dma_controller *musbhs_dma_controller_create(struct musb *musb, return &controller->controller; } EXPORT_SYMBOL_GPL(musbhs_dma_controller_create); + +struct dma_controller *musbhs_dma_controller_create_noirq(struct musb *musb, + void __iomem *base) +{ + struct musb_dma_controller *controller; + + controller = dma_controller_alloc(musb, base); + if (!controller) + return NULL; + + return &controller->controller; +} +EXPORT_SYMBOL_GPL(musbhs_dma_controller_create_noirq); -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 From: Subject: [PATCH v2 4/4] usb: musb: Add support for MediaTek musb controller Date: Tue, 15 Jan 2019 09:43:46 +0800 Message-ID: <1547516626-5084-5-git-send-email-min.guo@mediatek.com> References: <1547516626-5084-1-git-send-email-min.guo@mediatek.com> Mime-Version: 1.0 Content-Type: text/plain Return-path: In-Reply-To: <1547516626-5084-1-git-send-email-min.guo@mediatek.com> Sender: linux-kernel-owner@vger.kernel.org To: Bin Liu , Rob Herring Cc: Greg Kroah-Hartman , Mark Rutland , Matthias Brugger , Alan Stern , chunfeng.yun@mediatek.com, linux-usb@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, Min Guo , Yonglong Wu List-Id: devicetree@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 --- drivers/usb/musb/Kconfig | 8 +- drivers/usb/musb/Makefile | 1 + drivers/usb/musb/mediatek.c | 617 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/musb/musb_core.c | 69 +++++ drivers/usb/musb/musb_core.h | 9 + drivers/usb/musb/musb_dma.h | 9 + drivers/usb/musb/musb_host.c | 26 +- drivers/usb/musb/musb_io.h | 6 + drivers/usb/musb/musbhsdma.c | 55 ++-- 9 files changed, 762 insertions(+), 38 deletions(-) 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..7221989 --- /dev/null +++ b/drivers/usb/musb/mediatek.c @@ -0,0 +1,617 @@ +// 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->int_rx); + musb_clearw(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb_clearb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + + 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 void mtk_musb_clearb(void __iomem *addr, unsigned int offset, u8 data) +{ + /* W1C */ + musb_writeb(addr, offset, data); +} + +static void mtk_musb_clearw(void __iomem *addr, unsigned int offset, u16 data) +{ + /* W1C */ + musb_writew(addr, offset, data); +} + +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_in) +{ + struct musb *musb = qh->hw_ep->musb; + u8 epnum = qh->hw_ep->epnum; + u16 toggle; + + if (is_in) + toggle = musb_readw(musb->mregs, MUSB_RXTOG); + else + toggle = musb_readw(musb->mregs, MUSB_TXTOG); + + return toggle & (1 << epnum); +} + +static u16 mtk_musb_set_toggle(struct musb_qh *qh, int is_in, struct urb *urb) +{ + struct musb *musb = qh->hw_ep->musb; + u8 epnum = qh->hw_ep->epnum; + u16 toggle = 0; + + toggle = usb_gettoggle(urb->dev, qh->epnum, !is_in); + + if (is_in) { + musb_writew(musb->mregs, MUSB_RXTOGEN, (1 << epnum)); + musb_writew(musb->mregs, MUSB_RXTOG, (toggle << epnum)); + } else { + musb_writew(musb->mregs, MUSB_TXTOGEN, (1 << epnum)); + musb_writew(musb->mregs, MUSB_TXTOG, (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); + + dev_info(dev, "USB probe done!\n"); + 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"); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b7d5627..2c0d102 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -260,6 +260,11 @@ static void musb_default_writeb(void __iomem *addr, unsigned offset, u8 data) __raw_writeb(data, addr + offset); } +static void +musb_default_clearb(void __iomem *addr, unsigned int offset, u8 data) +{ +} + static u16 musb_default_readw(const void __iomem *addr, unsigned offset) { u16 data = __raw_readw(addr + offset); @@ -274,6 +279,43 @@ static void musb_default_writew(void __iomem *addr, unsigned offset, u16 data) __raw_writew(data, addr + offset); } +static void +musb_default_clearw(void __iomem *addr, unsigned int offset, u16 data) +{ +} + +static u16 musb_default_get_toggle(struct musb_qh *qh, int is_in) +{ + void __iomem *epio = qh->hw_ep->regs; + u16 csr; + + if (is_in) + csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE; + else + csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE; + + return csr; +} + +static u16 musb_default_set_toggle(struct musb_qh *qh, int is_in, + struct urb *urb) +{ + u16 csr = 0; + u16 toggle = 0; + + toggle = usb_gettoggle(urb->dev, qh->epnum, !is_in); + + if (is_in) + csr = toggle ? (MUSB_RXCSR_H_WR_DATATOGGLE + | MUSB_RXCSR_H_DATATOGGLE) : 0; + else + csr |= toggle ? (MUSB_TXCSR_H_WR_DATATOGGLE + | MUSB_TXCSR_H_DATATOGGLE) + : MUSB_TXCSR_CLRDATATOG; + + return csr; +} + /* * Load an endpoint's FIFO */ @@ -370,12 +412,18 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data); EXPORT_SYMBOL_GPL(musb_writeb); +void (*musb_clearb)(void __iomem *addr, unsigned int offset, u8 data); +EXPORT_SYMBOL_GPL(musb_clearb); + u16 (*musb_readw)(const void __iomem *addr, unsigned offset); EXPORT_SYMBOL_GPL(musb_readw); void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data); EXPORT_SYMBOL_GPL(musb_writew); +void (*musb_clearw)(void __iomem *addr, unsigned int offset, u16 data); +EXPORT_SYMBOL_GPL(musb_clearw); + u32 musb_readl(const void __iomem *addr, unsigned offset) { u32 data = __raw_readl(addr + offset); @@ -1028,6 +1076,11 @@ static void musb_disable_interrupts(struct musb *musb) temp = musb_readb(mbase, MUSB_INTRUSB); temp = musb_readw(mbase, MUSB_INTRTX); temp = musb_readw(mbase, MUSB_INTRRX); + + /* some platform needs clear pending interrupts by manual */ + musb_clearb(mbase, MUSB_INTRUSB, musb_readb(mbase, MUSB_INTRUSB)); + musb_clearw(mbase, MUSB_INTRRX, musb_readw(mbase, MUSB_INTRRX)); + musb_clearw(mbase, MUSB_INTRTX, musb_readw(mbase, MUSB_INTRTX)); } static void musb_enable_interrupts(struct musb *musb) @@ -2192,6 +2245,8 @@ static void musb_deassert_reset(struct work_struct *work) musb_writeb = musb_default_writeb; musb_readw = musb_default_readw; musb_writew = musb_default_writew; + musb_clearb = musb_default_clearb; + musb_clearw = musb_default_clearw; /* The musb_platform_init() call: * - adjusts musb->mregs @@ -2252,10 +2307,14 @@ static void musb_deassert_reset(struct work_struct *work) musb_readb = musb->ops->readb; if (musb->ops->writeb) musb_writeb = musb->ops->writeb; + if (musb->ops->clearb) + musb_clearb = musb->ops->clearb; if (musb->ops->readw) musb_readw = musb->ops->readw; if (musb->ops->writew) musb_writew = musb->ops->writew; + if (musb->ops->clearw) + musb_clearw = musb->ops->clearw; #ifndef CONFIG_MUSB_PIO_ONLY if (!musb->ops->dma_init || !musb->ops->dma_exit) { @@ -2277,6 +2336,16 @@ static void musb_deassert_reset(struct work_struct *work) else musb->io.write_fifo = musb_default_write_fifo; + if (musb->ops->get_toggle) + musb->io.get_toggle = musb->ops->get_toggle; + else + musb->io.get_toggle = musb_default_get_toggle; + + if (musb->ops->set_toggle) + musb->io.set_toggle = musb->ops->set_toggle; + else + musb->io.set_toggle = musb_default_set_toggle; + if (!musb->xceiv->io_ops) { musb->xceiv->io_dev = musb->controller; musb->xceiv->io_priv = musb->mregs; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 04203b7..71dca80 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -27,6 +27,7 @@ struct musb; struct musb_hw_ep; struct musb_ep; +struct musb_qh; /* Helper defines for struct musb->hwvers */ #define MUSB_HWVERS_MAJOR(x) ((x >> 10) & 0x1f) @@ -119,10 +120,14 @@ enum musb_g_ep0_state { * @fifo_offset: returns the fifo offset * @readb: read 8 bits * @writeb: write 8 bits + * @clearb: clear 8 bits * @readw: read 16 bits * @writew: write 16 bits + * @clearw: clear 16 bits * @read_fifo: reads the fifo * @write_fifo: writes to fifo + * @get_toggle: platform specific get toggle function + * @set_toggle: platform specific set toggle function * @dma_init: platform specific dma init function * @dma_exit: platform specific dma exit function * @init: turns on clocks, sets up platform-specific registers, etc @@ -163,10 +168,14 @@ struct musb_platform_ops { u32 (*busctl_offset)(u8 epnum, u16 offset); u8 (*readb)(const void __iomem *addr, unsigned offset); void (*writeb)(void __iomem *addr, unsigned offset, u8 data); + void (*clearb)(void __iomem *addr, unsigned int offset, u8 data); u16 (*readw)(const void __iomem *addr, unsigned offset); void (*writew)(void __iomem *addr, unsigned offset, u16 data); + void (*clearw)(void __iomem *addr, unsigned int offset, u16 data); void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); + u16 (*get_toggle)(struct musb_qh *qh, int is_in); + u16 (*set_toggle)(struct musb_qh *qh, int is_in, struct urb *urb); struct dma_controller * (*dma_init) (struct musb *musb, void __iomem *base); void (*dma_exit)(struct dma_controller *c); diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 8f60271..05103ea 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -35,6 +35,12 @@ * whether shared with the Inventra core or separate. */ +#define MUSB_HSDMA_BASE 0x200 +#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) +#define MUSB_HSDMA_CONTROL 0x4 +#define MUSB_HSDMA_ADDRESS 0x8 +#define MUSB_HSDMA_COUNT 0xc + #define DMA_ADDR_INVALID (~(dma_addr_t)0) #ifdef CONFIG_MUSB_PIO_ONLY @@ -191,6 +197,9 @@ static inline void musb_dma_controller_destroy(struct dma_controller *d) { } extern struct dma_controller * musbhs_dma_controller_create(struct musb *musb, void __iomem *base); extern void musbhs_dma_controller_destroy(struct dma_controller *c); +extern struct dma_controller * +musbhs_dma_controller_create_noirq(struct musb *musb, void __iomem *base); +extern irqreturn_t dma_controller_irq(int irq, void *private_data); extern struct dma_controller * tusb_dma_controller_create(struct musb *musb, void __iomem *base); diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 16d0ba4..ba66f44 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -290,39 +290,23 @@ static void musb_giveback(struct musb *musb, struct urb *urb, int status) static inline void musb_save_toggle(struct musb_qh *qh, int is_in, struct urb *urb) { - void __iomem *epio = qh->hw_ep->regs; - u16 csr; + struct musb *musb = qh->hw_ep->musb; + u16 csr; /* * FIXME: the current Mentor DMA code seems to have * problems getting toggle correct. */ - - if (is_in) - csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE; - else - csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE; - + csr = musb->io.get_toggle(qh, is_in); usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0); } static inline u16 musb_set_toggle(struct musb_qh *qh, int is_in, struct urb *urb) { - u16 csr = 0; - u16 toggle = 0; - - toggle = usb_gettoggle(urb->dev, qh->epnum, !is_in); - - if (is_in) - csr = toggle ? (MUSB_RXCSR_H_WR_DATATOGGLE - | MUSB_RXCSR_H_DATATOGGLE) : 0; - else - csr = toggle ? (MUSB_TXCSR_H_WR_DATATOGGLE - | MUSB_TXCSR_H_DATATOGGLE) - : MUSB_TXCSR_CLRDATATOG; + struct musb *musb = qh->hw_ep->musb; - return csr; + return musb->io.set_toggle(qh, is_in, urb); } /* diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 8058a58..9bae09b 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -22,6 +22,8 @@ * @read_fifo: platform specific function to read fifo * @write_fifo: platform specific function to write fifo * @busctl_offset: platform specific function to get busctl offset + * @get_toggle: platform specific function to get toggle + * @set_toggle: platform specific function to set toggle */ struct musb_io { u32 (*ep_offset)(u8 epnum, u16 offset); @@ -30,13 +32,17 @@ struct musb_io { void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); u32 (*busctl_offset)(u8 epnum, u16 offset); + u16 (*get_toggle)(struct musb_qh *qh, int is_in); + u16 (*set_toggle)(struct musb_qh *qh, int is_in, struct urb *urb); }; /* Do not add new entries here, add them the struct musb_io instead */ extern u8 (*musb_readb)(const void __iomem *addr, unsigned offset); extern void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data); +extern void (*musb_clearb)(void __iomem *addr, unsigned int offset, u8 data); extern u16 (*musb_readw)(const void __iomem *addr, unsigned offset); extern void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data); +extern void (*musb_clearw)(void __iomem *addr, unsigned int offset, u16 data); extern u32 musb_readl(const void __iomem *addr, unsigned offset); extern void musb_writel(void __iomem *addr, unsigned offset, u32 data); diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index a688f7f..b05fe68 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -10,12 +10,7 @@ #include #include #include "musb_core.h" - -#define MUSB_HSDMA_BASE 0x200 -#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) -#define MUSB_HSDMA_CONTROL 0x4 -#define MUSB_HSDMA_ADDRESS 0x8 -#define MUSB_HSDMA_COUNT 0xc +#include "musb_dma.h" #define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \ (MUSB_HSDMA_BASE + (_bchannel << 4) + _offset) @@ -268,7 +263,7 @@ static int dma_channel_abort(struct dma_channel *channel) return 0; } -static irqreturn_t dma_controller_irq(int irq, void *private_data) +irqreturn_t dma_controller_irq(int irq, void *private_data) { struct musb_dma_controller *controller = private_data; struct musb *musb = controller->private_data; @@ -291,6 +286,9 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR); + /* some platform needs clear pending interrupts by manual */ + musb_clearb(musb->mregs, MUSB_HSDMA_INTR, int_hsdma); + if (!int_hsdma) { musb_dbg(musb, "spurious DMA irq"); @@ -382,6 +380,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) spin_unlock_irqrestore(&musb->lock, flags); return retval; } +EXPORT_SYMBOL_GPL(dma_controller_irq); void musbhs_dma_controller_destroy(struct dma_controller *c) { @@ -397,18 +396,10 @@ void musbhs_dma_controller_destroy(struct dma_controller *c) } EXPORT_SYMBOL_GPL(musbhs_dma_controller_destroy); -struct dma_controller *musbhs_dma_controller_create(struct musb *musb, +static struct musb_dma_controller *dma_controller_alloc(struct musb *musb, void __iomem *base) { struct musb_dma_controller *controller; - struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); - int irq = platform_get_irq_byname(pdev, "dma"); - - if (irq <= 0) { - dev_err(dev, "No DMA interrupt line!\n"); - return NULL; - } controller = kzalloc(sizeof(*controller), GFP_KERNEL); if (!controller) @@ -422,6 +413,25 @@ struct dma_controller *musbhs_dma_controller_create(struct musb *musb, controller->controller.channel_release = dma_channel_release; controller->controller.channel_program = dma_channel_program; controller->controller.channel_abort = dma_channel_abort; + return controller; +} + +struct dma_controller *musbhs_dma_controller_create(struct musb *musb, + void __iomem *base) +{ + struct musb_dma_controller *controller; + struct device *dev = musb->controller; + struct platform_device *pdev = to_platform_device(dev); + int irq = platform_get_irq_byname(pdev, "dma"); + + if (irq <= 0) { + dev_err(dev, "No DMA interrupt line!\n"); + return NULL; + } + + controller = dma_controller_alloc(musb, base); + if (!controller) + return NULL; if (request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), &controller->controller)) { @@ -436,3 +446,16 @@ struct dma_controller *musbhs_dma_controller_create(struct musb *musb, return &controller->controller; } EXPORT_SYMBOL_GPL(musbhs_dma_controller_create); + +struct dma_controller *musbhs_dma_controller_create_noirq(struct musb *musb, + void __iomem *base) +{ + struct musb_dma_controller *controller; + + controller = dma_controller_alloc(musb, base); + if (!controller) + return NULL; + + return &controller->controller; +} +EXPORT_SYMBOL_GPL(musbhs_dma_controller_create_noirq); -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Subject: [v2,4/4] usb: musb: Add support for MediaTek musb controller From: min.guo@mediatek.com Message-Id: <1547516626-5084-5-git-send-email-min.guo@mediatek.com> Date: Tue, 15 Jan 2019 09:43:46 +0800 To: Bin Liu , Rob Herring Cc: Greg Kroah-Hartman , Mark Rutland , Matthias Brugger , Alan Stern , chunfeng.yun@mediatek.com, linux-usb@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, Min Guo , Yonglong Wu List-ID: RnJvbTogTWluIEd1byA8bWluLmd1b0BtZWRpYXRlay5jb20+CgpUaGlzIGFkZHMgc3VwcG9ydCBm b3IgTWVkaWFUZWsgbXVzYiBjb250cm9sbGVyIGluCmhvc3QsIHBlcmlwaGVyYWwgYW5kIG90ZyBt b2RlLgpUaGVyZSBhcmUgc29tZSBxdWlyayBvZiBNZWRpYVRlayBtdXNiIGNvbnRyb2xsZXIsIHN1 Y2ggYXM6CiAtVzFDIGludGVycnVwdCBzdGF0dXMgcmVnaXN0ZXJzCiAtUHJpdmF0ZSBkYXRhIHRv Z2dsZSByZWdpc3RlcnMKIC1ObyBkZWRpY2F0ZWQgRE1BIGludGVycnVwdCBsaW5lCgpTaWduZWQt b2ZmLWJ5OiBNaW4gR3VvIDxtaW4uZ3VvQG1lZGlhdGVrLmNvbT4KU2lnbmVkLW9mZi1ieTogWW9u Z2xvbmcgV3UgPHlvbmdsb25nLnd1QG1lZGlhdGVrLmNvbT4KLS0tCiBkcml2ZXJzL3VzYi9tdXNi L0tjb25maWcgICAgIHwgICA4ICstCiBkcml2ZXJzL3VzYi9tdXNiL01ha2VmaWxlICAgIHwgICAx ICsKIGRyaXZlcnMvdXNiL211c2IvbWVkaWF0ZWsuYyAgfCA2MTcgKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKysrKwogZHJpdmVycy91c2IvbXVzYi9tdXNiX2NvcmUuYyB8 ICA2OSArKysrKwogZHJpdmVycy91c2IvbXVzYi9tdXNiX2NvcmUuaCB8ICAgOSArCiBkcml2ZXJz L3VzYi9tdXNiL211c2JfZG1hLmggIHwgICA5ICsKIGRyaXZlcnMvdXNiL211c2IvbXVzYl9ob3N0 LmMgfCAgMjYgKy0KIGRyaXZlcnMvdXNiL211c2IvbXVzYl9pby5oICAgfCAgIDYgKwogZHJpdmVy cy91c2IvbXVzYi9tdXNiaHNkbWEuYyB8ICA1NSArKy0tCiA5IGZpbGVzIGNoYW5nZWQsIDc2MiBp bnNlcnRpb25zKCspLCAzOCBkZWxldGlvbnMoLSkKIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJz L3VzYi9tdXNiL21lZGlhdGVrLmMKCmRpZmYgLS1naXQgYS9kcml2ZXJzL3VzYi9tdXNiL0tjb25m aWcgYi9kcml2ZXJzL3VzYi9tdXNiL0tjb25maWcKaW5kZXggYWQwODg5NS4uYjcyYjdjMSAxMDA2 NDQKLS0tIGEvZHJpdmVycy91c2IvbXVzYi9LY29uZmlnCisrKyBiL2RyaXZlcnMvdXNiL211c2Iv S2NvbmZpZwpAQCAtMTE1LDYgKzExNSwxMiBAQCBjb25maWcgVVNCX01VU0JfSlo0NzQwCiAJZGVw ZW5kcyBvbiBVU0JfTVVTQl9HQURHRVQKIAlkZXBlbmRzIG9uIFVTQl9PVEdfQkxBQ0tMSVNUX0hV QgogCitjb25maWcgVVNCX01VU0JfTUVESUFURUsKKwl0cmlzdGF0ZSAiTWVkaWFUZWsgcGxhdGZv cm1zIgorCWRlcGVuZHMgb24gQVJDSF9NRURJQVRFSyB8fCBDT01QSUxFX1RFU1QKKwlkZXBlbmRz IG9uIE5PUF9VU0JfWENFSVYKKwlkZXBlbmRzIG9uIEdFTkVSSUNfUEhZCisKIGNvbmZpZyBVU0Jf TVVTQl9BTTMzNVhfQ0hJTEQKIAl0cmlzdGF0ZQogCkBAIC0xNDEsNyArMTQ3LDcgQEAgY29uZmln IFVTQl9VWDUwMF9ETUEKIAogY29uZmlnIFVTQl9JTlZFTlRSQV9ETUEKIAlib29sICdJbnZlbnRy YScKLQlkZXBlbmRzIG9uIFVTQl9NVVNCX09NQVAyUExVUworCWRlcGVuZHMgb24gVVNCX01VU0Jf T01BUDJQTFVTIHx8IFVTQl9NVVNCX01FRElBVEVLCiAJaGVscAogCSAgRW5hYmxlIERNQSB0cmFu c2ZlcnMgdXNpbmcgTWVudG9yJ3MgZW5naW5lLgogCmRpZmYgLS1naXQgYS9kcml2ZXJzL3VzYi9t dXNiL01ha2VmaWxlIGIvZHJpdmVycy91c2IvbXVzYi9NYWtlZmlsZQppbmRleCAzYTg4Yzc5Li42 M2Q4MmQwIDEwMDY0NAotLS0gYS9kcml2ZXJzL3VzYi9tdXNiL01ha2VmaWxlCisrKyBiL2RyaXZl cnMvdXNiL211c2IvTWFrZWZpbGUKQEAgLTI0LDYgKzI0LDcgQEAgb2JqLSQoQ09ORklHX1VTQl9N VVNCX0RBOFhYKQkJCSs9IGRhOHh4Lm8KIG9iai0kKENPTkZJR19VU0JfTVVTQl9VWDUwMCkJCQkr PSB1eDUwMC5vCiBvYmotJChDT05GSUdfVVNCX01VU0JfSlo0NzQwKQkJCSs9IGp6NDc0MC5vCiBv YmotJChDT05GSUdfVVNCX01VU0JfU1VOWEkpCQkJKz0gc3VueGkubworb2JqLSQoQ09ORklHX1VT Ql9NVVNCX01FRElBVEVLKSAgICAgIAkJKz0gbWVkaWF0ZWsubwogCiAKIG9iai0kKENPTkZJR19V U0JfTVVTQl9BTTMzNVhfQ0hJTEQpCQkrPSBtdXNiX2FtMzM1eC5vCmRpZmYgLS1naXQgYS9kcml2 ZXJzL3VzYi9tdXNiL21lZGlhdGVrLmMgYi9kcml2ZXJzL3VzYi9tdXNiL21lZGlhdGVrLmMKbmV3 IGZpbGUgbW9kZSAxMDA2NDQKaW5kZXggMDAwMDAwMC4uNzIyMTk4OQotLS0gL2Rldi9udWxsCisr KyBiL2RyaXZlcnMvdXNiL211c2IvbWVkaWF0ZWsuYwpAQCAtMCwwICsxLDYxNyBAQAorLy8gU1BE WC1MaWNlbnNlLUlkZW50aWZpZXI6IEdQTC0yLjAKKy8qCisgKiBDb3B5cmlnaHQgKEMpIDIwMTgg TWVkaWFUZWsgSW5jLgorICoKKyAqIEF1dGhvcjoKKyAqICBNaW4gR3VvIDxtaW4uZ3VvQG1lZGlh dGVrLmNvbT4KKyAqICBZb25nbG9uZyBXdSA8eW9uZ2xvbmcud3VAbWVkaWF0ZWsuY29tPgorICov CisKKyNpbmNsdWRlIDxsaW51eC9jbGsuaD4KKyNpbmNsdWRlIDxsaW51eC9kbWEtbWFwcGluZy5o PgorI2luY2x1ZGUgPGxpbnV4L21vZHVsZS5oPgorI2luY2x1ZGUgPGxpbnV4L3BsYXRmb3JtX2Rl dmljZS5oPgorI2luY2x1ZGUgPGxpbnV4L3VzYi91c2JfcGh5X2dlbmVyaWMuaD4KKyNpbmNsdWRl ICJtdXNiX2NvcmUuaCIKKyNpbmNsdWRlICJtdXNiX2RtYS5oIgorCisjZGVmaW5lIFVTQl9MMUlO VFMJMHgwMGEwCisjZGVmaW5lIFVTQl9MMUlOVE0JMHgwMGE0CisjZGVmaW5lIE1US19NVVNCX1RY RlVOQ0FERFIJMHgwNDgwCisKKy8qIE1lZGlhVGVrIGNvbnRyb2xsZXIgdG9nZ2xlIGVuYWJsZSBh bmQgc3RhdHVzIHJlZyAqLworI2RlZmluZSBNVVNCX1JYVE9HCQkweDgwCisjZGVmaW5lIE1VU0Jf UlhUT0dFTgkJMHg4MgorI2RlZmluZSBNVVNCX1RYVE9HCQkweDg0CisjZGVmaW5lIE1VU0JfVFhU T0dFTgkJMHg4NgorCisjZGVmaW5lIFRYX0lOVF9TVEFUVVMJCUJJVCgwKQorI2RlZmluZSBSWF9J TlRfU1RBVFVTCQlCSVQoMSkKKyNkZWZpbmUgVVNCQ09NX0lOVF9TVEFUVVMJQklUKDIpCisjZGVm aW5lIERNQV9JTlRfU1RBVFVTCQlCSVQoMykKKworI2RlZmluZSBETUFfSU5UUl9TVEFUVVNfTVNL CQlHRU5NQVNLKDcsIDApCisjZGVmaW5lIERNQV9JTlRSX1VOTUFTS19TRVRfTVNLCUdFTk1BU0so MzEsIDI0KQorCitlbnVtIG10a192YnVzX2lkX3N0YXRlIHsKKwlNVEtfSURfRkxPQVQgPSAxLAor CU1US19JRF9HUk9VTkQsCisJTVRLX1ZCVVNfT0ZGLAorCU1US19WQlVTX1ZBTElELAorfTsKKwor c3RydWN0IG10a19nbHVlIHsKKwlzdHJ1Y3QgZGV2aWNlICpkZXY7CisJc3RydWN0IG11c2IgKm11 c2I7CisJc3RydWN0IHBsYXRmb3JtX2RldmljZSAqbXVzYl9wZGV2OworCXN0cnVjdCBwbGF0Zm9y bV9kZXZpY2UgKnVzYl9waHk7CisJc3RydWN0IHBoeSAqcGh5OworCXN0cnVjdCB1c2JfcGh5ICp4 Y2VpdjsKKwllbnVtIHBoeV9tb2RlIHBoeV9tb2RlOworCXN0cnVjdCBjbGsgKm1haW47CisJc3Ry dWN0IGNsayAqbWN1OworCXN0cnVjdCBjbGsgKnVuaXZwbGw7CisJc3RydWN0IHJlZ3VsYXRvciAq dmJ1czsKKwlzdHJ1Y3QgZXh0Y29uX2RldiAqZWRldjsKKwlzdHJ1Y3Qgbm90aWZpZXJfYmxvY2sg dmJ1c19uYjsKKwlzdHJ1Y3Qgbm90aWZpZXJfYmxvY2sgaWRfbmI7Cit9OworCitzdGF0aWMgaW50 IG10a19tdXNiX2Nsa3NfZ2V0KHN0cnVjdCBtdGtfZ2x1ZSAqZ2x1ZSkKK3sKKwlzdHJ1Y3QgZGV2 aWNlICpkZXYgPSBnbHVlLT5kZXY7CisKKwlnbHVlLT5tYWluID0gZGV2bV9jbGtfZ2V0KGRldiwg Im1haW4iKTsKKwlpZiAoSVNfRVJSKGdsdWUtPm1haW4pKSB7CisJCWRldl9lcnIoZGV2LCAiZmFp bCB0byBnZXQgbWFpbiBjbG9ja1xuIik7CisJCXJldHVybiBQVFJfRVJSKGdsdWUtPm1haW4pOwor CX0KKworCWdsdWUtPm1jdSA9IGRldm1fY2xrX2dldChkZXYsICJtY3UiKTsKKwlpZiAoSVNfRVJS KGdsdWUtPm1jdSkpIHsKKwkJZGV2X2VycihkZXYsICJmYWlsIHRvIGdldCBtY3UgY2xvY2tcbiIp OworCQlyZXR1cm4gUFRSX0VSUihnbHVlLT5tY3UpOworCX0KKworCWdsdWUtPnVuaXZwbGwgPSBk ZXZtX2Nsa19nZXQoZGV2LCAidW5pdnBsbCIpOworCWlmIChJU19FUlIoZ2x1ZS0+dW5pdnBsbCkp IHsKKwkJZGV2X2VycihkZXYsICJmYWlsIHRvIGdldCB1bml2cGxsIGNsb2NrXG4iKTsKKwkJcmV0 dXJuIFBUUl9FUlIoZ2x1ZS0+dW5pdnBsbCk7CisJfQorCisJcmV0dXJuIDA7Cit9CisKK3N0YXRp YyBpbnQgbXRrX211c2JfY2xrc19lbmFibGUoc3RydWN0IG10a19nbHVlICpnbHVlKQoreworCWlu dCByZXQ7CisKKwlyZXQgPSBjbGtfcHJlcGFyZV9lbmFibGUoZ2x1ZS0+bWFpbik7CisJaWYgKHJl dCkgeworCQlkZXZfZXJyKGdsdWUtPmRldiwgImZhaWxlZCB0byBlbmFibGUgbWFpbiBjbG9ja1xu Iik7CisJCWdvdG8gZXJyX21haW5fY2xrOworCX0KKworCXJldCA9IGNsa19wcmVwYXJlX2VuYWJs ZShnbHVlLT5tY3UpOworCWlmIChyZXQpIHsKKwkJZGV2X2VycihnbHVlLT5kZXYsICJmYWlsZWQg dG8gZW5hYmxlIG1jdSBjbG9ja1xuIik7CisJCWdvdG8gZXJyX21jdV9jbGs7CisJfQorCisJcmV0 ID0gY2xrX3ByZXBhcmVfZW5hYmxlKGdsdWUtPnVuaXZwbGwpOworCWlmIChyZXQpIHsKKwkJZGV2 X2VycihnbHVlLT5kZXYsICJmYWlsZWQgdG8gZW5hYmxlIHVuaXZwbGwgY2xvY2tcbiIpOworCQln b3RvIGVycl91bml2cGxsX2NsazsKKwl9CisKKwlyZXR1cm4gMDsKKworZXJyX3VuaXZwbGxfY2xr OgorCWNsa19kaXNhYmxlX3VucHJlcGFyZShnbHVlLT5tY3UpOworZXJyX21jdV9jbGs6CisJY2xr X2Rpc2FibGVfdW5wcmVwYXJlKGdsdWUtPm1haW4pOworZXJyX21haW5fY2xrOgorCXJldHVybiBy ZXQ7Cit9CisKK3N0YXRpYyB2b2lkIG10a19tdXNiX2Nsa3NfZGlzYWJsZShzdHJ1Y3QgbXRrX2ds dWUgKmdsdWUpCit7CisJY2xrX2Rpc2FibGVfdW5wcmVwYXJlKGdsdWUtPnVuaXZwbGwpOworCWNs a19kaXNhYmxlX3VucHJlcGFyZShnbHVlLT5tY3UpOworCWNsa19kaXNhYmxlX3VucHJlcGFyZShn bHVlLT5tYWluKTsKK30KKworc3RhdGljIHZvaWQgbXRrX211c2Jfc2V0X3ZidXMoc3RydWN0IG11 c2IgKm11c2IsIGludCBpc19vbikKK3sKKwlzdHJ1Y3QgZGV2aWNlICpkZXYgPSBtdXNiLT5jb250 cm9sbGVyOworCXN0cnVjdCBtdGtfZ2x1ZSAqZ2x1ZSA9IGRldl9nZXRfZHJ2ZGF0YShkZXYtPnBh cmVudCk7CisJaW50IHJldDsKKworCS8qIHZidXMgaXMgb3B0aW9uYWwgKi8KKwlpZiAoIWdsdWUt PnZidXMpCisJCXJldHVybjsKKworCWRldl9kYmcobXVzYi0+Y29udHJvbGxlciwgIiVzLCBpc19v bj0lZFxyXG4iLCBfX2Z1bmNfXywgaXNfb24pOworCWlmIChpc19vbikgeworCQlyZXQgPSByZWd1 bGF0b3JfZW5hYmxlKGdsdWUtPnZidXMpOworCQlpZiAocmV0KSB7CisJCQlkZXZfZXJyKGdsdWUt PmRldiwgImZhaWwgdG8gZW5hYmxlIHZidXMgcmVndWxhdG9yXG4iKTsKKwkJCXJldHVybjsKKwkJ fQorCX0gZWxzZSB7CisJCXJlZ3VsYXRvcl9kaXNhYmxlKGdsdWUtPnZidXMpOworCX0KK30KKwor LyoKKyAqIHN3aXRjaCB0byBob3N0OiAtPiBNVEtfVkJVU19PRkYgLS0+IE1US19JRF9HUk9VTkQK KyAqIHN3aXRjaCB0byBkZXZpY2U6IC0+IE1US19JRF9GTE9BVCAtLT4gTVRLX1ZCVVNfVkFMSUQK KyAqLworc3RhdGljIHZvaWQgbXRrX211c2Jfc2V0X21haWxib3goc3RydWN0IG10a19nbHVlICpn bHVlLAorCWVudW0gbXRrX3ZidXNfaWRfc3RhdGUgc3RhdHVzKQoreworCXN0cnVjdCBtdXNiICpt dXNiID0gZ2x1ZS0+bXVzYjsKKwl1OCBkZXZjdGwgPSAwOworCisJZGV2X2RiZyhnbHVlLT5kZXYs ICJtYWlsYm94IHN0YXRlKCVkKVxuIiwgc3RhdHVzKTsKKwlzd2l0Y2ggKHN0YXR1cykgeworCWNh c2UgTVRLX0lEX0dST1VORDoKKwkJcGh5X3Bvd2VyX29uKGdsdWUtPnBoeSk7CisJCWRldmN0bCA9 IHJlYWRiKG11c2ItPm1yZWdzICsgTVVTQl9ERVZDVEwpOworCQltdXNiLT54Y2Vpdi0+b3RnLT5z dGF0ZSA9IE9UR19TVEFURV9BX1dBSVRfVlJJU0U7CisJCW10a19tdXNiX3NldF92YnVzKG11c2Is IDEpOworCQlnbHVlLT5waHlfbW9kZSA9IFBIWV9NT0RFX1VTQl9IT1NUOworCQlwaHlfc2V0X21v ZGUoZ2x1ZS0+cGh5LCBnbHVlLT5waHlfbW9kZSk7CisJCWRldmN0bCB8PSBNVVNCX0RFVkNUTF9T RVNTSU9OOworCQltdXNiX3dyaXRlYihtdXNiLT5tcmVncywgTVVTQl9ERVZDVEwsIGRldmN0bCk7 CisJCU1VU0JfSFNUX01PREUobXVzYik7CisJCWJyZWFrOworCS8qCisJICogTVRLX0lEX0ZMT0FU IHByb2Nlc3MgaXMgdGhlIHNhbWUgYXMgTVRLX1ZCVVNfVkFMSUQKKwkgKiBleGNlcHQgdGhhdCB0 dXJuIG9mZiBWQlVTCisJICovCisJY2FzZSBNVEtfSURfRkxPQVQ6CisJCW10a19tdXNiX3NldF92 YnVzKG11c2IsIDApOworCQkvKiBmYWxsIHRocm91Z2ggKi8KKwljYXNlIE1US19WQlVTX09GRjoK KwkJbXVzYi0+eGNlaXYtPm90Zy0+c3RhdGUgPSBPVEdfU1RBVEVfQl9JRExFOworCQlkZXZjdGwg Jj0gfk1VU0JfREVWQ1RMX1NFU1NJT047CisJCW11c2Jfd3JpdGViKG11c2ItPm1yZWdzLCBNVVNC X0RFVkNUTCwgZGV2Y3RsKTsKKwkJcGh5X3Bvd2VyX29mZihnbHVlLT5waHkpOworCQlicmVhazsK KwljYXNlIE1US19WQlVTX1ZBTElEOgorCQlwaHlfcG93ZXJfb24oZ2x1ZS0+cGh5KTsKKwkJZ2x1 ZS0+cGh5X21vZGUgPSBQSFlfTU9ERV9VU0JfREVWSUNFOworCQlwaHlfc2V0X21vZGUoZ2x1ZS0+ cGh5LCBnbHVlLT5waHlfbW9kZSk7CisJCU1VU0JfREVWX01PREUobXVzYik7CisJCWJyZWFrOwor CWRlZmF1bHQ6CisJCWRldl9lcnIoZ2x1ZS0+ZGV2LCAiaW52YWxpZCBzdGF0ZVxuIik7CisJfQor fQorCitzdGF0aWMgaW50IG10a19tdXNiX2lkX25vdGlmaWVyKHN0cnVjdCBub3RpZmllcl9ibG9j ayAqbmIsCisJdW5zaWduZWQgbG9uZyBldmVudCwgdm9pZCAqcHRyKQoreworCXN0cnVjdCBtdGtf Z2x1ZSAqZ2x1ZSA9IGNvbnRhaW5lcl9vZihuYiwgc3RydWN0IG10a19nbHVlLCBpZF9uYik7CisK KwlpZiAoZXZlbnQpCisJCW10a19tdXNiX3NldF9tYWlsYm94KGdsdWUsIE1US19JRF9HUk9VTkQp OworCWVsc2UKKwkJbXRrX211c2Jfc2V0X21haWxib3goZ2x1ZSwgTVRLX0lEX0ZMT0FUKTsKKwor CXJldHVybiBOT1RJRllfRE9ORTsKK30KKworc3RhdGljIGludCBtdGtfbXVzYl92YnVzX25vdGlm aWVyKHN0cnVjdCBub3RpZmllcl9ibG9jayAqbmIsCisJdW5zaWduZWQgbG9uZyBldmVudCwgdm9p ZCAqcHRyKQoreworCXN0cnVjdCBtdGtfZ2x1ZSAqZ2x1ZSA9IGNvbnRhaW5lcl9vZihuYiwgc3Ry dWN0IG10a19nbHVlLCB2YnVzX25iKTsKKworCWlmIChldmVudCkKKwkJbXRrX211c2Jfc2V0X21h aWxib3goZ2x1ZSwgTVRLX1ZCVVNfVkFMSUQpOworCWVsc2UKKwkJbXRrX211c2Jfc2V0X21haWxi b3goZ2x1ZSwgTVRLX1ZCVVNfT0ZGKTsKKworCXJldHVybiBOT1RJRllfRE9ORTsKK30KKworc3Rh dGljIHZvaWQgbXRrX290Z19zd2l0Y2hfaW5pdChzdHJ1Y3QgbXRrX2dsdWUgKmdsdWUpCit7CisJ aW50IHJldDsKKworCS8qIGV4dGNvbiBpcyBvcHRpb25hbCAqLworCWlmICghZ2x1ZS0+ZWRldikK KwkJcmV0dXJuOworCisJZ2x1ZS0+dmJ1c19uYi5ub3RpZmllcl9jYWxsID0gbXRrX211c2JfdmJ1 c19ub3RpZmllcjsKKwlyZXQgPSBkZXZtX2V4dGNvbl9yZWdpc3Rlcl9ub3RpZmllcihnbHVlLT5k ZXYsIGdsdWUtPmVkZXYsIEVYVENPTl9VU0IsCisJCQkJCSZnbHVlLT52YnVzX25iKTsKKwlpZiAo cmV0IDwgMCkKKwkJZGV2X2VycihnbHVlLT5kZXYsICJmYWlsZWQgdG8gcmVnaXN0ZXIgbm90aWZp ZXIgZm9yIFVTQlxuIik7CisKKwlnbHVlLT5pZF9uYi5ub3RpZmllcl9jYWxsID0gbXRrX211c2Jf aWRfbm90aWZpZXI7CisJcmV0ID0gZGV2bV9leHRjb25fcmVnaXN0ZXJfbm90aWZpZXIoZ2x1ZS0+ ZGV2LCBnbHVlLT5lZGV2LAorCQkJCQlFWFRDT05fVVNCX0hPU1QsICZnbHVlLT5pZF9uYik7CisJ aWYgKHJldCA8IDApCisJCWRldl9lcnIoZ2x1ZS0+ZGV2LCAiZmFpbGVkIHRvIHJlZ2lzdGVyIG5v dGlmaWVyIGZvciBVU0ItSE9TVFxuIik7CisKKwlkZXZfZGJnKGdsdWUtPmRldiwgIkVYVENPTl9V U0I6ICVkLCBFWFRDT05fVVNCX0hPU1Q6ICVkXG4iLAorCQlleHRjb25fZ2V0X3N0YXRlKGdsdWUt PmVkZXYsIEVYVENPTl9VU0IpLAorCQlleHRjb25fZ2V0X3N0YXRlKGdsdWUtPmVkZXYsIEVYVENP Tl9VU0JfSE9TVCkpOworCisJLyogZGVmYXVsdCBhcyBob3N0LCBzd2l0Y2ggdG8gZGV2aWNlIG1v ZGUgaWYgbmVlZGVkICovCisJaWYgKGV4dGNvbl9nZXRfc3RhdGUoZ2x1ZS0+ZWRldiwgRVhUQ09O X1VTQl9IT1NUKSA9PSBmYWxzZSkKKwkJbXRrX211c2Jfc2V0X21haWxib3goZ2x1ZSwgTVRLX0lE X0ZMT0FUKTsKKwlpZiAoZXh0Y29uX2dldF9zdGF0ZShnbHVlLT5lZGV2LCBFWFRDT05fVVNCKSA9 PSB0cnVlKQorCQltdGtfbXVzYl9zZXRfbWFpbGJveChnbHVlLCBNVEtfVkJVU19WQUxJRCk7Cit9 CisKK3N0YXRpYyBpcnFyZXR1cm5fdCBnZW5lcmljX2ludGVycnVwdChpbnQgaXJxLCB2b2lkICpf X2hjaSkKK3sKKwl1bnNpZ25lZCBsb25nIGZsYWdzOworCWlycXJldHVybl90IHJldHZhbCA9IElS UV9OT05FOworCXN0cnVjdCBtdXNiICptdXNiID0gX19oY2k7CisKKwlzcGluX2xvY2tfaXJxc2F2 ZSgmbXVzYi0+bG9jaywgZmxhZ3MpOworCW11c2ItPmludF91c2IgPSBtdXNiX3JlYWRiKG11c2It Pm1yZWdzLCBNVVNCX0lOVFJVU0IpICYKKwkgICAgbXVzYl9yZWFkYihtdXNiLT5tcmVncywgTVVT Ql9JTlRSVVNCRSk7CisJbXVzYi0+aW50X3R4ID0gbXVzYl9yZWFkdyhtdXNiLT5tcmVncywgTVVT Ql9JTlRSVFgpCisJICAgICYgbXVzYl9yZWFkdyhtdXNiLT5tcmVncywgTVVTQl9JTlRSVFhFKTsK KwltdXNiLT5pbnRfcnggPSBtdXNiX3JlYWR3KG11c2ItPm1yZWdzLCBNVVNCX0lOVFJSWCkKKwkg ICAgJiBtdXNiX3JlYWR3KG11c2ItPm1yZWdzLCBNVVNCX0lOVFJSWEUpOworCS8qIE1lZGlhVGVr IGNvbnRyb2xsZXIgaW50ZXJydXB0IHN0YXR1cyBpcyBXMUMgKi8KKwltdXNiX2NsZWFydyhtdXNi LT5tcmVncywgTVVTQl9JTlRSUlgsIG11c2ItPmludF9yeCk7CisJbXVzYl9jbGVhcncobXVzYi0+ bXJlZ3MsIE1VU0JfSU5UUlRYLCBtdXNiLT5pbnRfdHgpOworCW11c2JfY2xlYXJiKG11c2ItPm1y ZWdzLCBNVVNCX0lOVFJVU0IsIG11c2ItPmludF91c2IpOworCisJaWYgKG11c2ItPmludF91c2Ig fHwgbXVzYi0+aW50X3R4IHx8IG11c2ItPmludF9yeCkKKwkJcmV0dmFsID0gbXVzYl9pbnRlcnJ1 cHQobXVzYik7CisKKwlzcGluX3VubG9ja19pcnFyZXN0b3JlKCZtdXNiLT5sb2NrLCBmbGFncyk7 CisKKwlyZXR1cm4gcmV0dmFsOworfQorCitzdGF0aWMgaXJxcmV0dXJuX3QgbXRrX211c2JfaW50 ZXJydXB0KGludCBpcnEsIHZvaWQgKmRldl9pZCkKK3sKKwlpcnFyZXR1cm5fdCByZXR2YWwgPSBJ UlFfTk9ORTsKKwlzdHJ1Y3QgbXVzYiAqbXVzYiA9IChzdHJ1Y3QgbXVzYiAqKWRldl9pZDsKKwl1 MzIgbDFfaW50czsKKworCWwxX2ludHMgPSBtdXNiX3JlYWRsKG11c2ItPm1yZWdzLCBVU0JfTDFJ TlRTKSAmCisJCQltdXNiX3JlYWRsKG11c2ItPm1yZWdzLCBVU0JfTDFJTlRNKTsKKworCWlmIChs MV9pbnRzICYgKFRYX0lOVF9TVEFUVVMgfCBSWF9JTlRfU1RBVFVTIHwgVVNCQ09NX0lOVF9TVEFU VVMpKQorCQlyZXR2YWwgPSBnZW5lcmljX2ludGVycnVwdChpcnEsIG11c2IpOworCisjaWYgZGVm aW5lZChDT05GSUdfVVNCX0lOVkVOVFJBX0RNQSkKKwlpZiAobDFfaW50cyAmIERNQV9JTlRfU1RB VFVTKQorCQlyZXR2YWwgPSBkbWFfY29udHJvbGxlcl9pcnEoaXJxLCBtdXNiLT5kbWFfY29udHJv bGxlcik7CisjZW5kaWYKKwlyZXR1cm4gcmV0dmFsOworfQorCitzdGF0aWMgdTMyIG10a19tdXNi X2J1c2N0bF9vZmZzZXQodTggZXBudW0sIHUxNiBvZmZzZXQpCit7CisJcmV0dXJuIE1US19NVVNC X1RYRlVOQ0FERFIgKyBvZmZzZXQgKyA4ICogZXBudW07Cit9CisKK3N0YXRpYyB2b2lkIG10a19t dXNiX2NsZWFyYih2b2lkIF9faW9tZW0gKmFkZHIsIHVuc2lnbmVkIGludCBvZmZzZXQsIHU4IGRh dGEpCit7CisJLyogVzFDICovCisJbXVzYl93cml0ZWIoYWRkciwgb2Zmc2V0LCBkYXRhKTsKK30K Kworc3RhdGljIHZvaWQgbXRrX211c2JfY2xlYXJ3KHZvaWQgX19pb21lbSAqYWRkciwgdW5zaWdu ZWQgaW50IG9mZnNldCwgdTE2IGRhdGEpCit7CisJLyogVzFDICovCisJbXVzYl93cml0ZXcoYWRk ciwgb2Zmc2V0LCBkYXRhKTsKK30KKworc3RhdGljIGludCBtdGtfbXVzYl9pbml0KHN0cnVjdCBt dXNiICptdXNiKQoreworCXN0cnVjdCBkZXZpY2UgKmRldiA9IG11c2ItPmNvbnRyb2xsZXI7CisJ c3RydWN0IG10a19nbHVlICpnbHVlID0gZGV2X2dldF9kcnZkYXRhKGRldi0+cGFyZW50KTsKKwlp bnQgcmV0OworCisJZ2x1ZS0+bXVzYiA9IG11c2I7CisJbXVzYi0+cGh5ID0gZ2x1ZS0+cGh5Owor CW11c2ItPnhjZWl2ID0gZ2x1ZS0+eGNlaXY7CisJbXVzYi0+aXNfaG9zdCA9IGZhbHNlOworCW11 c2ItPmlzciA9IG10a19tdXNiX2ludGVycnVwdDsKKwlyZXQgPSBwaHlfaW5pdChnbHVlLT5waHkp OworCWlmIChyZXQpCisJCXJldHVybiByZXQ7CisKKwlyZXQgPSBwaHlfcG93ZXJfb24oZ2x1ZS0+ cGh5KTsKKwlpZiAocmV0KSB7CisJCXBoeV9leGl0KGdsdWUtPnBoeSk7CisJCXJldHVybiByZXQ7 CisJfQorCisJcGh5X3NldF9tb2RlKGdsdWUtPnBoeSwgZ2x1ZS0+cGh5X21vZGUpOworCisjaWYg ZGVmaW5lZChDT05GSUdfVVNCX0lOVkVOVFJBX0RNQSkKKwltdXNiX3dyaXRlbChtdXNiLT5tcmVn cywgTVVTQl9IU0RNQV9JTlRSLAorCQkgICAgRE1BX0lOVFJfU1RBVFVTX01TSyB8IERNQV9JTlRS X1VOTUFTS19TRVRfTVNLKTsKKyNlbmRpZgorCW11c2Jfd3JpdGVsKG11c2ItPm1yZWdzLCBVU0Jf TDFJTlRNLCBUWF9JTlRfU1RBVFVTIHwgUlhfSU5UX1NUQVRVUyB8CisJCSAgICBVU0JDT01fSU5U X1NUQVRVUyB8IERNQV9JTlRfU1RBVFVTKTsKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIHUxNiBt dGtfbXVzYl9nZXRfdG9nZ2xlKHN0cnVjdCBtdXNiX3FoICpxaCwgaW50IGlzX2luKQoreworCXN0 cnVjdCBtdXNiICptdXNiID0gcWgtPmh3X2VwLT5tdXNiOworCXU4IGVwbnVtID0gcWgtPmh3X2Vw LT5lcG51bTsKKwl1MTYgdG9nZ2xlOworCisJaWYgKGlzX2luKQorCQl0b2dnbGUgPSBtdXNiX3Jl YWR3KG11c2ItPm1yZWdzLCBNVVNCX1JYVE9HKTsKKwllbHNlCisJCXRvZ2dsZSA9IG11c2JfcmVh ZHcobXVzYi0+bXJlZ3MsIE1VU0JfVFhUT0cpOworCisJcmV0dXJuIHRvZ2dsZSAmICgxIDw8IGVw bnVtKTsKK30KKworc3RhdGljIHUxNiBtdGtfbXVzYl9zZXRfdG9nZ2xlKHN0cnVjdCBtdXNiX3Fo ICpxaCwgaW50IGlzX2luLCBzdHJ1Y3QgdXJiICp1cmIpCit7CisJc3RydWN0IG11c2IgKm11c2Ig PSBxaC0+aHdfZXAtPm11c2I7CisJdTggZXBudW0gPSBxaC0+aHdfZXAtPmVwbnVtOworCXUxNiB0 b2dnbGUgPSAwOworCisJdG9nZ2xlID0gdXNiX2dldHRvZ2dsZSh1cmItPmRldiwgcWgtPmVwbnVt LCAhaXNfaW4pOworCisJaWYgKGlzX2luKSB7CisJCW11c2Jfd3JpdGV3KG11c2ItPm1yZWdzLCBN VVNCX1JYVE9HRU4sICgxIDw8IGVwbnVtKSk7CisJCW11c2Jfd3JpdGV3KG11c2ItPm1yZWdzLCBN VVNCX1JYVE9HLCAodG9nZ2xlIDw8IGVwbnVtKSk7CisJfSBlbHNlIHsKKwkJbXVzYl93cml0ZXco bXVzYi0+bXJlZ3MsIE1VU0JfVFhUT0dFTiwgKDEgPDwgZXBudW0pKTsKKwkJbXVzYl93cml0ZXco bXVzYi0+bXJlZ3MsIE1VU0JfVFhUT0csICh0b2dnbGUgPDwgZXBudW0pKTsKKwl9CisKKwlyZXR1 cm4gMDsKK30KKworc3RhdGljIGludCBtdGtfbXVzYl9zZXRfbW9kZShzdHJ1Y3QgbXVzYiAqbXVz YiwgdTggbW9kZSkKK3sKKwlzdHJ1Y3QgZGV2aWNlICpkZXYgPSBtdXNiLT5jb250cm9sbGVyOwor CXN0cnVjdCBtdGtfZ2x1ZSAqZ2x1ZSA9IGRldl9nZXRfZHJ2ZGF0YShkZXYtPnBhcmVudCk7CisJ ZW51bSBwaHlfbW9kZSBuZXdfbW9kZTsKKworCXN3aXRjaCAobW9kZSkgeworCWNhc2UgTVVTQl9I T1NUOgorCQluZXdfbW9kZSA9IFBIWV9NT0RFX1VTQl9IT1NUOworCQltdGtfbXVzYl9zZXRfdmJ1 cyhtdXNiLCAxKTsKKwkJYnJlYWs7CisJY2FzZSBNVVNCX1BFUklQSEVSQUw6CisJCW5ld19tb2Rl ID0gUEhZX01PREVfVVNCX0RFVklDRTsKKwkJYnJlYWs7CisJY2FzZSBNVVNCX09URzoKKwkJbmV3 X21vZGUgPSBQSFlfTU9ERV9VU0JfSE9TVDsKKwkJYnJlYWs7CisJZGVmYXVsdDoKKwkJZGV2X2Vy cihtdXNiLT5jb250cm9sbGVyLT5wYXJlbnQsCisJCQkiRXJyb3IgcmVxdWVzdGVkIG1vZGUgbm90 IHN1cHBvcnRlZCBieSB0aGlzIGtlcm5lbFxuIik7CisJCXJldHVybiAtRUlOVkFMOworCX0KKwlp ZiAoZ2x1ZS0+cGh5X21vZGUgPT0gbmV3X21vZGUpCisJCXJldHVybiAwOworCisJbXRrX211c2Jf c2V0X21haWxib3goZ2x1ZSwgTVRLX0lEX0dST1VORCk7CisJcmV0dXJuIDA7Cit9CisKK3N0YXRp YyBpbnQgbXRrX211c2JfZXhpdChzdHJ1Y3QgbXVzYiAqbXVzYikKK3sKKwlzdHJ1Y3QgZGV2aWNl ICpkZXYgPSBtdXNiLT5jb250cm9sbGVyOworCXN0cnVjdCBtdGtfZ2x1ZSAqZ2x1ZSA9IGRldl9n ZXRfZHJ2ZGF0YShkZXYtPnBhcmVudCk7CisKKwlwaHlfcG93ZXJfb2ZmKGdsdWUtPnBoeSk7CisJ cGh5X2V4aXQoZ2x1ZS0+cGh5KTsKKwltdGtfbXVzYl9jbGtzX2Rpc2FibGUoZ2x1ZSk7CisKKwlw bV9ydW50aW1lX3B1dF9zeW5jKGRldik7CisJcG1fcnVudGltZV9kaXNhYmxlKGRldik7CisJcmV0 dXJuIDA7Cit9CisKK3N0YXRpYyBjb25zdCBzdHJ1Y3QgbXVzYl9wbGF0Zm9ybV9vcHMgbXRrX211 c2Jfb3BzID0geworCS5xdWlya3MgPSBNVVNCX0RNQV9JTlZFTlRSQSwKKwkuaW5pdCA9IG10a19t dXNiX2luaXQsCisJLmdldF90b2dnbGUgPSBtdGtfbXVzYl9nZXRfdG9nZ2xlLAorCS5zZXRfdG9n Z2xlID0gbXRrX211c2Jfc2V0X3RvZ2dsZSwKKwkuZXhpdCA9IG10a19tdXNiX2V4aXQsCisjaWZk ZWYgQ09ORklHX1VTQl9JTlZFTlRSQV9ETUEKKwkuZG1hX2luaXQgPSBtdXNiaHNfZG1hX2NvbnRy b2xsZXJfY3JlYXRlX25vaXJxLAorCS5kbWFfZXhpdCA9IG11c2Joc19kbWFfY29udHJvbGxlcl9k ZXN0cm95LAorI2VuZGlmCisJLmNsZWFyYiA9IG10a19tdXNiX2NsZWFyYiwKKwkuY2xlYXJ3ID0g bXRrX211c2JfY2xlYXJ3LAorCS5idXNjdGxfb2Zmc2V0ID0gbXRrX211c2JfYnVzY3RsX29mZnNl dCwKKwkuc2V0X21vZGUgPSBtdGtfbXVzYl9zZXRfbW9kZSwKKwkuc2V0X3ZidXMgPSBtdGtfbXVz Yl9zZXRfdmJ1cywKK307CisKKyNkZWZpbmUgTVRLX01VU0JfTUFYX0VQX05VTQk4CisjZGVmaW5l IE1US19NVVNCX1JBTV9CSVRTCTExCisKK3N0YXRpYyBzdHJ1Y3QgbXVzYl9maWZvX2NmZyBtdGtf bXVzYl9tb2RlX2NmZ1tdID0geworCXsgLmh3X2VwX251bSA9IDEsIC5zdHlsZSA9IEZJRk9fVFgs IC5tYXhwYWNrZXQgPSA1MTIsIH0sCisJeyAuaHdfZXBfbnVtID0gMSwgLnN0eWxlID0gRklGT19S WCwgLm1heHBhY2tldCA9IDUxMiwgfSwKKwl7IC5od19lcF9udW0gPSAyLCAuc3R5bGUgPSBGSUZP X1RYLCAubWF4cGFja2V0ID0gNTEyLCB9LAorCXsgLmh3X2VwX251bSA9IDIsIC5zdHlsZSA9IEZJ Rk9fUlgsIC5tYXhwYWNrZXQgPSA1MTIsIH0sCisJeyAuaHdfZXBfbnVtID0gMywgLnN0eWxlID0g RklGT19UWCwgLm1heHBhY2tldCA9IDUxMiwgfSwKKwl7IC5od19lcF9udW0gPSAzLCAuc3R5bGUg PSBGSUZPX1JYLCAubWF4cGFja2V0ID0gNTEyLCB9LAorCXsgLmh3X2VwX251bSA9IDQsIC5zdHls ZSA9IEZJRk9fVFgsIC5tYXhwYWNrZXQgPSA1MTIsIH0sCisJeyAuaHdfZXBfbnVtID0gNCwgLnN0 eWxlID0gRklGT19SWCwgLm1heHBhY2tldCA9IDUxMiwgfSwKKwl7IC5od19lcF9udW0gPSA1LCAu c3R5bGUgPSBGSUZPX1RYLCAubWF4cGFja2V0ID0gNTEyLCB9LAorCXsgLmh3X2VwX251bSA9IDUs IC5zdHlsZSA9IEZJRk9fUlgsIC5tYXhwYWNrZXQgPSA1MTIsIH0sCisJeyAuaHdfZXBfbnVtID0g NiwgLnN0eWxlID0gRklGT19UWCwgLm1heHBhY2tldCA9IDEwMjQsIH0sCisJeyAuaHdfZXBfbnVt ID0gNiwgLnN0eWxlID0gRklGT19SWCwgLm1heHBhY2tldCA9IDEwMjQsIH0sCisJeyAuaHdfZXBf bnVtID0gNywgLnN0eWxlID0gRklGT19UWCwgLm1heHBhY2tldCA9IDUxMiwgfSwKKwl7IC5od19l cF9udW0gPSA3LCAuc3R5bGUgPSBGSUZPX1JYLCAubWF4cGFja2V0ID0gNjQsIH0sCit9OworCitz dGF0aWMgY29uc3Qgc3RydWN0IG11c2JfaGRyY19jb25maWcgbXRrX211c2JfaGRyY19jb25maWcg PSB7CisJLmZpZm9fY2ZnID0gbXRrX211c2JfbW9kZV9jZmcsCisJLmZpZm9fY2ZnX3NpemUgPSBB UlJBWV9TSVpFKG10a19tdXNiX21vZGVfY2ZnKSwKKwkubXVsdGlwb2ludCA9IHRydWUsCisJLmR5 bl9maWZvID0gdHJ1ZSwKKwkubnVtX2VwcyA9IE1US19NVVNCX01BWF9FUF9OVU0sCisJLnJhbV9i aXRzID0gTVRLX01VU0JfUkFNX0JJVFMsCit9OworCitzdGF0aWMgY29uc3Qgc3RydWN0IHBsYXRm b3JtX2RldmljZV9pbmZvIG10a19kZXZfaW5mbyA9IHsKKwkubmFtZSA9ICJtdXNiLWhkcmMiLAor CS5pZCA9IFBMQVRGT1JNX0RFVklEX0FVVE8sCisJLmRtYV9tYXNrID0gRE1BX0JJVF9NQVNLKDMy KSwKK307CisKK3N0YXRpYyBpbnQgbXRrX211c2JfcHJvYmUoc3RydWN0IHBsYXRmb3JtX2Rldmlj ZSAqcGRldikKK3sKKwlzdHJ1Y3QgbXVzYl9oZHJjX3BsYXRmb3JtX2RhdGEgKnBkYXRhOworCXN0 cnVjdCBtdGtfZ2x1ZSAqZ2x1ZTsKKwlzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlX2luZm8gcGluZm87 CisJc3RydWN0IGRldmljZSAqZGV2ID0gJnBkZXYtPmRldjsKKwlzdHJ1Y3QgZGV2aWNlX25vZGUg Km5wID0gZGV2LT5vZl9ub2RlOworCWludCByZXQgPSAtRU5PTUVNOworCisJZ2x1ZSA9IGRldm1f a3phbGxvYyhkZXYsIHNpemVvZigqZ2x1ZSksIEdGUF9LRVJORUwpOworCWlmICghZ2x1ZSkKKwkJ cmV0dXJuIC1FTk9NRU07CisKKwlnbHVlLT5kZXYgPSBkZXY7CisJcGRhdGEgPSBkZXZtX2t6YWxs b2MoZGV2LCBzaXplb2YoKnBkYXRhKSwgR0ZQX0tFUk5FTCk7CisJaWYgKCFwZGF0YSkKKwkJcmV0 dXJuIC1FTk9NRU07CisKKwlyZXQgPSBtdGtfbXVzYl9jbGtzX2dldChnbHVlKTsKKwlpZiAocmV0 KQorCQlyZXR1cm4gcmV0OworCisJZ2x1ZS0+dmJ1cyA9IGRldm1fcmVndWxhdG9yX2dldChkZXYs ICJ2YnVzIik7CisJaWYgKElTX0VSUihnbHVlLT52YnVzKSkgeworCQlkZXZfZXJyKGRldiwgImZh aWwgdG8gZ2V0IHZidXNcbiIpOworCQlyZXR1cm4gUFRSX0VSUihnbHVlLT52YnVzKTsKKwl9CisK KwlwZGF0YS0+Y29uZmlnID0gJm10a19tdXNiX2hkcmNfY29uZmlnOworCXBkYXRhLT5wbGF0Zm9y bV9vcHMgPSAmbXRrX211c2Jfb3BzOworCWlmIChvZl9wcm9wZXJ0eV9yZWFkX2Jvb2wobnAsICJl eHRjb24iKSkgeworCQlnbHVlLT5lZGV2ID0gZXh0Y29uX2dldF9lZGV2X2J5X3BoYW5kbGUoZGV2 LCAwKTsKKwkJaWYgKElTX0VSUihnbHVlLT5lZGV2KSkgeworCQkJZGV2X2VycihkZXYsICJmYWls IHRvIGdldCBleHRjb25cbiIpOworCQkJcmV0dXJuIFBUUl9FUlIoZ2x1ZS0+ZWRldik7CisJCX0K Kwl9CisKKwlwZGF0YS0+bW9kZSA9IHVzYl9nZXRfZHJfbW9kZShkZXYpOworCXN3aXRjaCAocGRh dGEtPm1vZGUpIHsKKwljYXNlIFVTQl9EUl9NT0RFX0hPU1Q6CisJCWdsdWUtPnBoeV9tb2RlID0g UEhZX01PREVfVVNCX0hPU1Q7CisJCWJyZWFrOworCWNhc2UgVVNCX0RSX01PREVfUEVSSVBIRVJB TDoKKwkJZ2x1ZS0+cGh5X21vZGUgPSBQSFlfTU9ERV9VU0JfREVWSUNFOworCQlicmVhazsKKwlk ZWZhdWx0OgorCQlwZGF0YS0+bW9kZSA9IFVTQl9EUl9NT0RFX09URzsKKwkJLyogRkFMTCBUSFJP VUdIICovCisJY2FzZSBVU0JfRFJfTU9ERV9PVEc6CisJCWdsdWUtPnBoeV9tb2RlID0gUEhZX01P REVfVVNCX09URzsKKwkJYnJlYWs7CisJfQorCisJZ2x1ZS0+cGh5ID0gZGV2bV9vZl9waHlfZ2V0 X2J5X2luZGV4KGRldiwgbnAsIDApOworCWlmIChJU19FUlIoZ2x1ZS0+cGh5KSkgeworCQlkZXZf ZXJyKGRldiwgImZhaWwgdG8gZ2V0dGluZyBwaHkgJWxkXG4iLAorCQkJUFRSX0VSUihnbHVlLT5w aHkpKTsKKwkJcmV0dXJuIFBUUl9FUlIoZ2x1ZS0+cGh5KTsKKwl9CisKKwlnbHVlLT51c2JfcGh5 ID0gdXNiX3BoeV9nZW5lcmljX3JlZ2lzdGVyKCk7CisJaWYgKElTX0VSUihnbHVlLT51c2JfcGh5 KSkgeworCQlkZXZfZXJyKGRldiwgImZhaWwgdG8gcmVnaXN0ZXJpbmcgdXNiLXBoeSAlbGRcbiIs CisJCQlQVFJfRVJSKGdsdWUtPnVzYl9waHkpKTsKKwkJcmV0dXJuIFBUUl9FUlIoZ2x1ZS0+dXNi X3BoeSk7CisJfQorCisJZ2x1ZS0+eGNlaXYgPSBkZXZtX3VzYl9nZXRfcGh5KGRldiwgVVNCX1BI WV9UWVBFX1VTQjIpOworCWlmIChJU19FUlIoZ2x1ZS0+eGNlaXYpKSB7CisJCWRldl9lcnIoZGV2 LCAiZmFpbCB0byBnZXR0aW5nIHVzYi1waHkgJWRcbiIsIHJldCk7CisJCXJldCA9IFBUUl9FUlIo Z2x1ZS0+eGNlaXYpOworCQlnb3RvIGVycl91bnJlZ2lzdGVyX3VzYl9waHk7CisJfQorCisJcGxh dGZvcm1fc2V0X2RydmRhdGEocGRldiwgZ2x1ZSk7CisJcG1fcnVudGltZV9lbmFibGUoZGV2KTsK KwlwbV9ydW50aW1lX2dldF9zeW5jKGRldik7CisKKwlyZXQgPSBtdGtfbXVzYl9jbGtzX2VuYWJs ZShnbHVlKTsKKwlpZiAocmV0KQorCQlnb3RvIGVycl9lbmFibGVfY2xrOworCisJcGluZm8gPSBt dGtfZGV2X2luZm87CisJcGluZm8ucGFyZW50ID0gZGV2OworCXBpbmZvLnJlcyA9IHBkZXYtPnJl c291cmNlOworCXBpbmZvLm51bV9yZXMgPSBwZGV2LT5udW1fcmVzb3VyY2VzOworCXBpbmZvLmRh dGEgPSBwZGF0YTsKKwlwaW5mby5zaXplX2RhdGEgPSBzaXplb2YoKnBkYXRhKTsKKworCWdsdWUt Pm11c2JfcGRldiA9IHBsYXRmb3JtX2RldmljZV9yZWdpc3Rlcl9mdWxsKCZwaW5mbyk7CisJaWYg KElTX0VSUihnbHVlLT5tdXNiX3BkZXYpKSB7CisJCXJldCA9IFBUUl9FUlIoZ2x1ZS0+bXVzYl9w ZGV2KTsKKwkJZGV2X2VycihkZXYsICJmYWlsZWQgdG8gcmVnaXN0ZXIgbXVzYiBkZXZpY2U6ICVk XG4iLCByZXQpOworCQlnb3RvIGVycl9kZXZpY2VfcmVnaXN0ZXI7CisJfQorCisJaWYgKHBkYXRh LT5tb2RlID09IFVTQl9EUl9NT0RFX09URykKKwkJbXRrX290Z19zd2l0Y2hfaW5pdChnbHVlKTsK KworCWRldl9pbmZvKGRldiwgIlVTQiBwcm9iZSBkb25lIVxuIik7CisJcmV0dXJuIDA7CisKK2Vy cl9kZXZpY2VfcmVnaXN0ZXI6CisJbXRrX211c2JfY2xrc19kaXNhYmxlKGdsdWUpOworZXJyX2Vu YWJsZV9jbGs6CisJcG1fcnVudGltZV9wdXRfc3luYyhkZXYpOworCXBtX3J1bnRpbWVfZGlzYWJs ZShkZXYpOworZXJyX3VucmVnaXN0ZXJfdXNiX3BoeToKKwl1c2JfcGh5X2dlbmVyaWNfdW5yZWdp c3RlcihnbHVlLT51c2JfcGh5KTsKKwlyZXR1cm4gcmV0OworfQorCitzdGF0aWMgaW50IG10a19t dXNiX3JlbW92ZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KQoreworCXN0cnVjdCBtdGtf Z2x1ZSAqZ2x1ZSA9IHBsYXRmb3JtX2dldF9kcnZkYXRhKHBkZXYpOworCXN0cnVjdCBwbGF0Zm9y bV9kZXZpY2UgKnVzYl9waHkgPSBnbHVlLT51c2JfcGh5OworCisJcGxhdGZvcm1fZGV2aWNlX3Vu cmVnaXN0ZXIoZ2x1ZS0+bXVzYl9wZGV2KTsKKwl1c2JfcGh5X2dlbmVyaWNfdW5yZWdpc3Rlcih1 c2JfcGh5KTsKKworCXJldHVybiAwOworfQorCisjaWZkZWYgQ09ORklHX09GCitzdGF0aWMgY29u c3Qgc3RydWN0IG9mX2RldmljZV9pZCBtdGtfbXVzYl9tYXRjaFtdID0geworCXsuY29tcGF0aWJs ZSA9ICJtZWRpYXRlayxtdGstbXVzYiIsfSwKKwl7fSwKK307CitNT0RVTEVfREVWSUNFX1RBQkxF KG9mLCBtdGtfbXVzYl9tYXRjaCk7CisjZW5kaWYKKworc3RhdGljIHN0cnVjdCBwbGF0Zm9ybV9k cml2ZXIgbXRrX211c2JfZHJpdmVyID0geworCS5wcm9iZSA9IG10a19tdXNiX3Byb2JlLAorCS5y ZW1vdmUgPSBtdGtfbXVzYl9yZW1vdmUsCisJLmRyaXZlciA9IHsKKwkJICAgLm5hbWUgPSAibXVz Yi1tdGsiLAorCQkgICAub2ZfbWF0Y2hfdGFibGUgPSBvZl9tYXRjaF9wdHIobXRrX211c2JfbWF0 Y2gpLAorCX0sCit9OworCittb2R1bGVfcGxhdGZvcm1fZHJpdmVyKG10a19tdXNiX2RyaXZlcik7 CisKK01PRFVMRV9ERVNDUklQVElPTigiTWVkaWFUZWsgTVVTQiBHbHVlIExheWVyIik7CitNT0RV TEVfQVVUSE9SKCJNaW4gR3VvIDxtaW4uZ3VvQG1lZGlhdGVrLmNvbT4iKTsKK01PRFVMRV9MSUNF TlNFKCJHUEwgdjIiKTsKZGlmZiAtLWdpdCBhL2RyaXZlcnMvdXNiL211c2IvbXVzYl9jb3JlLmMg Yi9kcml2ZXJzL3VzYi9tdXNiL211c2JfY29yZS5jCmluZGV4IGI3ZDU2MjcuLjJjMGQxMDIgMTAw NjQ0Ci0tLSBhL2RyaXZlcnMvdXNiL211c2IvbXVzYl9jb3JlLmMKKysrIGIvZHJpdmVycy91c2Iv bXVzYi9tdXNiX2NvcmUuYwpAQCAtMjYwLDYgKzI2MCwxMSBAQCBzdGF0aWMgdm9pZCBtdXNiX2Rl ZmF1bHRfd3JpdGViKHZvaWQgX19pb21lbSAqYWRkciwgdW5zaWduZWQgb2Zmc2V0LCB1OCBkYXRh KQogCV9fcmF3X3dyaXRlYihkYXRhLCBhZGRyICsgb2Zmc2V0KTsKIH0KIAorc3RhdGljIHZvaWQK K211c2JfZGVmYXVsdF9jbGVhcmIodm9pZCBfX2lvbWVtICphZGRyLCB1bnNpZ25lZCBpbnQgb2Zm c2V0LCB1OCBkYXRhKQoreworfQorCiBzdGF0aWMgdTE2IG11c2JfZGVmYXVsdF9yZWFkdyhjb25z dCB2b2lkIF9faW9tZW0gKmFkZHIsIHVuc2lnbmVkIG9mZnNldCkKIHsKIAl1MTYgZGF0YSA9IF9f cmF3X3JlYWR3KGFkZHIgKyBvZmZzZXQpOwpAQCAtMjc0LDYgKzI3OSw0MyBAQCBzdGF0aWMgdm9p ZCBtdXNiX2RlZmF1bHRfd3JpdGV3KHZvaWQgX19pb21lbSAqYWRkciwgdW5zaWduZWQgb2Zmc2V0 LCB1MTYgZGF0YSkKIAlfX3Jhd193cml0ZXcoZGF0YSwgYWRkciArIG9mZnNldCk7CiB9CiAKK3N0 YXRpYyB2b2lkCittdXNiX2RlZmF1bHRfY2xlYXJ3KHZvaWQgX19pb21lbSAqYWRkciwgdW5zaWdu ZWQgaW50IG9mZnNldCwgdTE2IGRhdGEpCit7Cit9CisKK3N0YXRpYyB1MTYgbXVzYl9kZWZhdWx0 X2dldF90b2dnbGUoc3RydWN0IG11c2JfcWggKnFoLCBpbnQgaXNfaW4pCit7CisJdm9pZCBfX2lv bWVtICplcGlvID0gcWgtPmh3X2VwLT5yZWdzOworCXUxNiBjc3I7CisKKwlpZiAoaXNfaW4pCisJ CWNzciA9IG11c2JfcmVhZHcoZXBpbywgTVVTQl9SWENTUikgJiBNVVNCX1JYQ1NSX0hfREFUQVRP R0dMRTsKKwllbHNlCisJCWNzciA9IG11c2JfcmVhZHcoZXBpbywgTVVTQl9UWENTUikgJiBNVVNC X1RYQ1NSX0hfREFUQVRPR0dMRTsKKworCXJldHVybiBjc3I7Cit9CisKK3N0YXRpYyB1MTYgbXVz Yl9kZWZhdWx0X3NldF90b2dnbGUoc3RydWN0IG11c2JfcWggKnFoLCBpbnQgaXNfaW4sCisJc3Ry dWN0IHVyYiAqdXJiKQoreworCXUxNiBjc3IgPSAwOworCXUxNiB0b2dnbGUgPSAwOworCisJdG9n Z2xlID0gdXNiX2dldHRvZ2dsZSh1cmItPmRldiwgcWgtPmVwbnVtLCAhaXNfaW4pOworCisJaWYg KGlzX2luKQorCQljc3IgPSB0b2dnbGUgPyAoTVVTQl9SWENTUl9IX1dSX0RBVEFUT0dHTEUKKwkJ CQl8IE1VU0JfUlhDU1JfSF9EQVRBVE9HR0xFKSA6IDA7CisJZWxzZQorCQljc3IgfD0gdG9nZ2xl ID8gKE1VU0JfVFhDU1JfSF9XUl9EQVRBVE9HR0xFCisJCQkJfCBNVVNCX1RYQ1NSX0hfREFUQVRP R0dMRSkKKwkJCQk6IE1VU0JfVFhDU1JfQ0xSREFUQVRPRzsKKworCXJldHVybiBjc3I7Cit9CisK IC8qCiAgKiBMb2FkIGFuIGVuZHBvaW50J3MgRklGTwogICovCkBAIC0zNzAsMTIgKzQxMiwxOCBA QCBzdGF0aWMgdm9pZCBtdXNiX2RlZmF1bHRfcmVhZF9maWZvKHN0cnVjdCBtdXNiX2h3X2VwICpo d19lcCwgdTE2IGxlbiwgdTggKmRzdCkKIHZvaWQgKCptdXNiX3dyaXRlYikodm9pZCBfX2lvbWVt ICphZGRyLCB1bnNpZ25lZCBvZmZzZXQsIHU4IGRhdGEpOwogRVhQT1JUX1NZTUJPTF9HUEwobXVz Yl93cml0ZWIpOwogCit2b2lkICgqbXVzYl9jbGVhcmIpKHZvaWQgX19pb21lbSAqYWRkciwgdW5z aWduZWQgaW50IG9mZnNldCwgdTggZGF0YSk7CitFWFBPUlRfU1lNQk9MX0dQTChtdXNiX2NsZWFy Yik7CisKIHUxNiAoKm11c2JfcmVhZHcpKGNvbnN0IHZvaWQgX19pb21lbSAqYWRkciwgdW5zaWdu ZWQgb2Zmc2V0KTsKIEVYUE9SVF9TWU1CT0xfR1BMKG11c2JfcmVhZHcpOwogCiB2b2lkICgqbXVz Yl93cml0ZXcpKHZvaWQgX19pb21lbSAqYWRkciwgdW5zaWduZWQgb2Zmc2V0LCB1MTYgZGF0YSk7 CiBFWFBPUlRfU1lNQk9MX0dQTChtdXNiX3dyaXRldyk7CiAKK3ZvaWQgKCptdXNiX2NsZWFydyko dm9pZCBfX2lvbWVtICphZGRyLCB1bnNpZ25lZCBpbnQgb2Zmc2V0LCB1MTYgZGF0YSk7CitFWFBP UlRfU1lNQk9MX0dQTChtdXNiX2NsZWFydyk7CisKIHUzMiBtdXNiX3JlYWRsKGNvbnN0IHZvaWQg X19pb21lbSAqYWRkciwgdW5zaWduZWQgb2Zmc2V0KQogewogCXUzMiBkYXRhID0gX19yYXdfcmVh ZGwoYWRkciArIG9mZnNldCk7CkBAIC0xMDI4LDYgKzEwNzYsMTEgQEAgc3RhdGljIHZvaWQgbXVz Yl9kaXNhYmxlX2ludGVycnVwdHMoc3RydWN0IG11c2IgKm11c2IpCiAJdGVtcCA9IG11c2JfcmVh ZGIobWJhc2UsIE1VU0JfSU5UUlVTQik7CiAJdGVtcCA9IG11c2JfcmVhZHcobWJhc2UsIE1VU0Jf SU5UUlRYKTsKIAl0ZW1wID0gbXVzYl9yZWFkdyhtYmFzZSwgTVVTQl9JTlRSUlgpOworCisJLyog c29tZSBwbGF0Zm9ybSBuZWVkcyBjbGVhciBwZW5kaW5nIGludGVycnVwdHMgYnkgbWFudWFsICov CisJbXVzYl9jbGVhcmIobWJhc2UsIE1VU0JfSU5UUlVTQiwgbXVzYl9yZWFkYihtYmFzZSwgTVVT Ql9JTlRSVVNCKSk7CisJbXVzYl9jbGVhcncobWJhc2UsIE1VU0JfSU5UUlJYLCBtdXNiX3JlYWR3 KG1iYXNlLCBNVVNCX0lOVFJSWCkpOworCW11c2JfY2xlYXJ3KG1iYXNlLCBNVVNCX0lOVFJUWCwg bXVzYl9yZWFkdyhtYmFzZSwgTVVTQl9JTlRSVFgpKTsKIH0KIAogc3RhdGljIHZvaWQgbXVzYl9l bmFibGVfaW50ZXJydXB0cyhzdHJ1Y3QgbXVzYiAqbXVzYikKQEAgLTIxOTIsNiArMjI0NSw4IEBA IHN0YXRpYyB2b2lkIG11c2JfZGVhc3NlcnRfcmVzZXQoc3RydWN0IHdvcmtfc3RydWN0ICp3b3Jr KQogCW11c2Jfd3JpdGViID0gbXVzYl9kZWZhdWx0X3dyaXRlYjsKIAltdXNiX3JlYWR3ID0gbXVz Yl9kZWZhdWx0X3JlYWR3OwogCW11c2Jfd3JpdGV3ID0gbXVzYl9kZWZhdWx0X3dyaXRldzsKKwlt dXNiX2NsZWFyYiA9IG11c2JfZGVmYXVsdF9jbGVhcmI7CisJbXVzYl9jbGVhcncgPSBtdXNiX2Rl ZmF1bHRfY2xlYXJ3OwogCiAJLyogVGhlIG11c2JfcGxhdGZvcm1faW5pdCgpIGNhbGw6CiAJICog ICAtIGFkanVzdHMgbXVzYi0+bXJlZ3MKQEAgLTIyNTIsMTAgKzIzMDcsMTQgQEAgc3RhdGljIHZv aWQgbXVzYl9kZWFzc2VydF9yZXNldChzdHJ1Y3Qgd29ya19zdHJ1Y3QgKndvcmspCiAJCW11c2Jf cmVhZGIgPSBtdXNiLT5vcHMtPnJlYWRiOwogCWlmIChtdXNiLT5vcHMtPndyaXRlYikKIAkJbXVz Yl93cml0ZWIgPSBtdXNiLT5vcHMtPndyaXRlYjsKKwlpZiAobXVzYi0+b3BzLT5jbGVhcmIpCisJ CW11c2JfY2xlYXJiID0gbXVzYi0+b3BzLT5jbGVhcmI7CiAJaWYgKG11c2ItPm9wcy0+cmVhZHcp CiAJCW11c2JfcmVhZHcgPSBtdXNiLT5vcHMtPnJlYWR3OwogCWlmIChtdXNiLT5vcHMtPndyaXRl dykKIAkJbXVzYl93cml0ZXcgPSBtdXNiLT5vcHMtPndyaXRldzsKKwlpZiAobXVzYi0+b3BzLT5j bGVhcncpCisJCW11c2JfY2xlYXJ3ID0gbXVzYi0+b3BzLT5jbGVhcnc7CiAKICNpZm5kZWYgQ09O RklHX01VU0JfUElPX09OTFkKIAlpZiAoIW11c2ItPm9wcy0+ZG1hX2luaXQgfHwgIW11c2ItPm9w cy0+ZG1hX2V4aXQpIHsKQEAgLTIyNzcsNiArMjMzNiwxNiBAQCBzdGF0aWMgdm9pZCBtdXNiX2Rl YXNzZXJ0X3Jlc2V0KHN0cnVjdCB3b3JrX3N0cnVjdCAqd29yaykKIAllbHNlCiAJCW11c2ItPmlv LndyaXRlX2ZpZm8gPSBtdXNiX2RlZmF1bHRfd3JpdGVfZmlmbzsKIAorCWlmIChtdXNiLT5vcHMt PmdldF90b2dnbGUpCisJCW11c2ItPmlvLmdldF90b2dnbGUgPSBtdXNiLT5vcHMtPmdldF90b2dn bGU7CisJZWxzZQorCQltdXNiLT5pby5nZXRfdG9nZ2xlID0gbXVzYl9kZWZhdWx0X2dldF90b2dn bGU7CisKKwlpZiAobXVzYi0+b3BzLT5zZXRfdG9nZ2xlKQorCQltdXNiLT5pby5zZXRfdG9nZ2xl ID0gbXVzYi0+b3BzLT5zZXRfdG9nZ2xlOworCWVsc2UKKwkJbXVzYi0+aW8uc2V0X3RvZ2dsZSA9 IG11c2JfZGVmYXVsdF9zZXRfdG9nZ2xlOworCiAJaWYgKCFtdXNiLT54Y2Vpdi0+aW9fb3BzKSB7 CiAJCW11c2ItPnhjZWl2LT5pb19kZXYgPSBtdXNiLT5jb250cm9sbGVyOwogCQltdXNiLT54Y2Vp di0+aW9fcHJpdiA9IG11c2ItPm1yZWdzOwpkaWZmIC0tZ2l0IGEvZHJpdmVycy91c2IvbXVzYi9t dXNiX2NvcmUuaCBiL2RyaXZlcnMvdXNiL211c2IvbXVzYl9jb3JlLmgKaW5kZXggMDQyMDNiNy4u NzFkY2E4MCAxMDA2NDQKLS0tIGEvZHJpdmVycy91c2IvbXVzYi9tdXNiX2NvcmUuaAorKysgYi9k cml2ZXJzL3VzYi9tdXNiL211c2JfY29yZS5oCkBAIC0yNyw2ICsyNyw3IEBACiBzdHJ1Y3QgbXVz YjsKIHN0cnVjdCBtdXNiX2h3X2VwOwogc3RydWN0IG11c2JfZXA7CitzdHJ1Y3QgbXVzYl9xaDsK IAogLyogSGVscGVyIGRlZmluZXMgZm9yIHN0cnVjdCBtdXNiLT5od3ZlcnMgKi8KICNkZWZpbmUg TVVTQl9IV1ZFUlNfTUFKT1IoeCkJKCh4ID4+IDEwKSAmIDB4MWYpCkBAIC0xMTksMTAgKzEyMCwx NCBAQCBlbnVtIG11c2JfZ19lcDBfc3RhdGUgewogICogQGZpZm9fb2Zmc2V0OiByZXR1cm5zIHRo ZSBmaWZvIG9mZnNldAogICogQHJlYWRiOglyZWFkIDggYml0cwogICogQHdyaXRlYjoJd3JpdGUg OCBiaXRzCisgKiBAY2xlYXJiOgljbGVhciA4IGJpdHMKICAqIEByZWFkdzoJcmVhZCAxNiBiaXRz CiAgKiBAd3JpdGV3Ogl3cml0ZSAxNiBiaXRzCisgKiBAY2xlYXJ3OgljbGVhciAxNiBiaXRzCiAg KiBAcmVhZF9maWZvOglyZWFkcyB0aGUgZmlmbwogICogQHdyaXRlX2ZpZm86CXdyaXRlcyB0byBm aWZvCisgKiBAZ2V0X3RvZ2dsZToJcGxhdGZvcm0gc3BlY2lmaWMgZ2V0IHRvZ2dsZSBmdW5jdGlv bgorICogQHNldF90b2dnbGU6CXBsYXRmb3JtIHNwZWNpZmljIHNldCB0b2dnbGUgZnVuY3Rpb24K ICAqIEBkbWFfaW5pdDoJcGxhdGZvcm0gc3BlY2lmaWMgZG1hIGluaXQgZnVuY3Rpb24KICAqIEBk bWFfZXhpdDoJcGxhdGZvcm0gc3BlY2lmaWMgZG1hIGV4aXQgZnVuY3Rpb24KICAqIEBpbml0Ogl0 dXJucyBvbiBjbG9ja3MsIHNldHMgdXAgcGxhdGZvcm0tc3BlY2lmaWMgcmVnaXN0ZXJzLCBldGMK QEAgLTE2MywxMCArMTY4LDE0IEBAIHN0cnVjdCBtdXNiX3BsYXRmb3JtX29wcyB7CiAJdTMyCSgq YnVzY3RsX29mZnNldCkodTggZXBudW0sIHUxNiBvZmZzZXQpOwogCXU4CSgqcmVhZGIpKGNvbnN0 IHZvaWQgX19pb21lbSAqYWRkciwgdW5zaWduZWQgb2Zmc2V0KTsKIAl2b2lkCSgqd3JpdGViKSh2 b2lkIF9faW9tZW0gKmFkZHIsIHVuc2lnbmVkIG9mZnNldCwgdTggZGF0YSk7CisJdm9pZAkoKmNs ZWFyYikodm9pZCBfX2lvbWVtICphZGRyLCB1bnNpZ25lZCBpbnQgb2Zmc2V0LCB1OCBkYXRhKTsK IAl1MTYJKCpyZWFkdykoY29uc3Qgdm9pZCBfX2lvbWVtICphZGRyLCB1bnNpZ25lZCBvZmZzZXQp OwogCXZvaWQJKCp3cml0ZXcpKHZvaWQgX19pb21lbSAqYWRkciwgdW5zaWduZWQgb2Zmc2V0LCB1 MTYgZGF0YSk7CisJdm9pZAkoKmNsZWFydykodm9pZCBfX2lvbWVtICphZGRyLCB1bnNpZ25lZCBp bnQgb2Zmc2V0LCB1MTYgZGF0YSk7CiAJdm9pZAkoKnJlYWRfZmlmbykoc3RydWN0IG11c2JfaHdf ZXAgKmh3X2VwLCB1MTYgbGVuLCB1OCAqYnVmKTsKIAl2b2lkCSgqd3JpdGVfZmlmbykoc3RydWN0 IG11c2JfaHdfZXAgKmh3X2VwLCB1MTYgbGVuLCBjb25zdCB1OCAqYnVmKTsKKwl1MTYJKCpnZXRf dG9nZ2xlKShzdHJ1Y3QgbXVzYl9xaCAqcWgsIGludCBpc19pbik7CisJdTE2CSgqc2V0X3RvZ2ds ZSkoc3RydWN0IG11c2JfcWggKnFoLCBpbnQgaXNfaW4sIHN0cnVjdCB1cmIgKnVyYik7CiAJc3Ry dWN0IGRtYV9jb250cm9sbGVyICoKIAkJKCpkbWFfaW5pdCkgKHN0cnVjdCBtdXNiICptdXNiLCB2 b2lkIF9faW9tZW0gKmJhc2UpOwogCXZvaWQJKCpkbWFfZXhpdCkoc3RydWN0IGRtYV9jb250cm9s bGVyICpjKTsKZGlmZiAtLWdpdCBhL2RyaXZlcnMvdXNiL211c2IvbXVzYl9kbWEuaCBiL2RyaXZl cnMvdXNiL211c2IvbXVzYl9kbWEuaAppbmRleCA4ZjYwMjcxLi4wNTEwM2VhIDEwMDY0NAotLS0g YS9kcml2ZXJzL3VzYi9tdXNiL211c2JfZG1hLmgKKysrIGIvZHJpdmVycy91c2IvbXVzYi9tdXNi X2RtYS5oCkBAIC0zNSw2ICszNSwxMiBAQAogICogICAgd2hldGhlciBzaGFyZWQgd2l0aCB0aGUg SW52ZW50cmEgY29yZSBvciBzZXBhcmF0ZS4KICAqLwogCisjZGVmaW5lIE1VU0JfSFNETUFfQkFT RQkJMHgyMDAKKyNkZWZpbmUgTVVTQl9IU0RNQV9JTlRSCQkoTVVTQl9IU0RNQV9CQVNFICsgMCkK KyNkZWZpbmUgTVVTQl9IU0RNQV9DT05UUk9MCQkweDQKKyNkZWZpbmUgTVVTQl9IU0RNQV9BRERS RVNTCQkweDgKKyNkZWZpbmUgTVVTQl9IU0RNQV9DT1VOVAkJMHhjCisKICNkZWZpbmUJRE1BX0FE RFJfSU5WQUxJRAkofihkbWFfYWRkcl90KTApCiAKICNpZmRlZiBDT05GSUdfTVVTQl9QSU9fT05M WQpAQCAtMTkxLDYgKzE5Nyw5IEBAIHN0YXRpYyBpbmxpbmUgdm9pZCBtdXNiX2RtYV9jb250cm9s bGVyX2Rlc3Ryb3koc3RydWN0IGRtYV9jb250cm9sbGVyICpkKSB7IH0KIGV4dGVybiBzdHJ1Y3Qg ZG1hX2NvbnRyb2xsZXIgKgogbXVzYmhzX2RtYV9jb250cm9sbGVyX2NyZWF0ZShzdHJ1Y3QgbXVz YiAqbXVzYiwgdm9pZCBfX2lvbWVtICpiYXNlKTsKIGV4dGVybiB2b2lkIG11c2Joc19kbWFfY29u dHJvbGxlcl9kZXN0cm95KHN0cnVjdCBkbWFfY29udHJvbGxlciAqYyk7CitleHRlcm4gc3RydWN0 IGRtYV9jb250cm9sbGVyICoKK211c2Joc19kbWFfY29udHJvbGxlcl9jcmVhdGVfbm9pcnEoc3Ry dWN0IG11c2IgKm11c2IsIHZvaWQgX19pb21lbSAqYmFzZSk7CitleHRlcm4gaXJxcmV0dXJuX3Qg ZG1hX2NvbnRyb2xsZXJfaXJxKGludCBpcnEsIHZvaWQgKnByaXZhdGVfZGF0YSk7CiAKIGV4dGVy biBzdHJ1Y3QgZG1hX2NvbnRyb2xsZXIgKgogdHVzYl9kbWFfY29udHJvbGxlcl9jcmVhdGUoc3Ry dWN0IG11c2IgKm11c2IsIHZvaWQgX19pb21lbSAqYmFzZSk7CmRpZmYgLS1naXQgYS9kcml2ZXJz L3VzYi9tdXNiL211c2JfaG9zdC5jIGIvZHJpdmVycy91c2IvbXVzYi9tdXNiX2hvc3QuYwppbmRl eCAxNmQwYmE0Li5iYTY2ZjQ0IDEwMDY0NAotLS0gYS9kcml2ZXJzL3VzYi9tdXNiL211c2JfaG9z dC5jCisrKyBiL2RyaXZlcnMvdXNiL211c2IvbXVzYl9ob3N0LmMKQEAgLTI5MCwzOSArMjkwLDIz IEBAIHN0YXRpYyB2b2lkIG11c2JfZ2l2ZWJhY2soc3RydWN0IG11c2IgKm11c2IsIHN0cnVjdCB1 cmIgKnVyYiwgaW50IHN0YXR1cykKIHN0YXRpYyBpbmxpbmUgdm9pZCBtdXNiX3NhdmVfdG9nZ2xl KHN0cnVjdCBtdXNiX3FoICpxaCwgaW50IGlzX2luLAogCQkJCSAgICBzdHJ1Y3QgdXJiICp1cmIp CiB7Ci0Jdm9pZCBfX2lvbWVtCQkqZXBpbyA9IHFoLT5od19lcC0+cmVnczsKLQl1MTYJCQljc3I7 CisJc3RydWN0IG11c2IgKm11c2IgPSBxaC0+aHdfZXAtPm11c2I7CisJdTE2IGNzcjsKIAogCS8q CiAJICogRklYTUU6IHRoZSBjdXJyZW50IE1lbnRvciBETUEgY29kZSBzZWVtcyB0byBoYXZlCiAJ ICogcHJvYmxlbXMgZ2V0dGluZyB0b2dnbGUgY29ycmVjdC4KIAkgKi8KLQotCWlmIChpc19pbikK LQkJY3NyID0gbXVzYl9yZWFkdyhlcGlvLCBNVVNCX1JYQ1NSKSAmIE1VU0JfUlhDU1JfSF9EQVRB VE9HR0xFOwotCWVsc2UKLQkJY3NyID0gbXVzYl9yZWFkdyhlcGlvLCBNVVNCX1RYQ1NSKSAmIE1V U0JfVFhDU1JfSF9EQVRBVE9HR0xFOwotCisJY3NyID0gbXVzYi0+aW8uZ2V0X3RvZ2dsZShxaCwg aXNfaW4pOwogCXVzYl9zZXR0b2dnbGUodXJiLT5kZXYsIHFoLT5lcG51bSwgIWlzX2luLCBjc3Ig PyAxIDogMCk7CiB9CiAKIHN0YXRpYyBpbmxpbmUgdTE2IG11c2Jfc2V0X3RvZ2dsZShzdHJ1Y3Qg bXVzYl9xaCAqcWgsIGludCBpc19pbiwKIAlzdHJ1Y3QgdXJiICp1cmIpCiB7Ci0JdTE2IGNzciA9 IDA7Ci0JdTE2IHRvZ2dsZSA9IDA7Ci0KLQl0b2dnbGUgPSB1c2JfZ2V0dG9nZ2xlKHVyYi0+ZGV2 LCBxaC0+ZXBudW0sICFpc19pbik7Ci0KLQlpZiAoaXNfaW4pCi0JCWNzciA9IHRvZ2dsZSA/IChN VVNCX1JYQ1NSX0hfV1JfREFUQVRPR0dMRQotCQkJCXwgTVVTQl9SWENTUl9IX0RBVEFUT0dHTEUp IDogMDsKLQllbHNlCi0JCWNzciA9IHRvZ2dsZSA/IChNVVNCX1RYQ1NSX0hfV1JfREFUQVRPR0dM RQotCQkJCXwgTVVTQl9UWENTUl9IX0RBVEFUT0dHTEUpCi0JCQkJOiBNVVNCX1RYQ1NSX0NMUkRB VEFUT0c7CisJc3RydWN0IG11c2IgKm11c2IgPSBxaC0+aHdfZXAtPm11c2I7CiAKLQlyZXR1cm4g Y3NyOworCXJldHVybiBtdXNiLT5pby5zZXRfdG9nZ2xlKHFoLCBpc19pbiwgdXJiKTsKIH0KIAog LyoKZGlmZiAtLWdpdCBhL2RyaXZlcnMvdXNiL211c2IvbXVzYl9pby5oIGIvZHJpdmVycy91c2Iv bXVzYi9tdXNiX2lvLmgKaW5kZXggODA1OGE1OC4uOWJhZTA5YiAxMDA2NDQKLS0tIGEvZHJpdmVy cy91c2IvbXVzYi9tdXNiX2lvLmgKKysrIGIvZHJpdmVycy91c2IvbXVzYi9tdXNiX2lvLmgKQEAg LTIyLDYgKzIyLDggQEAKICAqIEByZWFkX2ZpZm86CXBsYXRmb3JtIHNwZWNpZmljIGZ1bmN0aW9u IHRvIHJlYWQgZmlmbwogICogQHdyaXRlX2ZpZm86CXBsYXRmb3JtIHNwZWNpZmljIGZ1bmN0aW9u IHRvIHdyaXRlIGZpZm8KICAqIEBidXNjdGxfb2Zmc2V0OiBwbGF0Zm9ybSBzcGVjaWZpYyBmdW5j dGlvbiB0byBnZXQgYnVzY3RsIG9mZnNldAorICogQGdldF90b2dnbGU6IHBsYXRmb3JtIHNwZWNp ZmljIGZ1bmN0aW9uIHRvIGdldCB0b2dnbGUKKyAqIEBzZXRfdG9nZ2xlOiBwbGF0Zm9ybSBzcGVj aWZpYyBmdW5jdGlvbiB0byBzZXQgdG9nZ2xlCiAgKi8KIHN0cnVjdCBtdXNiX2lvIHsKIAl1MzIJ KCplcF9vZmZzZXQpKHU4IGVwbnVtLCB1MTYgb2Zmc2V0KTsKQEAgLTMwLDEzICszMiwxNyBAQCBz dHJ1Y3QgbXVzYl9pbyB7CiAJdm9pZAkoKnJlYWRfZmlmbykoc3RydWN0IG11c2JfaHdfZXAgKmh3 X2VwLCB1MTYgbGVuLCB1OCAqYnVmKTsKIAl2b2lkCSgqd3JpdGVfZmlmbykoc3RydWN0IG11c2Jf aHdfZXAgKmh3X2VwLCB1MTYgbGVuLCBjb25zdCB1OCAqYnVmKTsKIAl1MzIJKCpidXNjdGxfb2Zm c2V0KSh1OCBlcG51bSwgdTE2IG9mZnNldCk7CisJdTE2CSgqZ2V0X3RvZ2dsZSkoc3RydWN0IG11 c2JfcWggKnFoLCBpbnQgaXNfaW4pOworCXUxNgkoKnNldF90b2dnbGUpKHN0cnVjdCBtdXNiX3Fo ICpxaCwgaW50IGlzX2luLCBzdHJ1Y3QgdXJiICp1cmIpOwogfTsKIAogLyogRG8gbm90IGFkZCBu ZXcgZW50cmllcyBoZXJlLCBhZGQgdGhlbSB0aGUgc3RydWN0IG11c2JfaW8gaW5zdGVhZCAqLwog ZXh0ZXJuIHU4ICgqbXVzYl9yZWFkYikoY29uc3Qgdm9pZCBfX2lvbWVtICphZGRyLCB1bnNpZ25l ZCBvZmZzZXQpOwogZXh0ZXJuIHZvaWQgKCptdXNiX3dyaXRlYikodm9pZCBfX2lvbWVtICphZGRy LCB1bnNpZ25lZCBvZmZzZXQsIHU4IGRhdGEpOworZXh0ZXJuIHZvaWQgKCptdXNiX2NsZWFyYiko dm9pZCBfX2lvbWVtICphZGRyLCB1bnNpZ25lZCBpbnQgb2Zmc2V0LCB1OCBkYXRhKTsKIGV4dGVy biB1MTYgKCptdXNiX3JlYWR3KShjb25zdCB2b2lkIF9faW9tZW0gKmFkZHIsIHVuc2lnbmVkIG9m ZnNldCk7CiBleHRlcm4gdm9pZCAoKm11c2Jfd3JpdGV3KSh2b2lkIF9faW9tZW0gKmFkZHIsIHVu c2lnbmVkIG9mZnNldCwgdTE2IGRhdGEpOworZXh0ZXJuIHZvaWQgKCptdXNiX2NsZWFydykodm9p ZCBfX2lvbWVtICphZGRyLCB1bnNpZ25lZCBpbnQgb2Zmc2V0LCB1MTYgZGF0YSk7CiBleHRlcm4g dTMyIG11c2JfcmVhZGwoY29uc3Qgdm9pZCBfX2lvbWVtICphZGRyLCB1bnNpZ25lZCBvZmZzZXQp OwogZXh0ZXJuIHZvaWQgbXVzYl93cml0ZWwodm9pZCBfX2lvbWVtICphZGRyLCB1bnNpZ25lZCBv ZmZzZXQsIHUzMiBkYXRhKTsKIApkaWZmIC0tZ2l0IGEvZHJpdmVycy91c2IvbXVzYi9tdXNiaHNk bWEuYyBiL2RyaXZlcnMvdXNiL211c2IvbXVzYmhzZG1hLmMKaW5kZXggYTY4OGY3Zi4uYjA1ZmU2 OCAxMDA2NDQKLS0tIGEvZHJpdmVycy91c2IvbXVzYi9tdXNiaHNkbWEuYworKysgYi9kcml2ZXJz L3VzYi9tdXNiL211c2Joc2RtYS5jCkBAIC0xMCwxMiArMTAsNyBAQAogI2luY2x1ZGUgPGxpbnV4 L3BsYXRmb3JtX2RldmljZS5oPgogI2luY2x1ZGUgPGxpbnV4L3NsYWIuaD4KICNpbmNsdWRlICJt dXNiX2NvcmUuaCIKLQotI2RlZmluZSBNVVNCX0hTRE1BX0JBU0UJCTB4MjAwCi0jZGVmaW5lIE1V U0JfSFNETUFfSU5UUgkJKE1VU0JfSFNETUFfQkFTRSArIDApCi0jZGVmaW5lIE1VU0JfSFNETUFf Q09OVFJPTAkJMHg0Ci0jZGVmaW5lIE1VU0JfSFNETUFfQUREUkVTUwkJMHg4Ci0jZGVmaW5lIE1V U0JfSFNETUFfQ09VTlQJCTB4YworI2luY2x1ZGUgIm11c2JfZG1hLmgiCiAKICNkZWZpbmUgTVVT Ql9IU0RNQV9DSEFOTkVMX09GRlNFVChfYmNoYW5uZWwsIF9vZmZzZXQpCQlcCiAJCShNVVNCX0hT RE1BX0JBU0UgKyAoX2JjaGFubmVsIDw8IDQpICsgX29mZnNldCkKQEAgLTI2OCw3ICsyNjMsNyBA QCBzdGF0aWMgaW50IGRtYV9jaGFubmVsX2Fib3J0KHN0cnVjdCBkbWFfY2hhbm5lbCAqY2hhbm5l bCkKIAlyZXR1cm4gMDsKIH0KIAotc3RhdGljIGlycXJldHVybl90IGRtYV9jb250cm9sbGVyX2ly cShpbnQgaXJxLCB2b2lkICpwcml2YXRlX2RhdGEpCitpcnFyZXR1cm5fdCBkbWFfY29udHJvbGxl cl9pcnEoaW50IGlycSwgdm9pZCAqcHJpdmF0ZV9kYXRhKQogewogCXN0cnVjdCBtdXNiX2RtYV9j b250cm9sbGVyICpjb250cm9sbGVyID0gcHJpdmF0ZV9kYXRhOwogCXN0cnVjdCBtdXNiICptdXNi ID0gY29udHJvbGxlci0+cHJpdmF0ZV9kYXRhOwpAQCAtMjkxLDYgKzI4Niw5IEBAIHN0YXRpYyBp cnFyZXR1cm5fdCBkbWFfY29udHJvbGxlcl9pcnEoaW50IGlycSwgdm9pZCAqcHJpdmF0ZV9kYXRh KQogCiAJaW50X2hzZG1hID0gbXVzYl9yZWFkYihtYmFzZSwgTVVTQl9IU0RNQV9JTlRSKTsKIAor CS8qIHNvbWUgcGxhdGZvcm0gbmVlZHMgY2xlYXIgcGVuZGluZyBpbnRlcnJ1cHRzIGJ5IG1hbnVh bCAqLworCW11c2JfY2xlYXJiKG11c2ItPm1yZWdzLCBNVVNCX0hTRE1BX0lOVFIsIGludF9oc2Rt YSk7CisKIAlpZiAoIWludF9oc2RtYSkgewogCQltdXNiX2RiZyhtdXNiLCAic3B1cmlvdXMgRE1B IGlycSIpOwogCkBAIC0zODIsNiArMzgwLDcgQEAgc3RhdGljIGlycXJldHVybl90IGRtYV9jb250 cm9sbGVyX2lycShpbnQgaXJxLCB2b2lkICpwcml2YXRlX2RhdGEpCiAJc3Bpbl91bmxvY2tfaXJx cmVzdG9yZSgmbXVzYi0+bG9jaywgZmxhZ3MpOwogCXJldHVybiByZXR2YWw7CiB9CitFWFBPUlRf U1lNQk9MX0dQTChkbWFfY29udHJvbGxlcl9pcnEpOwogCiB2b2lkIG11c2Joc19kbWFfY29udHJv bGxlcl9kZXN0cm95KHN0cnVjdCBkbWFfY29udHJvbGxlciAqYykKIHsKQEAgLTM5NywxOCArMzk2 LDEwIEBAIHZvaWQgbXVzYmhzX2RtYV9jb250cm9sbGVyX2Rlc3Ryb3koc3RydWN0IGRtYV9jb250 cm9sbGVyICpjKQogfQogRVhQT1JUX1NZTUJPTF9HUEwobXVzYmhzX2RtYV9jb250cm9sbGVyX2Rl c3Ryb3kpOwogCi1zdHJ1Y3QgZG1hX2NvbnRyb2xsZXIgKm11c2Joc19kbWFfY29udHJvbGxlcl9j cmVhdGUoc3RydWN0IG11c2IgKm11c2IsCitzdGF0aWMgc3RydWN0IG11c2JfZG1hX2NvbnRyb2xs ZXIgKmRtYV9jb250cm9sbGVyX2FsbG9jKHN0cnVjdCBtdXNiICptdXNiLAogCQkJCQkJICAgIHZv aWQgX19pb21lbSAqYmFzZSkKIHsKIAlzdHJ1Y3QgbXVzYl9kbWFfY29udHJvbGxlciAqY29udHJv bGxlcjsKLQlzdHJ1Y3QgZGV2aWNlICpkZXYgPSBtdXNiLT5jb250cm9sbGVyOwotCXN0cnVjdCBw bGF0Zm9ybV9kZXZpY2UgKnBkZXYgPSB0b19wbGF0Zm9ybV9kZXZpY2UoZGV2KTsKLQlpbnQgaXJx ID0gcGxhdGZvcm1fZ2V0X2lycV9ieW5hbWUocGRldiwgImRtYSIpOwotCi0JaWYgKGlycSA8PSAw KSB7Ci0JCWRldl9lcnIoZGV2LCAiTm8gRE1BIGludGVycnVwdCBsaW5lIVxuIik7Ci0JCXJldHVy biBOVUxMOwotCX0KIAogCWNvbnRyb2xsZXIgPSBremFsbG9jKHNpemVvZigqY29udHJvbGxlciks IEdGUF9LRVJORUwpOwogCWlmICghY29udHJvbGxlcikKQEAgLTQyMiw2ICs0MTMsMjUgQEAgc3Ry dWN0IGRtYV9jb250cm9sbGVyICptdXNiaHNfZG1hX2NvbnRyb2xsZXJfY3JlYXRlKHN0cnVjdCBt dXNiICptdXNiLAogCWNvbnRyb2xsZXItPmNvbnRyb2xsZXIuY2hhbm5lbF9yZWxlYXNlID0gZG1h X2NoYW5uZWxfcmVsZWFzZTsKIAljb250cm9sbGVyLT5jb250cm9sbGVyLmNoYW5uZWxfcHJvZ3Jh bSA9IGRtYV9jaGFubmVsX3Byb2dyYW07CiAJY29udHJvbGxlci0+Y29udHJvbGxlci5jaGFubmVs X2Fib3J0ID0gZG1hX2NoYW5uZWxfYWJvcnQ7CisJcmV0dXJuIGNvbnRyb2xsZXI7Cit9CisKK3N0 cnVjdCBkbWFfY29udHJvbGxlciAqbXVzYmhzX2RtYV9jb250cm9sbGVyX2NyZWF0ZShzdHJ1Y3Qg bXVzYiAqbXVzYiwKKwkJCQkJCSAgICB2b2lkIF9faW9tZW0gKmJhc2UpCit7CisJc3RydWN0IG11 c2JfZG1hX2NvbnRyb2xsZXIgKmNvbnRyb2xsZXI7CisJc3RydWN0IGRldmljZSAqZGV2ID0gbXVz Yi0+Y29udHJvbGxlcjsKKwlzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2ID0gdG9fcGxhdGZv cm1fZGV2aWNlKGRldik7CisJaW50IGlycSA9IHBsYXRmb3JtX2dldF9pcnFfYnluYW1lKHBkZXYs ICJkbWEiKTsKKworCWlmIChpcnEgPD0gMCkgeworCQlkZXZfZXJyKGRldiwgIk5vIERNQSBpbnRl cnJ1cHQgbGluZSFcbiIpOworCQlyZXR1cm4gTlVMTDsKKwl9CisKKwljb250cm9sbGVyID0gZG1h X2NvbnRyb2xsZXJfYWxsb2MobXVzYiwgYmFzZSk7CisJaWYgKCFjb250cm9sbGVyKQorCQlyZXR1 cm4gTlVMTDsKIAogCWlmIChyZXF1ZXN0X2lycShpcnEsIGRtYV9jb250cm9sbGVyX2lycSwgMCwK IAkJCWRldl9uYW1lKG11c2ItPmNvbnRyb2xsZXIpLCAmY29udHJvbGxlci0+Y29udHJvbGxlcikp IHsKQEAgLTQzNiwzICs0NDYsMTYgQEAgc3RydWN0IGRtYV9jb250cm9sbGVyICptdXNiaHNfZG1h X2NvbnRyb2xsZXJfY3JlYXRlKHN0cnVjdCBtdXNiICptdXNiLAogCXJldHVybiAmY29udHJvbGxl ci0+Y29udHJvbGxlcjsKIH0KIEVYUE9SVF9TWU1CT0xfR1BMKG11c2Joc19kbWFfY29udHJvbGxl cl9jcmVhdGUpOworCitzdHJ1Y3QgZG1hX2NvbnRyb2xsZXIgKm11c2Joc19kbWFfY29udHJvbGxl cl9jcmVhdGVfbm9pcnEoc3RydWN0IG11c2IgKm11c2IsCisJCQkJCQkgICAgdm9pZCBfX2lvbWVt ICpiYXNlKQoreworCXN0cnVjdCBtdXNiX2RtYV9jb250cm9sbGVyICpjb250cm9sbGVyOworCisJ Y29udHJvbGxlciA9IGRtYV9jb250cm9sbGVyX2FsbG9jKG11c2IsIGJhc2UpOworCWlmICghY29u dHJvbGxlcikKKwkJcmV0dXJuIE5VTEw7CisKKwlyZXR1cm4gJmNvbnRyb2xsZXItPmNvbnRyb2xs ZXI7Cit9CitFWFBPUlRfU1lNQk9MX0dQTChtdXNiaHNfZG1hX2NvbnRyb2xsZXJfY3JlYXRlX25v aXJxKTsK From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.0 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_PASS,UNPARSEABLE_RELAY,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 72C7EC43387 for ; Tue, 15 Jan 2019 01:45:36 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 40A8A205C9 for ; Tue, 15 Jan 2019 01:45:36 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="IsEndLqV" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 40A8A205C9 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=mediatek.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=kz3KdCJlTBdm5mZfVRdw1uI/M0h7UkErosY0gl22arY=; b=IsEndLqVSKP4On FDg/AN4zM1Pr4ooMWPvgpkMNJcOlSovn6XFBoqQ8tSRSqLVlPHTxAkcDyCrXPCvrKb7Ib28L5yn3G LK4e+vu0XzpwKAjIhl4S4628poKlCslsFYNOzPU1H/L7vFWLKn8Rhden4plnhIr+C1wvuWyFHoMHO imZCi5k29XVnGx1n0U33S1taFQfnGvNGRNG2posAHc6xWdNO7cpkzaH6rsqHeuo0PuZyrEyiMtCN0 W4JkNIxH08+zvwClg56oY7wWABBsByhyfx8QBE/PF0mUwxskukVZr1wtf0mHDV5Z5LtS0mGQeo6A9 HEbKTnLH7iQsqR3SKa7w==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gjDnG-0007xA-LB; Tue, 15 Jan 2019 01:45:30 +0000 Received: from mailgw02.mediatek.com ([216.200.240.185]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gjDm2-0005T2-MU; Tue, 15 Jan 2019 01:44:44 +0000 X-UUID: d1a124499ecd478a92df95df3ebcb35b-20190114 X-UUID: d1a124499ecd478a92df95df3ebcb35b-20190114 Received: from mtkcas66.mediatek.inc [(172.29.193.44)] by mailgw02.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 1653601050; Mon, 14 Jan 2019 17:44:07 -0800 Received: from MTKMBS31N2.mediatek.inc (172.27.4.87) by MTKMBS62N2.mediatek.inc (172.29.193.42) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 14 Jan 2019 17:44:05 -0800 Received: from mtkcas09.mediatek.inc (172.21.101.178) by MTKMBS31N2.mediatek.inc (172.27.4.87) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 15 Jan 2019 09:44:02 +0800 Received: from localhost.localdomain (10.17.3.153) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 15 Jan 2019 09:44:01 +0800 From: To: Bin Liu , Rob Herring Subject: [PATCH v2 4/4] usb: musb: Add support for MediaTek musb controller Date: Tue, 15 Jan 2019 09:43:46 +0800 Message-ID: <1547516626-5084-5-git-send-email-min.guo@mediatek.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1547516626-5084-1-git-send-email-min.guo@mediatek.com> References: <1547516626-5084-1-git-send-email-min.guo@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190114_174415_253566_812FAD42 X-CRM114-Status: GOOD ( 20.88 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , devicetree@vger.kernel.org, Yonglong Wu , Greg Kroah-Hartman , linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Matthias Brugger , Alan Stern , Min Guo , chunfeng.yun@mediatek.com, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.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 --- drivers/usb/musb/Kconfig | 8 +- drivers/usb/musb/Makefile | 1 + drivers/usb/musb/mediatek.c | 617 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/musb/musb_core.c | 69 +++++ drivers/usb/musb/musb_core.h | 9 + drivers/usb/musb/musb_dma.h | 9 + drivers/usb/musb/musb_host.c | 26 +- drivers/usb/musb/musb_io.h | 6 + drivers/usb/musb/musbhsdma.c | 55 ++-- 9 files changed, 762 insertions(+), 38 deletions(-) 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..7221989 --- /dev/null +++ b/drivers/usb/musb/mediatek.c @@ -0,0 +1,617 @@ +// 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->int_rx); + musb_clearw(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb_clearb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + + 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 void mtk_musb_clearb(void __iomem *addr, unsigned int offset, u8 data) +{ + /* W1C */ + musb_writeb(addr, offset, data); +} + +static void mtk_musb_clearw(void __iomem *addr, unsigned int offset, u16 data) +{ + /* W1C */ + musb_writew(addr, offset, data); +} + +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_in) +{ + struct musb *musb = qh->hw_ep->musb; + u8 epnum = qh->hw_ep->epnum; + u16 toggle; + + if (is_in) + toggle = musb_readw(musb->mregs, MUSB_RXTOG); + else + toggle = musb_readw(musb->mregs, MUSB_TXTOG); + + return toggle & (1 << epnum); +} + +static u16 mtk_musb_set_toggle(struct musb_qh *qh, int is_in, struct urb *urb) +{ + struct musb *musb = qh->hw_ep->musb; + u8 epnum = qh->hw_ep->epnum; + u16 toggle = 0; + + toggle = usb_gettoggle(urb->dev, qh->epnum, !is_in); + + if (is_in) { + musb_writew(musb->mregs, MUSB_RXTOGEN, (1 << epnum)); + musb_writew(musb->mregs, MUSB_RXTOG, (toggle << epnum)); + } else { + musb_writew(musb->mregs, MUSB_TXTOGEN, (1 << epnum)); + musb_writew(musb->mregs, MUSB_TXTOG, (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); + + dev_info(dev, "USB probe done!\n"); + 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"); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b7d5627..2c0d102 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -260,6 +260,11 @@ static void musb_default_writeb(void __iomem *addr, unsigned offset, u8 data) __raw_writeb(data, addr + offset); } +static void +musb_default_clearb(void __iomem *addr, unsigned int offset, u8 data) +{ +} + static u16 musb_default_readw(const void __iomem *addr, unsigned offset) { u16 data = __raw_readw(addr + offset); @@ -274,6 +279,43 @@ static void musb_default_writew(void __iomem *addr, unsigned offset, u16 data) __raw_writew(data, addr + offset); } +static void +musb_default_clearw(void __iomem *addr, unsigned int offset, u16 data) +{ +} + +static u16 musb_default_get_toggle(struct musb_qh *qh, int is_in) +{ + void __iomem *epio = qh->hw_ep->regs; + u16 csr; + + if (is_in) + csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE; + else + csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE; + + return csr; +} + +static u16 musb_default_set_toggle(struct musb_qh *qh, int is_in, + struct urb *urb) +{ + u16 csr = 0; + u16 toggle = 0; + + toggle = usb_gettoggle(urb->dev, qh->epnum, !is_in); + + if (is_in) + csr = toggle ? (MUSB_RXCSR_H_WR_DATATOGGLE + | MUSB_RXCSR_H_DATATOGGLE) : 0; + else + csr |= toggle ? (MUSB_TXCSR_H_WR_DATATOGGLE + | MUSB_TXCSR_H_DATATOGGLE) + : MUSB_TXCSR_CLRDATATOG; + + return csr; +} + /* * Load an endpoint's FIFO */ @@ -370,12 +412,18 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data); EXPORT_SYMBOL_GPL(musb_writeb); +void (*musb_clearb)(void __iomem *addr, unsigned int offset, u8 data); +EXPORT_SYMBOL_GPL(musb_clearb); + u16 (*musb_readw)(const void __iomem *addr, unsigned offset); EXPORT_SYMBOL_GPL(musb_readw); void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data); EXPORT_SYMBOL_GPL(musb_writew); +void (*musb_clearw)(void __iomem *addr, unsigned int offset, u16 data); +EXPORT_SYMBOL_GPL(musb_clearw); + u32 musb_readl(const void __iomem *addr, unsigned offset) { u32 data = __raw_readl(addr + offset); @@ -1028,6 +1076,11 @@ static void musb_disable_interrupts(struct musb *musb) temp = musb_readb(mbase, MUSB_INTRUSB); temp = musb_readw(mbase, MUSB_INTRTX); temp = musb_readw(mbase, MUSB_INTRRX); + + /* some platform needs clear pending interrupts by manual */ + musb_clearb(mbase, MUSB_INTRUSB, musb_readb(mbase, MUSB_INTRUSB)); + musb_clearw(mbase, MUSB_INTRRX, musb_readw(mbase, MUSB_INTRRX)); + musb_clearw(mbase, MUSB_INTRTX, musb_readw(mbase, MUSB_INTRTX)); } static void musb_enable_interrupts(struct musb *musb) @@ -2192,6 +2245,8 @@ static void musb_deassert_reset(struct work_struct *work) musb_writeb = musb_default_writeb; musb_readw = musb_default_readw; musb_writew = musb_default_writew; + musb_clearb = musb_default_clearb; + musb_clearw = musb_default_clearw; /* The musb_platform_init() call: * - adjusts musb->mregs @@ -2252,10 +2307,14 @@ static void musb_deassert_reset(struct work_struct *work) musb_readb = musb->ops->readb; if (musb->ops->writeb) musb_writeb = musb->ops->writeb; + if (musb->ops->clearb) + musb_clearb = musb->ops->clearb; if (musb->ops->readw) musb_readw = musb->ops->readw; if (musb->ops->writew) musb_writew = musb->ops->writew; + if (musb->ops->clearw) + musb_clearw = musb->ops->clearw; #ifndef CONFIG_MUSB_PIO_ONLY if (!musb->ops->dma_init || !musb->ops->dma_exit) { @@ -2277,6 +2336,16 @@ static void musb_deassert_reset(struct work_struct *work) else musb->io.write_fifo = musb_default_write_fifo; + if (musb->ops->get_toggle) + musb->io.get_toggle = musb->ops->get_toggle; + else + musb->io.get_toggle = musb_default_get_toggle; + + if (musb->ops->set_toggle) + musb->io.set_toggle = musb->ops->set_toggle; + else + musb->io.set_toggle = musb_default_set_toggle; + if (!musb->xceiv->io_ops) { musb->xceiv->io_dev = musb->controller; musb->xceiv->io_priv = musb->mregs; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 04203b7..71dca80 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -27,6 +27,7 @@ struct musb; struct musb_hw_ep; struct musb_ep; +struct musb_qh; /* Helper defines for struct musb->hwvers */ #define MUSB_HWVERS_MAJOR(x) ((x >> 10) & 0x1f) @@ -119,10 +120,14 @@ enum musb_g_ep0_state { * @fifo_offset: returns the fifo offset * @readb: read 8 bits * @writeb: write 8 bits + * @clearb: clear 8 bits * @readw: read 16 bits * @writew: write 16 bits + * @clearw: clear 16 bits * @read_fifo: reads the fifo * @write_fifo: writes to fifo + * @get_toggle: platform specific get toggle function + * @set_toggle: platform specific set toggle function * @dma_init: platform specific dma init function * @dma_exit: platform specific dma exit function * @init: turns on clocks, sets up platform-specific registers, etc @@ -163,10 +168,14 @@ struct musb_platform_ops { u32 (*busctl_offset)(u8 epnum, u16 offset); u8 (*readb)(const void __iomem *addr, unsigned offset); void (*writeb)(void __iomem *addr, unsigned offset, u8 data); + void (*clearb)(void __iomem *addr, unsigned int offset, u8 data); u16 (*readw)(const void __iomem *addr, unsigned offset); void (*writew)(void __iomem *addr, unsigned offset, u16 data); + void (*clearw)(void __iomem *addr, unsigned int offset, u16 data); void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); + u16 (*get_toggle)(struct musb_qh *qh, int is_in); + u16 (*set_toggle)(struct musb_qh *qh, int is_in, struct urb *urb); struct dma_controller * (*dma_init) (struct musb *musb, void __iomem *base); void (*dma_exit)(struct dma_controller *c); diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 8f60271..05103ea 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -35,6 +35,12 @@ * whether shared with the Inventra core or separate. */ +#define MUSB_HSDMA_BASE 0x200 +#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) +#define MUSB_HSDMA_CONTROL 0x4 +#define MUSB_HSDMA_ADDRESS 0x8 +#define MUSB_HSDMA_COUNT 0xc + #define DMA_ADDR_INVALID (~(dma_addr_t)0) #ifdef CONFIG_MUSB_PIO_ONLY @@ -191,6 +197,9 @@ static inline void musb_dma_controller_destroy(struct dma_controller *d) { } extern struct dma_controller * musbhs_dma_controller_create(struct musb *musb, void __iomem *base); extern void musbhs_dma_controller_destroy(struct dma_controller *c); +extern struct dma_controller * +musbhs_dma_controller_create_noirq(struct musb *musb, void __iomem *base); +extern irqreturn_t dma_controller_irq(int irq, void *private_data); extern struct dma_controller * tusb_dma_controller_create(struct musb *musb, void __iomem *base); diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 16d0ba4..ba66f44 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -290,39 +290,23 @@ static void musb_giveback(struct musb *musb, struct urb *urb, int status) static inline void musb_save_toggle(struct musb_qh *qh, int is_in, struct urb *urb) { - void __iomem *epio = qh->hw_ep->regs; - u16 csr; + struct musb *musb = qh->hw_ep->musb; + u16 csr; /* * FIXME: the current Mentor DMA code seems to have * problems getting toggle correct. */ - - if (is_in) - csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE; - else - csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE; - + csr = musb->io.get_toggle(qh, is_in); usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0); } static inline u16 musb_set_toggle(struct musb_qh *qh, int is_in, struct urb *urb) { - u16 csr = 0; - u16 toggle = 0; - - toggle = usb_gettoggle(urb->dev, qh->epnum, !is_in); - - if (is_in) - csr = toggle ? (MUSB_RXCSR_H_WR_DATATOGGLE - | MUSB_RXCSR_H_DATATOGGLE) : 0; - else - csr = toggle ? (MUSB_TXCSR_H_WR_DATATOGGLE - | MUSB_TXCSR_H_DATATOGGLE) - : MUSB_TXCSR_CLRDATATOG; + struct musb *musb = qh->hw_ep->musb; - return csr; + return musb->io.set_toggle(qh, is_in, urb); } /* diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 8058a58..9bae09b 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -22,6 +22,8 @@ * @read_fifo: platform specific function to read fifo * @write_fifo: platform specific function to write fifo * @busctl_offset: platform specific function to get busctl offset + * @get_toggle: platform specific function to get toggle + * @set_toggle: platform specific function to set toggle */ struct musb_io { u32 (*ep_offset)(u8 epnum, u16 offset); @@ -30,13 +32,17 @@ struct musb_io { void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); u32 (*busctl_offset)(u8 epnum, u16 offset); + u16 (*get_toggle)(struct musb_qh *qh, int is_in); + u16 (*set_toggle)(struct musb_qh *qh, int is_in, struct urb *urb); }; /* Do not add new entries here, add them the struct musb_io instead */ extern u8 (*musb_readb)(const void __iomem *addr, unsigned offset); extern void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data); +extern void (*musb_clearb)(void __iomem *addr, unsigned int offset, u8 data); extern u16 (*musb_readw)(const void __iomem *addr, unsigned offset); extern void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data); +extern void (*musb_clearw)(void __iomem *addr, unsigned int offset, u16 data); extern u32 musb_readl(const void __iomem *addr, unsigned offset); extern void musb_writel(void __iomem *addr, unsigned offset, u32 data); diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index a688f7f..b05fe68 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -10,12 +10,7 @@ #include #include #include "musb_core.h" - -#define MUSB_HSDMA_BASE 0x200 -#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) -#define MUSB_HSDMA_CONTROL 0x4 -#define MUSB_HSDMA_ADDRESS 0x8 -#define MUSB_HSDMA_COUNT 0xc +#include "musb_dma.h" #define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \ (MUSB_HSDMA_BASE + (_bchannel << 4) + _offset) @@ -268,7 +263,7 @@ static int dma_channel_abort(struct dma_channel *channel) return 0; } -static irqreturn_t dma_controller_irq(int irq, void *private_data) +irqreturn_t dma_controller_irq(int irq, void *private_data) { struct musb_dma_controller *controller = private_data; struct musb *musb = controller->private_data; @@ -291,6 +286,9 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR); + /* some platform needs clear pending interrupts by manual */ + musb_clearb(musb->mregs, MUSB_HSDMA_INTR, int_hsdma); + if (!int_hsdma) { musb_dbg(musb, "spurious DMA irq"); @@ -382,6 +380,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) spin_unlock_irqrestore(&musb->lock, flags); return retval; } +EXPORT_SYMBOL_GPL(dma_controller_irq); void musbhs_dma_controller_destroy(struct dma_controller *c) { @@ -397,18 +396,10 @@ void musbhs_dma_controller_destroy(struct dma_controller *c) } EXPORT_SYMBOL_GPL(musbhs_dma_controller_destroy); -struct dma_controller *musbhs_dma_controller_create(struct musb *musb, +static struct musb_dma_controller *dma_controller_alloc(struct musb *musb, void __iomem *base) { struct musb_dma_controller *controller; - struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); - int irq = platform_get_irq_byname(pdev, "dma"); - - if (irq <= 0) { - dev_err(dev, "No DMA interrupt line!\n"); - return NULL; - } controller = kzalloc(sizeof(*controller), GFP_KERNEL); if (!controller) @@ -422,6 +413,25 @@ struct dma_controller *musbhs_dma_controller_create(struct musb *musb, controller->controller.channel_release = dma_channel_release; controller->controller.channel_program = dma_channel_program; controller->controller.channel_abort = dma_channel_abort; + return controller; +} + +struct dma_controller *musbhs_dma_controller_create(struct musb *musb, + void __iomem *base) +{ + struct musb_dma_controller *controller; + struct device *dev = musb->controller; + struct platform_device *pdev = to_platform_device(dev); + int irq = platform_get_irq_byname(pdev, "dma"); + + if (irq <= 0) { + dev_err(dev, "No DMA interrupt line!\n"); + return NULL; + } + + controller = dma_controller_alloc(musb, base); + if (!controller) + return NULL; if (request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), &controller->controller)) { @@ -436,3 +446,16 @@ struct dma_controller *musbhs_dma_controller_create(struct musb *musb, return &controller->controller; } EXPORT_SYMBOL_GPL(musbhs_dma_controller_create); + +struct dma_controller *musbhs_dma_controller_create_noirq(struct musb *musb, + void __iomem *base) +{ + struct musb_dma_controller *controller; + + controller = dma_controller_alloc(musb, base); + if (!controller) + return NULL; + + return &controller->controller; +} +EXPORT_SYMBOL_GPL(musbhs_dma_controller_create_noirq); -- 1.9.1 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel