diff --git a/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt b/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt index 46a135dae6b3..89caa885d08c 100644 --- a/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt +++ b/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt @@ -26,6 +26,7 @@ Required properties (port (child) node): filled in "reg". It can also contain the offset of the system configuration registers used as glue-logic to setup the device for SATA/PCIe or USB3 devices. +- st,syscfg : Offset of the parent configuration register. - resets : phandle to the parent reset controller. - reset-names : Associated name must be "miphy-sw-rst". @@ -54,18 +55,12 @@ example: phy_port0: port@9b22000 { reg = <0x9b22000 0xff>, <0x9b09000 0xff>, - <0x9b04000 0xff>, - <0x114 0x4>, /* sysctrl MiPHY cntrl */ - <0x818 0x4>, /* sysctrl MiPHY status*/ - <0xe0 0x4>, /* sysctrl PCIe */ - <0xec 0x4>; /* sysctrl SATA */ + <0x9b04000 0xff>; reg-names = "sata-up", "pcie-up", - "pipew", - "miphy-ctrl-glue", - "miphy-status-glue", - "pcie-glue", - "sata-glue"; + "pipew"; + + st,syscfg = <0x114 0x818 0xe0 0xec>; #phy-cells = <1>; st,osc-rdy; reset-names = "miphy-sw-rst"; @@ -75,18 +70,13 @@ example: phy_port1: port@9b2a000 { reg = <0x9b2a000 0xff>, <0x9b19000 0xff>, - <0x9b14000 0xff>, - <0x118 0x4>, - <0x81c 0x4>, - <0xe4 0x4>, - <0xf0 0x4>; + <0x9b14000 0xff>; reg-names = "sata-up", "pcie-up", - "pipew", - "miphy-ctrl-glue", - "miphy-status-glue", - "pcie-glue", - "sata-glue"; + "pipew"; + + st,syscfg = <0x118 0x81c 0xe4 0xf0>; + #phy-cells = <1>; st,osc-force-ext; reset-names = "miphy-sw-rst"; @@ -95,13 +85,12 @@ example: phy_port2: port@8f95000 { reg = <0x8f95000 0xff>, - <0x8f90000 0xff>, - <0x11c 0x4>, - <0x820 0x4>; + <0x8f90000 0xff>; reg-names = "pipew", - "usb3-up", - "miphy-ctrl-glue", - "miphy-status-glue"; + "usb3-up"; + + st,syscfg = <0x11c 0x820>; + #phy-cells = <1>; reset-names = "miphy-sw-rst"; resets = <&softreset STIH407_MIPHY2_SOFTRESET>; @@ -125,4 +114,4 @@ example: Macro definitions for the supported miphy configuration can be found in: -include/dt-bindings/phy/phy-miphy28lp.h +include/dt-bindings/phy/phy.h diff --git a/Documentation/devicetree/bindings/phy/phy-miphy365x.txt b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt index 42c880886cf7..9802d5d911aa 100644 --- a/Documentation/devicetree/bindings/phy/phy-miphy365x.txt +++ b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt @@ -6,8 +6,10 @@ for SATA and PCIe. Required properties (controller (parent) node): - compatible : Should be "st,miphy365x-phy" -- st,syscfg : Should be a phandle of the system configuration register group - which contain the SATA, PCIe mode setting bits +- st,syscfg : Phandle / integer array property. Phandle of sysconfig group + containing the miphy registers and integer array should contain + an entry for each port sub-node, specifying the control + register offset inside the sysconfig group. Required nodes : A sub-node is required for each channel the controller provides. Address range information including the usual @@ -26,7 +28,6 @@ Required properties (port (child) node): registers filled in "reg": - sata: For SATA devices - pcie: For PCIe devices - - syscfg: To specify the syscfg based config register Optional properties (port (child) node): - st,sata-gen : Generation of locally attached SATA IP. Expected values @@ -39,20 +40,20 @@ Example: miphy365x_phy: miphy365x@fe382000 { compatible = "st,miphy365x-phy"; - st,syscfg = <&syscfg_rear>; + st,syscfg = <&syscfg_rear 0x824 0x828>; #address-cells = <1>; #size-cells = <1>; ranges; phy_port0: port@fe382000 { - reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>; - reg-names = "sata", "pcie", "syscfg"; + reg = <0xfe382000 0x100>, <0xfe394000 0x100>; + reg-names = "sata", "pcie"; #phy-cells = <1>; st,sata-gen = <3>; }; phy_port1: port@fe38a000 { - reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>;; + reg = <0xfe38a000 0x100>, <0xfe804000 0x100>;; reg-names = "sata", "pcie", "syscfg"; #phy-cells = <1>; st,pcie-tx-pol-inv; diff --git a/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt b/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt index 1ef8228db73b..de6a706abcdb 100644 --- a/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt +++ b/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt @@ -5,10 +5,7 @@ host controllers (when controlling usb2/1.1 devices) available on STiH407 SoC fa Required properties: - compatible : should be "st,stih407-usb2-phy" -- reg : contain the offset and length of the system configuration registers - used as glue logic to control & parameter phy -- reg-names : the names of the system configuration registers in "reg", should be "param" and "reg" -- st,syscfg : sysconfig register to manage phy parameter at driver level +- st,syscfg : phandle of sysconfig bank plus integer array containing phyparam and phyctrl register offsets - resets : list of phandle and reset specifier pairs. There should be two entries, one for the whole phy and one for the port - reset-names : list of reset signal names. Should be "global" and "port" @@ -19,11 +16,8 @@ Example: usb2_picophy0: usbpicophy@f8 { compatible = "st,stih407-usb2-phy"; - reg = <0xf8 0x04>, /* syscfg 5062 */ - <0xf4 0x04>; /* syscfg 5061 */ - reg-names = "param", "ctrl"; #phy-cells = <0>; - st,syscfg = <&syscfg_core>; + st,syscfg = <&syscfg_core 0x100 0xf4>; resets = <&softreset STIH407_PICOPHY_SOFTRESET>, <&picophyreset STIH407_PICOPHY0_RESET>; reset-names = "global", "port"; diff --git a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt new file mode 100644 index 000000000000..826454ac43bb --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt @@ -0,0 +1,37 @@ +ROCKCHIP USB2 PHY + +Required properties: + - compatible: rockchip,rk3288-usb-phy + - rockchip,grf : phandle to the syscon managing the "general + register files" + - #address-cells: should be 1 + - #size-cells: should be 0 + +Sub-nodes: +Each PHY should be represented as a sub-node. + +Sub-nodes +required properties: +- #phy-cells: should be 0 +- reg: PHY configure reg address offset in GRF + "0x320" - for PHY attach to OTG controller + "0x334" - for PHY attach to HOST0 controller + "0x348" - for PHY attach to HOST1 controller + +Optional Properties: +- clocks : phandle + clock specifier for the phy clocks +- clock-names: string, clock name, must be "phyclk" + +Example: + +usbphy: phy { + compatible = "rockchip,rk3288-usb-phy"; + rockchip,grf = <&grf>; + #address-cells = <1>; + #size-cells = <0>; + + usbphy0: usb-phy0 { + #phy-cells = <0>; + reg = <0x320>; + }; +}; diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt index d5bad920827f..91e38cfe1f8f 100644 --- a/Documentation/devicetree/bindings/phy/samsung-phy.txt +++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt @@ -3,8 +3,8 @@ Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY Required properties: - compatible : should be "samsung,s5pv210-mipi-video-phy"; -- reg : offset and length of the MIPI DPHY register set; - #phy-cells : from the generic phy bindings, must be 1; +- syscon - phandle to the PMU system controller; For "samsung,s5pv210-mipi-video-phy" compatible PHYs the second cell in the PHY specifier identifies the PHY and its meaning is as follows: diff --git a/arch/arm/boot/dts/stih416.dtsi b/arch/arm/boot/dts/stih416.dtsi index fad9073ddeed..85afe01c34fa 100644 --- a/arch/arm/boot/dts/stih416.dtsi +++ b/arch/arm/boot/dts/stih416.dtsi @@ -283,21 +283,21 @@ miphy365x_phy: phy@fe382000 { compatible = "st,miphy365x-phy"; - st,syscfg = <&syscfg_rear>; + st,syscfg = <&syscfg_rear 0x824 0x828>; #address-cells = <1>; #size-cells = <1>; ranges; phy_port0: port@fe382000 { #phy-cells = <1>; - reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>; - reg-names = "sata", "pcie", "syscfg"; + reg = <0xfe382000 0x100>, <0xfe394000 0x100>; + reg-names = "sata", "pcie"; }; phy_port1: port@fe38a000 { #phy-cells = <1>; - reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>; - reg-names = "sata", "pcie", "syscfg"; + reg = <0xfe38a000 0x100>, <0xfe804000 0x100>; + reg-names = "sata", "pcie"; }; }; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index ccad8809ecb1..b24500afba25 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -239,6 +239,13 @@ config PHY_QCOM_IPQ806X_SATA depends on OF select GENERIC_PHY +config PHY_ROCKCHIP_USB + tristate "Rockchip USB2 PHY Driver" + depends on ARCH_ROCKCHIP && OF + select GENERIC_PHY + help + Enable this to support the Rockchip USB 2.0 PHY. + config PHY_ST_SPEAR1310_MIPHY tristate "ST SPEAR1310-MIPHY driver" select GENERIC_PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index aa74f961e44e..48bf5a15f161 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -28,6 +28,7 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o +obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o diff --git a/drivers/phy/phy-armada375-usb2.c b/drivers/phy/phy-armada375-usb2.c index ac7d99d01cb3..7c99ca256f05 100644 --- a/drivers/phy/phy-armada375-usb2.c +++ b/drivers/phy/phy-armada375-usb2.c @@ -118,8 +118,8 @@ static int armada375_usb_phy_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); usb_cluster_base = devm_ioremap_resource(&pdev->dev, res); - if (!usb_cluster_base) - return -ENOMEM; + if (IS_ERR(usb_cluster_base)) + return PTR_ERR(usb_cluster_base); phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops); if (IS_ERR(phy)) { diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index 943e0f88a120..f017b2f2a54e 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -12,19 +12,18 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include -/* MIPI_PHYn_CONTROL register offset: n = 0..1 */ +/* MIPI_PHYn_CONTROL reg. offset (for base address from ioremap): n = 0..1 */ #define EXYNOS_MIPI_PHY_CONTROL(n) ((n) * 4) -#define EXYNOS_MIPI_PHY_ENABLE (1 << 0) -#define EXYNOS_MIPI_PHY_SRESETN (1 << 1) -#define EXYNOS_MIPI_PHY_MRESETN (1 << 2) -#define EXYNOS_MIPI_PHY_RESET_MASK (3 << 1) enum exynos_mipi_phy_id { EXYNOS_MIPI_PHY_ID_CSIS0, @@ -38,43 +37,62 @@ enum exynos_mipi_phy_id { ((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1) struct exynos_mipi_video_phy { - spinlock_t slock; struct video_phy_desc { struct phy *phy; unsigned int index; } phys[EXYNOS_MIPI_PHYS_NUM]; + spinlock_t slock; void __iomem *regs; + struct mutex mutex; + struct regmap *regmap; }; static int __set_phy_state(struct exynos_mipi_video_phy *state, enum exynos_mipi_phy_id id, unsigned int on) { + const unsigned int offset = EXYNOS4_MIPI_PHY_CONTROL(id / 2); void __iomem *addr; - u32 reg, reset; - - addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2); + u32 val, reset; if (is_mipi_dsim_phy_id(id)) - reset = EXYNOS_MIPI_PHY_MRESETN; + reset = EXYNOS4_MIPI_PHY_MRESETN; else - reset = EXYNOS_MIPI_PHY_SRESETN; + reset = EXYNOS4_MIPI_PHY_SRESETN; - spin_lock(&state->slock); - reg = readl(addr); - if (on) - reg |= reset; - else - reg &= ~reset; - writel(reg, addr); + if (state->regmap) { + mutex_lock(&state->mutex); + regmap_read(state->regmap, offset, &val); + if (on) + val |= reset; + else + val &= ~reset; + regmap_write(state->regmap, offset, val); + if (on) + val |= EXYNOS4_MIPI_PHY_ENABLE; + else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK)) + val &= ~EXYNOS4_MIPI_PHY_ENABLE; + regmap_write(state->regmap, offset, val); + mutex_unlock(&state->mutex); + } else { + addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2); - /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set. */ - if (on) - reg |= EXYNOS_MIPI_PHY_ENABLE; - else if (!(reg & EXYNOS_MIPI_PHY_RESET_MASK)) - reg &= ~EXYNOS_MIPI_PHY_ENABLE; + spin_lock(&state->slock); + val = readl(addr); + if (on) + val |= reset; + else + val &= ~reset; + writel(val, addr); + /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set */ + if (on) + val |= EXYNOS4_MIPI_PHY_ENABLE; + else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK)) + val &= ~EXYNOS4_MIPI_PHY_ENABLE; + + writel(val, addr); + spin_unlock(&state->slock); + } - writel(reg, addr); - spin_unlock(&state->slock); return 0; } @@ -118,7 +136,6 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) { struct exynos_mipi_video_phy *state; struct device *dev = &pdev->dev; - struct resource *res; struct phy_provider *phy_provider; unsigned int i; @@ -126,14 +143,22 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) if (!state) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + state->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); + if (IS_ERR(state->regmap)) { + struct resource *res; - state->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(state->regs)) - return PTR_ERR(state->regs); + dev_info(dev, "regmap lookup failed: %ld\n", + PTR_ERR(state->regmap)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + state->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(state->regs)) + return PTR_ERR(state->regs); + } dev_set_drvdata(dev, state); spin_lock_init(&state->slock); + mutex_init(&state->mutex); for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) { struct phy *phy = devm_phy_create(dev, NULL, diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index 27fa62ce6136..9b2848e6115d 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -194,6 +194,14 @@ #define MIPHY_SATA_BANK_NB 3 #define MIPHY_PCIE_BANK_NB 2 +enum { + SYSCFG_CTRL, + SYSCFG_STATUS, + SYSCFG_PCI, + SYSCFG_SATA, + SYSCFG_REG_MAX, +}; + struct miphy28lp_phy { struct phy *phy; struct miphy28lp_dev *phydev; @@ -211,10 +219,7 @@ struct miphy28lp_phy { u32 sata_gen; /* Sysconfig registers offsets needed to configure the device */ - u32 syscfg_miphy_ctrl; - u32 syscfg_miphy_status; - u32 syscfg_pci; - u32 syscfg_sata; + u32 syscfg_reg[SYSCFG_REG_MAX]; u8 type; }; @@ -834,12 +839,12 @@ static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy) if (!miphy_phy->osc_rdy) return 0; - if (!miphy_phy->syscfg_miphy_status) + if (!miphy_phy->syscfg_reg[SYSCFG_STATUS]) return -EINVAL; do { - regmap_read(miphy_dev->regmap, miphy_phy->syscfg_miphy_status, - &val); + regmap_read(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_STATUS], &val); if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY) cpu_relax(); @@ -888,7 +893,7 @@ static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val) int err; struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; - if (!miphy_phy->syscfg_miphy_ctrl) + if (!miphy_phy->syscfg_reg[SYSCFG_CTRL]) return -EINVAL; err = reset_control_assert(miphy_phy->miphy_rst); @@ -900,7 +905,8 @@ static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val) if (miphy_phy->osc_force_ext) miphy_val |= MIPHY_OSC_FORCE_EXT; - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_miphy_ctrl, + regmap_update_bits(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_CTRL], MIPHY_CTRL_MASK, miphy_val); err = reset_control_deassert(miphy_phy->miphy_rst); @@ -917,8 +923,9 @@ static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy) struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; int err, sata_conf = SATA_CTRL_SELECT_SATA; - if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci) - || (!miphy_phy->base)) + if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || + (!miphy_phy->syscfg_reg[SYSCFG_PCI]) || + (!miphy_phy->base)) return -EINVAL; dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base); @@ -926,10 +933,11 @@ static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy) /* Configure the glue-logic */ sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE); - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata, + regmap_update_bits(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_SATA], SATA_CTRL_MASK, sata_conf); - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci, + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE); /* MiPHY path and clocking init */ @@ -951,17 +959,19 @@ static int miphy28lp_init_pcie(struct miphy28lp_phy *miphy_phy) struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; int err; - if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci) + if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || + (!miphy_phy->syscfg_reg[SYSCFG_PCI]) || (!miphy_phy->base) || (!miphy_phy->pipebase)) return -EINVAL; dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base); /* Configure the glue-logic */ - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata, + regmap_update_bits(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_SATA], SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE); - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci, + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL); /* MiPHY path and clocking init */ @@ -1156,7 +1166,8 @@ static int miphy28lp_probe_resets(struct device_node *node, static int miphy28lp_of_probe(struct device_node *np, struct miphy28lp_phy *miphy_phy) { - struct resource res; + int i; + u32 ctrlreg; miphy_phy->osc_force_ext = of_property_read_bool(np, "st,osc-force-ext"); @@ -1175,18 +1186,10 @@ static int miphy28lp_of_probe(struct device_node *np, if (!miphy_phy->sata_gen) miphy_phy->sata_gen = SATA_GEN1; - if (!miphy28lp_get_resource_byname(np, "miphy-ctrl-glue", &res)) - miphy_phy->syscfg_miphy_ctrl = res.start; - - if (!miphy28lp_get_resource_byname(np, "miphy-status-glue", &res)) - miphy_phy->syscfg_miphy_status = res.start; - - if (!miphy28lp_get_resource_byname(np, "pcie-glue", &res)) - miphy_phy->syscfg_pci = res.start; - - if (!miphy28lp_get_resource_byname(np, "sata-glue", &res)) - miphy_phy->syscfg_sata = res.start; - + for (i = 0; i < SYSCFG_REG_MAX; i++) { + if (!of_property_read_u32_index(np, "st,syscfg", i, &ctrlreg)) + miphy_phy->syscfg_reg[i] = ctrlreg; + } return 0; } diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 6ab43a814ad2..6c80154e8bff 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -141,7 +141,7 @@ struct miphy365x_phy { bool pcie_tx_pol_inv; bool sata_tx_pol_inv; u32 sata_gen; - u64 ctrlreg; + u32 ctrlreg; u8 type; }; @@ -179,7 +179,7 @@ static int miphy365x_set_path(struct miphy365x_phy *miphy_phy, bool sata = (miphy_phy->type == MIPHY_TYPE_SATA); return regmap_update_bits(miphy_dev->regmap, - (unsigned int)miphy_phy->ctrlreg, + miphy_phy->ctrlreg, SYSCFG_SELECT_SATA_MASK, sata << SYSCFG_SELECT_SATA_POS); } @@ -445,7 +445,6 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, { struct device_node *phynode = miphy_phy->phy->dev.of_node; const char *name; - const __be32 *taddr; int type = miphy_phy->type; int ret; @@ -455,22 +454,6 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, return ret; } - if (!strncmp(name, "syscfg", 6)) { - taddr = of_get_address(phynode, index, NULL, NULL); - if (!taddr) { - dev_err(dev, "failed to fetch syscfg address\n"); - return -EINVAL; - } - - miphy_phy->ctrlreg = of_translate_address(phynode, taddr); - if (miphy_phy->ctrlreg == OF_BAD_ADDR) { - dev_err(dev, "failed to translate syscfg address\n"); - return -EINVAL; - } - - return 0; - } - if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) || (!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE))) return 0; @@ -606,7 +589,15 @@ static int miphy365x_probe(struct platform_device *pdev) return ret; phy_set_drvdata(phy, miphy_dev->phys[port]); + port++; + /* sysconfig offsets are indexed from 1 */ + ret = of_property_read_u32_index(np, "st,syscfg", port, + &miphy_phy->ctrlreg); + if (ret) { + dev_err(&pdev->dev, "No sysconfig offset found\n"); + return ret; + } } provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate); diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c new file mode 100644 index 000000000000..22011c3b6a4b --- /dev/null +++ b/drivers/phy/phy-rockchip-usb.c @@ -0,0 +1,158 @@ +/* + * Rockchip usb PHY driver + * + * Copyright (C) 2014 Yunzhi Li + * Copyright (C) 2014 ROCKCHIP, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The higher 16-bit of this register is used for write protection + * only if BIT(13 + 16) set to 1 the BIT(13) can be written. + */ +#define SIDDQ_WRITE_ENA BIT(29) +#define SIDDQ_ON BIT(13) +#define SIDDQ_OFF (0 << 13) + +struct rockchip_usb_phy { + unsigned int reg_offset; + struct regmap *reg_base; + struct clk *clk; + struct phy *phy; +}; + +static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy, + bool siddq) +{ + return regmap_write(phy->reg_base, phy->reg_offset, + SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF)); +} + +static int rockchip_usb_phy_power_off(struct phy *_phy) +{ + struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); + int ret = 0; + + /* Power down usb phy analog blocks by set siddq 1 */ + ret = rockchip_usb_phy_power(phy, 1); + if (ret) + return ret; + + clk_disable_unprepare(phy->clk); + if (ret) + return ret; + + return 0; +} + +static int rockchip_usb_phy_power_on(struct phy *_phy) +{ + struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); + int ret = 0; + + ret = clk_prepare_enable(phy->clk); + if (ret) + return ret; + + /* Power up usb phy analog blocks by set siddq 0 */ + ret = rockchip_usb_phy_power(phy, 0); + if (ret) + return ret; + + return 0; +} + +static struct phy_ops ops = { + .power_on = rockchip_usb_phy_power_on, + .power_off = rockchip_usb_phy_power_off, + .owner = THIS_MODULE, +}; + +static int rockchip_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rockchip_usb_phy *rk_phy; + struct phy_provider *phy_provider; + struct device_node *child; + struct regmap *grf; + unsigned int reg_offset; + + grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); + if (IS_ERR(grf)) { + dev_err(&pdev->dev, "Missing rockchip,grf property\n"); + return PTR_ERR(grf); + } + + for_each_available_child_of_node(dev->of_node, child) { + rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); + if (!rk_phy) + return -ENOMEM; + + if (of_property_read_u32(child, "reg", ®_offset)) { + dev_err(dev, "missing reg property in node %s\n", + child->name); + return -EINVAL; + } + + rk_phy->reg_offset = reg_offset; + rk_phy->reg_base = grf; + + rk_phy->clk = of_clk_get_by_name(child, "phyclk"); + if (IS_ERR(rk_phy->clk)) + rk_phy->clk = NULL; + + rk_phy->phy = devm_phy_create(dev, child, &ops); + if (IS_ERR(rk_phy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(rk_phy->phy); + } + phy_set_drvdata(rk_phy->phy, rk_phy); + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id rockchip_usb_phy_dt_ids[] = { + { .compatible = "rockchip,rk3288-usb-phy" }, + {} +}; + +MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids); + +static struct platform_driver rockchip_usb_driver = { + .probe = rockchip_usb_phy_probe, + .driver = { + .name = "rockchip-usb-phy", + .owner = THIS_MODULE, + .of_match_table = rockchip_usb_phy_dt_ids, + }, +}; + +module_platform_driver(rockchip_usb_driver); + +MODULE_AUTHOR("Yunzhi Li "); +MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-stih407-usb.c b/drivers/phy/phy-stih407-usb.c index 74f0fab3cd8a..1d5ae5f8ef69 100644 --- a/drivers/phy/phy-stih407-usb.c +++ b/drivers/phy/phy-stih407-usb.c @@ -22,6 +22,9 @@ #include #include +#define PHYPARAM_REG 1 +#define PHYCTRL_REG 2 + /* Default PHY_SEL and REFCLKSEL configuration */ #define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 #define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f @@ -93,7 +96,7 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct phy_provider *phy_provider; struct phy *phy; - struct resource *res; + int ret; phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); if (!phy_dev) @@ -123,19 +126,19 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev) return PTR_ERR(phy_dev->regmap); } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); - if (!res) { - dev_err(dev, "No ctrl reg found\n"); - return -ENXIO; + ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG, + &phy_dev->param); + if (ret) { + dev_err(dev, "can't get phyparam offset (%d)\n", ret); + return ret; } - phy_dev->ctrl = res->start; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "param"); - if (!res) { - dev_err(dev, "No param reg found\n"); - return -ENXIO; + ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG, + &phy_dev->ctrl); + if (ret) { + dev_err(dev, "can't get phyctrl offset (%d)\n", ret); + return ret; } - phy_dev->param = res->start; phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); if (IS_ERR(phy)) { diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 465de2c800f2..95c88f929f27 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -28,6 +28,7 @@ #include #include #include +#include #define PLL_STATUS 0x00000004 #define PLL_GO 0x00000008 @@ -82,6 +83,10 @@ struct ti_pipe3 { struct clk *refclk; struct clk *div_clk; struct pipe3_dpll_map *dpll_map; + bool enabled; + spinlock_t lock; /* serialize clock enable/disable */ + /* the below flag is needed specifically for SATA */ + bool refclk_enabled; }; static struct pipe3_dpll_map dpll_map_usb[] = { @@ -307,6 +312,7 @@ static int ti_pipe3_probe(struct platform_device *pdev) return -ENOMEM; phy->dev = &pdev->dev; + spin_lock_init(&phy->lock); if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { match = of_match_device(of_match_ptr(ti_pipe3_id_table), @@ -333,21 +339,24 @@ static int ti_pipe3_probe(struct platform_device *pdev) } } + phy->refclk = devm_clk_get(phy->dev, "refclk"); + if (IS_ERR(phy->refclk)) { + dev_err(&pdev->dev, "unable to get refclk\n"); + /* older DTBs have missing refclk in SATA PHY + * so don't bail out in case of SATA PHY. + */ + if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) + return PTR_ERR(phy->refclk); + } + if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); if (IS_ERR(phy->wkupclk)) { dev_err(&pdev->dev, "unable to get wkupclk\n"); return PTR_ERR(phy->wkupclk); } - - phy->refclk = devm_clk_get(phy->dev, "refclk"); - if (IS_ERR(phy->refclk)) { - dev_err(&pdev->dev, "unable to get refclk\n"); - return PTR_ERR(phy->refclk); - } } else { phy->wkupclk = ERR_PTR(-ENODEV); - phy->refclk = ERR_PTR(-ENODEV); } if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { @@ -426,33 +435,42 @@ static int ti_pipe3_remove(struct platform_device *pdev) } #ifdef CONFIG_PM - -static int ti_pipe3_runtime_suspend(struct device *dev) +static int ti_pipe3_enable_refclk(struct ti_pipe3 *phy) { - struct ti_pipe3 *phy = dev_get_drvdata(dev); + if (!IS_ERR(phy->refclk) && !phy->refclk_enabled) { + int ret; - if (!IS_ERR(phy->wkupclk)) - clk_disable_unprepare(phy->wkupclk); - if (!IS_ERR(phy->refclk)) - clk_disable_unprepare(phy->refclk); - if (!IS_ERR(phy->div_clk)) - clk_disable_unprepare(phy->div_clk); + ret = clk_prepare_enable(phy->refclk); + if (ret) { + dev_err(phy->dev, "Failed to enable refclk %d\n", ret); + return ret; + } + phy->refclk_enabled = true; + } return 0; } -static int ti_pipe3_runtime_resume(struct device *dev) +static void ti_pipe3_disable_refclk(struct ti_pipe3 *phy) { - u32 ret = 0; - struct ti_pipe3 *phy = dev_get_drvdata(dev); + if (!IS_ERR(phy->refclk)) + clk_disable_unprepare(phy->refclk); - if (!IS_ERR(phy->refclk)) { - ret = clk_prepare_enable(phy->refclk); - if (ret) { - dev_err(phy->dev, "Failed to enable refclk %d\n", ret); - goto err1; - } - } + phy->refclk_enabled = false; +} + +static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&phy->lock, flags); + if (phy->enabled) + goto err1; + + ret = ti_pipe3_enable_refclk(phy); + if (ret) + goto err1; if (!IS_ERR(phy->wkupclk)) { ret = clk_prepare_enable(phy->wkupclk); @@ -469,6 +487,9 @@ static int ti_pipe3_runtime_resume(struct device *dev) goto err3; } } + + phy->enabled = true; + spin_unlock_irqrestore(&phy->lock, flags); return 0; err3: @@ -479,20 +500,80 @@ err2: if (!IS_ERR(phy->refclk)) clk_disable_unprepare(phy->refclk); + ti_pipe3_disable_refclk(phy); err1: + spin_unlock_irqrestore(&phy->lock, flags); return ret; } +static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy) +{ + unsigned long flags; + + spin_lock_irqsave(&phy->lock, flags); + if (!phy->enabled) { + spin_unlock_irqrestore(&phy->lock, flags); + return; + } + + if (!IS_ERR(phy->wkupclk)) + clk_disable_unprepare(phy->wkupclk); + /* Don't disable refclk for SATA PHY due to Errata i783 */ + if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) + ti_pipe3_disable_refclk(phy); + if (!IS_ERR(phy->div_clk)) + clk_disable_unprepare(phy->div_clk); + phy->enabled = false; + spin_unlock_irqrestore(&phy->lock, flags); +} + +static int ti_pipe3_runtime_suspend(struct device *dev) +{ + struct ti_pipe3 *phy = dev_get_drvdata(dev); + + ti_pipe3_disable_clocks(phy); + return 0; +} + +static int ti_pipe3_runtime_resume(struct device *dev) +{ + struct ti_pipe3 *phy = dev_get_drvdata(dev); + int ret = 0; + + ret = ti_pipe3_enable_clocks(phy); + return ret; +} + +static int ti_pipe3_suspend(struct device *dev) +{ + struct ti_pipe3 *phy = dev_get_drvdata(dev); + + ti_pipe3_disable_clocks(phy); + return 0; +} + +static int ti_pipe3_resume(struct device *dev) +{ + struct ti_pipe3 *phy = dev_get_drvdata(dev); + int ret; + + ret = ti_pipe3_enable_clocks(phy); + if (ret) + return ret; + + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; +} +#endif + static const struct dev_pm_ops ti_pipe3_pm_ops = { SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend, ti_pipe3_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(ti_pipe3_suspend, ti_pipe3_resume) }; -#define DEV_PM_OPS (&ti_pipe3_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif - #ifdef CONFIG_OF static const struct of_device_id ti_pipe3_id_table[] = { { @@ -520,7 +601,7 @@ static struct platform_driver ti_pipe3_driver = { .remove = ti_pipe3_remove, .driver = { .name = "ti-pipe3", - .pm = DEV_PM_OPS, + .pm = &ti_pipe3_pm_ops, .of_match_table = of_match_ptr(ti_pipe3_id_table), }, }; diff --git a/include/linux/mfd/syscon/exynos4-pmu.h b/include/linux/mfd/syscon/exynos4-pmu.h new file mode 100644 index 000000000000..278b1b1549e9 --- /dev/null +++ b/include/linux/mfd/syscon/exynos4-pmu.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_MFD_SYSCON_PMU_EXYNOS4_H_ +#define _LINUX_MFD_SYSCON_PMU_EXYNOS4_H_ + +/* Exynos4 PMU register definitions */ + +/* MIPI_PHYn_CONTROL register offset: n = 0..1 */ +#define EXYNOS4_MIPI_PHY_CONTROL(n) (0x710 + (n) * 4) +#define EXYNOS4_MIPI_PHY_ENABLE (1 << 0) +#define EXYNOS4_MIPI_PHY_SRESETN (1 << 1) +#define EXYNOS4_MIPI_PHY_MRESETN (1 << 2) +#define EXYNOS4_MIPI_PHY_RESET_MASK (3 << 1) + +#endif /* _LINUX_MFD_SYSCON_PMU_EXYNOS4_H_ */