1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Renesas RZ/V2M USB3DRD driver 4 * 5 * Copyright (C) 2022 Renesas Electronics Corporation 6 */ 7 8#include <linux/io.h> 9#include <linux/of_platform.h> 10#include <linux/platform_device.h> 11#include <linux/pm_runtime.h> 12#include <linux/reset.h> 13#include <linux/usb/rzv2m_usb3drd.h> 14 15#define USB_PERI_DRD_CON 0x000 16 17#define USB_PERI_DRD_CON_PERI_RST BIT(31) 18#define USB_PERI_DRD_CON_HOST_RST BIT(30) 19#define USB_PERI_DRD_CON_PERI_CON BIT(24) 20 21static void rzv2m_usb3drd_set_bit(struct rzv2m_usb3drd *usb3, u32 bits, 22 u32 offs) 23{ 24 u32 val = readl(usb3->reg + offs); 25 26 val |= bits; 27 writel(val, usb3->reg + offs); 28} 29 30static void rzv2m_usb3drd_clear_bit(struct rzv2m_usb3drd *usb3, u32 bits, 31 u32 offs) 32{ 33 u32 val = readl(usb3->reg + offs); 34 35 val &= ~bits; 36 writel(val, usb3->reg + offs); 37} 38 39void rzv2m_usb3drd_reset(struct device *dev, bool host) 40{ 41 struct rzv2m_usb3drd *usb3 = dev_get_drvdata(dev); 42 43 if (host) { 44 rzv2m_usb3drd_clear_bit(usb3, USB_PERI_DRD_CON_PERI_CON, 45 USB_PERI_DRD_CON); 46 rzv2m_usb3drd_clear_bit(usb3, USB_PERI_DRD_CON_HOST_RST, 47 USB_PERI_DRD_CON); 48 rzv2m_usb3drd_set_bit(usb3, USB_PERI_DRD_CON_PERI_RST, 49 USB_PERI_DRD_CON); 50 } else { 51 rzv2m_usb3drd_set_bit(usb3, USB_PERI_DRD_CON_PERI_CON, 52 USB_PERI_DRD_CON); 53 rzv2m_usb3drd_set_bit(usb3, USB_PERI_DRD_CON_HOST_RST, 54 USB_PERI_DRD_CON); 55 rzv2m_usb3drd_clear_bit(usb3, USB_PERI_DRD_CON_PERI_RST, 56 USB_PERI_DRD_CON); 57 } 58} 59EXPORT_SYMBOL_GPL(rzv2m_usb3drd_reset); 60 61static void rzv2m_usb3drd_remove(struct platform_device *pdev) 62{ 63 struct rzv2m_usb3drd *usb3 = platform_get_drvdata(pdev); 64 65 of_platform_depopulate(usb3->dev); 66 pm_runtime_put(usb3->dev); 67 pm_runtime_disable(&pdev->dev); 68 reset_control_assert(usb3->drd_rstc); 69} 70 71static int rzv2m_usb3drd_probe(struct platform_device *pdev) 72{ 73 struct rzv2m_usb3drd *usb3; 74 int ret; 75 76 usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL); 77 if (!usb3) 78 return -ENOMEM; 79 80 usb3->dev = &pdev->dev; 81 82 usb3->drd_irq = platform_get_irq_byname(pdev, "drd"); 83 if (usb3->drd_irq < 0) 84 return usb3->drd_irq; 85 86 usb3->reg = devm_platform_ioremap_resource(pdev, 0); 87 if (IS_ERR(usb3->reg)) 88 return PTR_ERR(usb3->reg); 89 90 platform_set_drvdata(pdev, usb3); 91 92 usb3->drd_rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 93 if (IS_ERR(usb3->drd_rstc)) 94 return dev_err_probe(&pdev->dev, PTR_ERR(usb3->drd_rstc), 95 "failed to get drd reset"); 96 97 reset_control_deassert(usb3->drd_rstc); 98 pm_runtime_enable(&pdev->dev); 99 ret = pm_runtime_resume_and_get(usb3->dev); 100 if (ret) 101 goto err_rst; 102 103 ret = of_platform_populate(usb3->dev->of_node, NULL, NULL, usb3->dev); 104 if (ret) 105 goto err_pm; 106 107 return 0; 108 109err_pm: 110 pm_runtime_put(usb3->dev); 111 112err_rst: 113 pm_runtime_disable(&pdev->dev); 114 reset_control_assert(usb3->drd_rstc); 115 return ret; 116} 117 118static const struct of_device_id rzv2m_usb3drd_of_match[] = { 119 { .compatible = "renesas,rzv2m-usb3drd", }, 120 { /* Sentinel */ } 121}; 122MODULE_DEVICE_TABLE(of, rzv2m_usb3drd_of_match); 123 124static struct platform_driver rzv2m_usb3drd_driver = { 125 .driver = { 126 .name = "rzv2m-usb3drd", 127 .of_match_table = rzv2m_usb3drd_of_match, 128 }, 129 .probe = rzv2m_usb3drd_probe, 130 .remove_new = rzv2m_usb3drd_remove, 131}; 132module_platform_driver(rzv2m_usb3drd_driver); 133 134MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); 135MODULE_DESCRIPTION("Renesas RZ/V2M USB3DRD driver"); 136MODULE_LICENSE("GPL"); 137MODULE_ALIAS("platform:rzv2m_usb3drd"); 138