162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Ingenic JZ4740 "glue layer"
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013, Apelete Seketeli <apelete@seketeli.net>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1062306a36Sopenharmony_ci#include <linux/errno.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/phy/phy.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/usb/role.h>
1762306a36Sopenharmony_ci#include <linux/usb/usb_phy_generic.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "musb_core.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistruct jz4740_glue {
2262306a36Sopenharmony_ci	struct platform_device	*pdev;
2362306a36Sopenharmony_ci	struct musb		*musb;
2462306a36Sopenharmony_ci	struct clk		*clk;
2562306a36Sopenharmony_ci	struct usb_role_switch	*role_sw;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic irqreturn_t jz4740_musb_interrupt(int irq, void *__hci)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	unsigned long	flags;
3162306a36Sopenharmony_ci	irqreturn_t	retval = IRQ_NONE, retval_dma = IRQ_NONE;
3262306a36Sopenharmony_ci	struct musb	*musb = __hci;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_USB_INVENTRA_DMA) && musb->dma_controller)
3562306a36Sopenharmony_ci		retval_dma = dma_controller_irq(irq, musb->dma_controller);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	spin_lock_irqsave(&musb->lock, flags);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
4062306a36Sopenharmony_ci	musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
4162306a36Sopenharmony_ci	musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/*
4462306a36Sopenharmony_ci	 * The controller is gadget only, the state of the host mode IRQ bits is
4562306a36Sopenharmony_ci	 * undefined. Mask them to make sure that the musb driver core will
4662306a36Sopenharmony_ci	 * never see them set
4762306a36Sopenharmony_ci	 */
4862306a36Sopenharmony_ci	musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME |
4962306a36Sopenharmony_ci			 MUSB_INTR_RESET | MUSB_INTR_SOF;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (musb->int_usb || musb->int_tx || musb->int_rx)
5262306a36Sopenharmony_ci		retval = musb_interrupt(musb);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	spin_unlock_irqrestore(&musb->lock, flags);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (retval == IRQ_HANDLED || retval_dma == IRQ_HANDLED)
5762306a36Sopenharmony_ci		return IRQ_HANDLED;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return IRQ_NONE;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
6362306a36Sopenharmony_ci	{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
6462306a36Sopenharmony_ci	{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
6562306a36Sopenharmony_ci	{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, },
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic const struct musb_hdrc_config jz4740_musb_config = {
6962306a36Sopenharmony_ci	/* Silicon does not implement USB OTG. */
7062306a36Sopenharmony_ci	.multipoint	= 0,
7162306a36Sopenharmony_ci	/* Max EPs scanned, driver will decide which EP can be used. */
7262306a36Sopenharmony_ci	.num_eps	= 4,
7362306a36Sopenharmony_ci	/* RAMbits needed to configure EPs from table */
7462306a36Sopenharmony_ci	.ram_bits	= 9,
7562306a36Sopenharmony_ci	.fifo_cfg	= jz4740_musb_fifo_cfg,
7662306a36Sopenharmony_ci	.fifo_cfg_size	= ARRAY_SIZE(jz4740_musb_fifo_cfg),
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int jz4740_musb_role_switch_set(struct usb_role_switch *sw,
8062306a36Sopenharmony_ci				       enum usb_role role)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct jz4740_glue *glue = usb_role_switch_get_drvdata(sw);
8362306a36Sopenharmony_ci	struct usb_phy *phy = glue->musb->xceiv;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (!phy)
8662306a36Sopenharmony_ci		return 0;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	switch (role) {
8962306a36Sopenharmony_ci	case USB_ROLE_NONE:
9062306a36Sopenharmony_ci		atomic_notifier_call_chain(&phy->notifier, USB_EVENT_NONE, phy);
9162306a36Sopenharmony_ci		break;
9262306a36Sopenharmony_ci	case USB_ROLE_DEVICE:
9362306a36Sopenharmony_ci		atomic_notifier_call_chain(&phy->notifier, USB_EVENT_VBUS, phy);
9462306a36Sopenharmony_ci		break;
9562306a36Sopenharmony_ci	case USB_ROLE_HOST:
9662306a36Sopenharmony_ci		atomic_notifier_call_chain(&phy->notifier, USB_EVENT_ID, phy);
9762306a36Sopenharmony_ci		break;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return 0;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int jz4740_musb_init(struct musb *musb)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct device *dev = musb->controller->parent;
10662306a36Sopenharmony_ci	struct jz4740_glue *glue = dev_get_drvdata(dev);
10762306a36Sopenharmony_ci	struct usb_role_switch_desc role_sw_desc = {
10862306a36Sopenharmony_ci		.set = jz4740_musb_role_switch_set,
10962306a36Sopenharmony_ci		.driver_data = glue,
11062306a36Sopenharmony_ci		.fwnode = dev_fwnode(dev),
11162306a36Sopenharmony_ci	};
11262306a36Sopenharmony_ci	int err;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	glue->musb = musb;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_GENERIC_PHY)) {
11762306a36Sopenharmony_ci		musb->phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
11862306a36Sopenharmony_ci		if (IS_ERR(musb->phy)) {
11962306a36Sopenharmony_ci			err = PTR_ERR(musb->phy);
12062306a36Sopenharmony_ci			if (err != -ENODEV) {
12162306a36Sopenharmony_ci				dev_err(dev, "Unable to get PHY\n");
12262306a36Sopenharmony_ci				return err;
12362306a36Sopenharmony_ci			}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci			musb->phy = NULL;
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (musb->phy) {
13062306a36Sopenharmony_ci		err = phy_init(musb->phy);
13162306a36Sopenharmony_ci		if (err) {
13262306a36Sopenharmony_ci			dev_err(dev, "Failed to init PHY\n");
13362306a36Sopenharmony_ci			return err;
13462306a36Sopenharmony_ci		}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		err = phy_power_on(musb->phy);
13762306a36Sopenharmony_ci		if (err) {
13862306a36Sopenharmony_ci			dev_err(dev, "Unable to power on PHY\n");
13962306a36Sopenharmony_ci			goto err_phy_shutdown;
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci	} else {
14262306a36Sopenharmony_ci		if (dev->of_node)
14362306a36Sopenharmony_ci			musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0);
14462306a36Sopenharmony_ci		else
14562306a36Sopenharmony_ci			musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
14662306a36Sopenharmony_ci		if (IS_ERR(musb->xceiv)) {
14762306a36Sopenharmony_ci			dev_err(dev, "No transceiver configured\n");
14862306a36Sopenharmony_ci			return PTR_ERR(musb->xceiv);
14962306a36Sopenharmony_ci		}
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	glue->role_sw = usb_role_switch_register(dev, &role_sw_desc);
15362306a36Sopenharmony_ci	if (IS_ERR(glue->role_sw)) {
15462306a36Sopenharmony_ci		dev_err(dev, "Failed to register USB role switch\n");
15562306a36Sopenharmony_ci		err = PTR_ERR(glue->role_sw);
15662306a36Sopenharmony_ci		goto err_phy_power_down;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/*
16062306a36Sopenharmony_ci	 * Silicon does not implement ConfigData register.
16162306a36Sopenharmony_ci	 * Set dyn_fifo to avoid reading EP config from hardware.
16262306a36Sopenharmony_ci	 */
16362306a36Sopenharmony_ci	musb->dyn_fifo = true;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	musb->isr = jz4740_musb_interrupt;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return 0;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cierr_phy_power_down:
17062306a36Sopenharmony_ci	if (musb->phy)
17162306a36Sopenharmony_ci		phy_power_off(musb->phy);
17262306a36Sopenharmony_cierr_phy_shutdown:
17362306a36Sopenharmony_ci	if (musb->phy)
17462306a36Sopenharmony_ci		phy_exit(musb->phy);
17562306a36Sopenharmony_ci	return err;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic int jz4740_musb_exit(struct musb *musb)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct jz4740_glue *glue = dev_get_drvdata(musb->controller->parent);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	usb_role_switch_unregister(glue->role_sw);
18362306a36Sopenharmony_ci	if (musb->phy) {
18462306a36Sopenharmony_ci		phy_power_off(musb->phy);
18562306a36Sopenharmony_ci		phy_exit(musb->phy);
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return 0;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic const struct musb_platform_ops jz4740_musb_ops = {
19262306a36Sopenharmony_ci	.quirks		= MUSB_DMA_INVENTRA | MUSB_INDEXED_EP,
19362306a36Sopenharmony_ci	.fifo_mode	= 2,
19462306a36Sopenharmony_ci	.init		= jz4740_musb_init,
19562306a36Sopenharmony_ci	.exit		= jz4740_musb_exit,
19662306a36Sopenharmony_ci#ifdef CONFIG_USB_INVENTRA_DMA
19762306a36Sopenharmony_ci	.dma_init	= musbhs_dma_controller_create_noirq,
19862306a36Sopenharmony_ci	.dma_exit	= musbhs_dma_controller_destroy,
19962306a36Sopenharmony_ci#endif
20062306a36Sopenharmony_ci};
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic const struct musb_hdrc_platform_data jz4740_musb_pdata = {
20362306a36Sopenharmony_ci	.mode		= MUSB_PERIPHERAL,
20462306a36Sopenharmony_ci	.config		= &jz4740_musb_config,
20562306a36Sopenharmony_ci	.platform_ops	= &jz4740_musb_ops,
20662306a36Sopenharmony_ci};
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = {
20962306a36Sopenharmony_ci	{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
21062306a36Sopenharmony_ci	{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
21162306a36Sopenharmony_ci	{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
21262306a36Sopenharmony_ci	{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
21362306a36Sopenharmony_ci	{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
21462306a36Sopenharmony_ci	{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
21562306a36Sopenharmony_ci	{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
21662306a36Sopenharmony_ci	{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
21762306a36Sopenharmony_ci	{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
21862306a36Sopenharmony_ci	{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, },
21962306a36Sopenharmony_ci};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic struct musb_hdrc_config jz4770_musb_config = {
22262306a36Sopenharmony_ci	.multipoint	= 1,
22362306a36Sopenharmony_ci	.num_eps	= 11,
22462306a36Sopenharmony_ci	.ram_bits	= 11,
22562306a36Sopenharmony_ci	.fifo_cfg	= jz4770_musb_fifo_cfg,
22662306a36Sopenharmony_ci	.fifo_cfg_size	= ARRAY_SIZE(jz4770_musb_fifo_cfg),
22762306a36Sopenharmony_ci};
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic const struct musb_hdrc_platform_data jz4770_musb_pdata = {
23062306a36Sopenharmony_ci	.mode		= MUSB_PERIPHERAL, /* TODO: support OTG */
23162306a36Sopenharmony_ci	.config		= &jz4770_musb_config,
23262306a36Sopenharmony_ci	.platform_ops	= &jz4740_musb_ops,
23362306a36Sopenharmony_ci};
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int jz4740_probe(struct platform_device *pdev)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct device			*dev = &pdev->dev;
23862306a36Sopenharmony_ci	const struct musb_hdrc_platform_data *pdata;
23962306a36Sopenharmony_ci	struct platform_device		*musb;
24062306a36Sopenharmony_ci	struct jz4740_glue		*glue;
24162306a36Sopenharmony_ci	struct clk			*clk;
24262306a36Sopenharmony_ci	int				ret;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
24562306a36Sopenharmony_ci	if (!glue)
24662306a36Sopenharmony_ci		return -ENOMEM;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	pdata = of_device_get_match_data(dev);
24962306a36Sopenharmony_ci	if (!pdata) {
25062306a36Sopenharmony_ci		dev_err(dev, "missing platform data\n");
25162306a36Sopenharmony_ci		return -EINVAL;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
25562306a36Sopenharmony_ci	if (!musb) {
25662306a36Sopenharmony_ci		dev_err(dev, "failed to allocate musb device\n");
25762306a36Sopenharmony_ci		return -ENOMEM;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	clk = devm_clk_get(dev, "udc");
26162306a36Sopenharmony_ci	if (IS_ERR(clk)) {
26262306a36Sopenharmony_ci		dev_err(dev, "failed to get clock\n");
26362306a36Sopenharmony_ci		ret = PTR_ERR(clk);
26462306a36Sopenharmony_ci		goto err_platform_device_put;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	ret = clk_prepare_enable(clk);
26862306a36Sopenharmony_ci	if (ret) {
26962306a36Sopenharmony_ci		dev_err(dev, "failed to enable clock\n");
27062306a36Sopenharmony_ci		goto err_platform_device_put;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	musb->dev.parent		= dev;
27462306a36Sopenharmony_ci	musb->dev.dma_mask		= &musb->dev.coherent_dma_mask;
27562306a36Sopenharmony_ci	musb->dev.coherent_dma_mask	= DMA_BIT_MASK(32);
27662306a36Sopenharmony_ci	device_set_of_node_from_dev(&musb->dev, dev);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	glue->pdev			= musb;
27962306a36Sopenharmony_ci	glue->clk			= clk;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	platform_set_drvdata(pdev, glue);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ret = platform_device_add_resources(musb, pdev->resource,
28462306a36Sopenharmony_ci					    pdev->num_resources);
28562306a36Sopenharmony_ci	if (ret) {
28662306a36Sopenharmony_ci		dev_err(dev, "failed to add resources\n");
28762306a36Sopenharmony_ci		goto err_clk_disable;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
29162306a36Sopenharmony_ci	if (ret) {
29262306a36Sopenharmony_ci		dev_err(dev, "failed to add platform_data\n");
29362306a36Sopenharmony_ci		goto err_clk_disable;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	ret = platform_device_add(musb);
29762306a36Sopenharmony_ci	if (ret) {
29862306a36Sopenharmony_ci		dev_err(dev, "failed to register musb device\n");
29962306a36Sopenharmony_ci		goto err_clk_disable;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return 0;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cierr_clk_disable:
30562306a36Sopenharmony_ci	clk_disable_unprepare(clk);
30662306a36Sopenharmony_cierr_platform_device_put:
30762306a36Sopenharmony_ci	platform_device_put(musb);
30862306a36Sopenharmony_ci	return ret;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic void jz4740_remove(struct platform_device *pdev)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct jz4740_glue *glue = platform_get_drvdata(pdev);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	platform_device_unregister(glue->pdev);
31662306a36Sopenharmony_ci	clk_disable_unprepare(glue->clk);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic const struct of_device_id jz4740_musb_of_match[] = {
32062306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4740-musb", .data = &jz4740_musb_pdata },
32162306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4770-musb", .data = &jz4770_musb_pdata },
32262306a36Sopenharmony_ci	{ /* sentinel */ },
32362306a36Sopenharmony_ci};
32462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, jz4740_musb_of_match);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic struct platform_driver jz4740_driver = {
32762306a36Sopenharmony_ci	.probe		= jz4740_probe,
32862306a36Sopenharmony_ci	.remove_new	= jz4740_remove,
32962306a36Sopenharmony_ci	.driver		= {
33062306a36Sopenharmony_ci		.name	= "musb-jz4740",
33162306a36Sopenharmony_ci		.of_match_table = jz4740_musb_of_match,
33262306a36Sopenharmony_ci	},
33362306a36Sopenharmony_ci};
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ciMODULE_DESCRIPTION("JZ4740 MUSB Glue Layer");
33662306a36Sopenharmony_ciMODULE_AUTHOR("Apelete Seketeli <apelete@seketeli.net>");
33762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
33862306a36Sopenharmony_cimodule_platform_driver(jz4740_driver);
339