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

phy: Add new Exynos USB 2.0 PHY driver

Add a new driver for the Exynos USB 2.0 PHY. The new driver uses the generic
PHY framework. The driver includes support for the Exynos 4x10 and 4x12
SoC families.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
Reviewed-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>

authored by

Kamil Debski and committed by
Kishon Vijay Abraham I
06fb0137 b5d682f4

+1097
+53
Documentation/devicetree/bindings/phy/samsung-phy.txt
··· 20 20 - compatible : should be "samsung,exynos5250-dp-video-phy"; 21 21 - reg : offset and length of the Display Port PHY register set; 22 22 - #phy-cells : from the generic PHY bindings, must be 0; 23 + 24 + Samsung S5P/EXYNOS SoC series USB PHY 25 + ------------------------------------------------- 26 + 27 + Required properties: 28 + - compatible : should be one of the listed compatibles: 29 + - "samsung,exynos4210-usb2-phy" 30 + - "samsung,exynos4x12-usb2-phy" 31 + - reg : a list of registers used by phy driver 32 + - first and obligatory is the location of phy modules registers 33 + - samsung,sysreg-phandle - handle to syscon used to control the system registers 34 + - samsung,pmureg-phandle - handle to syscon used to control PMU registers 35 + - #phy-cells : from the generic phy bindings, must be 1; 36 + - clocks and clock-names: 37 + - the "phy" clock is required by the phy module, used as a gate 38 + - the "ref" clock is used to get the rate of the clock provided to the 39 + PHY module 40 + 41 + The first phandle argument in the PHY specifier identifies the PHY, its 42 + meaning is compatible dependent. For the currently supported SoCs (Exynos 4210 43 + and Exynos 4212) it is as follows: 44 + 0 - USB device ("device"), 45 + 1 - USB host ("host"), 46 + 2 - HSIC0 ("hsic0"), 47 + 3 - HSIC1 ("hsic1"), 48 + 49 + Exynos 4210 and Exynos 4212 use mode switching and require that mode switch 50 + register is supplied. 51 + 52 + Example: 53 + 54 + For Exynos 4412 (compatible with Exynos 4212): 55 + 56 + usbphy: phy@125b0000 { 57 + compatible = "samsung,exynos4x12-usb2-phy"; 58 + reg = <0x125b0000 0x100>; 59 + clocks = <&clock 305>, <&clock 2>; 60 + clock-names = "phy", "ref"; 61 + status = "okay"; 62 + #phy-cells = <1>; 63 + samsung,sysreg-phandle = <&sys_reg>; 64 + samsung,pmureg-phandle = <&pmu_reg>; 65 + }; 66 + 67 + Then the PHY can be used in other nodes such as: 68 + 69 + phy-consumer@12340000 { 70 + phys = <&usbphy 2>; 71 + phy-names = "phy"; 72 + }; 73 + 74 + Refer to DT bindings documentation of particular PHY consumer devices for more 75 + information about required PHYs and the way of specification.
+135
Documentation/phy/samsung-usb2.txt
··· 1 + .------------------------------------------------------------------------------+ 2 + | Samsung USB 2.0 PHY adaptation layer | 3 + +-----------------------------------------------------------------------------+' 4 + 5 + | 1. Description 6 + +---------------- 7 + 8 + The architecture of the USB 2.0 PHY module in Samsung SoCs is similar 9 + among many SoCs. In spite of the similarities it proved difficult to 10 + create a one driver that would fit all these PHY controllers. Often 11 + the differences were minor and were found in particular bits of the 12 + registers of the PHY. In some rare cases the order of register writes or 13 + the PHY powering up process had to be altered. This adaptation layer is 14 + a compromise between having separate drivers and having a single driver 15 + with added support for many special cases. 16 + 17 + | 2. Files description 18 + +---------------------- 19 + 20 + - phy-samsung-usb2.c 21 + This is the main file of the adaptation layer. This file contains 22 + the probe function and provides two callbacks to the Generic PHY 23 + Framework. This two callbacks are used to power on and power off the 24 + phy. They carry out the common work that has to be done on all version 25 + of the PHY module. Depending on which SoC was chosen they execute SoC 26 + specific callbacks. The specific SoC version is selected by choosing 27 + the appropriate compatible string. In addition, this file contains 28 + struct of_device_id definitions for particular SoCs. 29 + 30 + - phy-samsung-usb2.h 31 + This is the include file. It declares the structures used by this 32 + driver. In addition it should contain extern declarations for 33 + structures that describe particular SoCs. 34 + 35 + | 3. Supporting SoCs 36 + +-------------------- 37 + 38 + To support a new SoC a new file should be added to the drivers/phy 39 + directory. Each SoC's configuration is stored in an instance of the 40 + struct samsung_usb2_phy_config. 41 + 42 + struct samsung_usb2_phy_config { 43 + const struct samsung_usb2_common_phy *phys; 44 + int (*rate_to_clk)(unsigned long, u32 *); 45 + unsigned int num_phys; 46 + bool has_mode_switch; 47 + }; 48 + 49 + The num_phys is the number of phys handled by the driver. *phys is an 50 + array that contains the configuration for each phy. The has_mode_switch 51 + property is a boolean flag that determines whether the SoC has USB host 52 + and device on a single pair of pins. If so, a special register has to 53 + be modified to change the internal routing of these pins between a USB 54 + device or host module. 55 + 56 + For example the configuration for Exynos 4210 is following: 57 + 58 + const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = { 59 + .has_mode_switch = 0, 60 + .num_phys = EXYNOS4210_NUM_PHYS, 61 + .phys = exynos4210_phys, 62 + .rate_to_clk = exynos4210_rate_to_clk, 63 + } 64 + 65 + - int (*rate_to_clk)(unsigned long, u32 *) 66 + The rate_to_clk callback is to convert the rate of the clock 67 + used as the reference clock for the PHY module to the value 68 + that should be written in the hardware register. 69 + 70 + The exynos4210_phys configuration array is as follows: 71 + 72 + static const struct samsung_usb2_common_phy exynos4210_phys[] = { 73 + { 74 + .label = "device", 75 + .id = EXYNOS4210_DEVICE, 76 + .power_on = exynos4210_power_on, 77 + .power_off = exynos4210_power_off, 78 + }, 79 + { 80 + .label = "host", 81 + .id = EXYNOS4210_HOST, 82 + .power_on = exynos4210_power_on, 83 + .power_off = exynos4210_power_off, 84 + }, 85 + { 86 + .label = "hsic0", 87 + .id = EXYNOS4210_HSIC0, 88 + .power_on = exynos4210_power_on, 89 + .power_off = exynos4210_power_off, 90 + }, 91 + { 92 + .label = "hsic1", 93 + .id = EXYNOS4210_HSIC1, 94 + .power_on = exynos4210_power_on, 95 + .power_off = exynos4210_power_off, 96 + }, 97 + {}, 98 + }; 99 + 100 + - int (*power_on)(struct samsung_usb2_phy_instance *); 101 + - int (*power_off)(struct samsung_usb2_phy_instance *); 102 + These two callbacks are used to power on and power off the phy 103 + by modifying appropriate registers. 104 + 105 + Final change to the driver is adding appropriate compatible value to the 106 + phy-samsung-usb2.c file. In case of Exynos 4210 the following lines were 107 + added to the struct of_device_id samsung_usb2_phy_of_match[] array: 108 + 109 + #ifdef CONFIG_PHY_EXYNOS4210_USB2 110 + { 111 + .compatible = "samsung,exynos4210-usb2-phy", 112 + .data = &exynos4210_usb2_phy_config, 113 + }, 114 + #endif 115 + 116 + To add further flexibility to the driver the Kconfig file enables to 117 + include support for selected SoCs in the compiled driver. The Kconfig 118 + entry for Exynos 4210 is following: 119 + 120 + config PHY_EXYNOS4210_USB2 121 + bool "Support for Exynos 4210" 122 + depends on PHY_SAMSUNG_USB2 123 + depends on CPU_EXYNOS4210 124 + help 125 + Enable USB PHY support for Exynos 4210. This option requires that 126 + Samsung USB 2.0 PHY driver is enabled and means that support for this 127 + particular SoC is compiled in the driver. In case of Exynos 4210 four 128 + phys are available - device, host, HSCI0 and HSCI1. 129 + 130 + The newly created file that supports the new SoC has to be also added to the 131 + Makefile. In case of Exynos 4210 the added line is following: 132 + 133 + obj-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o 134 + 135 + After completing these steps the support for the new SoC should be ready.
+29
drivers/phy/Kconfig
··· 107 107 This driver controls the entire USB PHY block, both the USB OTG 108 108 parts, as well as the 2 regular USB 2 host PHYs. 109 109 110 + config PHY_SAMSUNG_USB2 111 + tristate "Samsung USB 2.0 PHY driver" 112 + select GENERIC_PHY 113 + select MFD_SYSCON 114 + help 115 + Enable this to support the Samsung USB 2.0 PHY driver for Samsung 116 + SoCs. This driver provides the interface for USB 2.0 PHY. Support for 117 + particular SoCs has to be enabled in addition to this driver. Number 118 + and type of supported phys depends on the SoC. 119 + 120 + config PHY_EXYNOS4210_USB2 121 + bool "Support for Exynos 4210" 122 + depends on PHY_SAMSUNG_USB2 123 + depends on CPU_EXYNOS4210 124 + help 125 + Enable USB PHY support for Exynos 4210. This option requires that 126 + Samsung USB 2.0 PHY driver is enabled and means that support for this 127 + particular SoC is compiled in the driver. In case of Exynos 4210 four 128 + phys are available - device, host, HSIC0 and HSIC1. 129 + 130 + config PHY_EXYNOS4X12_USB2 131 + bool "Support for Exynos 4x12" 132 + depends on PHY_SAMSUNG_USB2 133 + depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412) 134 + help 135 + Enable USB PHY support for Exynos 4x12. This option requires that 136 + Samsung USB 2.0 PHY driver is enabled and means that support for this 137 + particular SoC is compiled in the driver. In case of Exynos 4x12 four 138 + phys are available - device, host, HSIC0 and HSIC1. 110 139 endmenu
+3
drivers/phy/Makefile
··· 12 12 obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o 13 13 obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o 14 14 obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o 15 + obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-samsung-usb2.o 16 + obj-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o 17 + obj-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o
+261
drivers/phy/phy-exynos4210-usb2.c
··· 1 + /* 2 + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support 3 + * 4 + * Copyright (C) 2013 Samsung Electronics Co., Ltd. 5 + * Author: Kamil Debski <k.debski@samsung.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + */ 11 + 12 + #include <linux/delay.h> 13 + #include <linux/io.h> 14 + #include <linux/phy/phy.h> 15 + #include <linux/regmap.h> 16 + #include "phy-samsung-usb2.h" 17 + 18 + /* Exynos USB PHY registers */ 19 + 20 + /* PHY power control */ 21 + #define EXYNOS_4210_UPHYPWR 0x0 22 + 23 + #define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0) 24 + #define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3) 25 + #define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4) 26 + #define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5) 27 + #define EXYNOS_4210_UPHYPWR_PHY0 ( \ 28 + EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \ 29 + EXYNOS_4210_UPHYPWR_PHY0_PWR | \ 30 + EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \ 31 + EXYNOS_4210_UPHYPWR_PHY0_SLEEP) 32 + 33 + #define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6) 34 + #define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7) 35 + #define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8) 36 + #define EXYNOS_4210_UPHYPWR_PHY1 ( \ 37 + EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \ 38 + EXYNOS_4210_UPHYPWR_PHY1_PWR | \ 39 + EXYNOS_4210_UPHYPWR_PHY1_SLEEP) 40 + 41 + #define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9) 42 + #define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10) 43 + #define EXYNOS_4210_UPHYPWR_HSIC0 ( \ 44 + EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \ 45 + EXYNOS_4210_UPHYPWR_HSIC0_SLEEP) 46 + 47 + #define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11) 48 + #define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12) 49 + #define EXYNOS_4210_UPHYPWR_HSIC1 ( \ 50 + EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \ 51 + EXYNOS_4210_UPHYPWR_HSIC1_SLEEP) 52 + 53 + /* PHY clock control */ 54 + #define EXYNOS_4210_UPHYCLK 0x4 55 + 56 + #define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) 57 + #define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0 58 + #define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) 59 + #define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) 60 + #define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) 61 + 62 + #define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2) 63 + #define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4) 64 + #define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7) 65 + 66 + /* PHY reset control */ 67 + #define EXYNOS_4210_UPHYRST 0x8 68 + 69 + #define EXYNOS_4210_URSTCON_PHY0 BIT(0) 70 + #define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1) 71 + #define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2) 72 + #define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3) 73 + #define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4) 74 + #define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5) 75 + #define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6) 76 + #define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7) 77 + #define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8) 78 + #define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9) 79 + 80 + /* Isolation, configured in the power management unit */ 81 + #define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704 82 + #define EXYNOS_4210_USB_ISOL_DEVICE BIT(0) 83 + #define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708 84 + #define EXYNOS_4210_USB_ISOL_HOST BIT(0) 85 + 86 + /* USBYPHY1 Floating prevention */ 87 + #define EXYNOS_4210_UPHY1CON 0x34 88 + #define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1 89 + 90 + /* Mode switching SUB Device <-> Host */ 91 + #define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c 92 + #define EXYNOS_4210_MODE_SWITCH_MASK 1 93 + #define EXYNOS_4210_MODE_SWITCH_DEVICE 0 94 + #define EXYNOS_4210_MODE_SWITCH_HOST 1 95 + 96 + enum exynos4210_phy_id { 97 + EXYNOS4210_DEVICE, 98 + EXYNOS4210_HOST, 99 + EXYNOS4210_HSIC0, 100 + EXYNOS4210_HSIC1, 101 + EXYNOS4210_NUM_PHYS, 102 + }; 103 + 104 + /* 105 + * exynos4210_rate_to_clk() converts the supplied clock rate to the value that 106 + * can be written to the phy register. 107 + */ 108 + static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg) 109 + { 110 + switch (rate) { 111 + case 12 * MHZ: 112 + *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ; 113 + break; 114 + case 24 * MHZ: 115 + *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ; 116 + break; 117 + case 48 * MHZ: 118 + *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ; 119 + break; 120 + default: 121 + return -EINVAL; 122 + } 123 + 124 + return 0; 125 + } 126 + 127 + static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on) 128 + { 129 + struct samsung_usb2_phy_driver *drv = inst->drv; 130 + u32 offset; 131 + u32 mask; 132 + 133 + switch (inst->cfg->id) { 134 + case EXYNOS4210_DEVICE: 135 + offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET; 136 + mask = EXYNOS_4210_USB_ISOL_DEVICE; 137 + break; 138 + case EXYNOS4210_HOST: 139 + offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET; 140 + mask = EXYNOS_4210_USB_ISOL_HOST; 141 + break; 142 + default: 143 + return; 144 + }; 145 + 146 + regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); 147 + } 148 + 149 + static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) 150 + { 151 + struct samsung_usb2_phy_driver *drv = inst->drv; 152 + u32 rstbits = 0; 153 + u32 phypwr = 0; 154 + u32 rst; 155 + u32 pwr; 156 + u32 clk; 157 + 158 + switch (inst->cfg->id) { 159 + case EXYNOS4210_DEVICE: 160 + phypwr = EXYNOS_4210_UPHYPWR_PHY0; 161 + rstbits = EXYNOS_4210_URSTCON_PHY0; 162 + break; 163 + case EXYNOS4210_HOST: 164 + phypwr = EXYNOS_4210_UPHYPWR_PHY1; 165 + rstbits = EXYNOS_4210_URSTCON_PHY1_ALL | 166 + EXYNOS_4210_URSTCON_PHY1_P0 | 167 + EXYNOS_4210_URSTCON_PHY1_P1P2 | 168 + EXYNOS_4210_URSTCON_HOST_LINK_ALL | 169 + EXYNOS_4210_URSTCON_HOST_LINK_P0; 170 + writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON); 171 + break; 172 + case EXYNOS4210_HSIC0: 173 + phypwr = EXYNOS_4210_UPHYPWR_HSIC0; 174 + rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | 175 + EXYNOS_4210_URSTCON_HOST_LINK_P1; 176 + break; 177 + case EXYNOS4210_HSIC1: 178 + phypwr = EXYNOS_4210_UPHYPWR_HSIC1; 179 + rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | 180 + EXYNOS_4210_URSTCON_HOST_LINK_P2; 181 + break; 182 + }; 183 + 184 + if (on) { 185 + clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK); 186 + clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK; 187 + clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET; 188 + writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK); 189 + 190 + pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); 191 + pwr &= ~phypwr; 192 + writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); 193 + 194 + rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST); 195 + rst |= rstbits; 196 + writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); 197 + udelay(10); 198 + rst &= ~rstbits; 199 + writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); 200 + /* The following delay is necessary for the reset sequence to be 201 + * completed */ 202 + udelay(80); 203 + } else { 204 + pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); 205 + pwr |= phypwr; 206 + writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); 207 + } 208 + } 209 + 210 + static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst) 211 + { 212 + /* Order of initialisation is important - first power then isolation */ 213 + exynos4210_phy_pwr(inst, 1); 214 + exynos4210_isol(inst, 0); 215 + 216 + return 0; 217 + } 218 + 219 + static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst) 220 + { 221 + exynos4210_isol(inst, 1); 222 + exynos4210_phy_pwr(inst, 0); 223 + 224 + return 0; 225 + } 226 + 227 + 228 + static const struct samsung_usb2_common_phy exynos4210_phys[] = { 229 + { 230 + .label = "device", 231 + .id = EXYNOS4210_DEVICE, 232 + .power_on = exynos4210_power_on, 233 + .power_off = exynos4210_power_off, 234 + }, 235 + { 236 + .label = "host", 237 + .id = EXYNOS4210_HOST, 238 + .power_on = exynos4210_power_on, 239 + .power_off = exynos4210_power_off, 240 + }, 241 + { 242 + .label = "hsic0", 243 + .id = EXYNOS4210_HSIC0, 244 + .power_on = exynos4210_power_on, 245 + .power_off = exynos4210_power_off, 246 + }, 247 + { 248 + .label = "hsic1", 249 + .id = EXYNOS4210_HSIC1, 250 + .power_on = exynos4210_power_on, 251 + .power_off = exynos4210_power_off, 252 + }, 253 + {}, 254 + }; 255 + 256 + const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = { 257 + .has_mode_switch = 0, 258 + .num_phys = EXYNOS4210_NUM_PHYS, 259 + .phys = exynos4210_phys, 260 + .rate_to_clk = exynos4210_rate_to_clk, 261 + };
+328
drivers/phy/phy-exynos4x12-usb2.c
··· 1 + /* 2 + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support 3 + * 4 + * Copyright (C) 2013 Samsung Electronics Co., Ltd. 5 + * Author: Kamil Debski <k.debski@samsung.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + */ 11 + 12 + #include <linux/delay.h> 13 + #include <linux/io.h> 14 + #include <linux/phy/phy.h> 15 + #include <linux/regmap.h> 16 + #include "phy-samsung-usb2.h" 17 + 18 + /* Exynos USB PHY registers */ 19 + 20 + /* PHY power control */ 21 + #define EXYNOS_4x12_UPHYPWR 0x0 22 + 23 + #define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND BIT(0) 24 + #define EXYNOS_4x12_UPHYPWR_PHY0_PWR BIT(3) 25 + #define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR BIT(4) 26 + #define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP BIT(5) 27 + #define EXYNOS_4x12_UPHYPWR_PHY0 ( \ 28 + EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \ 29 + EXYNOS_4x12_UPHYPWR_PHY0_PWR | \ 30 + EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \ 31 + EXYNOS_4x12_UPHYPWR_PHY0_SLEEP) 32 + 33 + #define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND BIT(6) 34 + #define EXYNOS_4x12_UPHYPWR_PHY1_PWR BIT(7) 35 + #define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP BIT(8) 36 + #define EXYNOS_4x12_UPHYPWR_PHY1 ( \ 37 + EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \ 38 + EXYNOS_4x12_UPHYPWR_PHY1_PWR | \ 39 + EXYNOS_4x12_UPHYPWR_PHY1_SLEEP) 40 + 41 + #define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND BIT(9) 42 + #define EXYNOS_4x12_UPHYPWR_HSIC0_PWR BIT(10) 43 + #define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP BIT(11) 44 + #define EXYNOS_4x12_UPHYPWR_HSIC0 ( \ 45 + EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \ 46 + EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \ 47 + EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP) 48 + 49 + #define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND BIT(12) 50 + #define EXYNOS_4x12_UPHYPWR_HSIC1_PWR BIT(13) 51 + #define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP BIT(14) 52 + #define EXYNOS_4x12_UPHYPWR_HSIC1 ( \ 53 + EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \ 54 + EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \ 55 + EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP) 56 + 57 + /* PHY clock control */ 58 + #define EXYNOS_4x12_UPHYCLK 0x4 59 + 60 + #define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK (0x7 << 0) 61 + #define EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET 0 62 + #define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6 (0x0 << 0) 63 + #define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ (0x1 << 0) 64 + #define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) 65 + #define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2 (0x3 << 0) 66 + #define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ (0x4 << 0) 67 + #define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0) 68 + #define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0) 69 + 70 + #define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3) 71 + #define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4) 72 + #define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7) 73 + 74 + #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK (0x7f << 10) 75 + #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET 10 76 + #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ (0x24 << 10) 77 + #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ (0x1c << 10) 78 + #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ (0x1a << 10) 79 + #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10) 80 + #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ (0x14 << 10) 81 + 82 + /* PHY reset control */ 83 + #define EXYNOS_4x12_UPHYRST 0x8 84 + 85 + #define EXYNOS_4x12_URSTCON_PHY0 BIT(0) 86 + #define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) 87 + #define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) 88 + #define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) 89 + #define EXYNOS_4x12_URSTCON_PHY1 BIT(4) 90 + #define EXYNOS_4x12_URSTCON_HSIC0 BIT(5) 91 + #define EXYNOS_4x12_URSTCON_HSIC1 BIT(6) 92 + #define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) 93 + #define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(8) 94 + #define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9) 95 + #define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(10) 96 + 97 + /* Isolation, configured in the power management unit */ 98 + #define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 99 + #define EXYNOS_4x12_USB_ISOL_OTG BIT(0) 100 + #define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET 0x708 101 + #define EXYNOS_4x12_USB_ISOL_HSIC0 BIT(0) 102 + #define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET 0x70c 103 + #define EXYNOS_4x12_USB_ISOL_HSIC1 BIT(0) 104 + 105 + /* Mode switching SUB Device <-> Host */ 106 + #define EXYNOS_4x12_MODE_SWITCH_OFFSET 0x21c 107 + #define EXYNOS_4x12_MODE_SWITCH_MASK 1 108 + #define EXYNOS_4x12_MODE_SWITCH_DEVICE 0 109 + #define EXYNOS_4x12_MODE_SWITCH_HOST 1 110 + 111 + enum exynos4x12_phy_id { 112 + EXYNOS4x12_DEVICE, 113 + EXYNOS4x12_HOST, 114 + EXYNOS4x12_HSIC0, 115 + EXYNOS4x12_HSIC1, 116 + EXYNOS4x12_NUM_PHYS, 117 + }; 118 + 119 + /* 120 + * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that 121 + * can be written to the phy register. 122 + */ 123 + static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg) 124 + { 125 + /* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */ 126 + 127 + switch (rate) { 128 + case 9600 * KHZ: 129 + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6; 130 + break; 131 + case 10 * MHZ: 132 + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ; 133 + break; 134 + case 12 * MHZ: 135 + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ; 136 + break; 137 + case 19200 * KHZ: 138 + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2; 139 + break; 140 + case 20 * MHZ: 141 + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ; 142 + break; 143 + case 24 * MHZ: 144 + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ; 145 + break; 146 + case 50 * MHZ: 147 + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ; 148 + break; 149 + default: 150 + return -EINVAL; 151 + } 152 + 153 + return 0; 154 + } 155 + 156 + static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on) 157 + { 158 + struct samsung_usb2_phy_driver *drv = inst->drv; 159 + u32 offset; 160 + u32 mask; 161 + 162 + switch (inst->cfg->id) { 163 + case EXYNOS4x12_DEVICE: 164 + case EXYNOS4x12_HOST: 165 + offset = EXYNOS_4x12_USB_ISOL_OFFSET; 166 + mask = EXYNOS_4x12_USB_ISOL_OTG; 167 + break; 168 + case EXYNOS4x12_HSIC0: 169 + offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET; 170 + mask = EXYNOS_4x12_USB_ISOL_HSIC0; 171 + break; 172 + case EXYNOS4x12_HSIC1: 173 + offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET; 174 + mask = EXYNOS_4x12_USB_ISOL_HSIC1; 175 + break; 176 + default: 177 + return; 178 + }; 179 + 180 + regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); 181 + } 182 + 183 + static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) 184 + { 185 + struct samsung_usb2_phy_driver *drv = inst->drv; 186 + u32 clk; 187 + 188 + clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); 189 + clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; 190 + clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; 191 + writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); 192 + } 193 + 194 + static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) 195 + { 196 + struct samsung_usb2_phy_driver *drv = inst->drv; 197 + u32 rstbits = 0; 198 + u32 phypwr = 0; 199 + u32 rst; 200 + u32 pwr; 201 + u32 mode = 0; 202 + u32 switch_mode = 0; 203 + 204 + switch (inst->cfg->id) { 205 + case EXYNOS4x12_DEVICE: 206 + phypwr = EXYNOS_4x12_UPHYPWR_PHY0; 207 + rstbits = EXYNOS_4x12_URSTCON_PHY0; 208 + mode = EXYNOS_4x12_MODE_SWITCH_DEVICE; 209 + switch_mode = 1; 210 + break; 211 + case EXYNOS4x12_HOST: 212 + phypwr = EXYNOS_4x12_UPHYPWR_PHY1; 213 + rstbits = EXYNOS_4x12_URSTCON_HOST_PHY; 214 + mode = EXYNOS_4x12_MODE_SWITCH_HOST; 215 + switch_mode = 1; 216 + break; 217 + case EXYNOS4x12_HSIC0: 218 + phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; 219 + rstbits = EXYNOS_4x12_URSTCON_HSIC1 | 220 + EXYNOS_4x12_URSTCON_HOST_LINK_P0 | 221 + EXYNOS_4x12_URSTCON_HOST_PHY; 222 + break; 223 + case EXYNOS4x12_HSIC1: 224 + phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; 225 + rstbits = EXYNOS_4x12_URSTCON_HSIC1 | 226 + EXYNOS_4x12_URSTCON_HOST_LINK_P1; 227 + break; 228 + }; 229 + 230 + if (on) { 231 + if (switch_mode) 232 + regmap_update_bits(drv->reg_sys, 233 + EXYNOS_4x12_MODE_SWITCH_OFFSET, 234 + EXYNOS_4x12_MODE_SWITCH_MASK, mode); 235 + 236 + pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); 237 + pwr &= ~phypwr; 238 + writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); 239 + 240 + rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST); 241 + rst |= rstbits; 242 + writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); 243 + udelay(10); 244 + rst &= ~rstbits; 245 + writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); 246 + /* The following delay is necessary for the reset sequence to be 247 + * completed */ 248 + udelay(80); 249 + } else { 250 + pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); 251 + pwr |= phypwr; 252 + writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); 253 + } 254 + } 255 + 256 + static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) 257 + { 258 + struct samsung_usb2_phy_driver *drv = inst->drv; 259 + 260 + inst->enabled = 1; 261 + exynos4x12_setup_clk(inst); 262 + exynos4x12_phy_pwr(inst, 1); 263 + exynos4x12_isol(inst, 0); 264 + 265 + /* Power on the device, as it is necessary for HSIC to work */ 266 + if (inst->cfg->id == EXYNOS4x12_HSIC0) { 267 + struct samsung_usb2_phy_instance *device = 268 + &drv->instances[EXYNOS4x12_DEVICE]; 269 + exynos4x12_phy_pwr(device, 1); 270 + exynos4x12_isol(device, 0); 271 + } 272 + 273 + return 0; 274 + } 275 + 276 + static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) 277 + { 278 + struct samsung_usb2_phy_driver *drv = inst->drv; 279 + struct samsung_usb2_phy_instance *device = 280 + &drv->instances[EXYNOS4x12_DEVICE]; 281 + 282 + inst->enabled = 0; 283 + exynos4x12_isol(inst, 1); 284 + exynos4x12_phy_pwr(inst, 0); 285 + 286 + if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) { 287 + exynos4x12_isol(device, 1); 288 + exynos4x12_phy_pwr(device, 0); 289 + } 290 + 291 + return 0; 292 + } 293 + 294 + 295 + static const struct samsung_usb2_common_phy exynos4x12_phys[] = { 296 + { 297 + .label = "device", 298 + .id = EXYNOS4x12_DEVICE, 299 + .power_on = exynos4x12_power_on, 300 + .power_off = exynos4x12_power_off, 301 + }, 302 + { 303 + .label = "host", 304 + .id = EXYNOS4x12_HOST, 305 + .power_on = exynos4x12_power_on, 306 + .power_off = exynos4x12_power_off, 307 + }, 308 + { 309 + .label = "hsic0", 310 + .id = EXYNOS4x12_HSIC0, 311 + .power_on = exynos4x12_power_on, 312 + .power_off = exynos4x12_power_off, 313 + }, 314 + { 315 + .label = "hsic1", 316 + .id = EXYNOS4x12_HSIC1, 317 + .power_on = exynos4x12_power_on, 318 + .power_off = exynos4x12_power_off, 319 + }, 320 + {}, 321 + }; 322 + 323 + const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = { 324 + .has_mode_switch = 1, 325 + .num_phys = EXYNOS4x12_NUM_PHYS, 326 + .phys = exynos4x12_phys, 327 + .rate_to_clk = exynos4x12_rate_to_clk, 328 + };
+222
drivers/phy/phy-samsung-usb2.c
··· 1 + /* 2 + * Samsung SoC USB 1.1/2.0 PHY driver 3 + * 4 + * Copyright (C) 2013 Samsung Electronics Co., Ltd. 5 + * Author: Kamil Debski <k.debski@samsung.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + */ 11 + 12 + #include <linux/clk.h> 13 + #include <linux/mfd/syscon.h> 14 + #include <linux/module.h> 15 + #include <linux/of.h> 16 + #include <linux/of_address.h> 17 + #include <linux/phy/phy.h> 18 + #include <linux/platform_device.h> 19 + #include <linux/spinlock.h> 20 + #include "phy-samsung-usb2.h" 21 + 22 + static int samsung_usb2_phy_power_on(struct phy *phy) 23 + { 24 + struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); 25 + struct samsung_usb2_phy_driver *drv = inst->drv; 26 + int ret; 27 + 28 + dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n", 29 + inst->cfg->label); 30 + ret = clk_prepare_enable(drv->clk); 31 + if (ret) 32 + goto err_main_clk; 33 + ret = clk_prepare_enable(drv->ref_clk); 34 + if (ret) 35 + goto err_instance_clk; 36 + if (inst->cfg->power_on) { 37 + spin_lock(&drv->lock); 38 + ret = inst->cfg->power_on(inst); 39 + spin_unlock(&drv->lock); 40 + } 41 + 42 + return 0; 43 + 44 + err_instance_clk: 45 + clk_disable_unprepare(drv->clk); 46 + err_main_clk: 47 + return ret; 48 + } 49 + 50 + static int samsung_usb2_phy_power_off(struct phy *phy) 51 + { 52 + struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); 53 + struct samsung_usb2_phy_driver *drv = inst->drv; 54 + int ret = 0; 55 + 56 + dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n", 57 + inst->cfg->label); 58 + if (inst->cfg->power_off) { 59 + spin_lock(&drv->lock); 60 + ret = inst->cfg->power_off(inst); 61 + spin_unlock(&drv->lock); 62 + } 63 + clk_disable_unprepare(drv->ref_clk); 64 + clk_disable_unprepare(drv->clk); 65 + return ret; 66 + } 67 + 68 + static struct phy_ops samsung_usb2_phy_ops = { 69 + .power_on = samsung_usb2_phy_power_on, 70 + .power_off = samsung_usb2_phy_power_off, 71 + .owner = THIS_MODULE, 72 + }; 73 + 74 + static struct phy *samsung_usb2_phy_xlate(struct device *dev, 75 + struct of_phandle_args *args) 76 + { 77 + struct samsung_usb2_phy_driver *drv; 78 + 79 + drv = dev_get_drvdata(dev); 80 + if (!drv) 81 + return ERR_PTR(-EINVAL); 82 + 83 + if (WARN_ON(args->args[0] >= drv->cfg->num_phys)) 84 + return ERR_PTR(-ENODEV); 85 + 86 + return drv->instances[args->args[0]].phy; 87 + } 88 + 89 + static const struct of_device_id samsung_usb2_phy_of_match[] = { 90 + #ifdef CONFIG_PHY_EXYNOS4210_USB2 91 + { 92 + .compatible = "samsung,exynos4210-usb2-phy", 93 + .data = &exynos4210_usb2_phy_config, 94 + }, 95 + #endif 96 + #ifdef CONFIG_PHY_EXYNOS4X12_USB2 97 + { 98 + .compatible = "samsung,exynos4x12-usb2-phy", 99 + .data = &exynos4x12_usb2_phy_config, 100 + }, 101 + #endif 102 + { }, 103 + }; 104 + 105 + static int samsung_usb2_phy_probe(struct platform_device *pdev) 106 + { 107 + const struct of_device_id *match; 108 + const struct samsung_usb2_phy_config *cfg; 109 + struct device *dev = &pdev->dev; 110 + struct phy_provider *phy_provider; 111 + struct resource *mem; 112 + struct samsung_usb2_phy_driver *drv; 113 + int i, ret; 114 + 115 + if (!pdev->dev.of_node) { 116 + dev_err(dev, "This driver is required to be instantiated from device tree\n"); 117 + return -EINVAL; 118 + } 119 + 120 + match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node); 121 + if (!match) { 122 + dev_err(dev, "of_match_node() failed\n"); 123 + return -EINVAL; 124 + } 125 + cfg = match->data; 126 + 127 + drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) + 128 + cfg->num_phys * sizeof(struct samsung_usb2_phy_instance), 129 + GFP_KERNEL); 130 + if (!drv) 131 + return -ENOMEM; 132 + 133 + dev_set_drvdata(dev, drv); 134 + spin_lock_init(&drv->lock); 135 + 136 + drv->cfg = cfg; 137 + drv->dev = dev; 138 + 139 + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 140 + drv->reg_phy = devm_ioremap_resource(dev, mem); 141 + if (IS_ERR(drv->reg_phy)) { 142 + dev_err(dev, "Failed to map register memory (phy)\n"); 143 + return PTR_ERR(drv->reg_phy); 144 + } 145 + 146 + drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 147 + "samsung,pmureg-phandle"); 148 + if (IS_ERR(drv->reg_pmu)) { 149 + dev_err(dev, "Failed to map PMU registers (via syscon)\n"); 150 + return PTR_ERR(drv->reg_pmu); 151 + } 152 + 153 + if (drv->cfg->has_mode_switch) { 154 + drv->reg_sys = syscon_regmap_lookup_by_phandle( 155 + pdev->dev.of_node, "samsung,sysreg-phandle"); 156 + if (IS_ERR(drv->reg_sys)) { 157 + dev_err(dev, "Failed to map system registers (via syscon)\n"); 158 + return PTR_ERR(drv->reg_sys); 159 + } 160 + } 161 + 162 + drv->clk = devm_clk_get(dev, "phy"); 163 + if (IS_ERR(drv->clk)) { 164 + dev_err(dev, "Failed to get clock of phy controller\n"); 165 + return PTR_ERR(drv->clk); 166 + } 167 + 168 + drv->ref_clk = devm_clk_get(dev, "ref"); 169 + if (IS_ERR(drv->ref_clk)) { 170 + dev_err(dev, "Failed to get reference clock for the phy controller\n"); 171 + return PTR_ERR(drv->ref_clk); 172 + } 173 + 174 + drv->ref_rate = clk_get_rate(drv->ref_clk); 175 + if (drv->cfg->rate_to_clk) { 176 + ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val); 177 + if (ret) 178 + return ret; 179 + } 180 + 181 + for (i = 0; i < drv->cfg->num_phys; i++) { 182 + char *label = drv->cfg->phys[i].label; 183 + struct samsung_usb2_phy_instance *p = &drv->instances[i]; 184 + 185 + dev_dbg(dev, "Creating phy \"%s\"\n", label); 186 + p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL); 187 + if (IS_ERR(p->phy)) { 188 + dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n", 189 + label); 190 + return PTR_ERR(p->phy); 191 + } 192 + 193 + p->cfg = &drv->cfg->phys[i]; 194 + p->drv = drv; 195 + phy_set_bus_width(p->phy, 8); 196 + phy_set_drvdata(p->phy, p); 197 + } 198 + 199 + phy_provider = devm_of_phy_provider_register(dev, 200 + samsung_usb2_phy_xlate); 201 + if (IS_ERR(phy_provider)) { 202 + dev_err(drv->dev, "Failed to register phy provider\n"); 203 + return PTR_ERR(phy_provider); 204 + } 205 + 206 + return 0; 207 + } 208 + 209 + static struct platform_driver samsung_usb2_phy_driver = { 210 + .probe = samsung_usb2_phy_probe, 211 + .driver = { 212 + .of_match_table = samsung_usb2_phy_of_match, 213 + .name = "samsung-usb2-phy", 214 + .owner = THIS_MODULE, 215 + } 216 + }; 217 + 218 + module_platform_driver(samsung_usb2_phy_driver); 219 + MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver"); 220 + MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>"); 221 + MODULE_LICENSE("GPL v2"); 222 + MODULE_ALIAS("platform:samsung-usb2-phy");
+66
drivers/phy/phy-samsung-usb2.h
··· 1 + /* 2 + * Samsung SoC USB 1.1/2.0 PHY driver 3 + * 4 + * Copyright (C) 2013 Samsung Electronics Co., Ltd. 5 + * Author: Kamil Debski <k.debski@samsung.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + */ 11 + 12 + #ifndef _PHY_EXYNOS_USB2_H 13 + #define _PHY_EXYNOS_USB2_H 14 + 15 + #include <linux/clk.h> 16 + #include <linux/phy/phy.h> 17 + #include <linux/device.h> 18 + #include <linux/regmap.h> 19 + #include <linux/spinlock.h> 20 + 21 + #define KHZ 1000 22 + #define MHZ (KHZ * KHZ) 23 + 24 + struct samsung_usb2_phy_driver; 25 + struct samsung_usb2_phy_instance; 26 + struct samsung_usb2_phy_config; 27 + 28 + struct samsung_usb2_phy_instance { 29 + const struct samsung_usb2_common_phy *cfg; 30 + struct phy *phy; 31 + struct samsung_usb2_phy_driver *drv; 32 + bool enabled; 33 + }; 34 + 35 + struct samsung_usb2_phy_driver { 36 + const struct samsung_usb2_phy_config *cfg; 37 + struct clk *clk; 38 + struct clk *ref_clk; 39 + unsigned long ref_rate; 40 + u32 ref_reg_val; 41 + struct device *dev; 42 + void __iomem *reg_phy; 43 + struct regmap *reg_pmu; 44 + struct regmap *reg_sys; 45 + spinlock_t lock; 46 + struct samsung_usb2_phy_instance instances[0]; 47 + }; 48 + 49 + struct samsung_usb2_common_phy { 50 + int (*power_on)(struct samsung_usb2_phy_instance *); 51 + int (*power_off)(struct samsung_usb2_phy_instance *); 52 + unsigned int id; 53 + char *label; 54 + }; 55 + 56 + 57 + struct samsung_usb2_phy_config { 58 + const struct samsung_usb2_common_phy *phys; 59 + int (*rate_to_clk)(unsigned long, u32 *); 60 + unsigned int num_phys; 61 + bool has_mode_switch; 62 + }; 63 + 64 + extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config; 65 + extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config; 66 + #endif