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_device.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 21 static 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 30 static 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 39 void 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 } 59 EXPORT_SYMBOL_GPL(rzv2m_usb3drd_reset); 60 61 static int 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 return 0; 71 } 72 73 static int rzv2m_usb3drd_probe(struct platform_device *pdev) 74 { 75 struct rzv2m_usb3drd *usb3; 76 int ret; 77 78 usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL); 79 if (!usb3) 80 return -ENOMEM; 81 82 usb3->dev = &pdev->dev; 83 84 usb3->drd_irq = platform_get_irq_byname(pdev, "drd"); 85 if (usb3->drd_irq < 0) 86 return usb3->drd_irq; 87 88 usb3->reg = devm_platform_ioremap_resource(pdev, 0); 89 if (IS_ERR(usb3->reg)) 90 return PTR_ERR(usb3->reg); 91 92 platform_set_drvdata(pdev, usb3); 93 94 usb3->drd_rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 95 if (IS_ERR(usb3->drd_rstc)) 96 return dev_err_probe(&pdev->dev, PTR_ERR(usb3->drd_rstc), 97 "failed to get drd reset"); 98 99 reset_control_deassert(usb3->drd_rstc); 100 pm_runtime_enable(&pdev->dev); 101 ret = pm_runtime_resume_and_get(usb3->dev); 102 if (ret) 103 goto err_rst; 104 105 ret = of_platform_populate(usb3->dev->of_node, NULL, NULL, usb3->dev); 106 if (ret) 107 goto err_pm; 108 109 return 0; 110 111 err_pm: 112 pm_runtime_put(usb3->dev); 113 114 err_rst: 115 pm_runtime_disable(&pdev->dev); 116 reset_control_assert(usb3->drd_rstc); 117 return ret; 118 } 119 120 static const struct of_device_id rzv2m_usb3drd_of_match[] = { 121 { .compatible = "renesas,rzv2m-usb3drd", }, 122 { /* Sentinel */ } 123 }; 124 MODULE_DEVICE_TABLE(of, rzv2m_usb3drd_of_match); 125 126 static struct platform_driver rzv2m_usb3drd_driver = { 127 .driver = { 128 .name = "rzv2m-usb3drd", 129 .of_match_table = of_match_ptr(rzv2m_usb3drd_of_match), 130 }, 131 .probe = rzv2m_usb3drd_probe, 132 .remove = rzv2m_usb3drd_remove, 133 }; 134 module_platform_driver(rzv2m_usb3drd_driver); 135 136 MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); 137 MODULE_DESCRIPTION("Renesas RZ/V2M USB3DRD driver"); 138 MODULE_LICENSE("GPL"); 139 MODULE_ALIAS("platform:rzv2m_usb3drd"); 140