18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2019 MediaTek Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author:
68c2ecf20Sopenharmony_ci *  Min Guo <min.guo@mediatek.com>
78c2ecf20Sopenharmony_ci *  Yonglong Wu <yonglong.wu@mediatek.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/usb/role.h>
168c2ecf20Sopenharmony_ci#include <linux/usb/usb_phy_generic.h>
178c2ecf20Sopenharmony_ci#include "musb_core.h"
188c2ecf20Sopenharmony_ci#include "musb_dma.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define USB_L1INTS		0x00a0
218c2ecf20Sopenharmony_ci#define USB_L1INTM		0x00a4
228c2ecf20Sopenharmony_ci#define MTK_MUSB_TXFUNCADDR	0x0480
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* MediaTek controller toggle enable and status reg */
258c2ecf20Sopenharmony_ci#define MUSB_RXTOG		0x80
268c2ecf20Sopenharmony_ci#define MUSB_RXTOGEN		0x82
278c2ecf20Sopenharmony_ci#define MUSB_TXTOG		0x84
288c2ecf20Sopenharmony_ci#define MUSB_TXTOGEN		0x86
298c2ecf20Sopenharmony_ci#define MTK_TOGGLE_EN		GENMASK(15, 0)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define TX_INT_STATUS		BIT(0)
328c2ecf20Sopenharmony_ci#define RX_INT_STATUS		BIT(1)
338c2ecf20Sopenharmony_ci#define USBCOM_INT_STATUS	BIT(2)
348c2ecf20Sopenharmony_ci#define DMA_INT_STATUS		BIT(3)
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define DMA_INTR_STATUS_MSK	GENMASK(7, 0)
378c2ecf20Sopenharmony_ci#define DMA_INTR_UNMASK_SET_MSK	GENMASK(31, 24)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct mtk_glue {
408c2ecf20Sopenharmony_ci	struct device *dev;
418c2ecf20Sopenharmony_ci	struct musb *musb;
428c2ecf20Sopenharmony_ci	struct platform_device *musb_pdev;
438c2ecf20Sopenharmony_ci	struct platform_device *usb_phy;
448c2ecf20Sopenharmony_ci	struct phy *phy;
458c2ecf20Sopenharmony_ci	struct usb_phy *xceiv;
468c2ecf20Sopenharmony_ci	enum phy_mode phy_mode;
478c2ecf20Sopenharmony_ci	struct clk *main;
488c2ecf20Sopenharmony_ci	struct clk *mcu;
498c2ecf20Sopenharmony_ci	struct clk *univpll;
508c2ecf20Sopenharmony_ci	enum usb_role role;
518c2ecf20Sopenharmony_ci	struct usb_role_switch *role_sw;
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic int mtk_musb_clks_get(struct mtk_glue *glue)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct device *dev = glue->dev;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	glue->main = devm_clk_get(dev, "main");
598c2ecf20Sopenharmony_ci	if (IS_ERR(glue->main)) {
608c2ecf20Sopenharmony_ci		dev_err(dev, "fail to get main clock\n");
618c2ecf20Sopenharmony_ci		return PTR_ERR(glue->main);
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	glue->mcu = devm_clk_get(dev, "mcu");
658c2ecf20Sopenharmony_ci	if (IS_ERR(glue->mcu)) {
668c2ecf20Sopenharmony_ci		dev_err(dev, "fail to get mcu clock\n");
678c2ecf20Sopenharmony_ci		return PTR_ERR(glue->mcu);
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	glue->univpll = devm_clk_get(dev, "univpll");
718c2ecf20Sopenharmony_ci	if (IS_ERR(glue->univpll)) {
728c2ecf20Sopenharmony_ci		dev_err(dev, "fail to get univpll clock\n");
738c2ecf20Sopenharmony_ci		return PTR_ERR(glue->univpll);
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int mtk_musb_clks_enable(struct mtk_glue *glue)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	int ret;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(glue->main);
848c2ecf20Sopenharmony_ci	if (ret) {
858c2ecf20Sopenharmony_ci		dev_err(glue->dev, "failed to enable main clock\n");
868c2ecf20Sopenharmony_ci		goto err_main_clk;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(glue->mcu);
908c2ecf20Sopenharmony_ci	if (ret) {
918c2ecf20Sopenharmony_ci		dev_err(glue->dev, "failed to enable mcu clock\n");
928c2ecf20Sopenharmony_ci		goto err_mcu_clk;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(glue->univpll);
968c2ecf20Sopenharmony_ci	if (ret) {
978c2ecf20Sopenharmony_ci		dev_err(glue->dev, "failed to enable univpll clock\n");
988c2ecf20Sopenharmony_ci		goto err_univpll_clk;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cierr_univpll_clk:
1048c2ecf20Sopenharmony_ci	clk_disable_unprepare(glue->mcu);
1058c2ecf20Sopenharmony_cierr_mcu_clk:
1068c2ecf20Sopenharmony_ci	clk_disable_unprepare(glue->main);
1078c2ecf20Sopenharmony_cierr_main_clk:
1088c2ecf20Sopenharmony_ci	return ret;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic void mtk_musb_clks_disable(struct mtk_glue *glue)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	clk_disable_unprepare(glue->univpll);
1148c2ecf20Sopenharmony_ci	clk_disable_unprepare(glue->mcu);
1158c2ecf20Sopenharmony_ci	clk_disable_unprepare(glue->main);
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int mtk_otg_switch_set(struct mtk_glue *glue, enum usb_role role)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct musb *musb = glue->musb;
1218c2ecf20Sopenharmony_ci	u8 devctl = readb(musb->mregs + MUSB_DEVCTL);
1228c2ecf20Sopenharmony_ci	enum usb_role new_role;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (role == glue->role)
1258c2ecf20Sopenharmony_ci		return 0;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	switch (role) {
1288c2ecf20Sopenharmony_ci	case USB_ROLE_HOST:
1298c2ecf20Sopenharmony_ci		musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
1308c2ecf20Sopenharmony_ci		glue->phy_mode = PHY_MODE_USB_HOST;
1318c2ecf20Sopenharmony_ci		new_role = USB_ROLE_HOST;
1328c2ecf20Sopenharmony_ci		if (glue->role == USB_ROLE_NONE)
1338c2ecf20Sopenharmony_ci			phy_power_on(glue->phy);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		devctl |= MUSB_DEVCTL_SESSION;
1368c2ecf20Sopenharmony_ci		musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
1378c2ecf20Sopenharmony_ci		MUSB_HST_MODE(musb);
1388c2ecf20Sopenharmony_ci		break;
1398c2ecf20Sopenharmony_ci	case USB_ROLE_DEVICE:
1408c2ecf20Sopenharmony_ci		musb->xceiv->otg->state = OTG_STATE_B_IDLE;
1418c2ecf20Sopenharmony_ci		glue->phy_mode = PHY_MODE_USB_DEVICE;
1428c2ecf20Sopenharmony_ci		new_role = USB_ROLE_DEVICE;
1438c2ecf20Sopenharmony_ci		devctl &= ~MUSB_DEVCTL_SESSION;
1448c2ecf20Sopenharmony_ci		musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
1458c2ecf20Sopenharmony_ci		if (glue->role == USB_ROLE_NONE)
1468c2ecf20Sopenharmony_ci			phy_power_on(glue->phy);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		MUSB_DEV_MODE(musb);
1498c2ecf20Sopenharmony_ci		break;
1508c2ecf20Sopenharmony_ci	case USB_ROLE_NONE:
1518c2ecf20Sopenharmony_ci		glue->phy_mode = PHY_MODE_USB_OTG;
1528c2ecf20Sopenharmony_ci		new_role = USB_ROLE_NONE;
1538c2ecf20Sopenharmony_ci		devctl &= ~MUSB_DEVCTL_SESSION;
1548c2ecf20Sopenharmony_ci		musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
1558c2ecf20Sopenharmony_ci		if (glue->role != USB_ROLE_NONE)
1568c2ecf20Sopenharmony_ci			phy_power_off(glue->phy);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci	default:
1608c2ecf20Sopenharmony_ci		dev_err(glue->dev, "Invalid State\n");
1618c2ecf20Sopenharmony_ci		return -EINVAL;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	glue->role = new_role;
1658c2ecf20Sopenharmony_ci	phy_set_mode(glue->phy, glue->phy_mode);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic int musb_usb_role_sx_set(struct usb_role_switch *sw, enum usb_role role)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	return mtk_otg_switch_set(usb_role_switch_get_drvdata(sw), role);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic enum usb_role musb_usb_role_sx_get(struct usb_role_switch *sw)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct mtk_glue *glue = usb_role_switch_get_drvdata(sw);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return glue->role;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int mtk_otg_switch_init(struct mtk_glue *glue)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct usb_role_switch_desc role_sx_desc = { 0 };
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	role_sx_desc.set = musb_usb_role_sx_set;
1878c2ecf20Sopenharmony_ci	role_sx_desc.get = musb_usb_role_sx_get;
1888c2ecf20Sopenharmony_ci	role_sx_desc.fwnode = dev_fwnode(glue->dev);
1898c2ecf20Sopenharmony_ci	role_sx_desc.driver_data = glue;
1908c2ecf20Sopenharmony_ci	glue->role_sw = usb_role_switch_register(glue->dev, &role_sx_desc);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(glue->role_sw);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic void mtk_otg_switch_exit(struct mtk_glue *glue)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	return usb_role_switch_unregister(glue->role_sw);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic irqreturn_t generic_interrupt(int irq, void *__hci)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	unsigned long flags;
2038c2ecf20Sopenharmony_ci	irqreturn_t retval = IRQ_NONE;
2048c2ecf20Sopenharmony_ci	struct musb *musb = __hci;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	spin_lock_irqsave(&musb->lock, flags);
2078c2ecf20Sopenharmony_ci	musb->int_usb = musb_clearb(musb->mregs, MUSB_INTRUSB);
2088c2ecf20Sopenharmony_ci	musb->int_rx = musb_clearw(musb->mregs, MUSB_INTRRX);
2098c2ecf20Sopenharmony_ci	musb->int_tx = musb_clearw(musb->mregs, MUSB_INTRTX);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if ((musb->int_usb & MUSB_INTR_RESET) && !is_host_active(musb)) {
2128c2ecf20Sopenharmony_ci		/* ep0 FADDR must be 0 when (re)entering peripheral mode */
2138c2ecf20Sopenharmony_ci		musb_ep_select(musb->mregs, 0);
2148c2ecf20Sopenharmony_ci		musb_writeb(musb->mregs, MUSB_FADDR, 0);
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (musb->int_usb || musb->int_tx || musb->int_rx)
2188c2ecf20Sopenharmony_ci		retval = musb_interrupt(musb);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&musb->lock, flags);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return retval;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic irqreturn_t mtk_musb_interrupt(int irq, void *dev_id)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	irqreturn_t retval = IRQ_NONE;
2288c2ecf20Sopenharmony_ci	struct musb *musb = (struct musb *)dev_id;
2298c2ecf20Sopenharmony_ci	u32 l1_ints;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	l1_ints = musb_readl(musb->mregs, USB_L1INTS) &
2328c2ecf20Sopenharmony_ci			musb_readl(musb->mregs, USB_L1INTM);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (l1_ints & (TX_INT_STATUS | RX_INT_STATUS | USBCOM_INT_STATUS))
2358c2ecf20Sopenharmony_ci		retval = generic_interrupt(irq, musb);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci#if defined(CONFIG_USB_INVENTRA_DMA)
2388c2ecf20Sopenharmony_ci	if (l1_ints & DMA_INT_STATUS)
2398c2ecf20Sopenharmony_ci		retval = dma_controller_irq(irq, musb->dma_controller);
2408c2ecf20Sopenharmony_ci#endif
2418c2ecf20Sopenharmony_ci	return retval;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic u32 mtk_musb_busctl_offset(u8 epnum, u16 offset)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	return MTK_MUSB_TXFUNCADDR + offset + 8 * epnum;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic u8 mtk_musb_clearb(void __iomem *addr, unsigned int offset)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	u8 data;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* W1C */
2548c2ecf20Sopenharmony_ci	data = musb_readb(addr, offset);
2558c2ecf20Sopenharmony_ci	musb_writeb(addr, offset, data);
2568c2ecf20Sopenharmony_ci	return data;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic u16 mtk_musb_clearw(void __iomem *addr, unsigned int offset)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	u16 data;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* W1C */
2648c2ecf20Sopenharmony_ci	data = musb_readw(addr, offset);
2658c2ecf20Sopenharmony_ci	musb_writew(addr, offset, data);
2668c2ecf20Sopenharmony_ci	return data;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int mtk_musb_set_mode(struct musb *musb, u8 mode)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	struct device *dev = musb->controller;
2728c2ecf20Sopenharmony_ci	struct mtk_glue *glue = dev_get_drvdata(dev->parent);
2738c2ecf20Sopenharmony_ci	enum phy_mode new_mode;
2748c2ecf20Sopenharmony_ci	enum usb_role new_role;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	switch (mode) {
2778c2ecf20Sopenharmony_ci	case MUSB_HOST:
2788c2ecf20Sopenharmony_ci		new_mode = PHY_MODE_USB_HOST;
2798c2ecf20Sopenharmony_ci		new_role = USB_ROLE_HOST;
2808c2ecf20Sopenharmony_ci		break;
2818c2ecf20Sopenharmony_ci	case MUSB_PERIPHERAL:
2828c2ecf20Sopenharmony_ci		new_mode = PHY_MODE_USB_DEVICE;
2838c2ecf20Sopenharmony_ci		new_role = USB_ROLE_DEVICE;
2848c2ecf20Sopenharmony_ci		break;
2858c2ecf20Sopenharmony_ci	case MUSB_OTG:
2868c2ecf20Sopenharmony_ci		new_mode = PHY_MODE_USB_OTG;
2878c2ecf20Sopenharmony_ci		new_role = USB_ROLE_NONE;
2888c2ecf20Sopenharmony_ci		break;
2898c2ecf20Sopenharmony_ci	default:
2908c2ecf20Sopenharmony_ci		dev_err(glue->dev, "Invalid mode request\n");
2918c2ecf20Sopenharmony_ci		return -EINVAL;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (glue->phy_mode == new_mode)
2958c2ecf20Sopenharmony_ci		return 0;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (musb->port_mode != MUSB_OTG) {
2988c2ecf20Sopenharmony_ci		dev_err(glue->dev, "Does not support changing modes\n");
2998c2ecf20Sopenharmony_ci		return -EINVAL;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	mtk_otg_switch_set(glue, new_role);
3038c2ecf20Sopenharmony_ci	return 0;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int mtk_musb_init(struct musb *musb)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct device *dev = musb->controller;
3098c2ecf20Sopenharmony_ci	struct mtk_glue *glue = dev_get_drvdata(dev->parent);
3108c2ecf20Sopenharmony_ci	int ret;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	glue->musb = musb;
3138c2ecf20Sopenharmony_ci	musb->phy = glue->phy;
3148c2ecf20Sopenharmony_ci	musb->xceiv = glue->xceiv;
3158c2ecf20Sopenharmony_ci	musb->is_host = false;
3168c2ecf20Sopenharmony_ci	musb->isr = mtk_musb_interrupt;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* Set TX/RX toggle enable */
3198c2ecf20Sopenharmony_ci	musb_writew(musb->mregs, MUSB_TXTOGEN, MTK_TOGGLE_EN);
3208c2ecf20Sopenharmony_ci	musb_writew(musb->mregs, MUSB_RXTOGEN, MTK_TOGGLE_EN);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (musb->port_mode == MUSB_OTG) {
3238c2ecf20Sopenharmony_ci		ret = mtk_otg_switch_init(glue);
3248c2ecf20Sopenharmony_ci		if (ret)
3258c2ecf20Sopenharmony_ci			return ret;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	ret = phy_init(glue->phy);
3298c2ecf20Sopenharmony_ci	if (ret)
3308c2ecf20Sopenharmony_ci		goto err_phy_init;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	ret = phy_power_on(glue->phy);
3338c2ecf20Sopenharmony_ci	if (ret)
3348c2ecf20Sopenharmony_ci		goto err_phy_power_on;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	phy_set_mode(glue->phy, glue->phy_mode);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci#if defined(CONFIG_USB_INVENTRA_DMA)
3398c2ecf20Sopenharmony_ci	musb_writel(musb->mregs, MUSB_HSDMA_INTR,
3408c2ecf20Sopenharmony_ci		    DMA_INTR_STATUS_MSK | DMA_INTR_UNMASK_SET_MSK);
3418c2ecf20Sopenharmony_ci#endif
3428c2ecf20Sopenharmony_ci	musb_writel(musb->mregs, USB_L1INTM, TX_INT_STATUS | RX_INT_STATUS |
3438c2ecf20Sopenharmony_ci		    USBCOM_INT_STATUS | DMA_INT_STATUS);
3448c2ecf20Sopenharmony_ci	return 0;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cierr_phy_power_on:
3478c2ecf20Sopenharmony_ci	phy_exit(glue->phy);
3488c2ecf20Sopenharmony_cierr_phy_init:
3498c2ecf20Sopenharmony_ci	mtk_otg_switch_exit(glue);
3508c2ecf20Sopenharmony_ci	return ret;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic u16 mtk_musb_get_toggle(struct musb_qh *qh, int is_out)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct musb *musb = qh->hw_ep->musb;
3568c2ecf20Sopenharmony_ci	u8 epnum = qh->hw_ep->epnum;
3578c2ecf20Sopenharmony_ci	u16 toggle;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	toggle = musb_readw(musb->mregs, is_out ? MUSB_TXTOG : MUSB_RXTOG);
3608c2ecf20Sopenharmony_ci	return toggle & (1 << epnum);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic u16 mtk_musb_set_toggle(struct musb_qh *qh, int is_out, struct urb *urb)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct musb *musb = qh->hw_ep->musb;
3668c2ecf20Sopenharmony_ci	u8 epnum = qh->hw_ep->epnum;
3678c2ecf20Sopenharmony_ci	u16 value, toggle;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	toggle = usb_gettoggle(urb->dev, qh->epnum, is_out);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (is_out) {
3728c2ecf20Sopenharmony_ci		value = musb_readw(musb->mregs, MUSB_TXTOG);
3738c2ecf20Sopenharmony_ci		value |= toggle << epnum;
3748c2ecf20Sopenharmony_ci		musb_writew(musb->mregs, MUSB_TXTOG, value);
3758c2ecf20Sopenharmony_ci	} else {
3768c2ecf20Sopenharmony_ci		value = musb_readw(musb->mregs, MUSB_RXTOG);
3778c2ecf20Sopenharmony_ci		value |= toggle << epnum;
3788c2ecf20Sopenharmony_ci		musb_writew(musb->mregs, MUSB_RXTOG, value);
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	return 0;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int mtk_musb_exit(struct musb *musb)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct device *dev = musb->controller;
3878c2ecf20Sopenharmony_ci	struct mtk_glue *glue = dev_get_drvdata(dev->parent);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	mtk_otg_switch_exit(glue);
3908c2ecf20Sopenharmony_ci	phy_power_off(glue->phy);
3918c2ecf20Sopenharmony_ci	phy_exit(glue->phy);
3928c2ecf20Sopenharmony_ci	mtk_musb_clks_disable(glue);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	pm_runtime_put_sync(dev);
3958c2ecf20Sopenharmony_ci	pm_runtime_disable(dev);
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic const struct musb_platform_ops mtk_musb_ops = {
4008c2ecf20Sopenharmony_ci	.quirks = MUSB_DMA_INVENTRA,
4018c2ecf20Sopenharmony_ci	.init = mtk_musb_init,
4028c2ecf20Sopenharmony_ci	.get_toggle = mtk_musb_get_toggle,
4038c2ecf20Sopenharmony_ci	.set_toggle = mtk_musb_set_toggle,
4048c2ecf20Sopenharmony_ci	.exit = mtk_musb_exit,
4058c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_INVENTRA_DMA
4068c2ecf20Sopenharmony_ci	.dma_init = musbhs_dma_controller_create_noirq,
4078c2ecf20Sopenharmony_ci	.dma_exit = musbhs_dma_controller_destroy,
4088c2ecf20Sopenharmony_ci#endif
4098c2ecf20Sopenharmony_ci	.clearb = mtk_musb_clearb,
4108c2ecf20Sopenharmony_ci	.clearw = mtk_musb_clearw,
4118c2ecf20Sopenharmony_ci	.busctl_offset = mtk_musb_busctl_offset,
4128c2ecf20Sopenharmony_ci	.set_mode = mtk_musb_set_mode,
4138c2ecf20Sopenharmony_ci};
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci#define MTK_MUSB_MAX_EP_NUM	8
4168c2ecf20Sopenharmony_ci#define MTK_MUSB_RAM_BITS	11
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic struct musb_fifo_cfg mtk_musb_mode_cfg[] = {
4198c2ecf20Sopenharmony_ci	{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
4208c2ecf20Sopenharmony_ci	{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
4218c2ecf20Sopenharmony_ci	{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
4228c2ecf20Sopenharmony_ci	{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
4238c2ecf20Sopenharmony_ci	{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
4248c2ecf20Sopenharmony_ci	{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
4258c2ecf20Sopenharmony_ci	{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
4268c2ecf20Sopenharmony_ci	{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
4278c2ecf20Sopenharmony_ci	{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
4288c2ecf20Sopenharmony_ci	{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, },
4298c2ecf20Sopenharmony_ci	{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 1024, },
4308c2ecf20Sopenharmony_ci	{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 1024, },
4318c2ecf20Sopenharmony_ci	{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, },
4328c2ecf20Sopenharmony_ci	{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 64, },
4338c2ecf20Sopenharmony_ci};
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic const struct musb_hdrc_config mtk_musb_hdrc_config = {
4368c2ecf20Sopenharmony_ci	.fifo_cfg = mtk_musb_mode_cfg,
4378c2ecf20Sopenharmony_ci	.fifo_cfg_size = ARRAY_SIZE(mtk_musb_mode_cfg),
4388c2ecf20Sopenharmony_ci	.multipoint = true,
4398c2ecf20Sopenharmony_ci	.dyn_fifo = true,
4408c2ecf20Sopenharmony_ci	.num_eps = MTK_MUSB_MAX_EP_NUM,
4418c2ecf20Sopenharmony_ci	.ram_bits = MTK_MUSB_RAM_BITS,
4428c2ecf20Sopenharmony_ci};
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_cistatic const struct platform_device_info mtk_dev_info = {
4458c2ecf20Sopenharmony_ci	.name = "musb-hdrc",
4468c2ecf20Sopenharmony_ci	.id = PLATFORM_DEVID_AUTO,
4478c2ecf20Sopenharmony_ci	.dma_mask = DMA_BIT_MASK(32),
4488c2ecf20Sopenharmony_ci};
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic int mtk_musb_probe(struct platform_device *pdev)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct musb_hdrc_platform_data *pdata;
4538c2ecf20Sopenharmony_ci	struct mtk_glue *glue;
4548c2ecf20Sopenharmony_ci	struct platform_device_info pinfo;
4558c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
4568c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
4578c2ecf20Sopenharmony_ci	int ret;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
4608c2ecf20Sopenharmony_ci	if (!glue)
4618c2ecf20Sopenharmony_ci		return -ENOMEM;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	glue->dev = dev;
4648c2ecf20Sopenharmony_ci	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
4658c2ecf20Sopenharmony_ci	if (!pdata)
4668c2ecf20Sopenharmony_ci		return -ENOMEM;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	ret = of_platform_populate(np, NULL, NULL, dev);
4698c2ecf20Sopenharmony_ci	if (ret) {
4708c2ecf20Sopenharmony_ci		dev_err(dev, "failed to create child devices at %p\n", np);
4718c2ecf20Sopenharmony_ci		return ret;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	ret = mtk_musb_clks_get(glue);
4758c2ecf20Sopenharmony_ci	if (ret)
4768c2ecf20Sopenharmony_ci		return ret;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	pdata->config = &mtk_musb_hdrc_config;
4798c2ecf20Sopenharmony_ci	pdata->platform_ops = &mtk_musb_ops;
4808c2ecf20Sopenharmony_ci	pdata->mode = usb_get_dr_mode(dev);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_USB_MUSB_HOST))
4838c2ecf20Sopenharmony_ci		pdata->mode = USB_DR_MODE_HOST;
4848c2ecf20Sopenharmony_ci	else if (IS_ENABLED(CONFIG_USB_MUSB_GADGET))
4858c2ecf20Sopenharmony_ci		pdata->mode = USB_DR_MODE_PERIPHERAL;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	switch (pdata->mode) {
4888c2ecf20Sopenharmony_ci	case USB_DR_MODE_HOST:
4898c2ecf20Sopenharmony_ci		glue->phy_mode = PHY_MODE_USB_HOST;
4908c2ecf20Sopenharmony_ci		glue->role = USB_ROLE_HOST;
4918c2ecf20Sopenharmony_ci		break;
4928c2ecf20Sopenharmony_ci	case USB_DR_MODE_PERIPHERAL:
4938c2ecf20Sopenharmony_ci		glue->phy_mode = PHY_MODE_USB_DEVICE;
4948c2ecf20Sopenharmony_ci		glue->role = USB_ROLE_DEVICE;
4958c2ecf20Sopenharmony_ci		break;
4968c2ecf20Sopenharmony_ci	case USB_DR_MODE_OTG:
4978c2ecf20Sopenharmony_ci		glue->phy_mode = PHY_MODE_USB_OTG;
4988c2ecf20Sopenharmony_ci		glue->role = USB_ROLE_NONE;
4998c2ecf20Sopenharmony_ci		break;
5008c2ecf20Sopenharmony_ci	default:
5018c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error 'dr_mode' property\n");
5028c2ecf20Sopenharmony_ci		return -EINVAL;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	glue->phy = devm_of_phy_get_by_index(dev, np, 0);
5068c2ecf20Sopenharmony_ci	if (IS_ERR(glue->phy)) {
5078c2ecf20Sopenharmony_ci		dev_err(dev, "fail to getting phy %ld\n",
5088c2ecf20Sopenharmony_ci			PTR_ERR(glue->phy));
5098c2ecf20Sopenharmony_ci		return PTR_ERR(glue->phy);
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	glue->usb_phy = usb_phy_generic_register();
5138c2ecf20Sopenharmony_ci	if (IS_ERR(glue->usb_phy)) {
5148c2ecf20Sopenharmony_ci		dev_err(dev, "fail to registering usb-phy %ld\n",
5158c2ecf20Sopenharmony_ci			PTR_ERR(glue->usb_phy));
5168c2ecf20Sopenharmony_ci		return PTR_ERR(glue->usb_phy);
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	glue->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
5208c2ecf20Sopenharmony_ci	if (IS_ERR(glue->xceiv)) {
5218c2ecf20Sopenharmony_ci		ret = PTR_ERR(glue->xceiv);
5228c2ecf20Sopenharmony_ci		dev_err(dev, "fail to getting usb-phy %d\n", ret);
5238c2ecf20Sopenharmony_ci		goto err_unregister_usb_phy;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, glue);
5278c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
5288c2ecf20Sopenharmony_ci	pm_runtime_get_sync(dev);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	ret = mtk_musb_clks_enable(glue);
5318c2ecf20Sopenharmony_ci	if (ret)
5328c2ecf20Sopenharmony_ci		goto err_enable_clk;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	pinfo = mtk_dev_info;
5358c2ecf20Sopenharmony_ci	pinfo.parent = dev;
5368c2ecf20Sopenharmony_ci	pinfo.res = pdev->resource;
5378c2ecf20Sopenharmony_ci	pinfo.num_res = pdev->num_resources;
5388c2ecf20Sopenharmony_ci	pinfo.data = pdata;
5398c2ecf20Sopenharmony_ci	pinfo.size_data = sizeof(*pdata);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	glue->musb_pdev = platform_device_register_full(&pinfo);
5428c2ecf20Sopenharmony_ci	if (IS_ERR(glue->musb_pdev)) {
5438c2ecf20Sopenharmony_ci		ret = PTR_ERR(glue->musb_pdev);
5448c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register musb device: %d\n", ret);
5458c2ecf20Sopenharmony_ci		goto err_device_register;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	return 0;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cierr_device_register:
5518c2ecf20Sopenharmony_ci	mtk_musb_clks_disable(glue);
5528c2ecf20Sopenharmony_cierr_enable_clk:
5538c2ecf20Sopenharmony_ci	pm_runtime_put_sync(dev);
5548c2ecf20Sopenharmony_ci	pm_runtime_disable(dev);
5558c2ecf20Sopenharmony_cierr_unregister_usb_phy:
5568c2ecf20Sopenharmony_ci	usb_phy_generic_unregister(glue->usb_phy);
5578c2ecf20Sopenharmony_ci	return ret;
5588c2ecf20Sopenharmony_ci}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_cistatic int mtk_musb_remove(struct platform_device *pdev)
5618c2ecf20Sopenharmony_ci{
5628c2ecf20Sopenharmony_ci	struct mtk_glue *glue = platform_get_drvdata(pdev);
5638c2ecf20Sopenharmony_ci	struct platform_device *usb_phy = glue->usb_phy;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	platform_device_unregister(glue->musb_pdev);
5668c2ecf20Sopenharmony_ci	usb_phy_generic_unregister(usb_phy);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	return 0;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
5728c2ecf20Sopenharmony_cistatic const struct of_device_id mtk_musb_match[] = {
5738c2ecf20Sopenharmony_ci	{.compatible = "mediatek,mtk-musb",},
5748c2ecf20Sopenharmony_ci	{},
5758c2ecf20Sopenharmony_ci};
5768c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_musb_match);
5778c2ecf20Sopenharmony_ci#endif
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic struct platform_driver mtk_musb_driver = {
5808c2ecf20Sopenharmony_ci	.probe = mtk_musb_probe,
5818c2ecf20Sopenharmony_ci	.remove = mtk_musb_remove,
5828c2ecf20Sopenharmony_ci	.driver = {
5838c2ecf20Sopenharmony_ci		   .name = "musb-mtk",
5848c2ecf20Sopenharmony_ci		   .of_match_table = of_match_ptr(mtk_musb_match),
5858c2ecf20Sopenharmony_ci	},
5868c2ecf20Sopenharmony_ci};
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cimodule_platform_driver(mtk_musb_driver);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MediaTek MUSB Glue Layer");
5918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Min Guo <min.guo@mediatek.com>");
5928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
593