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

PHY: Add driver for Pistachio USB2.0 PHY

Add a driver for the USB2.0 PHY found on the IMG Pistachio SoC.

Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Cc: devicetree@vger.kernel.org
Cc: linux-mips@linux-mips.org
Cc: linux-kernel@vger.kernel.org
Cc: James Hartley <james.hartley@imgtec.com>
Cc: Damien Horsley <Damien.Horsley@imgtec.com>
Patchwork: https://patchwork.linux-mips.org/patch/9728/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

Andrew Bresticker and committed by
Ralf Baechle
57991eba 90bc35c5

+214
+7
drivers/phy/Kconfig
··· 247 247 This driver provides PHY interface for USB 3.0 DRD controller 248 248 present on Exynos5 SoC series. 249 249 250 + config PHY_PISTACHIO_USB 251 + tristate "IMG Pistachio USB2.0 PHY driver" 252 + depends on MACH_PISTACHIO 253 + select GENERIC_PHY 254 + help 255 + Enable this to support the USB2.0 PHY on the IMG Pistachio SoC. 256 + 250 257 config PHY_QCOM_APQ8064_SATA 251 258 tristate "Qualcomm APQ8064 SATA SerDes/PHY driver" 252 259 depends on ARCH_QCOM
+1
drivers/phy/Makefile
··· 40 40 obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o 41 41 obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o 42 42 obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o 43 + obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
+206
drivers/phy/phy-pistachio-usb.c
··· 1 + /* 2 + * IMG Pistachio USB PHY driver 3 + * 4 + * Copyright (C) 2015 Google, Inc. 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms and conditions of the GNU General Public License, 8 + * version 2, as published by the Free Software Foundation. 9 + */ 10 + 11 + #include <linux/clk.h> 12 + #include <linux/delay.h> 13 + #include <linux/io.h> 14 + #include <linux/kernel.h> 15 + #include <linux/mfd/syscon.h> 16 + #include <linux/module.h> 17 + #include <linux/of.h> 18 + #include <linux/phy/phy.h> 19 + #include <linux/platform_device.h> 20 + #include <linux/regmap.h> 21 + 22 + #include <dt-bindings/phy/phy-pistachio-usb.h> 23 + 24 + #define USB_PHY_CONTROL1 0x04 25 + #define USB_PHY_CONTROL1_FSEL_SHIFT 2 26 + #define USB_PHY_CONTROL1_FSEL_MASK 0x7 27 + 28 + #define USB_PHY_STRAP_CONTROL 0x10 29 + #define USB_PHY_STRAP_CONTROL_REFCLK_SHIFT 4 30 + #define USB_PHY_STRAP_CONTROL_REFCLK_MASK 0x3 31 + 32 + #define USB_PHY_STATUS 0x14 33 + #define USB_PHY_STATUS_RX_PHY_CLK BIT(9) 34 + #define USB_PHY_STATUS_RX_UTMI_CLK BIT(8) 35 + #define USB_PHY_STATUS_VBUS_FAULT BIT(7) 36 + 37 + struct pistachio_usb_phy { 38 + struct device *dev; 39 + struct regmap *cr_top; 40 + struct clk *phy_clk; 41 + unsigned int refclk; 42 + }; 43 + 44 + static const unsigned long fsel_rate_map[] = { 45 + 9600000, 46 + 10000000, 47 + 12000000, 48 + 19200000, 49 + 20000000, 50 + 24000000, 51 + 0, 52 + 50000000, 53 + }; 54 + 55 + static int pistachio_usb_phy_power_on(struct phy *phy) 56 + { 57 + struct pistachio_usb_phy *p_phy = phy_get_drvdata(phy); 58 + unsigned long timeout, rate; 59 + unsigned int i; 60 + int ret; 61 + 62 + ret = clk_prepare_enable(p_phy->phy_clk); 63 + if (ret < 0) { 64 + dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret); 65 + return ret; 66 + } 67 + 68 + regmap_update_bits(p_phy->cr_top, USB_PHY_STRAP_CONTROL, 69 + USB_PHY_STRAP_CONTROL_REFCLK_MASK << 70 + USB_PHY_STRAP_CONTROL_REFCLK_SHIFT, 71 + p_phy->refclk << USB_PHY_STRAP_CONTROL_REFCLK_SHIFT); 72 + 73 + rate = clk_get_rate(p_phy->phy_clk); 74 + if (p_phy->refclk == REFCLK_XO_CRYSTAL && rate != 12000000) { 75 + dev_err(p_phy->dev, "Unsupported rate for XO crystal: %ld\n", 76 + rate); 77 + ret = -EINVAL; 78 + goto disable_clk; 79 + } 80 + 81 + for (i = 0; i < ARRAY_SIZE(fsel_rate_map); i++) { 82 + if (rate == fsel_rate_map[i]) 83 + break; 84 + } 85 + if (i == ARRAY_SIZE(fsel_rate_map)) { 86 + dev_err(p_phy->dev, "Unsupported clock rate: %lu\n", rate); 87 + ret = -EINVAL; 88 + goto disable_clk; 89 + } 90 + 91 + regmap_update_bits(p_phy->cr_top, USB_PHY_CONTROL1, 92 + USB_PHY_CONTROL1_FSEL_MASK << 93 + USB_PHY_CONTROL1_FSEL_SHIFT, 94 + i << USB_PHY_CONTROL1_FSEL_SHIFT); 95 + 96 + timeout = jiffies + msecs_to_jiffies(200); 97 + while (time_before(jiffies, timeout)) { 98 + unsigned int val; 99 + 100 + regmap_read(p_phy->cr_top, USB_PHY_STATUS, &val); 101 + if (val & USB_PHY_STATUS_VBUS_FAULT) { 102 + dev_err(p_phy->dev, "VBUS fault detected\n"); 103 + ret = -EIO; 104 + goto disable_clk; 105 + } 106 + if ((val & USB_PHY_STATUS_RX_PHY_CLK) && 107 + (val & USB_PHY_STATUS_RX_UTMI_CLK)) 108 + return 0; 109 + usleep_range(1000, 1500); 110 + } 111 + 112 + dev_err(p_phy->dev, "Timed out waiting for PHY to power on\n"); 113 + ret = -ETIMEDOUT; 114 + 115 + disable_clk: 116 + clk_disable_unprepare(p_phy->phy_clk); 117 + return ret; 118 + } 119 + 120 + static int pistachio_usb_phy_power_off(struct phy *phy) 121 + { 122 + struct pistachio_usb_phy *p_phy = phy_get_drvdata(phy); 123 + 124 + clk_disable_unprepare(p_phy->phy_clk); 125 + 126 + return 0; 127 + } 128 + 129 + static const struct phy_ops pistachio_usb_phy_ops = { 130 + .power_on = pistachio_usb_phy_power_on, 131 + .power_off = pistachio_usb_phy_power_off, 132 + .owner = THIS_MODULE, 133 + }; 134 + 135 + static int pistachio_usb_phy_probe(struct platform_device *pdev) 136 + { 137 + struct pistachio_usb_phy *p_phy; 138 + struct phy_provider *provider; 139 + struct phy *phy; 140 + int ret; 141 + 142 + p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL); 143 + if (!p_phy) 144 + return -ENOMEM; 145 + p_phy->dev = &pdev->dev; 146 + platform_set_drvdata(pdev, p_phy); 147 + 148 + p_phy->cr_top = syscon_regmap_lookup_by_phandle(p_phy->dev->of_node, 149 + "img,cr-top"); 150 + if (IS_ERR(p_phy->cr_top)) { 151 + dev_err(p_phy->dev, "Failed to get CR_TOP registers: %ld\n", 152 + PTR_ERR(p_phy->cr_top)); 153 + return PTR_ERR(p_phy->cr_top); 154 + } 155 + 156 + p_phy->phy_clk = devm_clk_get(p_phy->dev, "usb_phy"); 157 + if (IS_ERR(p_phy->phy_clk)) { 158 + dev_err(p_phy->dev, "Failed to get usb_phy clock: %ld\n", 159 + PTR_ERR(p_phy->phy_clk)); 160 + return PTR_ERR(p_phy->phy_clk); 161 + } 162 + 163 + ret = of_property_read_u32(p_phy->dev->of_node, "img,refclk", 164 + &p_phy->refclk); 165 + if (ret < 0) { 166 + dev_err(p_phy->dev, "No reference clock selector specified\n"); 167 + return ret; 168 + } 169 + 170 + phy = devm_phy_create(p_phy->dev, NULL, &pistachio_usb_phy_ops); 171 + if (IS_ERR(phy)) { 172 + dev_err(p_phy->dev, "Failed to create PHY: %ld\n", 173 + PTR_ERR(phy)); 174 + return PTR_ERR(phy); 175 + } 176 + phy_set_drvdata(phy, p_phy); 177 + 178 + provider = devm_of_phy_provider_register(p_phy->dev, 179 + of_phy_simple_xlate); 180 + if (IS_ERR(provider)) { 181 + dev_err(p_phy->dev, "Failed to register PHY provider: %ld\n", 182 + PTR_ERR(provider)); 183 + return PTR_ERR(provider); 184 + } 185 + 186 + return 0; 187 + } 188 + 189 + static const struct of_device_id pistachio_usb_phy_of_match[] = { 190 + { .compatible = "img,pistachio-usb-phy", }, 191 + { }, 192 + }; 193 + MODULE_DEVICE_TABLE(of, pistachio_usb_phy_of_match); 194 + 195 + static struct platform_driver pistachio_usb_phy_driver = { 196 + .probe = pistachio_usb_phy_probe, 197 + .driver = { 198 + .name = "pistachio-usb-phy", 199 + .of_match_table = pistachio_usb_phy_of_match, 200 + }, 201 + }; 202 + module_platform_driver(pistachio_usb_phy_driver); 203 + 204 + MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>"); 205 + MODULE_DESCRIPTION("IMG Pistachio USB2.0 PHY driver"); 206 + MODULE_LICENSE("GPL v2");