diff options
| -rw-r--r-- | Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt | 15 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt | 22 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt | 19 | ||||
| -rw-r--r-- | Documentation/kernel-parameters.txt | 6 | ||||
| -rw-r--r-- | drivers/phy/Kconfig | 16 | ||||
| -rw-r--r-- | drivers/phy/Makefile | 2 | ||||
| -rw-r--r-- | drivers/phy/phy-rcar-gen3-usb2.c | 83 | ||||
| -rw-r--r-- | drivers/phy/phy-rockchip-dp.c | 151 | ||||
| -rw-r--r-- | drivers/phy/phy-rockchip-emmc.c | 229 | ||||
| -rw-r--r-- | drivers/phy/phy-rockchip-usb.c | 233 |
10 files changed, 656 insertions, 120 deletions
diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt index 2390e4e9c84c..eaf7e9b7ce6b 100644 --- a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt +++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt | |||
| @@ -7,33 +7,26 @@ Required properties: | |||
| 7 | - compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795 | 7 | - compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795 |
| 8 | SoC. | 8 | SoC. |
| 9 | - reg: offset and length of the partial USB 2.0 Host register block. | 9 | - reg: offset and length of the partial USB 2.0 Host register block. |
| 10 | - reg-names: must be "usb2_host". | ||
| 11 | - clocks: clock phandle and specifier pair(s). | 10 | - clocks: clock phandle and specifier pair(s). |
| 12 | - #phy-cells: see phy-bindings.txt in the same directory, must be <0>. | 11 | - #phy-cells: see phy-bindings.txt in the same directory, must be <0>. |
| 13 | 12 | ||
| 14 | Optional properties: | 13 | Optional properties: |
| 15 | To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are | 14 | To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are |
| 16 | combined, the device tree node should set HSUSB properties to reg and reg-names | 15 | combined, the device tree node should set interrupt properties to use the |
| 17 | properties. This is because HSUSB has registers to select USB 2.0 host or | 16 | channel as USB OTG: |
| 18 | peripheral at that channel: | ||
| 19 | - reg: offset and length of the partial HSUSB register block. | ||
| 20 | - reg-names: must be "hsusb". | ||
| 21 | - interrupts: interrupt specifier for the PHY. | 17 | - interrupts: interrupt specifier for the PHY. |
| 22 | 18 | ||
| 23 | Example (R-Car H3): | 19 | Example (R-Car H3): |
| 24 | 20 | ||
| 25 | usb-phy@ee080200 { | 21 | usb-phy@ee080200 { |
| 26 | compatible = "renesas,usb2-phy-r8a7795"; | 22 | compatible = "renesas,usb2-phy-r8a7795"; |
| 27 | reg = <0 0xee080200 0 0x700>, <0 0xe6590100 0 0x100>; | 23 | reg = <0 0xee080200 0 0x700>; |
| 28 | reg-names = "usb2_host", "hsusb"; | ||
| 29 | interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; | 24 | interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; |
| 30 | clocks = <&mstp7_clks R8A7795_CLK_EHCI0>, | 25 | clocks = <&mstp7_clks R8A7795_CLK_EHCI0>; |
| 31 | <&mstp7_clks R8A7795_CLK_HSUSB>; | ||
| 32 | }; | 26 | }; |
| 33 | 27 | ||
| 34 | usb-phy@ee0a0200 { | 28 | usb-phy@ee0a0200 { |
| 35 | compatible = "renesas,usb2-phy-r8a7795"; | 29 | compatible = "renesas,usb2-phy-r8a7795"; |
| 36 | reg = <0 0xee0a0200 0 0x700>; | 30 | reg = <0 0xee0a0200 0 0x700>; |
| 37 | reg-names = "usb2_host"; | ||
| 38 | clocks = <&mstp7_clks R8A7795_CLK_EHCI0>; | 31 | clocks = <&mstp7_clks R8A7795_CLK_EHCI0>; |
| 39 | }; | 32 | }; |
diff --git a/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt new file mode 100644 index 000000000000..50c4f9b00adf --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | Rockchip specific extensions to the Analogix Display Port PHY | ||
| 2 | ------------------------------------ | ||
| 3 | |||
| 4 | Required properties: | ||
| 5 | - compatible : should be one of the following supported values: | ||
| 6 | - "rockchip.rk3288-dp-phy" | ||
| 7 | - clocks: from common clock binding: handle to dp clock. | ||
| 8 | of memory mapped region. | ||
| 9 | - clock-names: from common clock binding: | ||
| 10 | Required elements: "24m" | ||
| 11 | - rockchip,grf: phandle to the syscon managing the "general register files" | ||
| 12 | - #phy-cells : from the generic PHY bindings, must be 0; | ||
| 13 | |||
| 14 | Example: | ||
| 15 | |||
| 16 | edp_phy: edp-phy { | ||
| 17 | compatible = "rockchip,rk3288-dp-phy"; | ||
| 18 | rockchip,grf = <&grf>; | ||
| 19 | clocks = <&cru SCLK_EDP_24M>; | ||
| 20 | clock-names = "24m"; | ||
| 21 | #phy-cells = <0>; | ||
| 22 | }; | ||
diff --git a/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt new file mode 100644 index 000000000000..61916f15a949 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | Rockchip EMMC PHY | ||
| 2 | ----------------------- | ||
| 3 | |||
| 4 | Required properties: | ||
| 5 | - compatible: rockchip,rk3399-emmc-phy | ||
| 6 | - rockchip,grf : phandle to the syscon managing the "general | ||
| 7 | register files" | ||
| 8 | - #phy-cells: must be 0 | ||
| 9 | - reg: PHY configure reg address offset in "general | ||
| 10 | register files" | ||
| 11 | |||
| 12 | Example: | ||
| 13 | |||
| 14 | emmcphy: phy { | ||
| 15 | compatible = "rockchip,rk3399-emmc-phy"; | ||
| 16 | rockchip,grf = <&grf>; | ||
| 17 | reg = <0xf780>; | ||
| 18 | #phy-cells = <0>; | ||
| 19 | }; | ||
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 9a53c929f017..8a6c6c02f916 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
| @@ -3491,6 +3491,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted. | |||
| 3491 | 3491 | ||
| 3492 | ro [KNL] Mount root device read-only on boot | 3492 | ro [KNL] Mount root device read-only on boot |
| 3493 | 3493 | ||
| 3494 | rockchip.usb_uart | ||
| 3495 | Enable the uart passthrough on the designated usb port | ||
| 3496 | on Rockchip SoCs. When active, the signals of the | ||
| 3497 | debug-uart get routed to the D+ and D- pins of the usb | ||
| 3498 | port and the regular usb controller gets disabled. | ||
| 3499 | |||
| 3494 | root= [KNL] Root filesystem | 3500 | root= [KNL] Root filesystem |
| 3495 | See name_to_dev_t comment in init/do_mounts.c. | 3501 | See name_to_dev_t comment in init/do_mounts.c. |
| 3496 | 3502 | ||
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 0124d17bd9fe..26566db09de0 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig | |||
| @@ -32,7 +32,7 @@ config PHY_BERLIN_SATA | |||
| 32 | config ARMADA375_USBCLUSTER_PHY | 32 | config ARMADA375_USBCLUSTER_PHY |
| 33 | def_bool y | 33 | def_bool y |
| 34 | depends on MACH_ARMADA_375 || COMPILE_TEST | 34 | depends on MACH_ARMADA_375 || COMPILE_TEST |
| 35 | depends on OF | 35 | depends on OF && HAS_IOMEM |
| 36 | select GENERIC_PHY | 36 | select GENERIC_PHY |
| 37 | 37 | ||
| 38 | config PHY_DM816X_USB | 38 | config PHY_DM816X_USB |
| @@ -337,6 +337,20 @@ config PHY_ROCKCHIP_USB | |||
| 337 | help | 337 | help |
| 338 | Enable this to support the Rockchip USB 2.0 PHY. | 338 | Enable this to support the Rockchip USB 2.0 PHY. |
| 339 | 339 | ||
| 340 | config PHY_ROCKCHIP_EMMC | ||
| 341 | tristate "Rockchip EMMC PHY Driver" | ||
| 342 | depends on ARCH_ROCKCHIP && OF | ||
| 343 | select GENERIC_PHY | ||
| 344 | help | ||
| 345 | Enable this to support the Rockchip EMMC PHY. | ||
| 346 | |||
| 347 | config PHY_ROCKCHIP_DP | ||
| 348 | tristate "Rockchip Display Port PHY Driver" | ||
| 349 | depends on ARCH_ROCKCHIP && OF | ||
| 350 | select GENERIC_PHY | ||
| 351 | help | ||
| 352 | Enable this to support the Rockchip Display Port PHY. | ||
| 353 | |||
| 340 | config PHY_ST_SPEAR1310_MIPHY | 354 | config PHY_ST_SPEAR1310_MIPHY |
| 341 | tristate "ST SPEAR1310-MIPHY driver" | 355 | tristate "ST SPEAR1310-MIPHY driver" |
| 342 | select GENERIC_PHY | 356 | select GENERIC_PHY |
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c80f09df3bb8..24596a96a887 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile | |||
| @@ -37,6 +37,8 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o | |||
| 37 | obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o | 37 | obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o |
| 38 | obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o | 38 | obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o |
| 39 | obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o | 39 | obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o |
| 40 | obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o | ||
| 41 | obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o | ||
| 40 | obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o | 42 | obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o |
| 41 | obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o | 43 | obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o |
| 42 | obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o | 44 | obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o |
diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c index ef332ef4abc7..bc4f7dd821aa 100644 --- a/drivers/phy/phy-rcar-gen3-usb2.c +++ b/drivers/phy/phy-rcar-gen3-usb2.c | |||
| @@ -74,20 +74,6 @@ | |||
| 74 | #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ | 74 | #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ |
| 75 | #define USB2_ADPCTRL_DRVVBUS BIT(4) | 75 | #define USB2_ADPCTRL_DRVVBUS BIT(4) |
| 76 | 76 | ||
| 77 | /******* HSUSB registers (original offset is +0x100) *******/ | ||
| 78 | #define HSUSB_LPSTS 0x02 | ||
| 79 | #define HSUSB_UGCTRL2 0x84 | ||
| 80 | |||
| 81 | /* Low Power Status register (LPSTS) */ | ||
| 82 | #define HSUSB_LPSTS_SUSPM 0x4000 | ||
| 83 | |||
| 84 | /* USB General control register 2 (UGCTRL2) */ | ||
| 85 | #define HSUSB_UGCTRL2_MASK 0x00000031 /* bit[31:6] should be 0 */ | ||
| 86 | #define HSUSB_UGCTRL2_USB0SEL 0x00000030 | ||
| 87 | #define HSUSB_UGCTRL2_USB0SEL_HOST 0x00000010 | ||
| 88 | #define HSUSB_UGCTRL2_USB0SEL_HS_USB 0x00000020 | ||
| 89 | #define HSUSB_UGCTRL2_USB0SEL_OTG 0x00000030 | ||
| 90 | |||
| 91 | struct rcar_gen3_data { | 77 | struct rcar_gen3_data { |
| 92 | void __iomem *base; | 78 | void __iomem *base; |
| 93 | struct clk *clk; | 79 | struct clk *clk; |
| @@ -95,8 +81,8 @@ struct rcar_gen3_data { | |||
| 95 | 81 | ||
| 96 | struct rcar_gen3_chan { | 82 | struct rcar_gen3_chan { |
| 97 | struct rcar_gen3_data usb2; | 83 | struct rcar_gen3_data usb2; |
| 98 | struct rcar_gen3_data hsusb; | ||
| 99 | struct phy *phy; | 84 | struct phy *phy; |
| 85 | bool has_otg; | ||
| 100 | }; | 86 | }; |
| 101 | 87 | ||
| 102 | static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) | 88 | static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) |
| @@ -202,24 +188,15 @@ static int rcar_gen3_phy_usb2_init(struct phy *p) | |||
| 202 | { | 188 | { |
| 203 | struct rcar_gen3_chan *channel = phy_get_drvdata(p); | 189 | struct rcar_gen3_chan *channel = phy_get_drvdata(p); |
| 204 | void __iomem *usb2_base = channel->usb2.base; | 190 | void __iomem *usb2_base = channel->usb2.base; |
| 205 | void __iomem *hsusb_base = channel->hsusb.base; | ||
| 206 | u32 val; | ||
| 207 | 191 | ||
| 208 | /* Initialize USB2 part */ | 192 | /* Initialize USB2 part */ |
| 209 | writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE); | 193 | writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE); |
| 210 | writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET); | 194 | writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET); |
| 211 | writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); | 195 | writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); |
| 212 | 196 | ||
| 213 | /* Initialize HSUSB part */ | 197 | /* Initialize otg part */ |
| 214 | if (hsusb_base) { | 198 | if (channel->has_otg) |
| 215 | val = readl(hsusb_base + HSUSB_UGCTRL2); | ||
| 216 | val = (val & ~HSUSB_UGCTRL2_USB0SEL) | | ||
| 217 | HSUSB_UGCTRL2_USB0SEL_OTG; | ||
| 218 | writel(val & HSUSB_UGCTRL2_MASK, hsusb_base + HSUSB_UGCTRL2); | ||
| 219 | |||
| 220 | /* Initialize otg part */ | ||
| 221 | rcar_gen3_init_otg(channel); | 199 | rcar_gen3_init_otg(channel); |
| 222 | } | ||
| 223 | 200 | ||
| 224 | return 0; | 201 | return 0; |
| 225 | } | 202 | } |
| @@ -237,7 +214,6 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p) | |||
| 237 | { | 214 | { |
| 238 | struct rcar_gen3_chan *channel = phy_get_drvdata(p); | 215 | struct rcar_gen3_chan *channel = phy_get_drvdata(p); |
| 239 | void __iomem *usb2_base = channel->usb2.base; | 216 | void __iomem *usb2_base = channel->usb2.base; |
| 240 | void __iomem *hsusb_base = channel->hsusb.base; | ||
| 241 | u32 val; | 217 | u32 val; |
| 242 | 218 | ||
| 243 | val = readl(usb2_base + USB2_USBCTR); | 219 | val = readl(usb2_base + USB2_USBCTR); |
| @@ -246,33 +222,6 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p) | |||
| 246 | val &= ~USB2_USBCTR_PLL_RST; | 222 | val &= ~USB2_USBCTR_PLL_RST; |
| 247 | writel(val, usb2_base + USB2_USBCTR); | 223 | writel(val, usb2_base + USB2_USBCTR); |
| 248 | 224 | ||
| 249 | /* | ||
| 250 | * TODO: To reduce power consuming, this driver should set the SUSPM | ||
| 251 | * after the PHY detects ID pin as peripheral. | ||
| 252 | */ | ||
| 253 | if (hsusb_base) { | ||
| 254 | /* Power on HSUSB PHY */ | ||
| 255 | val = readw(hsusb_base + HSUSB_LPSTS); | ||
| 256 | val |= HSUSB_LPSTS_SUSPM; | ||
| 257 | writew(val, hsusb_base + HSUSB_LPSTS); | ||
| 258 | } | ||
| 259 | |||
| 260 | return 0; | ||
| 261 | } | ||
| 262 | |||
| 263 | static int rcar_gen3_phy_usb2_power_off(struct phy *p) | ||
| 264 | { | ||
| 265 | struct rcar_gen3_chan *channel = phy_get_drvdata(p); | ||
| 266 | void __iomem *hsusb_base = channel->hsusb.base; | ||
| 267 | u32 val; | ||
| 268 | |||
| 269 | if (hsusb_base) { | ||
| 270 | /* Power off HSUSB PHY */ | ||
| 271 | val = readw(hsusb_base + HSUSB_LPSTS); | ||
| 272 | val &= ~HSUSB_LPSTS_SUSPM; | ||
| 273 | writew(val, hsusb_base + HSUSB_LPSTS); | ||
| 274 | } | ||
| 275 | |||
| 276 | return 0; | 225 | return 0; |
| 277 | } | 226 | } |
| 278 | 227 | ||
| @@ -280,7 +229,6 @@ static struct phy_ops rcar_gen3_phy_usb2_ops = { | |||
| 280 | .init = rcar_gen3_phy_usb2_init, | 229 | .init = rcar_gen3_phy_usb2_init, |
| 281 | .exit = rcar_gen3_phy_usb2_exit, | 230 | .exit = rcar_gen3_phy_usb2_exit, |
| 282 | .power_on = rcar_gen3_phy_usb2_power_on, | 231 | .power_on = rcar_gen3_phy_usb2_power_on, |
| 283 | .power_off = rcar_gen3_phy_usb2_power_off, | ||
| 284 | .owner = THIS_MODULE, | 232 | .owner = THIS_MODULE, |
| 285 | }; | 233 | }; |
| 286 | 234 | ||
| @@ -313,6 +261,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) | |||
| 313 | struct rcar_gen3_chan *channel; | 261 | struct rcar_gen3_chan *channel; |
| 314 | struct phy_provider *provider; | 262 | struct phy_provider *provider; |
| 315 | struct resource *res; | 263 | struct resource *res; |
| 264 | int irq; | ||
| 316 | 265 | ||
| 317 | if (!dev->of_node) { | 266 | if (!dev->of_node) { |
| 318 | dev_err(dev, "This driver needs device tree\n"); | 267 | dev_err(dev, "This driver needs device tree\n"); |
| @@ -323,29 +272,19 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) | |||
| 323 | if (!channel) | 272 | if (!channel) |
| 324 | return -ENOMEM; | 273 | return -ENOMEM; |
| 325 | 274 | ||
| 326 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2_host"); | 275 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 327 | channel->usb2.base = devm_ioremap_resource(dev, res); | 276 | channel->usb2.base = devm_ioremap_resource(dev, res); |
| 328 | if (IS_ERR(channel->usb2.base)) | 277 | if (IS_ERR(channel->usb2.base)) |
| 329 | return PTR_ERR(channel->usb2.base); | 278 | return PTR_ERR(channel->usb2.base); |
| 330 | 279 | ||
| 331 | /* "hsusb" memory resource is optional */ | 280 | /* call request_irq for OTG */ |
| 332 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsusb"); | 281 | irq = platform_get_irq(pdev, 0); |
| 333 | 282 | if (irq >= 0) { | |
| 334 | /* To avoid error message by devm_ioremap_resource() */ | 283 | irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, |
| 335 | if (res) { | 284 | IRQF_SHARED, dev_name(dev), channel); |
| 336 | int irq; | ||
| 337 | |||
| 338 | channel->hsusb.base = devm_ioremap_resource(dev, res); | ||
| 339 | if (IS_ERR(channel->hsusb.base)) | ||
| 340 | channel->hsusb.base = NULL; | ||
| 341 | /* call request_irq for OTG */ | ||
| 342 | irq = platform_get_irq(pdev, 0); | ||
| 343 | if (irq >= 0) | ||
| 344 | irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, | ||
| 345 | IRQF_SHARED, dev_name(dev), | ||
| 346 | channel); | ||
| 347 | if (irq < 0) | 285 | if (irq < 0) |
| 348 | dev_err(dev, "No irq handler (%d)\n", irq); | 286 | dev_err(dev, "No irq handler (%d)\n", irq); |
| 287 | channel->has_otg = true; | ||
| 349 | } | 288 | } |
| 350 | 289 | ||
| 351 | /* devm_phy_create() will call pm_runtime_enable(dev); */ | 290 | /* devm_phy_create() will call pm_runtime_enable(dev); */ |
diff --git a/drivers/phy/phy-rockchip-dp.c b/drivers/phy/phy-rockchip-dp.c new file mode 100644 index 000000000000..77e2d02e6bee --- /dev/null +++ b/drivers/phy/phy-rockchip-dp.c | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | /* | ||
| 2 | * Rockchip DP PHY driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2016 FuZhou Rockchip Co., Ltd. | ||
| 5 | * Author: Yakir Yang <ykk@@rock-chips.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 as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License. | ||
| 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/phy/phy.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/regmap.h> | ||
| 19 | |||
| 20 | #define GRF_SOC_CON12 0x0274 | ||
| 21 | |||
| 22 | #define GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK BIT(20) | ||
| 23 | #define GRF_EDP_REF_CLK_SEL_INTER BIT(4) | ||
| 24 | |||
| 25 | #define GRF_EDP_PHY_SIDDQ_HIWORD_MASK BIT(21) | ||
| 26 | #define GRF_EDP_PHY_SIDDQ_ON 0 | ||
| 27 | #define GRF_EDP_PHY_SIDDQ_OFF BIT(5) | ||
| 28 | |||
| 29 | struct rockchip_dp_phy { | ||
| 30 | struct device *dev; | ||
| 31 | struct regmap *grf; | ||
| 32 | struct clk *phy_24m; | ||
| 33 | }; | ||
| 34 | |||
| 35 | static int rockchip_set_phy_state(struct phy *phy, bool enable) | ||
| 36 | { | ||
| 37 | struct rockchip_dp_phy *dp = phy_get_drvdata(phy); | ||
| 38 | int ret; | ||
| 39 | |||
| 40 | if (enable) { | ||
| 41 | ret = regmap_write(dp->grf, GRF_SOC_CON12, | ||
| 42 | GRF_EDP_PHY_SIDDQ_HIWORD_MASK | | ||
| 43 | GRF_EDP_PHY_SIDDQ_ON); | ||
| 44 | if (ret < 0) { | ||
| 45 | dev_err(dp->dev, "Can't enable PHY power %d\n", ret); | ||
| 46 | return ret; | ||
| 47 | } | ||
| 48 | |||
| 49 | ret = clk_prepare_enable(dp->phy_24m); | ||
| 50 | } else { | ||
| 51 | clk_disable_unprepare(dp->phy_24m); | ||
| 52 | |||
| 53 | ret = regmap_write(dp->grf, GRF_SOC_CON12, | ||
| 54 | GRF_EDP_PHY_SIDDQ_HIWORD_MASK | | ||
| 55 | GRF_EDP_PHY_SIDDQ_OFF); | ||
| 56 | } | ||
| 57 | |||
| 58 | return ret; | ||
| 59 | } | ||
| 60 | |||
| 61 | static int rockchip_dp_phy_power_on(struct phy *phy) | ||
| 62 | { | ||
| 63 | return rockchip_set_phy_state(phy, true); | ||
| 64 | } | ||
| 65 | |||
| 66 | static int rockchip_dp_phy_power_off(struct phy *phy) | ||
| 67 | { | ||
| 68 | return rockchip_set_phy_state(phy, false); | ||
| 69 | } | ||
| 70 | |||
| 71 | static const struct phy_ops rockchip_dp_phy_ops = { | ||
| 72 | .power_on = rockchip_dp_phy_power_on, | ||
| 73 | .power_off = rockchip_dp_phy_power_off, | ||
| 74 | .owner = THIS_MODULE, | ||
| 75 | }; | ||
| 76 | |||
| 77 | static int rockchip_dp_phy_probe(struct platform_device *pdev) | ||
| 78 | { | ||
| 79 | struct device *dev = &pdev->dev; | ||
| 80 | struct device_node *np = dev->of_node; | ||
| 81 | struct phy_provider *phy_provider; | ||
| 82 | struct rockchip_dp_phy *dp; | ||
| 83 | struct phy *phy; | ||
| 84 | int ret; | ||
| 85 | |||
| 86 | if (!np) | ||
| 87 | return -ENODEV; | ||
| 88 | |||
| 89 | dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); | ||
| 90 | if (IS_ERR(dp)) | ||
| 91 | return -ENOMEM; | ||
| 92 | |||
| 93 | dp->dev = dev; | ||
| 94 | |||
| 95 | dp->phy_24m = devm_clk_get(dev, "24m"); | ||
| 96 | if (IS_ERR(dp->phy_24m)) { | ||
| 97 | dev_err(dev, "cannot get clock 24m\n"); | ||
| 98 | return PTR_ERR(dp->phy_24m); | ||
| 99 | } | ||
| 100 | |||
| 101 | ret = clk_set_rate(dp->phy_24m, 24000000); | ||
| 102 | if (ret < 0) { | ||
| 103 | dev_err(dp->dev, "cannot set clock phy_24m %d\n", ret); | ||
| 104 | return ret; | ||
| 105 | } | ||
| 106 | |||
| 107 | dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); | ||
| 108 | if (IS_ERR(dp->grf)) { | ||
| 109 | dev_err(dev, "rk3288-dp needs rockchip,grf property\n"); | ||
| 110 | return PTR_ERR(dp->grf); | ||
| 111 | } | ||
| 112 | |||
| 113 | ret = regmap_write(dp->grf, GRF_SOC_CON12, GRF_EDP_REF_CLK_SEL_INTER | | ||
| 114 | GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK); | ||
| 115 | if (ret != 0) { | ||
| 116 | dev_err(dp->dev, "Could not config GRF edp ref clk: %d\n", ret); | ||
| 117 | return ret; | ||
| 118 | } | ||
| 119 | |||
| 120 | phy = devm_phy_create(dev, np, &rockchip_dp_phy_ops); | ||
| 121 | if (IS_ERR(phy)) { | ||
| 122 | dev_err(dev, "failed to create phy\n"); | ||
| 123 | return PTR_ERR(phy); | ||
| 124 | } | ||
| 125 | phy_set_drvdata(phy, dp); | ||
| 126 | |||
| 127 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | ||
| 128 | |||
| 129 | return PTR_ERR_OR_ZERO(phy_provider); | ||
| 130 | } | ||
| 131 | |||
| 132 | static const struct of_device_id rockchip_dp_phy_dt_ids[] = { | ||
| 133 | { .compatible = "rockchip,rk3288-dp-phy" }, | ||
| 134 | {} | ||
| 135 | }; | ||
| 136 | |||
| 137 | MODULE_DEVICE_TABLE(of, rockchip_dp_phy_dt_ids); | ||
| 138 | |||
| 139 | static struct platform_driver rockchip_dp_phy_driver = { | ||
| 140 | .probe = rockchip_dp_phy_probe, | ||
| 141 | .driver = { | ||
| 142 | .name = "rockchip-dp-phy", | ||
| 143 | .of_match_table = rockchip_dp_phy_dt_ids, | ||
| 144 | }, | ||
| 145 | }; | ||
| 146 | |||
| 147 | module_platform_driver(rockchip_dp_phy_driver); | ||
| 148 | |||
| 149 | MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>"); | ||
| 150 | MODULE_DESCRIPTION("Rockchip DP PHY driver"); | ||
| 151 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c new file mode 100644 index 000000000000..887b4c27195f --- /dev/null +++ b/drivers/phy/phy-rockchip-emmc.c | |||
| @@ -0,0 +1,229 @@ | |||
| 1 | /* | ||
| 2 | * Rockchip emmc PHY driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com> | ||
| 5 | * Copyright (C) 2016 ROCKCHIP, Inc. | ||
| 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 as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/delay.h> | ||
| 18 | #include <linux/mfd/syscon.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/of.h> | ||
| 21 | #include <linux/of_address.h> | ||
| 22 | #include <linux/phy/phy.h> | ||
| 23 | #include <linux/platform_device.h> | ||
| 24 | #include <linux/regmap.h> | ||
| 25 | |||
| 26 | /* | ||
| 27 | * The higher 16-bit of this register is used for write protection | ||
| 28 | * only if BIT(x + 16) set to 1 the BIT(x) can be written. | ||
| 29 | */ | ||
| 30 | #define HIWORD_UPDATE(val, mask, shift) \ | ||
| 31 | ((val) << (shift) | (mask) << ((shift) + 16)) | ||
| 32 | |||
| 33 | /* Register definition */ | ||
| 34 | #define GRF_EMMCPHY_CON0 0x0 | ||
| 35 | #define GRF_EMMCPHY_CON1 0x4 | ||
| 36 | #define GRF_EMMCPHY_CON2 0x8 | ||
| 37 | #define GRF_EMMCPHY_CON3 0xc | ||
| 38 | #define GRF_EMMCPHY_CON4 0x10 | ||
| 39 | #define GRF_EMMCPHY_CON5 0x14 | ||
| 40 | #define GRF_EMMCPHY_CON6 0x18 | ||
| 41 | #define GRF_EMMCPHY_STATUS 0x20 | ||
| 42 | |||
| 43 | #define PHYCTRL_PDB_MASK 0x1 | ||
| 44 | #define PHYCTRL_PDB_SHIFT 0x0 | ||
| 45 | #define PHYCTRL_PDB_PWR_ON 0x1 | ||
| 46 | #define PHYCTRL_PDB_PWR_OFF 0x0 | ||
| 47 | #define PHYCTRL_ENDLL_MASK 0x1 | ||
| 48 | #define PHYCTRL_ENDLL_SHIFT 0x1 | ||
| 49 | #define PHYCTRL_ENDLL_ENABLE 0x1 | ||
| 50 | #define PHYCTRL_ENDLL_DISABLE 0x0 | ||
| 51 | #define PHYCTRL_CALDONE_MASK 0x1 | ||
| 52 | #define PHYCTRL_CALDONE_SHIFT 0x6 | ||
| 53 | #define PHYCTRL_CALDONE_DONE 0x1 | ||
| 54 | #define PHYCTRL_CALDONE_GOING 0x0 | ||
| 55 | #define PHYCTRL_DLLRDY_MASK 0x1 | ||
| 56 | #define PHYCTRL_DLLRDY_SHIFT 0x5 | ||
| 57 | #define PHYCTRL_DLLRDY_DONE 0x1 | ||
| 58 | #define PHYCTRL_DLLRDY_GOING 0x0 | ||
| 59 | |||
| 60 | struct rockchip_emmc_phy { | ||
| 61 | unsigned int reg_offset; | ||
| 62 | struct regmap *reg_base; | ||
| 63 | }; | ||
| 64 | |||
| 65 | static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, | ||
| 66 | bool on_off) | ||
| 67 | { | ||
| 68 | unsigned int caldone; | ||
| 69 | unsigned int dllrdy; | ||
| 70 | |||
| 71 | /* | ||
| 72 | * Keep phyctrl_pdb and phyctrl_endll low to allow | ||
| 73 | * initialization of CALIO state M/C DFFs | ||
| 74 | */ | ||
| 75 | regmap_write(rk_phy->reg_base, | ||
| 76 | rk_phy->reg_offset + GRF_EMMCPHY_CON6, | ||
| 77 | HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF, | ||
| 78 | PHYCTRL_PDB_MASK, | ||
| 79 | PHYCTRL_PDB_SHIFT)); | ||
| 80 | regmap_write(rk_phy->reg_base, | ||
| 81 | rk_phy->reg_offset + GRF_EMMCPHY_CON6, | ||
| 82 | HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE, | ||
| 83 | PHYCTRL_ENDLL_MASK, | ||
| 84 | PHYCTRL_ENDLL_SHIFT)); | ||
| 85 | |||
| 86 | /* Already finish power_off above */ | ||
| 87 | if (on_off == PHYCTRL_PDB_PWR_OFF) | ||
| 88 | return 0; | ||
| 89 | |||
| 90 | /* | ||
| 91 | * According to the user manual, calpad calibration | ||
| 92 | * cycle takes more than 2us without the minimal recommended | ||
| 93 | * value, so we may need a little margin here | ||
| 94 | */ | ||
| 95 | udelay(3); | ||
| 96 | regmap_write(rk_phy->reg_base, | ||
| 97 | rk_phy->reg_offset + GRF_EMMCPHY_CON6, | ||
| 98 | HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON, | ||
| 99 | PHYCTRL_PDB_MASK, | ||
| 100 | PHYCTRL_PDB_SHIFT)); | ||
| 101 | |||
| 102 | /* | ||
| 103 | * According to the user manual, it asks driver to | ||
| 104 | * wait 5us for calpad busy trimming | ||
| 105 | */ | ||
| 106 | udelay(5); | ||
| 107 | regmap_read(rk_phy->reg_base, | ||
| 108 | rk_phy->reg_offset + GRF_EMMCPHY_STATUS, | ||
| 109 | &caldone); | ||
| 110 | caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; | ||
| 111 | if (caldone != PHYCTRL_CALDONE_DONE) { | ||
| 112 | pr_err("rockchip_emmc_phy_power: caldone timeout.\n"); | ||
| 113 | return -ETIMEDOUT; | ||
| 114 | } | ||
| 115 | |||
| 116 | regmap_write(rk_phy->reg_base, | ||
| 117 | rk_phy->reg_offset + GRF_EMMCPHY_CON6, | ||
| 118 | HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, | ||
| 119 | PHYCTRL_ENDLL_MASK, | ||
| 120 | PHYCTRL_ENDLL_SHIFT)); | ||
| 121 | /* | ||
| 122 | * After enable analog DLL circuits, we need extra 10.2us | ||
| 123 | * for dll to be ready for work. | ||
| 124 | */ | ||
| 125 | udelay(11); | ||
| 126 | regmap_read(rk_phy->reg_base, | ||
| 127 | rk_phy->reg_offset + GRF_EMMCPHY_STATUS, | ||
| 128 | &dllrdy); | ||
| 129 | dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; | ||
| 130 | if (dllrdy != PHYCTRL_DLLRDY_DONE) { | ||
| 131 | pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n"); | ||
| 132 | return -ETIMEDOUT; | ||
| 133 | } | ||
| 134 | |||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | static int rockchip_emmc_phy_power_off(struct phy *phy) | ||
| 139 | { | ||
| 140 | struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); | ||
| 141 | int ret = 0; | ||
| 142 | |||
| 143 | /* Power down emmc phy analog blocks */ | ||
| 144 | ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF); | ||
| 145 | if (ret) | ||
| 146 | return ret; | ||
| 147 | |||
| 148 | return 0; | ||
| 149 | } | ||
| 150 | |||
| 151 | static int rockchip_emmc_phy_power_on(struct phy *phy) | ||
| 152 | { | ||
| 153 | struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); | ||
| 154 | int ret = 0; | ||
| 155 | |||
| 156 | /* Power up emmc phy analog blocks */ | ||
| 157 | ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); | ||
| 158 | if (ret) | ||
| 159 | return ret; | ||
| 160 | |||
| 161 | return 0; | ||
| 162 | } | ||
| 163 | |||
| 164 | static const struct phy_ops ops = { | ||
| 165 | .power_on = rockchip_emmc_phy_power_on, | ||
| 166 | .power_off = rockchip_emmc_phy_power_off, | ||
| 167 | .owner = THIS_MODULE, | ||
| 168 | }; | ||
| 169 | |||
| 170 | static int rockchip_emmc_phy_probe(struct platform_device *pdev) | ||
| 171 | { | ||
| 172 | struct device *dev = &pdev->dev; | ||
| 173 | struct rockchip_emmc_phy *rk_phy; | ||
| 174 | struct phy *generic_phy; | ||
| 175 | struct phy_provider *phy_provider; | ||
| 176 | struct regmap *grf; | ||
| 177 | unsigned int reg_offset; | ||
| 178 | |||
| 179 | grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); | ||
| 180 | if (IS_ERR(grf)) { | ||
| 181 | dev_err(dev, "Missing rockchip,grf property\n"); | ||
| 182 | return PTR_ERR(grf); | ||
| 183 | } | ||
| 184 | |||
| 185 | rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); | ||
| 186 | if (!rk_phy) | ||
| 187 | return -ENOMEM; | ||
| 188 | |||
| 189 | if (of_property_read_u32(dev->of_node, "reg", ®_offset)) { | ||
| 190 | dev_err(dev, "missing reg property in node %s\n", | ||
| 191 | dev->of_node->name); | ||
| 192 | return -EINVAL; | ||
| 193 | } | ||
| 194 | |||
| 195 | rk_phy->reg_offset = reg_offset; | ||
| 196 | rk_phy->reg_base = grf; | ||
| 197 | |||
| 198 | generic_phy = devm_phy_create(dev, dev->of_node, &ops); | ||
| 199 | if (IS_ERR(generic_phy)) { | ||
| 200 | dev_err(dev, "failed to create PHY\n"); | ||
| 201 | return PTR_ERR(generic_phy); | ||
| 202 | } | ||
| 203 | |||
| 204 | phy_set_drvdata(generic_phy, rk_phy); | ||
| 205 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | ||
| 206 | |||
| 207 | return PTR_ERR_OR_ZERO(phy_provider); | ||
| 208 | } | ||
| 209 | |||
| 210 | static const struct of_device_id rockchip_emmc_phy_dt_ids[] = { | ||
| 211 | { .compatible = "rockchip,rk3399-emmc-phy" }, | ||
| 212 | {} | ||
| 213 | }; | ||
| 214 | |||
| 215 | MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids); | ||
| 216 | |||
| 217 | static struct platform_driver rockchip_emmc_driver = { | ||
| 218 | .probe = rockchip_emmc_phy_probe, | ||
| 219 | .driver = { | ||
| 220 | .name = "rockchip-emmc-phy", | ||
| 221 | .of_match_table = rockchip_emmc_phy_dt_ids, | ||
| 222 | }, | ||
| 223 | }; | ||
| 224 | |||
| 225 | module_platform_driver(rockchip_emmc_driver); | ||
| 226 | |||
| 227 | MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>"); | ||
| 228 | MODULE_DESCRIPTION("Rockchip EMMC PHY driver"); | ||
| 229 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c index 33a80eba1cb4..f62d899063a3 100644 --- a/drivers/phy/phy-rockchip-usb.c +++ b/drivers/phy/phy-rockchip-usb.c | |||
| @@ -30,21 +30,23 @@ | |||
| 30 | #include <linux/regmap.h> | 30 | #include <linux/regmap.h> |
| 31 | #include <linux/mfd/syscon.h> | 31 | #include <linux/mfd/syscon.h> |
| 32 | 32 | ||
| 33 | /* | 33 | static int enable_usb_uart; |
| 34 | * The higher 16-bit of this register is used for write protection | 34 | |
| 35 | * only if BIT(13 + 16) set to 1 the BIT(13) can be written. | 35 | #define HIWORD_UPDATE(val, mask) \ |
| 36 | */ | 36 | ((val) | (mask) << 16) |
| 37 | #define SIDDQ_WRITE_ENA BIT(29) | 37 | |
| 38 | #define SIDDQ_ON BIT(13) | 38 | #define UOC_CON0_SIDDQ BIT(13) |
| 39 | #define SIDDQ_OFF (0 << 13) | ||
| 40 | 39 | ||
| 41 | struct rockchip_usb_phys { | 40 | struct rockchip_usb_phys { |
| 42 | int reg; | 41 | int reg; |
| 43 | const char *pll_name; | 42 | const char *pll_name; |
| 44 | }; | 43 | }; |
| 45 | 44 | ||
| 45 | struct rockchip_usb_phy_base; | ||
| 46 | struct rockchip_usb_phy_pdata { | 46 | struct rockchip_usb_phy_pdata { |
| 47 | struct rockchip_usb_phys *phys; | 47 | struct rockchip_usb_phys *phys; |
| 48 | int (*init_usb_uart)(struct regmap *grf); | ||
| 49 | int usb_uart_phy; | ||
| 48 | }; | 50 | }; |
| 49 | 51 | ||
| 50 | struct rockchip_usb_phy_base { | 52 | struct rockchip_usb_phy_base { |
| @@ -61,13 +63,15 @@ struct rockchip_usb_phy { | |||
| 61 | struct clk *clk480m; | 63 | struct clk *clk480m; |
| 62 | struct clk_hw clk480m_hw; | 64 | struct clk_hw clk480m_hw; |
| 63 | struct phy *phy; | 65 | struct phy *phy; |
| 66 | bool uart_enabled; | ||
| 64 | }; | 67 | }; |
| 65 | 68 | ||
| 66 | static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy, | 69 | static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy, |
| 67 | bool siddq) | 70 | bool siddq) |
| 68 | { | 71 | { |
| 69 | return regmap_write(phy->base->reg_base, phy->reg_offset, | 72 | u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ); |
| 70 | SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF)); | 73 | |
| 74 | return regmap_write(phy->base->reg_base, phy->reg_offset, val); | ||
| 71 | } | 75 | } |
| 72 | 76 | ||
| 73 | static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw, | 77 | static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw, |
| @@ -108,7 +112,7 @@ static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw) | |||
| 108 | if (ret < 0) | 112 | if (ret < 0) |
| 109 | return ret; | 113 | return ret; |
| 110 | 114 | ||
| 111 | return (val & SIDDQ_ON) ? 0 : 1; | 115 | return (val & UOC_CON0_SIDDQ) ? 0 : 1; |
| 112 | } | 116 | } |
| 113 | 117 | ||
| 114 | static const struct clk_ops rockchip_usb_phy480m_ops = { | 118 | static const struct clk_ops rockchip_usb_phy480m_ops = { |
| @@ -122,6 +126,9 @@ static int rockchip_usb_phy_power_off(struct phy *_phy) | |||
| 122 | { | 126 | { |
| 123 | struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); | 127 | struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); |
| 124 | 128 | ||
| 129 | if (phy->uart_enabled) | ||
| 130 | return -EBUSY; | ||
| 131 | |||
| 125 | clk_disable_unprepare(phy->clk480m); | 132 | clk_disable_unprepare(phy->clk480m); |
| 126 | 133 | ||
| 127 | return 0; | 134 | return 0; |
| @@ -131,6 +138,9 @@ static int rockchip_usb_phy_power_on(struct phy *_phy) | |||
| 131 | { | 138 | { |
| 132 | struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); | 139 | struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); |
| 133 | 140 | ||
| 141 | if (phy->uart_enabled) | ||
| 142 | return -EBUSY; | ||
| 143 | |||
| 134 | return clk_prepare_enable(phy->clk480m); | 144 | return clk_prepare_enable(phy->clk480m); |
| 135 | } | 145 | } |
| 136 | 146 | ||
| @@ -144,8 +154,10 @@ static void rockchip_usb_phy_action(void *data) | |||
| 144 | { | 154 | { |
| 145 | struct rockchip_usb_phy *rk_phy = data; | 155 | struct rockchip_usb_phy *rk_phy = data; |
| 146 | 156 | ||
| 147 | of_clk_del_provider(rk_phy->np); | 157 | if (!rk_phy->uart_enabled) { |
| 148 | clk_unregister(rk_phy->clk480m); | 158 | of_clk_del_provider(rk_phy->np); |
| 159 | clk_unregister(rk_phy->clk480m); | ||
| 160 | } | ||
| 149 | 161 | ||
| 150 | if (rk_phy->clk) | 162 | if (rk_phy->clk) |
| 151 | clk_put(rk_phy->clk); | 163 | clk_put(rk_phy->clk); |
| @@ -194,30 +206,35 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base, | |||
| 194 | return -EINVAL; | 206 | return -EINVAL; |
| 195 | } | 207 | } |
| 196 | 208 | ||
| 197 | if (rk_phy->clk) { | 209 | if (enable_usb_uart && base->pdata->usb_uart_phy == i) { |
| 198 | clk_name = __clk_get_name(rk_phy->clk); | 210 | dev_dbg(base->dev, "phy%d used as uart output\n", i); |
| 199 | init.flags = 0; | 211 | rk_phy->uart_enabled = true; |
| 200 | init.parent_names = &clk_name; | ||
| 201 | init.num_parents = 1; | ||
| 202 | } else { | 212 | } else { |
| 203 | init.flags = CLK_IS_ROOT; | 213 | if (rk_phy->clk) { |
| 204 | init.parent_names = NULL; | 214 | clk_name = __clk_get_name(rk_phy->clk); |
| 205 | init.num_parents = 0; | 215 | init.flags = 0; |
| 206 | } | 216 | init.parent_names = &clk_name; |
| 217 | init.num_parents = 1; | ||
| 218 | } else { | ||
| 219 | init.flags = CLK_IS_ROOT; | ||
| 220 | init.parent_names = NULL; | ||
| 221 | init.num_parents = 0; | ||
| 222 | } | ||
| 207 | 223 | ||
| 208 | init.ops = &rockchip_usb_phy480m_ops; | 224 | init.ops = &rockchip_usb_phy480m_ops; |
| 209 | rk_phy->clk480m_hw.init = &init; | 225 | rk_phy->clk480m_hw.init = &init; |
| 210 | 226 | ||
| 211 | rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw); | 227 | rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw); |
| 212 | if (IS_ERR(rk_phy->clk480m)) { | 228 | if (IS_ERR(rk_phy->clk480m)) { |
| 213 | err = PTR_ERR(rk_phy->clk480m); | 229 | err = PTR_ERR(rk_phy->clk480m); |
| 214 | goto err_clk; | 230 | goto err_clk; |
| 215 | } | 231 | } |
| 216 | 232 | ||
| 217 | err = of_clk_add_provider(child, of_clk_src_simple_get, | 233 | err = of_clk_add_provider(child, of_clk_src_simple_get, |
| 218 | rk_phy->clk480m); | 234 | rk_phy->clk480m); |
| 219 | if (err < 0) | 235 | if (err < 0) |
| 220 | goto err_clk_prov; | 236 | goto err_clk_prov; |
| 237 | } | ||
| 221 | 238 | ||
| 222 | err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy); | 239 | err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy); |
| 223 | if (err) | 240 | if (err) |
| @@ -230,13 +247,21 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base, | |||
| 230 | } | 247 | } |
| 231 | phy_set_drvdata(rk_phy->phy, rk_phy); | 248 | phy_set_drvdata(rk_phy->phy, rk_phy); |
| 232 | 249 | ||
| 233 | /* only power up usb phy when it use, so disable it when init*/ | 250 | /* |
| 234 | return rockchip_usb_phy_power(rk_phy, 1); | 251 | * When acting as uart-pipe, just keep clock on otherwise |
| 252 | * only power up usb phy when it use, so disable it when init | ||
| 253 | */ | ||
| 254 | if (rk_phy->uart_enabled) | ||
| 255 | return clk_prepare_enable(rk_phy->clk); | ||
| 256 | else | ||
| 257 | return rockchip_usb_phy_power(rk_phy, 1); | ||
| 235 | 258 | ||
| 236 | err_devm_action: | 259 | err_devm_action: |
| 237 | of_clk_del_provider(child); | 260 | if (!rk_phy->uart_enabled) |
| 261 | of_clk_del_provider(child); | ||
| 238 | err_clk_prov: | 262 | err_clk_prov: |
| 239 | clk_unregister(rk_phy->clk480m); | 263 | if (!rk_phy->uart_enabled) |
| 264 | clk_unregister(rk_phy->clk480m); | ||
| 240 | err_clk: | 265 | err_clk: |
| 241 | if (rk_phy->clk) | 266 | if (rk_phy->clk) |
| 242 | clk_put(rk_phy->clk); | 267 | clk_put(rk_phy->clk); |
| @@ -259,6 +284,86 @@ static const struct rockchip_usb_phy_pdata rk3188_pdata = { | |||
| 259 | }, | 284 | }, |
| 260 | }; | 285 | }; |
| 261 | 286 | ||
| 287 | #define RK3288_UOC0_CON0 0x320 | ||
| 288 | #define RK3288_UOC0_CON0_COMMON_ON_N BIT(0) | ||
| 289 | #define RK3288_UOC0_CON0_DISABLE BIT(4) | ||
| 290 | |||
| 291 | #define RK3288_UOC0_CON2 0x328 | ||
| 292 | #define RK3288_UOC0_CON2_SOFT_CON_SEL BIT(2) | ||
| 293 | |||
| 294 | #define RK3288_UOC0_CON3 0x32c | ||
| 295 | #define RK3288_UOC0_CON3_UTMI_SUSPENDN BIT(0) | ||
| 296 | #define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING (1 << 1) | ||
| 297 | #define RK3288_UOC0_CON3_UTMI_OPMODE_MASK (3 << 1) | ||
| 298 | #define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3) | ||
| 299 | #define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK (3 << 3) | ||
| 300 | #define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED BIT(5) | ||
| 301 | #define RK3288_UOC0_CON3_BYPASSDMEN BIT(6) | ||
| 302 | #define RK3288_UOC0_CON3_BYPASSSEL BIT(7) | ||
| 303 | |||
| 304 | /* | ||
| 305 | * Enable the bypass of uart2 data through the otg usb phy. | ||
| 306 | * Original description in the TRM. | ||
| 307 | * 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1. | ||
| 308 | * 2. Disable the pull-up resistance on the D+ line by setting | ||
| 309 | * OPMODE0[1:0] to 2’b01. | ||
| 310 | * 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend | ||
| 311 | * mode, set COMMONONN to 1’b1. | ||
| 312 | * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0. | ||
| 313 | * 5. Set BYPASSSEL0 to 1’b1. | ||
| 314 | * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0. | ||
| 315 | * To receive data, monitor FSVPLUS0. | ||
| 316 | * | ||
| 317 | * The actual code in the vendor kernel does some things differently. | ||
| 318 | */ | ||
| 319 | static int __init rk3288_init_usb_uart(struct regmap *grf) | ||
| 320 | { | ||
| 321 | u32 val; | ||
| 322 | int ret; | ||
| 323 | |||
| 324 | /* | ||
| 325 | * COMMON_ON and DISABLE settings are described in the TRM, | ||
| 326 | * but were not present in the original code. | ||
| 327 | * Also disable the analog phy components to save power. | ||
| 328 | */ | ||
| 329 | val = HIWORD_UPDATE(RK3288_UOC0_CON0_COMMON_ON_N | ||
| 330 | | RK3288_UOC0_CON0_DISABLE | ||
| 331 | | UOC_CON0_SIDDQ, | ||
| 332 | RK3288_UOC0_CON0_COMMON_ON_N | ||
| 333 | | RK3288_UOC0_CON0_DISABLE | ||
| 334 | | UOC_CON0_SIDDQ); | ||
| 335 | ret = regmap_write(grf, RK3288_UOC0_CON0, val); | ||
| 336 | if (ret) | ||
| 337 | return ret; | ||
| 338 | |||
| 339 | val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL, | ||
| 340 | RK3288_UOC0_CON2_SOFT_CON_SEL); | ||
| 341 | ret = regmap_write(grf, RK3288_UOC0_CON2, val); | ||
| 342 | if (ret) | ||
| 343 | return ret; | ||
| 344 | |||
| 345 | val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING | ||
| 346 | | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC | ||
| 347 | | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED, | ||
| 348 | RK3288_UOC0_CON3_UTMI_SUSPENDN | ||
| 349 | | RK3288_UOC0_CON3_UTMI_OPMODE_MASK | ||
| 350 | | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK | ||
| 351 | | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED); | ||
| 352 | ret = regmap_write(grf, RK3288_UOC0_CON3, val); | ||
| 353 | if (ret) | ||
| 354 | return ret; | ||
| 355 | |||
| 356 | val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL | ||
| 357 | | RK3288_UOC0_CON3_BYPASSDMEN, | ||
| 358 | RK3288_UOC0_CON3_BYPASSSEL | ||
| 359 | | RK3288_UOC0_CON3_BYPASSDMEN); | ||
| 360 | ret = regmap_write(grf, RK3288_UOC0_CON3, val); | ||
| 361 | if (ret) | ||
| 362 | return ret; | ||
| 363 | |||
| 364 | return 0; | ||
| 365 | } | ||
| 366 | |||
| 262 | static const struct rockchip_usb_phy_pdata rk3288_pdata = { | 367 | static const struct rockchip_usb_phy_pdata rk3288_pdata = { |
| 263 | .phys = (struct rockchip_usb_phys[]){ | 368 | .phys = (struct rockchip_usb_phys[]){ |
| 264 | { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" }, | 369 | { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" }, |
| @@ -266,6 +371,8 @@ static const struct rockchip_usb_phy_pdata rk3288_pdata = { | |||
| 266 | { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" }, | 371 | { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" }, |
| 267 | { /* sentinel */ } | 372 | { /* sentinel */ } |
| 268 | }, | 373 | }, |
| 374 | .init_usb_uart = rk3288_init_usb_uart, | ||
| 375 | .usb_uart_phy = 0, | ||
| 269 | }; | 376 | }; |
| 270 | 377 | ||
| 271 | static int rockchip_usb_phy_probe(struct platform_device *pdev) | 378 | static int rockchip_usb_phy_probe(struct platform_device *pdev) |
| @@ -328,6 +435,60 @@ static struct platform_driver rockchip_usb_driver = { | |||
| 328 | 435 | ||
| 329 | module_platform_driver(rockchip_usb_driver); | 436 | module_platform_driver(rockchip_usb_driver); |
| 330 | 437 | ||
| 438 | #ifndef MODULE | ||
| 439 | static int __init rockchip_init_usb_uart(void) | ||
| 440 | { | ||
| 441 | const struct of_device_id *match; | ||
| 442 | const struct rockchip_usb_phy_pdata *data; | ||
| 443 | struct device_node *np; | ||
| 444 | struct regmap *grf; | ||
| 445 | int ret; | ||
| 446 | |||
| 447 | if (!enable_usb_uart) | ||
| 448 | return 0; | ||
| 449 | |||
| 450 | np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids, | ||
| 451 | &match); | ||
| 452 | if (!np) { | ||
| 453 | pr_err("%s: failed to find usbphy node\n", __func__); | ||
| 454 | return -ENOTSUPP; | ||
| 455 | } | ||
| 456 | |||
| 457 | pr_debug("%s: using settings for %s\n", __func__, match->compatible); | ||
| 458 | data = match->data; | ||
| 459 | |||
| 460 | if (!data->init_usb_uart) { | ||
| 461 | pr_err("%s: usb-uart not available on %s\n", | ||
| 462 | __func__, match->compatible); | ||
| 463 | return -ENOTSUPP; | ||
| 464 | } | ||
| 465 | |||
| 466 | grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); | ||
| 467 | if (IS_ERR(grf)) { | ||
| 468 | pr_err("%s: Missing rockchip,grf property, %lu\n", | ||
| 469 | __func__, PTR_ERR(grf)); | ||
| 470 | return PTR_ERR(grf); | ||
| 471 | } | ||
| 472 | |||
| 473 | ret = data->init_usb_uart(grf); | ||
| 474 | if (ret) { | ||
| 475 | pr_err("%s: could not init usb_uart, %d\n", __func__, ret); | ||
| 476 | enable_usb_uart = 0; | ||
| 477 | return ret; | ||
| 478 | } | ||
| 479 | |||
| 480 | return 0; | ||
| 481 | } | ||
| 482 | early_initcall(rockchip_init_usb_uart); | ||
| 483 | |||
| 484 | static int __init rockchip_usb_uart(char *buf) | ||
| 485 | { | ||
| 486 | enable_usb_uart = true; | ||
| 487 | return 0; | ||
| 488 | } | ||
| 489 | early_param("rockchip.usb_uart", rockchip_usb_uart); | ||
| 490 | #endif | ||
| 491 | |||
| 331 | MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>"); | 492 | MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>"); |
| 332 | MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver"); | 493 | MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver"); |
| 333 | MODULE_LICENSE("GPL v2"); | 494 | MODULE_LICENSE("GPL v2"); |
