diff options
-rw-r--r-- | Documentation/devicetree/bindings/phy/samsung-phy.txt | 53 | ||||
-rw-r--r-- | Documentation/phy/samsung-usb2.txt | 135 | ||||
-rw-r--r-- | drivers/phy/Kconfig | 29 | ||||
-rw-r--r-- | drivers/phy/Makefile | 3 | ||||
-rw-r--r-- | drivers/phy/phy-exynos4210-usb2.c | 261 | ||||
-rw-r--r-- | drivers/phy/phy-exynos4x12-usb2.c | 328 | ||||
-rw-r--r-- | drivers/phy/phy-samsung-usb2.c | 222 | ||||
-rw-r--r-- | drivers/phy/phy-samsung-usb2.h | 66 |
8 files changed, 1097 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt index c0fccaa1671e..bf955abba5d9 100644 --- a/Documentation/devicetree/bindings/phy/samsung-phy.txt +++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt | |||
@@ -20,3 +20,56 @@ Required properties: | |||
20 | - compatible : should be "samsung,exynos5250-dp-video-phy"; | 20 | - compatible : should be "samsung,exynos5250-dp-video-phy"; |
21 | - reg : offset and length of the Display Port PHY register set; | 21 | - reg : offset and length of the Display Port PHY register set; |
22 | - #phy-cells : from the generic PHY bindings, must be 0; | 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. | ||
diff --git a/Documentation/phy/samsung-usb2.txt b/Documentation/phy/samsung-usb2.txt new file mode 100644 index 000000000000..ed12d437189d --- /dev/null +++ b/Documentation/phy/samsung-usb2.txt | |||
@@ -0,0 +1,135 @@ | |||
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. | ||
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index fde4192cc322..337483653754 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig | |||
@@ -107,4 +107,33 @@ config PHY_SUN4I_USB | |||
107 | This driver controls the entire USB PHY block, both the USB OTG | 107 | This driver controls the entire USB PHY block, both the USB OTG |
108 | parts, as well as the 2 regular USB 2 host PHYs. | 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 | endmenu | 139 | endmenu |
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 977a50ead5a4..c4ddd23d6d24 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile | |||
@@ -12,3 +12,6 @@ obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o | |||
12 | obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o | 12 | obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o |
13 | obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o | 13 | obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o |
14 | obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o | 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 | ||
diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c new file mode 100644 index 000000000000..236a52ad94eb --- /dev/null +++ b/drivers/phy/phy-exynos4210-usb2.c | |||
@@ -0,0 +1,261 @@ | |||
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 | }; | ||
diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c new file mode 100644 index 000000000000..d92a7cc5698a --- /dev/null +++ b/drivers/phy/phy-exynos4x12-usb2.c | |||
@@ -0,0 +1,328 @@ | |||
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 | }; | ||
diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c new file mode 100644 index 000000000000..c3b771907f18 --- /dev/null +++ b/drivers/phy/phy-samsung-usb2.c | |||
@@ -0,0 +1,222 @@ | |||
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"); | ||
diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h new file mode 100644 index 000000000000..51a16016a214 --- /dev/null +++ b/drivers/phy/phy-samsung-usb2.h | |||
@@ -0,0 +1,66 @@ | |||
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 | ||