Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

phy: starfive: Add JH7110 USB 2.0 PHY driver

Add Starfive JH7110 SoC USB 2.0 PHY driver support.
USB 2.0 PHY default connect to Cadence USB controller.

Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
Reviewed-by: Roger Quadros <rogerq@kernel.org>
Link: https://lore.kernel.org/r/20230629075115.11934-4-minda.chen@starfivetech.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Minda Chen and committed by
Vinod Koul
16d3a71c 69d41115

+176
+6
MAINTAINERS
··· 20335 20335 F: Documentation/devicetree/bindings/watchdog/starfive* 20336 20336 F: drivers/watchdog/starfive-wdt.c 20337 20337 20338 + STARFIVE JH71X0 USB PHY DRIVER 20339 + M: Minda Chen <minda.chen@starfivetech.com> 20340 + S: Supported 20341 + F: Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml 20342 + F: drivers/phy/starfive/phy-jh7110-usb.c 20343 + 20338 20344 STATIC BRANCH/CALL 20339 20345 M: Peter Zijlstra <peterz@infradead.org> 20340 20346 M: Josh Poimboeuf <jpoimboe@kernel.org>
+1
drivers/phy/Kconfig
··· 92 92 source "drivers/phy/samsung/Kconfig" 93 93 source "drivers/phy/socionext/Kconfig" 94 94 source "drivers/phy/st/Kconfig" 95 + source "drivers/phy/starfive/Kconfig" 95 96 source "drivers/phy/sunplus/Kconfig" 96 97 source "drivers/phy/tegra/Kconfig" 97 98 source "drivers/phy/ti/Kconfig"
+1
drivers/phy/Makefile
··· 31 31 samsung/ \ 32 32 socionext/ \ 33 33 st/ \ 34 + starfive/ \ 34 35 sunplus/ \ 35 36 tegra/ \ 36 37 ti/ \
+14
drivers/phy/starfive/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Phy drivers for StarFive platforms 4 + # 5 + 6 + config PHY_STARFIVE_JH7110_USB 7 + tristate "Starfive JH7110 USB 2.0 PHY support" 8 + depends on USB_SUPPORT 9 + select GENERIC_PHY 10 + help 11 + Enable this to support the StarFive USB 2.0 PHY, 12 + used with the Cadence USB controller. 13 + If M is selected, the module will be called 14 + phy-jh7110-usb.ko.
+2
drivers/phy/starfive/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + obj-$(CONFIG_PHY_STARFIVE_JH7110_USB) += phy-jh7110-usb.o
+152
drivers/phy/starfive/phy-jh7110-usb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * StarFive JH7110 USB 2.0 PHY driver 4 + * 5 + * Copyright (C) 2023 StarFive Technology Co., Ltd. 6 + * Author: Minda Chen <minda.chen@starfivetech.com> 7 + */ 8 + 9 + #include <linux/bits.h> 10 + #include <linux/clk.h> 11 + #include <linux/err.h> 12 + #include <linux/io.h> 13 + #include <linux/module.h> 14 + #include <linux/phy/phy.h> 15 + #include <linux/platform_device.h> 16 + #include <linux/usb/of.h> 17 + 18 + #define USB_125M_CLK_RATE 125000000 19 + #define USB_LS_KEEPALIVE_OFF 0x4 20 + #define USB_LS_KEEPALIVE_ENABLE BIT(4) 21 + 22 + struct jh7110_usb2_phy { 23 + struct phy *phy; 24 + void __iomem *regs; 25 + struct clk *usb_125m_clk; 26 + struct clk *app_125m; 27 + enum phy_mode mode; 28 + }; 29 + 30 + static void usb2_set_ls_keepalive(struct jh7110_usb2_phy *phy, bool set) 31 + { 32 + unsigned int val; 33 + 34 + /* Host mode enable the LS speed keep-alive signal */ 35 + val = readl(phy->regs + USB_LS_KEEPALIVE_OFF); 36 + if (set) 37 + val |= USB_LS_KEEPALIVE_ENABLE; 38 + else 39 + val &= ~USB_LS_KEEPALIVE_ENABLE; 40 + 41 + writel(val, phy->regs + USB_LS_KEEPALIVE_OFF); 42 + } 43 + 44 + static int usb2_phy_set_mode(struct phy *_phy, 45 + enum phy_mode mode, int submode) 46 + { 47 + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); 48 + 49 + switch (mode) { 50 + case PHY_MODE_USB_HOST: 51 + case PHY_MODE_USB_DEVICE: 52 + case PHY_MODE_USB_OTG: 53 + break; 54 + default: 55 + return -EINVAL; 56 + } 57 + 58 + if (mode != phy->mode) { 59 + dev_dbg(&_phy->dev, "Changing phy to %d\n", mode); 60 + phy->mode = mode; 61 + usb2_set_ls_keepalive(phy, (mode != PHY_MODE_USB_DEVICE)); 62 + } 63 + 64 + return 0; 65 + } 66 + 67 + static int jh7110_usb2_phy_init(struct phy *_phy) 68 + { 69 + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); 70 + int ret; 71 + 72 + ret = clk_set_rate(phy->usb_125m_clk, USB_125M_CLK_RATE); 73 + if (ret) 74 + return ret; 75 + 76 + ret = clk_prepare_enable(phy->app_125m); 77 + if (ret) 78 + return ret; 79 + 80 + return 0; 81 + } 82 + 83 + static int jh7110_usb2_phy_exit(struct phy *_phy) 84 + { 85 + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); 86 + 87 + clk_disable_unprepare(phy->app_125m); 88 + 89 + return 0; 90 + } 91 + 92 + static const struct phy_ops jh7110_usb2_phy_ops = { 93 + .init = jh7110_usb2_phy_init, 94 + .exit = jh7110_usb2_phy_exit, 95 + .set_mode = usb2_phy_set_mode, 96 + .owner = THIS_MODULE, 97 + }; 98 + 99 + static int jh7110_usb_phy_probe(struct platform_device *pdev) 100 + { 101 + struct jh7110_usb2_phy *phy; 102 + struct device *dev = &pdev->dev; 103 + struct phy_provider *phy_provider; 104 + 105 + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 106 + if (!phy) 107 + return -ENOMEM; 108 + 109 + phy->usb_125m_clk = devm_clk_get(dev, "125m"); 110 + if (IS_ERR(phy->usb_125m_clk)) 111 + return dev_err_probe(dev, PTR_ERR(phy->usb_125m_clk), 112 + "Failed to get 125m clock\n"); 113 + 114 + phy->app_125m = devm_clk_get(dev, "app_125m"); 115 + if (IS_ERR(phy->app_125m)) 116 + return dev_err_probe(dev, PTR_ERR(phy->app_125m), 117 + "Failed to get app 125m clock\n"); 118 + 119 + phy->regs = devm_platform_ioremap_resource(pdev, 0); 120 + if (IS_ERR(phy->regs)) 121 + return dev_err_probe(dev, PTR_ERR(phy->regs), 122 + "Failed to map phy base\n"); 123 + 124 + phy->phy = devm_phy_create(dev, NULL, &jh7110_usb2_phy_ops); 125 + if (IS_ERR(phy->phy)) 126 + return dev_err_probe(dev, PTR_ERR(phy->phy), 127 + "Failed to create phy\n"); 128 + 129 + phy_set_drvdata(phy->phy, phy); 130 + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 131 + 132 + return PTR_ERR_OR_ZERO(phy_provider); 133 + } 134 + 135 + static const struct of_device_id jh7110_usb_phy_of_match[] = { 136 + { .compatible = "starfive,jh7110-usb-phy" }, 137 + { /* sentinel */ }, 138 + }; 139 + MODULE_DEVICE_TABLE(of, jh7110_usb_phy_of_match); 140 + 141 + static struct platform_driver jh7110_usb_phy_driver = { 142 + .probe = jh7110_usb_phy_probe, 143 + .driver = { 144 + .of_match_table = jh7110_usb_phy_of_match, 145 + .name = "jh7110-usb-phy", 146 + } 147 + }; 148 + module_platform_driver(jh7110_usb_phy_driver); 149 + 150 + MODULE_DESCRIPTION("StarFive JH7110 USB 2.0 PHY driver"); 151 + MODULE_AUTHOR("Minda Chen <minda.chen@starfivetech.com>"); 152 + MODULE_LICENSE("GPL");