From 2fbbc96d16009276153fd33823616877dcca30d8 Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Tue, 4 Nov 2014 11:51:18 +0100 Subject: [PATCH 01/25] phy: Add PHY header file for DT x Driver defines This provides the shared header file which will be reference from both PHY driver and its associated Device Tree node(s). Signed-off-by: Gabriel Fernandez --- include/dt-bindings/phy/phy.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 include/dt-bindings/phy/phy.h diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h new file mode 100644 index 000000000000..e8c6a3f04b85 --- /dev/null +++ b/include/dt-bindings/phy/phy.h @@ -0,0 +1,18 @@ +/* + * + * This header provides constants for the phy framework + * + * Copyright (C) 2014 STMicroelectronics + * Author: Gabriel Fernandez + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _DT_BINDINGS_PHY +#define _DT_BINDINGS_PHY + +#define PHY_TYPE_SATA 1 +#define PHY_TYPE_PCIE 2 +#define PHY_TYPE_USB2 3 +#define PHY_TYPE_USB3 4 + +#endif /* _DT_BINDINGS_PHY */ From 5de985de12d18ceca5d60cfd9d72a03cdfa94f4f Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Tue, 4 Nov 2014 11:51:17 +0100 Subject: [PATCH 02/25] phy: miphy28lp: Add Device Tree bindings for the MiPHY28lp The MiPHY28lp is a Generic PHY which can serve various SATA or PCIe or USB3 devices. Signed-off-by: alexandre torgue Signed-off-by: Giuseppe Cavallaro Signed-off-by: Gabriel Fernandez Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/phy-miphy28lp.txt | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/phy-miphy28lp.txt diff --git a/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt b/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt new file mode 100644 index 000000000000..b7c13ad81a22 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt @@ -0,0 +1,126 @@ +STMicroelectronics STi MIPHY28LP PHY binding +============================================ + +This binding describes a miphy device that is used to control PHY hardware +for SATA, PCIe or USB3. + +Required properties (controller (parent) node): +- compatible : Should be "st,miphy28lp-phy". +- st,syscfg : Should be a phandle of the system configuration register group + which contain the SATA, PCIe or USB3 mode setting bits. + +Required nodes : A sub-node is required for each channel the controller + provides. Address range information including the usual + 'reg' and 'reg-names' properties are used inside these + nodes to describe the controller's topology. These nodes + are translated by the driver's .xlate() function. + +Required properties (port (child) node): +- #phy-cells : Should be 1 (See second example) + Cell after port phandle is device type from: + - PHY_TYPE_SATA + - PHY_TYPE_PCI + - PHY_TYPE_USB3 +- reg : Address and length of the register set for the device. +- reg-names : The names of the register addresses corresponding to the registers + 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. +- resets : phandle to the parent reset controller. +- reset-names : Associated name must be "miphy-sw-rst". + +Optional properties (port (child) node): +- st,osc-rdy : to check the MIPHY0_OSC_RDY status in the glue-logic. This + is not available in all the MiPHY. For example, for STiH407, only the + MiPHY0 has this bit. +- st,osc-force-ext : to select the external oscillator. This can change from + different MiPHY inside the same SoC. +- st,sata_gen : to select which SATA_SPDMODE has to be set in the SATA system config + register. +- st,px_rx_pol_inv : to invert polarity of RXn/RXp (respectively negative line and positive + line). + +example: + + miphy28lp_phy: miphy28lp@9b22000 { + compatible = "st,miphy28lp-phy"; + st,syscfg = <&syscfg_core>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + 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 */ + reg-names = "sata-up", + "pcie-up", + "pipew", + "miphy-ctrl-glue", + "miphy-status-glue", + "pcie-glue", + "sata-glue"; + #phy-cells = <1>; + st,osc-rdy; + reset-names = "miphy-sw-rst"; + resets = <&softreset STIH407_MIPHY0_SOFTRESET>; + }; + + phy_port1: port@9b2a000 { + reg = <0x9b2a000 0xff>, + <0x9b19000 0xff>, + <0x9b14000 0xff>, + <0x118 0x4>, + <0x81c 0x4>, + <0xe4 0x4>, + <0xf0 0x4>; + reg-names = "sata-up", + "pcie-up", + "pipew", + "miphy-ctrl-glue", + "miphy-status-glue", + "pcie-glue", + "sata-glue"; + #phy-cells = <1>; + st,osc-force-ext; + reset-names = "miphy-sw-rst"; + resets = <&softreset STIH407_MIPHY1_SOFTRESET>; + }; + + phy_port2: port@8f95000 { + reg = <0x8f95000 0xff>, + <0x8f90000 0xff>, + <0x11c 0x4>, + <0x820 0x4>; + reg-names = "pipew", + "usb3-up", + "miphy-ctrl-glue", + "miphy-status-glue"; + #phy-cells = <1>; + reset-names = "miphy-sw-rst"; + resets = <&softreset STIH407_MIPHY2_SOFTRESET>; + }; + }; + + +Specifying phy control of devices +================================= + +Device nodes should specify the configuration required in their "phys" +property, containing a phandle to the miphy device node and an index +specifying which configuration to use, as described in phy-bindings.txt. + +example: + sata0: sata@9b20000 { + ... + phys = <&phy_port0 PHY_TYPE_SATA>; + ... + }; + +Macro definitions for the supported miphy configuration can be found in: + +include/dt-bindings/phy/phy-miphy28lp.h From 2c14e9be0c60bb3d89cfa16a40222ddcb83660ab Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Tue, 4 Nov 2014 11:51:19 +0100 Subject: [PATCH 03/25] phy: miphy28lp: Provide support for the MiPHY28lp Generic PHY The MiPHY28lp is a Generic PHY which can serve various SATA or PCIe or USB3 devices. Signed-off-by: alexandre torgue Signed-off-by: Giuseppe Cavallaro Signed-off-by: Gabriel Fernandez Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 8 + drivers/phy/Makefile | 1 + drivers/phy/phy-miphy28lp.c | 1177 +++++++++++++++++++++++++++++++++++ 3 files changed, 1186 insertions(+) create mode 100644 drivers/phy/phy-miphy28lp.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 2a436e607f99..cfaced92e0c8 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -38,6 +38,14 @@ config PHY_MVEBU_SATA depends on OF select GENERIC_PHY +config PHY_MIPHY28LP + tristate "STMicroelectronics MIPHY28LP PHY driver for STiH407" + depends on ARCH_STI + select GENERIC_PHY + help + Enable this to support the miphy transceiver (for SATA/PCIE/USB3) + that is part of STMicroelectronics STiH407 SoC. + config PHY_MIPHY365X tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series" depends on ARCH_STI diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c4590fce082f..30d90a8d72e8 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o +obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c new file mode 100644 index 000000000000..7d592e6392ac --- /dev/null +++ b/drivers/phy/phy-miphy28lp.c @@ -0,0 +1,1177 @@ +/* + * Copyright (C) 2014 STMicroelectronics + * + * STMicroelectronics PHY driver MiPHY28lp (for SoC STiH407). + * + * Author: Alexandre Torgue + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* MiPHY registers */ +#define MIPHY_CONF_RESET 0x00 +#define RST_APPLI_SW BIT(0) +#define RST_CONF_SW BIT(1) +#define RST_MACRO_SW BIT(2) + +#define MIPHY_RESET 0x01 +#define RST_PLL_SW BIT(0) +#define RST_COMP_SW BIT(2) + +#define MIPHY_STATUS_1 0x02 +#define PHY_RDY BIT(0) +#define HFC_RDY BIT(1) +#define HFC_PLL BIT(2) + +#define MIPHY_CONTROL 0x04 +#define TERM_EN_SW BIT(2) +#define DIS_LINK_RST BIT(3) +#define AUTO_RST_RX BIT(4) +#define PX_RX_POL BIT(5) + +#define MIPHY_BOUNDARY_SEL 0x0a +#define TX_SEL BIT(6) +#define SSC_SEL BIT(4) +#define GENSEL_SEL BIT(0) + +#define MIPHY_BOUNDARY_1 0x0b +#define MIPHY_BOUNDARY_2 0x0c +#define SSC_EN_SW BIT(2) + +#define MIPHY_PLL_CLKREF_FREQ 0x0d +#define MIPHY_SPEED 0x0e +#define TX_SPDSEL_80DEC 0 +#define TX_SPDSEL_40DEC 1 +#define TX_SPDSEL_20DEC 2 +#define RX_SPDSEL_80DEC 0 +#define RX_SPDSEL_40DEC (1 << 2) +#define RX_SPDSEL_20DEC (2 << 2) + +#define MIPHY_CONF 0x0f +#define MIPHY_CTRL_TEST_SEL 0x20 +#define MIPHY_CTRL_TEST_1 0x21 +#define MIPHY_CTRL_TEST_2 0x22 +#define MIPHY_CTRL_TEST_3 0x23 +#define MIPHY_CTRL_TEST_4 0x24 +#define MIPHY_FEEDBACK_TEST 0x25 +#define MIPHY_DEBUG_BUS 0x26 +#define MIPHY_DEBUG_STATUS_MSB 0x27 +#define MIPHY_DEBUG_STATUS_LSB 0x28 +#define MIPHY_PWR_RAIL_1 0x29 +#define MIPHY_PWR_RAIL_2 0x2a +#define MIPHY_SYNCHAR_CONTROL 0x30 + +#define MIPHY_COMP_FSM_1 0x3a +#define COMP_START BIT(6) + +#define MIPHY_COMP_FSM_6 0x3f +#define COMP_DONE BIT(7) + +#define MIPHY_COMP_POSTP 0x42 +#define MIPHY_TX_CTRL_1 0x49 +#define TX_REG_STEP_0V 0 +#define TX_REG_STEP_P_25MV 1 +#define TX_REG_STEP_P_50MV 2 +#define TX_REG_STEP_N_25MV 7 +#define TX_REG_STEP_N_50MV 6 +#define TX_REG_STEP_N_75MV 5 + +#define MIPHY_TX_CTRL_2 0x4a +#define TX_SLEW_SW_40_PS 0 +#define TX_SLEW_SW_80_PS 1 +#define TX_SLEW_SW_120_PS 2 + +#define MIPHY_TX_CTRL_3 0x4b +#define MIPHY_TX_CAL_MAN 0x4e +#define TX_SLEW_CAL_MAN_EN BIT(0) + +#define MIPHY_TST_BIAS_BOOST_2 0x62 +#define MIPHY_BIAS_BOOST_1 0x63 +#define MIPHY_BIAS_BOOST_2 0x64 +#define MIPHY_RX_DESBUFF_FDB_2 0x67 +#define MIPHY_RX_DESBUFF_FDB_3 0x68 +#define MIPHY_SIGDET_COMPENS1 0x69 +#define MIPHY_SIGDET_COMPENS2 0x6a +#define MIPHY_JITTER_PERIOD 0x6b +#define MIPHY_JITTER_AMPLITUDE_1 0x6c +#define MIPHY_JITTER_AMPLITUDE_2 0x6d +#define MIPHY_JITTER_AMPLITUDE_3 0x6e +#define MIPHY_RX_K_GAIN 0x78 +#define MIPHY_RX_BUFFER_CTRL 0x7a +#define VGA_GAIN BIT(0) +#define EQ_DC_GAIN BIT(2) +#define EQ_BOOST_GAIN BIT(3) + +#define MIPHY_RX_VGA_GAIN 0x7b +#define MIPHY_RX_EQU_GAIN_1 0x7f +#define MIPHY_RX_EQU_GAIN_2 0x80 +#define MIPHY_RX_EQU_GAIN_3 0x81 +#define MIPHY_RX_CAL_CTRL_1 0x97 +#define MIPHY_RX_CAL_CTRL_2 0x98 + +#define MIPHY_RX_CAL_OFFSET_CTRL 0x99 +#define CAL_OFFSET_VGA_64 (0x03 << 0) +#define CAL_OFFSET_THRESHOLD_64 (0x03 << 2) +#define VGA_OFFSET_POLARITY BIT(4) +#define OFFSET_COMPENSATION_EN BIT(6) + +#define MIPHY_RX_CAL_VGA_STEP 0x9a +#define MIPHY_RX_CAL_EYE_MIN 0x9d +#define MIPHY_RX_CAL_OPT_LENGTH 0x9f +#define MIPHY_RX_LOCK_CTRL_1 0xc1 +#define MIPHY_RX_LOCK_SETTINGS_OPT 0xc2 +#define MIPHY_RX_LOCK_STEP 0xc4 + +#define MIPHY_RX_SIGDET_SLEEP_OA 0xc9 +#define MIPHY_RX_SIGDET_SLEEP_SEL 0xca +#define MIPHY_RX_SIGDET_WAIT_SEL 0xcb +#define MIPHY_RX_SIGDET_DATA_SEL 0xcc +#define EN_ULTRA_LOW_POWER BIT(0) +#define EN_FIRST_HALF BIT(1) +#define EN_SECOND_HALF BIT(2) +#define EN_DIGIT_SIGNAL_CHECK BIT(3) + +#define MIPHY_RX_POWER_CTRL_1 0xcd +#define MIPHY_RX_POWER_CTRL_2 0xce +#define MIPHY_PLL_CALSET_CTRL 0xd3 +#define MIPHY_PLL_CALSET_1 0xd4 +#define MIPHY_PLL_CALSET_2 0xd5 +#define MIPHY_PLL_CALSET_3 0xd6 +#define MIPHY_PLL_CALSET_4 0xd7 +#define MIPHY_PLL_SBR_1 0xe3 +#define SET_NEW_CHANGE BIT(1) + +#define MIPHY_PLL_SBR_2 0xe4 +#define MIPHY_PLL_SBR_3 0xe5 +#define MIPHY_PLL_SBR_4 0xe6 +#define MIPHY_PLL_COMMON_MISC_2 0xe9 +#define START_ACT_FILT BIT(6) + +#define MIPHY_PLL_SPAREIN 0xeb + +/* + * On STiH407 the glue logic can be different among MiPHY devices; for example: + * MiPHY0: OSC_FORCE_EXT means: + * 0: 30MHz crystal clk - 1: 100MHz ext clk routed through MiPHY1 + * MiPHY1: OSC_FORCE_EXT means: + * 1: 30MHz crystal clk - 0: 100MHz ext clk routed through MiPHY1 + * Some devices have not the possibility to check if the osc is ready. + */ +#define MIPHY_OSC_FORCE_EXT BIT(3) +#define MIPHY_OSC_RDY BIT(5) + +#define MIPHY_CTRL_MASK 0x0f +#define MIPHY_CTRL_DEFAULT 0 +#define MIPHY_CTRL_SYNC_D_EN BIT(2) + +/* SATA / PCIe defines */ +#define SATA_CTRL_MASK 0x07 +#define PCIE_CTRL_MASK 0xff +#define SATA_CTRL_SELECT_SATA 1 +#define SATA_CTRL_SELECT_PCIE 0 +#define SYSCFG_PCIE_PCIE_VAL 0x80 +#define SATA_SPDMODE 1 + +struct miphy28lp_phy { + struct phy *phy; + struct miphy28lp_dev *phydev; + void __iomem *base; + void __iomem *pipebase; + + bool osc_force_ext; + bool osc_rdy; + bool px_rx_pol_inv; + + struct reset_control *miphy_rst; + + 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; + u8 type; +}; + +struct miphy28lp_dev { + struct device *dev; + struct regmap *regmap; + struct mutex miphy_mutex; + struct miphy28lp_phy **phys; +}; + +struct miphy_initval { + u16 reg; + u16 val; +}; + +enum miphy_sata_gen { SATA_GEN1, SATA_GEN2, SATA_GEN3 }; + +static char *PHY_TYPE_name[] = { "sata-up", "pcie-up", "", "usb3-up" }; + +struct pll_ratio { + int clk_ref; + int calset_1; + int calset_2; + int calset_3; + int calset_4; + int cal_ctrl; +}; + +static struct pll_ratio sata_pll_ratio = { + .clk_ref = 0x1e, + .calset_1 = 0xc8, + .calset_2 = 0x00, + .calset_3 = 0x00, + .calset_4 = 0x00, + .cal_ctrl = 0x00, +}; + +static struct pll_ratio pcie_pll_ratio = { + .clk_ref = 0x1e, + .calset_1 = 0xa6, + .calset_2 = 0xaa, + .calset_3 = 0xaa, + .calset_4 = 0x00, + .cal_ctrl = 0x00, +}; + +static struct pll_ratio usb3_pll_ratio = { + .clk_ref = 0x1e, + .calset_1 = 0xa6, + .calset_2 = 0xaa, + .calset_3 = 0xaa, + .calset_4 = 0x04, + .cal_ctrl = 0x00, +}; + +struct miphy28lp_pll_gen { + int bank; + int speed; + int bias_boost_1; + int bias_boost_2; + int tx_ctrl_1; + int tx_ctrl_2; + int tx_ctrl_3; + int rx_k_gain; + int rx_vga_gain; + int rx_equ_gain_1; + int rx_equ_gain_2; + int rx_equ_gain_3; + int rx_buff_ctrl; +}; + +static struct miphy28lp_pll_gen sata_pll_gen[] = { + { + .bank = 0x00, + .speed = TX_SPDSEL_80DEC | RX_SPDSEL_80DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xae, + .tx_ctrl_2 = 0x53, + .tx_ctrl_3 = 0x00, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x7d, + .rx_equ_gain_2 = 0x56, + .rx_equ_gain_3 = 0x00, + }, + { + .bank = 0x01, + .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xae, + .tx_ctrl_2 = 0x72, + .tx_ctrl_3 = 0x20, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x7d, + .rx_equ_gain_2 = 0x56, + .rx_equ_gain_3 = 0x00, + }, + { + .bank = 0x02, + .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xae, + .tx_ctrl_2 = 0xc0, + .tx_ctrl_3 = 0x20, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x7d, + .rx_equ_gain_2 = 0x56, + .rx_equ_gain_3 = 0x00, + }, +}; + +static struct miphy28lp_pll_gen pcie_pll_gen[] = { + { + .bank = 0x00, + .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xa5, + .tx_ctrl_1 = TX_REG_STEP_N_25MV, + .tx_ctrl_2 = 0x71, + .tx_ctrl_3 = 0x60, + .rx_k_gain = 0x98, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x79, + .rx_equ_gain_2 = 0x56, + }, + { + .bank = 0x01, + .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xa5, + .tx_ctrl_1 = TX_REG_STEP_N_25MV, + .tx_ctrl_2 = 0x70, + .tx_ctrl_3 = 0x60, + .rx_k_gain = 0xcc, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x78, + .rx_equ_gain_2 = 0x07, + }, +}; + +static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy) +{ + void *base = miphy_phy->base; + u8 val; + + /* Putting Macro in reset */ + writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); + + val = RST_APPLI_SW | RST_CONF_SW; + writeb_relaxed(val, base + MIPHY_CONF_RESET); + + writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); + + /* Bringing the MIPHY-CPU registers out of reset */ + if (miphy_phy->type == PHY_TYPE_PCIE) { + val = AUTO_RST_RX | TERM_EN_SW; + writeb_relaxed(val, base + MIPHY_CONTROL); + } else { + val = AUTO_RST_RX | TERM_EN_SW | DIS_LINK_RST; + writeb_relaxed(val, base + MIPHY_CONTROL); + } +} + +static inline void miphy28lp_pll_calibration(struct miphy28lp_phy *miphy_phy, + struct pll_ratio *pll_ratio) +{ + void *base = miphy_phy->base; + u8 val; + + /* Applying PLL Settings */ + writeb_relaxed(0x1d, base + MIPHY_PLL_SPAREIN); + writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); + + /* PLL Ratio */ + writeb_relaxed(pll_ratio->calset_1, base + MIPHY_PLL_CALSET_1); + writeb_relaxed(pll_ratio->calset_2, base + MIPHY_PLL_CALSET_2); + writeb_relaxed(pll_ratio->calset_3, base + MIPHY_PLL_CALSET_3); + writeb_relaxed(pll_ratio->calset_4, base + MIPHY_PLL_CALSET_4); + writeb_relaxed(pll_ratio->cal_ctrl, base + MIPHY_PLL_CALSET_CTRL); + + writeb_relaxed(TX_SEL, base + MIPHY_BOUNDARY_SEL); + + val = (0x68 << 1) | TX_SLEW_CAL_MAN_EN; + writeb_relaxed(val, base + MIPHY_TX_CAL_MAN); + + val = VGA_OFFSET_POLARITY | CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; + + if (miphy_phy->type != PHY_TYPE_SATA) + val |= OFFSET_COMPENSATION_EN; + + writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); + + if (miphy_phy->type == PHY_TYPE_USB3) { + writeb_relaxed(0x00, base + MIPHY_CONF); + writeb_relaxed(0x70, base + MIPHY_RX_LOCK_STEP); + writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_OA); + writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_SEL); + writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_WAIT_SEL); + + val = EN_DIGIT_SIGNAL_CHECK | EN_FIRST_HALF; + writeb_relaxed(val, base + MIPHY_RX_SIGDET_DATA_SEL); + } + +} + +static inline void miphy28lp_sata_config_gen(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int i; + + for (i = 0; i < ARRAY_SIZE(sata_pll_gen); i++) { + struct miphy28lp_pll_gen *gen = &sata_pll_gen[i]; + + /* Banked settings */ + writeb_relaxed(gen->bank, base + MIPHY_CONF); + writeb_relaxed(gen->speed, base + MIPHY_SPEED); + writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); + writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); + + /* TX buffer Settings */ + writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); + writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); + + /* RX Buffer Settings */ + writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); + writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); + writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); + writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); + writeb_relaxed(gen->rx_equ_gain_3, base + MIPHY_RX_EQU_GAIN_3); + } +} + +static inline void miphy28lp_pcie_config_gen(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int i; + + for (i = 0; i < ARRAY_SIZE(pcie_pll_gen); i++) { + struct miphy28lp_pll_gen *gen = &pcie_pll_gen[i]; + + /* Banked settings */ + writeb_relaxed(gen->bank, base + MIPHY_CONF); + writeb_relaxed(gen->speed, base + MIPHY_SPEED); + writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); + writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); + + /* TX buffer Settings */ + writeb_relaxed(gen->tx_ctrl_1, base + MIPHY_TX_CTRL_1); + writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); + writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); + + writeb_relaxed(gen->rx_k_gain, base + MIPHY_RX_K_GAIN); + + /* RX Buffer Settings */ + writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); + writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); + writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); + writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); + } +} + +static inline int miphy28lp_wait_compensation(struct miphy28lp_phy *miphy_phy) +{ + unsigned long finish = jiffies + 5 * HZ; + u8 val; + + /* Waiting for Compensation to complete */ + do { + val = readb_relaxed(miphy_phy->base + MIPHY_COMP_FSM_6); + + if (time_after_eq(jiffies, finish)) + return -EBUSY; + cpu_relax(); + } while (!(val & COMP_DONE)); + + return 0; +} + + +static inline int miphy28lp_compensation(struct miphy28lp_phy *miphy_phy, + struct pll_ratio *pll_ratio) +{ + void __iomem *base = miphy_phy->base; + + /* Poll for HFC ready after reset release */ + /* Compensation measurement */ + writeb_relaxed(RST_PLL_SW | RST_COMP_SW, base + MIPHY_RESET); + + writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); + writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); + + if (miphy_phy->type == PHY_TYPE_PCIE) + writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); + + writeb_relaxed(0x00, base + MIPHY_RESET); + writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + + /* TX compensation offset to re-center TX impedance */ + writeb_relaxed(0x00, base + MIPHY_COMP_POSTP); + + if (miphy_phy->type == PHY_TYPE_PCIE) + return miphy28lp_wait_compensation(miphy_phy); + + return 0; +} + +static inline void miphy28_usb3_miphy_reset(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* MIPHY Reset */ + writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); + writeb_relaxed(0x00, base + MIPHY_CONF_RESET); + writeb_relaxed(RST_COMP_SW, base + MIPHY_RESET); + + val = RST_COMP_SW | RST_PLL_SW; + writeb_relaxed(val, base + MIPHY_RESET); + + writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(0x1e, base + MIPHY_PLL_CLKREF_FREQ); + writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); + writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); + writeb_relaxed(0x00, base + MIPHY_RESET); + writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(0x00, base + MIPHY_CONF); + writeb_relaxed(0x00, base + MIPHY_BOUNDARY_1); + writeb_relaxed(0x00, base + MIPHY_TST_BIAS_BOOST_2); + writeb_relaxed(0x00, base + MIPHY_CONF); + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + writeb_relaxed(0xa5, base + MIPHY_DEBUG_BUS); + writeb_relaxed(0x00, base + MIPHY_CONF); +} + +static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int err; + u8 val; + + /* Putting Macro in reset */ + miphy28lp_set_reset(miphy_phy); + + /* PLL calibration */ + miphy28lp_pll_calibration(miphy_phy, &sata_pll_ratio); + + /* Banked settings Gen1/Gen2/Gen3 */ + miphy28lp_sata_config_gen(miphy_phy); + + /* Power control */ + /* Input bridge enable, manual input bridge control */ + writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); + + /* Macro out of reset */ + writeb_relaxed(0x00, base + MIPHY_CONF_RESET); + + /* Poll for HFC ready after reset release */ + /* Compensation measurement */ + err = miphy28lp_compensation(miphy_phy, &sata_pll_ratio); + if (err) + return err; + + if (miphy_phy->px_rx_pol_inv) { + /* Invert Rx polarity */ + val = readb_relaxed(miphy_phy->base + MIPHY_CONTROL); + val |= PX_RX_POL; + writeb_relaxed(val, miphy_phy->base + MIPHY_CONTROL); + } + + return 0; +} + +static inline int miphy28lp_configure_pcie(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int err; + + /* Putting Macro in reset */ + miphy28lp_set_reset(miphy_phy); + + /* PLL calibration */ + miphy28lp_pll_calibration(miphy_phy, &pcie_pll_ratio); + + /* Banked settings Gen1/Gen2 */ + miphy28lp_pcie_config_gen(miphy_phy); + + /* Power control */ + /* Input bridge enable, manual input bridge control */ + writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); + + /* Macro out of reset */ + writeb_relaxed(0x00, base + MIPHY_CONF_RESET); + + /* Poll for HFC ready after reset release */ + /* Compensation measurement */ + err = miphy28lp_compensation(miphy_phy, &pcie_pll_ratio); + if (err) + return err; + + return 0; +} + + +static inline void miphy28lp_configure_usb3(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* Putting Macro in reset */ + miphy28lp_set_reset(miphy_phy); + + /* PLL calibration */ + miphy28lp_pll_calibration(miphy_phy, &usb3_pll_ratio); + + /* Writing The Speed Rate */ + writeb_relaxed(0x00, base + MIPHY_CONF); + + val = RX_SPDSEL_20DEC | TX_SPDSEL_20DEC; + writeb_relaxed(val, base + MIPHY_SPEED); + + /* RX Channel compensation and calibration */ + writeb_relaxed(0x1c, base + MIPHY_RX_LOCK_SETTINGS_OPT); + writeb_relaxed(0x51, base + MIPHY_RX_CAL_CTRL_1); + writeb_relaxed(0x70, base + MIPHY_RX_CAL_CTRL_2); + + val = OFFSET_COMPENSATION_EN | VGA_OFFSET_POLARITY | + CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; + writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); + writeb_relaxed(0x22, base + MIPHY_RX_CAL_VGA_STEP); + writeb_relaxed(0x0e, base + MIPHY_RX_CAL_OPT_LENGTH); + + val = EQ_DC_GAIN | VGA_GAIN; + writeb_relaxed(val, base + MIPHY_RX_BUFFER_CTRL); + writeb_relaxed(0x78, base + MIPHY_RX_EQU_GAIN_1); + writeb_relaxed(0x1b, base + MIPHY_SYNCHAR_CONTROL); + + /* TX compensation offset to re-center TX impedance */ + writeb_relaxed(0x02, base + MIPHY_COMP_POSTP); + + /* Enable GENSEL_SEL and SSC */ + /* TX_SEL=0 swing preemp forced by pipe registres */ + val = SSC_SEL | GENSEL_SEL; + writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); + + /* MIPHY Bias boost */ + writeb_relaxed(0x00, base + MIPHY_BIAS_BOOST_1); + writeb_relaxed(0xa7, base + MIPHY_BIAS_BOOST_2); + + /* SSC modulation */ + writeb_relaxed(SSC_EN_SW, base + MIPHY_BOUNDARY_2); + + /* MIPHY TX control */ + writeb_relaxed(0x00, base + MIPHY_CONF); + + /* Validate Step component */ + writeb_relaxed(0x5a, base + MIPHY_PLL_SBR_3); + writeb_relaxed(0xa0, base + MIPHY_PLL_SBR_4); + + /* Validate Period component */ + writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); + writeb_relaxed(0xa1, base + MIPHY_PLL_SBR_4); + + /* Clear any previous request */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* requests the PLL to take in account new parameters */ + writeb_relaxed(0x02, base + MIPHY_PLL_SBR_1); + + /* To be sure there is no other pending requests */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* Rx PI controller settings */ + writeb_relaxed(0xca, base + MIPHY_RX_K_GAIN); + + /* MIPHY RX input bridge control */ + /* INPUT_BRIDGE_EN_SW=1, manual input bridge control[0]=1 */ + writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); + writeb_relaxed(0x29, base + MIPHY_RX_POWER_CTRL_1); + writeb_relaxed(0x1a, base + MIPHY_RX_POWER_CTRL_2); + + /* MIPHY Reset for usb3 */ + miphy28_usb3_miphy_reset(miphy_phy); +} + +static inline int miphy_is_ready(struct miphy28lp_phy *miphy_phy) +{ + unsigned long finish = jiffies + 5 * HZ; + u8 mask = HFC_PLL | HFC_RDY; + u8 val; + + /* + * For PCIe and USB3 check only that PLL and HFC are ready + * For SATA check also that phy is ready! + */ + if (miphy_phy->type == PHY_TYPE_SATA) + mask |= PHY_RDY; + + do { + val = readb_relaxed(miphy_phy->base + MIPHY_STATUS_1); + if ((val & mask) != mask) + cpu_relax(); + else + return 0; + } while (!time_after_eq(jiffies, finish)); + + return -EBUSY; +} + +static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + unsigned long finish = jiffies + 5 * HZ; + u32 val; + + if (!miphy_phy->osc_rdy) + return 0; + + if (!miphy_phy->syscfg_miphy_status) + return -EINVAL; + + do { + regmap_read(miphy_dev->regmap, miphy_phy->syscfg_miphy_status, + &val); + + if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY) + cpu_relax(); + else + return 0; + } while (!time_after_eq(jiffies, finish)); + + return -EBUSY; +} + +static int miphy28lp_get_resource_byname(struct device_node *child, + char *rname, struct resource *res) +{ + int index; + + index = of_property_match_string(child, "reg-names", rname); + if (index < 0) + return -ENODEV; + + return of_address_to_resource(child, index, res); +} + +static int miphy28lp_get_one_addr(struct device *dev, + struct device_node *child, char *rname, + void __iomem **base) +{ + struct resource res; + int ret; + + ret = miphy28lp_get_resource_byname(child, rname, &res); + if (!ret) { + *base = devm_ioremap(dev, res.start, resource_size(&res)); + if (!*base) { + dev_err(dev, "failed to ioremap %s address region\n" + , rname); + return -ENOENT; + } + } + + return 0; +} + +/* MiPHY reset and sysconf setup */ +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) + return -EINVAL; + + err = reset_control_assert(miphy_phy->miphy_rst); + if (err) { + dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); + return err; + } + + if (miphy_phy->osc_force_ext) + miphy_val |= MIPHY_OSC_FORCE_EXT; + + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_miphy_ctrl, + MIPHY_CTRL_MASK, miphy_val); + + err = reset_control_deassert(miphy_phy->miphy_rst); + if (err) { + dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); + return err; + } + + return miphy_osc_is_ready(miphy_phy); +} + +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)) + return -EINVAL; + + dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base); + + /* Configure the glue-logic */ + sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE); + + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata, + SATA_CTRL_MASK, sata_conf); + + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci, + PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE); + + /* MiPHY path and clocking init */ + err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); + + if (err) { + dev_err(miphy_dev->dev, "SATA phy setup failed\n"); + return err; + } + + /* initialize miphy */ + miphy28lp_configure_sata(miphy_phy); + + return miphy_is_ready(miphy_phy); +} + +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) + || (!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, + SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE); + + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci, + PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL); + + /* MiPHY path and clocking init */ + err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); + + if (err) { + dev_err(miphy_dev->dev, "PCIe phy setup failed\n"); + return err; + } + + /* initialize miphy */ + err = miphy28lp_configure_pcie(miphy_phy); + if (err) + return err; + + /* PIPE Wrapper Configuration */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x104); /* Rise_0 */ + writeb_relaxed(0x61, miphy_phy->pipebase + 0x105); /* Rise_1 */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x108); /* Fall_0 */ + writeb_relaxed(0x61, miphy_phy->pipebase + 0x109); /* Fall-1 */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x10c); /* Threshold_0 */ + writeb_relaxed(0x60, miphy_phy->pipebase + 0x10d); /* Threshold_1 */ + + /* Wait for phy_ready */ + return miphy_is_ready(miphy_phy); +} + +static int miphy28lp_init_usb3(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int err; + + if ((!miphy_phy->base) || (!miphy_phy->pipebase)) + return -EINVAL; + + dev_info(miphy_dev->dev, "usb3-up mode, addr 0x%p\n", miphy_phy->base); + + /* MiPHY path and clocking init */ + err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_SYNC_D_EN); + if (err) { + dev_err(miphy_dev->dev, "USB3 phy setup failed\n"); + return err; + } + + /* initialize miphy */ + miphy28lp_configure_usb3(miphy_phy); + + /* PIPE Wrapper Configuration */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x23); + writeb_relaxed(0x61, miphy_phy->pipebase + 0x24); + writeb_relaxed(0x68, miphy_phy->pipebase + 0x26); + writeb_relaxed(0x61, miphy_phy->pipebase + 0x27); + writeb_relaxed(0x18, miphy_phy->pipebase + 0x29); + writeb_relaxed(0x61, miphy_phy->pipebase + 0x2a); + + /* pipe Wrapper usb3 TX swing de-emph margin PREEMPH[7:4], SWING[3:0] */ + writeb_relaxed(0X67, miphy_phy->pipebase + 0x68); + writeb_relaxed(0x0d, miphy_phy->pipebase + 0x69); + writeb_relaxed(0X67, miphy_phy->pipebase + 0x6a); + writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6b); + writeb_relaxed(0X67, miphy_phy->pipebase + 0x6c); + writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6d); + writeb_relaxed(0X67, miphy_phy->pipebase + 0x6e); + writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6f); + + return miphy_is_ready(miphy_phy); +} + +static int miphy28lp_init(struct phy *phy) +{ + struct miphy28lp_phy *miphy_phy = phy_get_drvdata(phy); + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int ret; + + mutex_lock(&miphy_dev->miphy_mutex); + + switch (miphy_phy->type) { + + case PHY_TYPE_SATA: + ret = miphy28lp_init_sata(miphy_phy); + break; + case PHY_TYPE_PCIE: + ret = miphy28lp_init_pcie(miphy_phy); + break; + case PHY_TYPE_USB3: + ret = miphy28lp_init_usb3(miphy_phy); + break; + default: + return -EINVAL; + } + + mutex_unlock(&miphy_dev->miphy_mutex); + + return ret; +} + +static int miphy28lp_get_addr(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + struct device_node *phynode = miphy_phy->phy->dev.of_node; + int err; + + if ((miphy_phy->type != PHY_TYPE_SATA) && + (miphy_phy->type != PHY_TYPE_PCIE) && + (miphy_phy->type != PHY_TYPE_USB3)) { + return -EINVAL; + } + + err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, + PHY_TYPE_name[miphy_phy->type - PHY_TYPE_SATA], + &miphy_phy->base); + if (err) + return err; + + if ((miphy_phy->type == PHY_TYPE_PCIE) || + (miphy_phy->type == PHY_TYPE_USB3)) { + err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, "pipew", + &miphy_phy->pipebase); + if (err) + return err; + } + + return 0; +} + +static struct phy *miphy28lp_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct miphy28lp_dev *miphy_dev = dev_get_drvdata(dev); + struct miphy28lp_phy *miphy_phy = NULL; + struct device_node *phynode = args->np; + int ret, index = 0; + + if (!of_device_is_available(phynode)) { + dev_warn(dev, "Requested PHY is disabled\n"); + return ERR_PTR(-ENODEV); + } + + if (args->args_count != 1) { + dev_err(dev, "Invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + for (index = 0; index < of_get_child_count(dev->of_node); index++) + if (phynode == miphy_dev->phys[index]->phy->dev.of_node) { + miphy_phy = miphy_dev->phys[index]; + break; + } + + if (!miphy_phy) { + dev_err(dev, "Failed to find appropriate phy\n"); + return ERR_PTR(-EINVAL); + } + + miphy_phy->type = args->args[0]; + + ret = miphy28lp_get_addr(miphy_phy); + if (ret < 0) + return ERR_PTR(ret); + + return miphy_phy->phy; +} + +static struct phy_ops miphy28lp_ops = { + .init = miphy28lp_init, +}; + +static int miphy28lp_probe_resets(struct device_node *node, + struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int err; + + miphy_phy->miphy_rst = of_reset_control_get(node, "miphy-sw-rst"); + + if (IS_ERR(miphy_phy->miphy_rst)) { + dev_err(miphy_dev->dev, + "miphy soft reset control not defined\n"); + return PTR_ERR(miphy_phy->miphy_rst); + } + + err = reset_control_deassert(miphy_phy->miphy_rst); + if (err) { + dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); + return err; + } + + return 0; +} + +static int miphy28lp_of_probe(struct device_node *np, + struct miphy28lp_phy *miphy_phy) +{ + struct resource res; + + miphy_phy->osc_force_ext = + of_property_read_bool(np, "st,osc-force-ext"); + + miphy_phy->osc_rdy = of_property_read_bool(np, "st,osc-rdy"); + + miphy_phy->px_rx_pol_inv = + of_property_read_bool(np, "st,px_rx_pol_inv"); + + of_property_read_u32(np, "st,sata-gen", &miphy_phy->sata_gen); + 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; + + + return 0; +} + +static int miphy28lp_probe(struct platform_device *pdev) +{ + struct device_node *child, *np = pdev->dev.of_node; + struct miphy28lp_dev *miphy_dev; + struct phy_provider *provider; + struct phy *phy; + int chancount, port = 0; + int ret; + + miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); + if (!miphy_dev) + return -ENOMEM; + + chancount = of_get_child_count(np); + miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount, + GFP_KERNEL); + if (!miphy_dev->phys) + return -ENOMEM; + + miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(miphy_dev->regmap)) { + dev_err(miphy_dev->dev, "No syscfg phandle specified\n"); + return PTR_ERR(miphy_dev->regmap); + } + + miphy_dev->dev = &pdev->dev; + + dev_set_drvdata(&pdev->dev, miphy_dev); + + mutex_init(&miphy_dev->miphy_mutex); + + for_each_child_of_node(np, child) { + struct miphy28lp_phy *miphy_phy; + + miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy), + GFP_KERNEL); + if (!miphy_phy) + return -ENOMEM; + + miphy_dev->phys[port] = miphy_phy; + + phy = devm_phy_create(&pdev->dev, child, &miphy28lp_ops, NULL); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + miphy_dev->phys[port]->phy = phy; + miphy_dev->phys[port]->phydev = miphy_dev; + + ret = miphy28lp_of_probe(child, miphy_phy); + if (ret) + return ret; + + ret = miphy28lp_probe_resets(child, miphy_dev->phys[port]); + if (ret) + return ret; + + phy_set_drvdata(phy, miphy_dev->phys[port]); + port++; + + } + + provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate); + if (IS_ERR(provider)) + return PTR_ERR(provider); + + return 0; +} + +static const struct of_device_id miphy28lp_of_match[] = { + {.compatible = "st,miphy28lp-phy", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, miphy28lp_of_match); + +static struct platform_driver miphy28lp_driver = { + .probe = miphy28lp_probe, + .driver = { + .name = "miphy28lp-phy", + .owner = THIS_MODULE, + .of_match_table = miphy28lp_of_match, + } +}; + +module_platform_driver(miphy28lp_driver); + +MODULE_AUTHOR("Alexandre Torgue "); +MODULE_DESCRIPTION("STMicroelectronics miphy28lp driver"); +MODULE_LICENSE("GPL v2"); From 2b041b27a83fbe951d4b1cb1523001d4a8a5cccb Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Tue, 4 Nov 2014 11:51:21 +0100 Subject: [PATCH 04/25] phy: miphy28lp: Add SSC support for SATA This patch to tune on/off the ssc on miphy sata setup. User can now enable ssc via dt blob, it is useful to reduce effects of EMI. Signed-off-by: Giuseppe Condorelli Signed-off-by: Gabriel Fernandez Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/phy-miphy28lp.txt | 1 + drivers/phy/phy-miphy28lp.c | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt b/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt index b7c13ad81a22..4a3b4af3c1e6 100644 --- a/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt +++ b/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt @@ -39,6 +39,7 @@ Optional properties (port (child) node): register. - st,px_rx_pol_inv : to invert polarity of RXn/RXp (respectively negative line and positive line). +- st,scc-on : enable ssc to reduce effects of EMI (only for sata or PCIe). example: diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index 7d592e6392ac..d2f797c79fbc 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -191,6 +191,8 @@ #define SYSCFG_PCIE_PCIE_VAL 0x80 #define SATA_SPDMODE 1 +#define MIPHY_SATA_BANK_NB 3 + struct miphy28lp_phy { struct phy *phy; struct miphy28lp_dev *phydev; @@ -200,6 +202,7 @@ struct miphy28lp_phy { bool osc_force_ext; bool osc_rdy; bool px_rx_pol_inv; + bool ssc; struct reset_control *miphy_rst; @@ -550,6 +553,44 @@ static inline void miphy28_usb3_miphy_reset(struct miphy28lp_phy *miphy_phy) writeb_relaxed(0x00, base + MIPHY_CONF); } +static void miphy_sata_tune_ssc(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* Compensate Tx impedance to avoid out of range values */ + /* + * Enable the SSC on PLL for all banks + * SSC Modulation @ 31 KHz and 4000 ppm modulation amp + */ + val = readb_relaxed(base + MIPHY_BOUNDARY_2); + val |= SSC_EN_SW; + writeb_relaxed(val, base + MIPHY_BOUNDARY_2); + + val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); + val |= SSC_SEL; + writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); + + for (val = 0; val < MIPHY_SATA_BANK_NB; val++) { + writeb_relaxed(val, base + MIPHY_CONF); + + /* Add value to each reference clock cycle */ + /* and define the period length of the SSC */ + writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); + writeb_relaxed(0x6c, base + MIPHY_PLL_SBR_3); + writeb_relaxed(0x81, base + MIPHY_PLL_SBR_4); + + /* Clear any previous request */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* requests the PLL to take in account new parameters */ + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + + /* To be sure there is no other pending requests */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + } +} + static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy) { void __iomem *base = miphy_phy->base; @@ -585,6 +626,9 @@ static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy) writeb_relaxed(val, miphy_phy->base + MIPHY_CONTROL); } + if (miphy_phy->ssc) + miphy_sata_tune_ssc(miphy_phy); + return 0; } @@ -1064,6 +1108,8 @@ static int miphy28lp_of_probe(struct device_node *np, miphy_phy->px_rx_pol_inv = of_property_read_bool(np, "st,px_rx_pol_inv"); + miphy_phy->ssc = of_property_read_bool(np, "st,ssc-on"); + of_property_read_u32(np, "st,sata-gen", &miphy_phy->sata_gen); if (!miphy_phy->sata_gen) miphy_phy->sata_gen = SATA_GEN1; From a2108dee3cd39f7291d013c50c33c2a639a17b42 Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Tue, 4 Nov 2014 11:51:22 +0100 Subject: [PATCH 05/25] phy: miphy28lp: Add SSC support for PCIE SSC is the technique of modulating the operating frequency of a signal slightly to spread its radiated emissions over a range of frequencies. This reduction in the maximum emission for a given frequency helps meet radiated emission requirements. These settings are applicable for PCIE with Internal clock. Signed-off-by: Harsh Gupta Signed-off-by: Gabriel Fernandez Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-miphy28lp.c | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index d2f797c79fbc..d8ff8956ed05 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -192,6 +192,7 @@ #define SATA_SPDMODE 1 #define MIPHY_SATA_BANK_NB 3 +#define MIPHY_PCIE_BANK_NB 2 struct miphy28lp_phy { struct phy *phy; @@ -591,6 +592,46 @@ static void miphy_sata_tune_ssc(struct miphy28lp_phy *miphy_phy) } } +static void miphy_pcie_tune_ssc(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* Compensate Tx impedance to avoid out of range values */ + /* + * Enable the SSC on PLL for all banks + * SSC Modulation @ 31 KHz and 4000 ppm modulation amp + */ + val = readb_relaxed(base + MIPHY_BOUNDARY_2); + val |= SSC_EN_SW; + writeb_relaxed(val, base + MIPHY_BOUNDARY_2); + + val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); + val |= SSC_SEL; + writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); + + for (val = 0; val < MIPHY_PCIE_BANK_NB; val++) { + writeb_relaxed(val, base + MIPHY_CONF); + + /* Validate Step component */ + writeb_relaxed(0x69, base + MIPHY_PLL_SBR_3); + writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); + + /* Validate Period component */ + writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); + writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); + + /* Clear any previous request */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* requests the PLL to take in account new parameters */ + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + + /* To be sure there is no other pending requests */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + } +} + static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy) { void __iomem *base = miphy_phy->base; @@ -659,6 +700,9 @@ static inline int miphy28lp_configure_pcie(struct miphy28lp_phy *miphy_phy) if (err) return err; + if (miphy_phy->ssc) + miphy_pcie_tune_ssc(miphy_phy); + return 0; } From 28ba384dc5e42a23328dca7e5e72d19e2dd4c9ce Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Tue, 4 Nov 2014 11:51:23 +0100 Subject: [PATCH 06/25] phy: miphy28lp: Tune tx impedance across Soc cuts This patch to compensate tx impedance (Sata, PCIe) depending on Soc cuts the kernel is built for. Signed-off-by: Giuseppe Condorelli Signed-off-by: Gabriel Fernandez Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/phy-miphy28lp.txt | 1 + drivers/phy/phy-miphy28lp.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt b/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt index 4a3b4af3c1e6..46a135dae6b3 100644 --- a/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt +++ b/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt @@ -40,6 +40,7 @@ Optional properties (port (child) node): - st,px_rx_pol_inv : to invert polarity of RXn/RXp (respectively negative line and positive line). - st,scc-on : enable ssc to reduce effects of EMI (only for sata or PCIe). +- st,tx-impedance-comp : to compensate tx impedance avoiding out of range values. example: diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index d8ff8956ed05..87dcc9ab7f23 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -204,6 +204,7 @@ struct miphy28lp_phy { bool osc_rdy; bool px_rx_pol_inv; bool ssc; + bool tx_impedance; struct reset_control *miphy_rst; @@ -632,6 +633,12 @@ static void miphy_pcie_tune_ssc(struct miphy28lp_phy *miphy_phy) } } +static inline void miphy_tune_tx_impedance(struct miphy28lp_phy *miphy_phy) +{ + /* Compensate Tx impedance to avoid out of range values */ + writeb_relaxed(0x02, miphy_phy->base + MIPHY_COMP_POSTP); +} + static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy) { void __iomem *base = miphy_phy->base; @@ -670,6 +677,9 @@ static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy) if (miphy_phy->ssc) miphy_sata_tune_ssc(miphy_phy); + if (miphy_phy->tx_impedance) + miphy_tune_tx_impedance(miphy_phy); + return 0; } @@ -703,6 +713,9 @@ static inline int miphy28lp_configure_pcie(struct miphy28lp_phy *miphy_phy) if (miphy_phy->ssc) miphy_pcie_tune_ssc(miphy_phy); + if (miphy_phy->tx_impedance) + miphy_tune_tx_impedance(miphy_phy); + return 0; } @@ -1154,6 +1167,9 @@ static int miphy28lp_of_probe(struct device_node *np, miphy_phy->ssc = of_property_read_bool(np, "st,ssc-on"); + miphy_phy->tx_impedance = + of_property_read_bool(np, "st,tx-impedance-comp"); + of_property_read_u32(np, "st,sata-gen", &miphy_phy->sata_gen); if (!miphy_phy->sata_gen) miphy_phy->sata_gen = SATA_GEN1; From 491e049064a4ea69ba4b35bdf614f12bc762a717 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 31 Oct 2014 14:04:20 +0530 Subject: [PATCH 07/25] phy: phy-core: use the np present in of_phandle_args to get the PHY Instead of using the node pointer of the PHY provider and then scanning its child nodes to get a reference to the PHY, directly use the node pointer present in of_phandle_args to get a reference to the PHY. Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-core.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index ff5eec5af817..1606ce9805d0 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -414,21 +414,13 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args { struct phy *phy; struct class_dev_iter iter; - struct device_node *node = dev->of_node; - struct device_node *child; class_dev_iter_init(&iter, phy_class, NULL, NULL); while ((dev = class_dev_iter_next(&iter))) { phy = to_phy(dev); - if (node != phy->dev.of_node) { - for_each_child_of_node(node, child) { - if (child == phy->dev.of_node) - goto phy_found; - } + if (args->np != phy->dev.of_node) continue; - } -phy_found: class_dev_iter_exit(&iter); return phy; } From 6827a46f59942208d45e0c40e53f649bfc7792ed Mon Sep 17 00:00:00 2001 From: Roman Byshko Date: Mon, 10 Nov 2014 19:55:06 +0100 Subject: [PATCH 08/25] phy: sun4i: add support for USB phy0 The driver for sun4i USB phys currently supports only phy1 and phy2 which are used for USB host controllers. This patch adds support for USB phy0, which is used by the musb hdrc USB controller. Signed-off-by: Roman Byshko Acked-by: Maxime Ripard Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-sun4i-usb.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index 0baf5efc8a40..6bd2b0c972cc 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -157,6 +157,10 @@ static int sun4i_usb_phy_init(struct phy *_phy) return ret; } + /* Enable USB 45 Ohm resistor calibration */ + if (phy->index == 0) + sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); + /* Adjust PHY's magnitude and rate */ sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); @@ -213,7 +217,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev, { struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); - if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys)) + if (args->args[0] >= data->num_phys) return ERR_PTR(-ENODEV); return data->phys[args->args[0]].phy; @@ -255,8 +259,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) if (IS_ERR(data->base)) return PTR_ERR(data->base); - /* Skip 0, 0 is the phy for otg which is not yet supported. */ - for (i = 1; i < data->num_phys; i++) { + for (i = 0; i < data->num_phys; i++) { struct sun4i_usb_phy *phy = data->phys + i; char name[16]; From a98d41d6a1204b61bac03bb3eabdbc2fe93b495d Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Thu, 30 Oct 2014 11:21:24 +0100 Subject: [PATCH 09/25] phy: berlin-sata: Move PHY_BASE into private data struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, Berlin SATA PHY driver assumes PHY_BASE address being constant. While this PHY_BASE is correct for BG2Q, older BG2 PHY_BASE is different. Prepare the driver for BG2 support by moving the phy_base into private driver data. Acked-by: Antoine Ténart Signed-off-by: Sebastian Hesselbarth Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-berlin-sata.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c index 69ced52d72aa..cdb46d1203a4 100644 --- a/drivers/phy/phy-berlin-sata.c +++ b/drivers/phy/phy-berlin-sata.c @@ -30,7 +30,7 @@ #define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) #define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) -#define PHY_BASE 0x200 +#define BG2Q_PHY_BASE 0x200 /* register 0x01 */ #define REF_FREF_SEL_25 BIT(0) @@ -61,15 +61,16 @@ struct phy_berlin_priv { struct clk *clk; struct phy_berlin_desc **phys; unsigned nphys; + u32 phy_base; }; -static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg, - u32 mask, u32 val) +static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, + u32 phy_base, u32 reg, u32 mask, u32 val) { u32 regval; /* select register */ - writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR); + writel(phy_base + reg, ctrl_reg + PORT_VSR_ADDR); /* set bits */ regval = readl(ctrl_reg + PORT_VSR_DATA); @@ -103,17 +104,20 @@ static int phy_berlin_sata_power_on(struct phy *phy) writel(regval, priv->base + HOST_VSA_DATA); /* set PHY mode and ref freq to 25 MHz */ - phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff, - REF_FREF_SEL_25 | PHY_MODE_SATA); + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x01, + 0x00ff, REF_FREF_SEL_25 | PHY_MODE_SATA); /* set PHY up to 6 Gbps */ - phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0); + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x25, + 0x0c00, PHY_GEN_MAX_6_0); /* set 40 bits width */ - phy_berlin_sata_reg_setbits(ctrl_reg, 0x23, 0xc00, DATA_BIT_WIDTH_40); + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x23, + 0x0c00, DATA_BIT_WIDTH_40); /* use max pll rate */ - phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE); + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x02, + 0x0000, USE_MAX_PLL_RATE); /* set Gen3 controller speed */ regval = readl(ctrl_reg + PORT_SCR_CTL); @@ -218,6 +222,8 @@ static int phy_berlin_sata_probe(struct platform_device *pdev) if (!priv->phys) return -ENOMEM; + priv->phy_base = BG2Q_PHY_BASE; + dev_set_drvdata(dev, priv); spin_lock_init(&priv->lock); From 8203f8b4619a158a685ba317356a679b5f1ac422 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Thu, 30 Oct 2014 11:21:25 +0100 Subject: [PATCH 10/25] phy: berlin-sata: Add support for BG2 SATA PHY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Berlin BG2 also has a SATA PHY compatible with the current driver except different PHY_BASE. Add a new compatible to the driver reflecting the different PHY_BASE. Acked-by: Antoine Ténart Signed-off-by: Sebastian Hesselbarth Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-berlin-sata.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c index cdb46d1203a4..873e7a890fee 100644 --- a/drivers/phy/phy-berlin-sata.c +++ b/drivers/phy/phy-berlin-sata.c @@ -30,6 +30,7 @@ #define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) #define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) +#define BG2_PHY_BASE 0x080 #define BG2Q_PHY_BASE 0x200 /* register 0x01 */ @@ -222,7 +223,10 @@ static int phy_berlin_sata_probe(struct platform_device *pdev) if (!priv->phys) return -ENOMEM; - priv->phy_base = BG2Q_PHY_BASE; + if (of_device_is_compatible(dev->of_node, "marvell,berlin2-sata-phy")) + priv->phy_base = BG2_PHY_BASE; + else + priv->phy_base = BG2Q_PHY_BASE; dev_set_drvdata(dev, priv); spin_lock_init(&priv->lock); @@ -271,6 +275,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev) } static const struct of_device_id phy_berlin_sata_of_match[] = { + { .compatible = "marvell,berlin2-sata-phy" }, { .compatible = "marvell,berlin2q-sata-phy" }, { }, }; From ec4637bfff1c7d5f2bc7e51d180dd4aa51883af0 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Thu, 30 Oct 2014 11:21:26 +0100 Subject: [PATCH 11/25] phy: berlin-sata: Document BG2 compatible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Berlin BG2 SATA PHY is slightly different from currently supported BG2Q SATA PHY. Document the new compatible for BG2's PHY. Acked-by: Antoine Ténart Signed-off-by: Sebastian Hesselbarth Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/berlin-sata-phy.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt b/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt index 88f8c23384c0..c0155f842f62 100644 --- a/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt +++ b/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt @@ -2,7 +2,9 @@ Berlin SATA PHY --------------- Required properties: -- compatible: should be "marvell,berlin2q-sata-phy" +- compatible: should be one of + "marvell,berlin2-sata-phy" + "marvell,berlin2q-sata-phy" - address-cells: should be 1 - size-cells: should be 0 - phy-cells: from the generic PHY bindings, must be 1 From 13ebb68cb5a227abe2437094f99a699736e39e0a Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Thu, 20 Nov 2014 22:53:25 +0100 Subject: [PATCH 12/25] phy: add the Berlin USB PHY driver Add the driver driving the Marvell Berlin USB PHY. This allows to initialize the PHY and to use it from the USB driver later. Signed-off-by: Antoine Tenart Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 7 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-berlin-usb.c | 224 +++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 drivers/phy/phy-berlin-usb.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index cfaced92e0c8..4144d250524f 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -15,6 +15,13 @@ config GENERIC_PHY phy users can obtain reference to the PHY. All the users of this framework should select this config. +config PHY_BERLIN_USB + tristate "Marvell Berlin USB PHY Driver" + depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF + select GENERIC_PHY + help + Enable this to support the USB PHY on Marvell Berlin SoCs. + config PHY_BERLIN_SATA tristate "Marvell Berlin SATA PHY driver" depends on ARCH_BERLIN && HAS_IOMEM && OF diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 30d90a8d72e8..68022f698f51 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_GENERIC_PHY) += phy-core.o +obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o diff --git a/drivers/phy/phy-berlin-usb.c b/drivers/phy/phy-berlin-usb.c new file mode 100644 index 000000000000..f9f13067f50f --- /dev/null +++ b/drivers/phy/phy-berlin-usb.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2014 Marvell Technology Group Ltd. + * + * Antoine Tenart + * Jisheng Zhang + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_PHY_PLL 0x04 +#define USB_PHY_PLL_CONTROL 0x08 +#define USB_PHY_TX_CTRL0 0x10 +#define USB_PHY_TX_CTRL1 0x14 +#define USB_PHY_TX_CTRL2 0x18 +#define USB_PHY_RX_CTRL 0x20 +#define USB_PHY_ANALOG 0x34 + +/* USB_PHY_PLL */ +#define CLK_REF_DIV(x) ((x) << 4) +#define FEEDBACK_CLK_DIV(x) ((x) << 8) + +/* USB_PHY_PLL_CONTROL */ +#define CLK_STABLE BIT(0) +#define PLL_CTRL_PIN BIT(1) +#define PLL_CTRL_REG BIT(2) +#define PLL_ON BIT(3) +#define PHASE_OFF_TOL_125 (0x0 << 5) +#define PHASE_OFF_TOL_250 BIT(5) +#define KVC0_CALIB (0x0 << 9) +#define KVC0_REG_CTRL BIT(9) +#define KVC0_HIGH (0x0 << 10) +#define KVC0_LOW (0x3 << 10) +#define CLK_BLK_EN BIT(13) + +/* USB_PHY_TX_CTRL0 */ +#define EXT_HS_RCAL_EN BIT(3) +#define EXT_FS_RCAL_EN BIT(4) +#define IMPCAL_VTH_DIV(x) ((x) << 5) +#define EXT_RS_RCAL_DIV(x) ((x) << 8) +#define EXT_FS_RCAL_DIV(x) ((x) << 12) + +/* USB_PHY_TX_CTRL1 */ +#define TX_VDD15_14 (0x0 << 4) +#define TX_VDD15_15 BIT(4) +#define TX_VDD15_16 (0x2 << 4) +#define TX_VDD15_17 (0x3 << 4) +#define TX_VDD12_VDD (0x0 << 6) +#define TX_VDD12_11 BIT(6) +#define TX_VDD12_12 (0x2 << 6) +#define TX_VDD12_13 (0x3 << 6) +#define LOW_VDD_EN BIT(8) +#define TX_OUT_AMP(x) ((x) << 9) + +/* USB_PHY_TX_CTRL2 */ +#define TX_CHAN_CTRL_REG(x) ((x) << 0) +#define DRV_SLEWRATE(x) ((x) << 4) +#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6) +#define IMP_CAL_FS_HS_DLY_1 BIT(6) +#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6) +#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6) +#define FS_DRV_EN_MASK(x) ((x) << 8) +#define HS_DRV_EN_MASK(x) ((x) << 12) + +/* USB_PHY_RX_CTRL */ +#define PHASE_FREEZE_DLY_2_CL (0x0 << 0) +#define PHASE_FREEZE_DLY_4_CL BIT(0) +#define ACK_LENGTH_8_CL (0x0 << 2) +#define ACK_LENGTH_12_CL BIT(2) +#define ACK_LENGTH_16_CL (0x2 << 2) +#define ACK_LENGTH_20_CL (0x3 << 2) +#define SQ_LENGTH_3 (0x0 << 4) +#define SQ_LENGTH_6 BIT(4) +#define SQ_LENGTH_9 (0x2 << 4) +#define SQ_LENGTH_12 (0x3 << 4) +#define DISCON_THRESHOLD_260 (0x0 << 6) +#define DISCON_THRESHOLD_270 BIT(6) +#define DISCON_THRESHOLD_280 (0x2 << 6) +#define DISCON_THRESHOLD_290 (0x3 << 6) +#define SQ_THRESHOLD(x) ((x) << 8) +#define LPF_COEF(x) ((x) << 12) +#define INTPL_CUR_10 (0x0 << 14) +#define INTPL_CUR_20 BIT(14) +#define INTPL_CUR_30 (0x2 << 14) +#define INTPL_CUR_40 (0x3 << 14) + +/* USB_PHY_ANALOG */ +#define ANA_PWR_UP BIT(1) +#define ANA_PWR_DOWN BIT(2) +#define V2I_VCO_RATIO(x) ((x) << 7) +#define R_ROTATE_90 (0x0 << 10) +#define R_ROTATE_0 BIT(10) +#define MODE_TEST_EN BIT(11) +#define ANA_TEST_DC_CTRL(x) ((x) << 12) + +#define to_phy_berlin_usb_priv(p) \ + container_of((p), struct phy_berlin_usb_priv, phy) + +static const u32 phy_berlin_pll_dividers[] = { + /* Berlin 2 */ + CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54), + /* Berlin 2CD */ + CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55), +}; + +struct phy_berlin_usb_priv { + void __iomem *base; + struct phy *phy; + struct reset_control *rst_ctrl; + u32 pll_divider; +}; + +static int phy_berlin_usb_power_on(struct phy *phy) +{ + struct phy_berlin_usb_priv *priv = dev_get_drvdata(phy->dev.parent); + + reset_control_reset(priv->rst_ctrl); + + writel(priv->pll_divider, + priv->base + USB_PHY_PLL); + writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL | + CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL); + writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5), + priv->base + USB_PHY_ANALOG); + writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 | + DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) | + INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL); + + writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1); + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), + priv->base + USB_PHY_TX_CTRL0); + + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) | + EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0); + + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), + priv->base + USB_PHY_TX_CTRL0); + writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 | + FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2); + + return 0; +} + +static struct phy_ops phy_berlin_usb_ops = { + .power_on = phy_berlin_usb_power_on, + .owner = THIS_MODULE, +}; + +static const struct of_device_id phy_berlin_sata_of_match[] = { + { + .compatible = "marvell,berlin2-usb-phy", + .data = &phy_berlin_pll_dividers[0], + }, + { + .compatible = "marvell,berlin2cd-usb-phy", + .data = &phy_berlin_pll_dividers[1], + }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match); + +static int phy_berlin_usb_probe(struct platform_device *pdev) +{ + const struct of_device_id *match = + of_match_device(phy_berlin_sata_of_match, &pdev->dev); + struct phy_berlin_usb_priv *priv; + struct resource *res; + struct phy_provider *phy_provider; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(priv->rst_ctrl)) + return PTR_ERR(priv->rst_ctrl); + + priv->pll_divider = *((u32 *)match->data); + + priv->phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops, + NULL); + if (IS_ERR(priv->phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(priv->phy); + } + + platform_set_drvdata(pdev, priv); + + phy_provider = + devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +static struct platform_driver phy_berlin_usb_driver = { + .probe = phy_berlin_usb_probe, + .driver = { + .name = "phy-berlin-usb", + .owner = THIS_MODULE, + .of_match_table = phy_berlin_sata_of_match, + }, +}; +module_platform_driver(phy_berlin_usb_driver); + +MODULE_AUTHOR("Antoine Tenart "); +MODULE_DESCRIPTION("Marvell Berlin PHY driver for USB"); +MODULE_LICENSE("GPL"); From 2f3fa3a0e5672f87f5b972910737a2ef60941b62 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Thu, 20 Nov 2014 22:53:26 +0100 Subject: [PATCH 13/25] Documentation: bindings: add doc for the Berlin USB PHY Document the bindings of the Marvell Berlin USB PHY driver. Signed-off-by: Antoine Tenart Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/berlin-usb-phy.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/berlin-usb-phy.txt diff --git a/Documentation/devicetree/bindings/phy/berlin-usb-phy.txt b/Documentation/devicetree/bindings/phy/berlin-usb-phy.txt new file mode 100644 index 000000000000..be33780f668e --- /dev/null +++ b/Documentation/devicetree/bindings/phy/berlin-usb-phy.txt @@ -0,0 +1,16 @@ +* Marvell Berlin USB PHY + +Required properties: +- compatible: "marvell,berlin2-usb-phy" or "marvell,berlin2cd-usb-phy" +- reg: base address and length of the registers +- #phys-cells: should be 0 +- resets: reference to the reset controller + +Example: + + usb-phy@f774000 { + compatible = "marvell,berlin2-usb-phy"; + reg = <0xf774000 0x128>; + #phy-cells = <0>; + resets = <&chip 0x104 14>; + }; From d451057464a7ea2fe400e56c8a7e004c875f2a84 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 19 Nov 2014 17:28:17 +0200 Subject: [PATCH 14/25] phy: safer to_phy() macro This makes to_phy() macro work with other variable names besides "dev". Signed-off-by: Heikki Krogerus Tested-by: Vivek Gautam Acked-by: Felipe Balbi Signed-off-by: Kishon Vijay Abraham I --- include/linux/phy/phy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 8cb6f815475b..9fda68324298 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -110,7 +110,7 @@ struct phy_init_data { .port = _port, \ } -#define to_phy(dev) (container_of((dev), struct phy, dev)) +#define to_phy(a) (container_of((a), struct phy, dev)) #define of_phy_provider_register(dev, xlate) \ __of_phy_provider_register((dev), THIS_MODULE, (xlate)) From b7bc15b98e843926d01eb03b9c0e196d8ddbadeb Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 19 Nov 2014 17:28:18 +0200 Subject: [PATCH 15/25] phy: improved lookup method Separates registration of the phy and the lookup. The method is copied from clkdev.c, Signed-off-by: Heikki Krogerus Signed-off-by: Kishon Vijay Abraham I --- Documentation/phy.txt | 54 +++++++------------------- drivers/phy/phy-core.c | 84 ++++++++++++++++++++++++++++++++++++++++- include/linux/phy/phy.h | 16 ++++++++ 3 files changed, 112 insertions(+), 42 deletions(-) diff --git a/Documentation/phy.txt b/Documentation/phy.txt index c6594af94d25..371361c69a4b 100644 --- a/Documentation/phy.txt +++ b/Documentation/phy.txt @@ -54,18 +54,14 @@ The PHY driver should create the PHY in order for other peripheral controllers to make use of it. The PHY framework provides 2 APIs to create the PHY. struct phy *phy_create(struct device *dev, struct device_node *node, - const struct phy_ops *ops, - struct phy_init_data *init_data); + const struct phy_ops *ops); struct phy *devm_phy_create(struct device *dev, struct device_node *node, - const struct phy_ops *ops, - struct phy_init_data *init_data); + const struct phy_ops *ops); The PHY drivers can use one of the above 2 APIs to create the PHY by passing -the device pointer, phy ops and init_data. +the device pointer and phy ops. phy_ops is a set of function pointers for performing PHY operations such as -init, exit, power_on and power_off. *init_data* is mandatory to get a reference -to the PHY in the case of non-dt boot. See section *Board File Initialization* -on how init_data should be used. +init, exit, power_on and power_off. Inorder to dereference the private data (in phy_ops), the phy provider driver can use phy_set_drvdata() after creating the PHY and use phy_get_drvdata() in @@ -137,42 +133,18 @@ There are exported APIs like phy_pm_runtime_get, phy_pm_runtime_get_sync, phy_pm_runtime_put, phy_pm_runtime_put_sync, phy_pm_runtime_allow and phy_pm_runtime_forbid for performing PM operations. -8. Board File Initialization +8. PHY Mappings -Certain board file initialization is necessary in order to get a reference -to the PHY in the case of non-dt boot. -Say we have a single device that implements 3 PHYs that of USB, SATA and PCIe, -then in the board file the following initialization should be done. +In order to get reference to a PHY without help from DeviceTree, the framework +offers lookups which can be compared to clkdev that allow clk structures to be +bound to devices. A lookup can be made be made during runtime when a handle to +the struct phy already exists. -struct phy_consumer consumers[] = { - PHY_CONSUMER("dwc3.0", "usb"), - PHY_CONSUMER("pcie.0", "pcie"), - PHY_CONSUMER("sata.0", "sata"), -}; -PHY_CONSUMER takes 2 parameters, first is the device name of the controller -(PHY consumer) and second is the port name. +The framework offers the following API for registering and unregistering the +lookups. -struct phy_init_data init_data = { - .consumers = consumers, - .num_consumers = ARRAY_SIZE(consumers), -}; - -static const struct platform_device pipe3_phy_dev = { - .name = "pipe3-phy", - .id = -1, - .dev = { - .platform_data = { - .init_data = &init_data, - }, - }, -}; - -then, while doing phy_create, the PHY driver should pass this init_data - phy_create(dev, ops, pdata->init_data); - -and the controller driver (phy consumer) should pass the port name along with -the device to get a reference to the PHY - phy_get(dev, "pcie"); +int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id); +void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id); 9. DeviceTree Binding diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 1606ce9805d0..bc830773fe05 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -26,6 +26,7 @@ static struct class *phy_class; static DEFINE_MUTEX(phy_provider_mutex); static LIST_HEAD(phy_provider_list); +static LIST_HEAD(phys); static DEFINE_IDA(phy_ida); static void devm_phy_release(struct device *dev, void *res) @@ -84,6 +85,87 @@ static struct phy *phy_lookup(struct device *device, const char *port) return ERR_PTR(-ENODEV); } +/** + * phy_create_lookup() - allocate and register PHY/device association + * @phy: the phy of the association + * @con_id: connection ID string on device + * @dev_id: the device of the association + * + * Creates and registers phy_lookup entry. + */ +int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id) +{ + struct phy_lookup *pl; + + if (!phy || !dev_id || !con_id) + return -EINVAL; + + pl = kzalloc(sizeof(*pl), GFP_KERNEL); + if (!pl) + return -ENOMEM; + + pl->dev_id = dev_id; + pl->con_id = con_id; + pl->phy = phy; + + mutex_lock(&phy_provider_mutex); + list_add_tail(&pl->node, &phys); + mutex_unlock(&phy_provider_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(phy_create_lookup); + +/** + * phy_remove_lookup() - find and remove PHY/device association + * @phy: the phy of the association + * @con_id: connection ID string on device + * @dev_id: the device of the association + * + * Finds and unregisters phy_lookup entry that was created with + * phy_create_lookup(). + */ +void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id) +{ + struct phy_lookup *pl; + + if (!phy || !dev_id || !con_id) + return; + + mutex_lock(&phy_provider_mutex); + list_for_each_entry(pl, &phys, node) + if (pl->phy == phy && !strcmp(pl->dev_id, dev_id) && + !strcmp(pl->con_id, con_id)) { + list_del(&pl->node); + kfree(pl); + break; + } + mutex_unlock(&phy_provider_mutex); +} +EXPORT_SYMBOL_GPL(phy_remove_lookup); + +static struct phy *phy_find(struct device *dev, const char *con_id) +{ + const char *dev_id = dev_name(dev); + struct phy_lookup *p, *pl = NULL; + struct phy *phy; + + mutex_lock(&phy_provider_mutex); + list_for_each_entry(p, &phys, node) + if (!strcmp(p->dev_id, dev_id) && !strcmp(p->con_id, con_id)) { + pl = p; + break; + } + mutex_unlock(&phy_provider_mutex); + + phy = pl ? pl->phy : ERR_PTR(-ENODEV); + + /* fall-back to the old lookup method for now */ + if (IS_ERR(phy)) + phy = phy_lookup(dev, con_id); + return phy; +} + static struct phy_provider *of_phy_provider_lookup(struct device_node *node) { struct phy_provider *phy_provider; @@ -455,7 +537,7 @@ struct phy *phy_get(struct device *dev, const char *string) string); phy = _of_phy_get(dev->of_node, index); } else { - phy = phy_lookup(dev, string); + phy = phy_find(dev, string); } if (IS_ERR(phy)) return phy; diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 9fda68324298..849284e5873f 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -110,6 +110,13 @@ struct phy_init_data { .port = _port, \ } +struct phy_lookup { + struct list_head node; + const char *dev_id; + const char *con_id; + struct phy *phy; +}; + #define to_phy(a) (container_of((a), struct phy, dev)) #define of_phy_provider_register(dev, xlate) \ @@ -174,6 +181,8 @@ struct phy_provider *__devm_of_phy_provider_register(struct device *dev, void of_phy_provider_unregister(struct phy_provider *phy_provider); void devm_of_phy_provider_unregister(struct device *dev, struct phy_provider *phy_provider); +int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id); +void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id); #else static inline int phy_pm_runtime_get(struct phy *phy) { @@ -345,6 +354,13 @@ static inline void devm_of_phy_provider_unregister(struct device *dev, struct phy_provider *phy_provider) { } +static inline int +phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id) +{ + return 0; +} +static inline void phy_remove_lookup(struct phy *phy, const char *con_id, + const char *dev_id) { } #endif #endif /* __DRIVERS_PHY_H */ From 61211b1bda0321803fdedc61158c55eab2685ad6 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 19 Nov 2014 17:28:19 +0200 Subject: [PATCH 16/25] phy: twl4030: use the new lookup method Creates the lookup separately. Hard coding the consumer as it can't be anything else except musb. Signed-off-by: Heikki Krogerus Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-twl4030-usb.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 7b04befd5271..c45a3aa4f2cc 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -644,7 +644,6 @@ static int twl4030_usb_probe(struct platform_device *pdev) struct usb_otg *otg; struct device_node *np = pdev->dev.of_node; struct phy_provider *phy_provider; - struct phy_init_data *init_data = NULL; twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); if (!twl) @@ -655,7 +654,6 @@ static int twl4030_usb_probe(struct platform_device *pdev) (enum twl4030_usb_mode *)&twl->usb_mode); else if (pdata) { twl->usb_mode = pdata->usb_mode; - init_data = pdata->init_data; } else { dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); return -EINVAL; @@ -680,7 +678,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) otg->set_host = twl4030_set_host; otg->set_peripheral = twl4030_set_peripheral; - phy = devm_phy_create(twl->dev, NULL, &ops, init_data); + phy = devm_phy_create(twl->dev, NULL, &ops, NULL); if (IS_ERR(phy)) { dev_dbg(&pdev->dev, "Failed to create PHY\n"); return PTR_ERR(phy); @@ -733,6 +731,11 @@ static int twl4030_usb_probe(struct platform_device *pdev) return status; } + if (pdata) + err = phy_create_lookup(phy, "usb", "musb-hdrc.0"); + if (err) + return err; + pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(twl->dev); From dbc98635e0d42f0e62ea92813df1e0e4c90f8375 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 19 Nov 2014 17:28:21 +0200 Subject: [PATCH 17/25] phy: remove the old lookup method The users of the old method are now converted to the new one. Signed-off-by: Heikki Krogerus [ kishon@ti.com : made phy-berlin-usb.c and phy-miphy28lp.c to use the updated devm_phy_create API.] Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-bcm-kona-usb2.c | 2 +- drivers/phy/phy-berlin-sata.c | 2 +- drivers/phy/phy-berlin-usb.c | 3 +- drivers/phy/phy-core.c | 49 +++------------------------- drivers/phy/phy-exynos-dp-video.c | 2 +- drivers/phy/phy-exynos-mipi-video.c | 2 +- drivers/phy/phy-exynos5-usbdrd.c | 3 +- drivers/phy/phy-exynos5250-sata.c | 2 +- drivers/phy/phy-hix5hd2-sata.c | 2 +- drivers/phy/phy-miphy28lp.c | 2 +- drivers/phy/phy-miphy365x.c | 2 +- drivers/phy/phy-mvebu-sata.c | 2 +- drivers/phy/phy-omap-usb2.c | 2 +- drivers/phy/phy-qcom-apq8064-sata.c | 3 +- drivers/phy/phy-qcom-ipq806x-sata.c | 3 +- drivers/phy/phy-rcar-gen2.c | 2 +- drivers/phy/phy-samsung-usb2.c | 3 +- drivers/phy/phy-spear1310-miphy.c | 2 +- drivers/phy/phy-spear1340-miphy.c | 2 +- drivers/phy/phy-stih407-usb.c | 2 +- drivers/phy/phy-stih41x-usb.c | 2 +- drivers/phy/phy-sun4i-usb.c | 2 +- drivers/phy/phy-ti-pipe3.c | 2 +- drivers/phy/phy-twl4030-usb.c | 2 +- drivers/phy/phy-xgene.c | 2 +- drivers/pinctrl/pinctrl-tegra-xusb.c | 4 +-- include/linux/phy/phy.h | 38 +++------------------ 27 files changed, 34 insertions(+), 110 deletions(-) diff --git a/drivers/phy/phy-bcm-kona-usb2.c b/drivers/phy/phy-bcm-kona-usb2.c index c1e0ca335c0e..ef2dc1aab2b9 100644 --- a/drivers/phy/phy-bcm-kona-usb2.c +++ b/drivers/phy/phy-bcm-kona-usb2.c @@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); - gphy = devm_phy_create(dev, NULL, &ops, NULL); + gphy = devm_phy_create(dev, NULL, &ops); if (IS_ERR(gphy)) return PTR_ERR(gphy); diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c index 873e7a890fee..3e599dc96164 100644 --- a/drivers/phy/phy-berlin-sata.c +++ b/drivers/phy/phy-berlin-sata.c @@ -249,7 +249,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev) if (!phy_desc) return -ENOMEM; - phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL); + phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY %d\n", phy_id); return PTR_ERR(phy); diff --git a/drivers/phy/phy-berlin-usb.c b/drivers/phy/phy-berlin-usb.c index f9f13067f50f..c8a8d53a6ece 100644 --- a/drivers/phy/phy-berlin-usb.c +++ b/drivers/phy/phy-berlin-usb.c @@ -192,8 +192,7 @@ static int phy_berlin_usb_probe(struct platform_device *pdev) priv->pll_divider = *((u32 *)match->data); - priv->phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops, - NULL); + priv->phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops); if (IS_ERR(priv->phy)) { dev_err(&pdev->dev, "failed to create PHY\n"); return PTR_ERR(priv->phy); diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index bc830773fe05..a12d35338313 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -55,36 +55,6 @@ static int devm_phy_match(struct device *dev, void *res, void *match_data) return res == match_data; } -static struct phy *phy_lookup(struct device *device, const char *port) -{ - unsigned int count; - struct phy *phy; - struct device *dev; - struct phy_consumer *consumers; - struct class_dev_iter iter; - - class_dev_iter_init(&iter, phy_class, NULL, NULL); - while ((dev = class_dev_iter_next(&iter))) { - phy = to_phy(dev); - - if (!phy->init_data) - continue; - count = phy->init_data->num_consumers; - consumers = phy->init_data->consumers; - while (count--) { - if (!strcmp(consumers->dev_name, dev_name(device)) && - !strcmp(consumers->port, port)) { - class_dev_iter_exit(&iter); - return phy; - } - consumers++; - } - } - - class_dev_iter_exit(&iter); - return ERR_PTR(-ENODEV); -} - /** * phy_create_lookup() - allocate and register PHY/device association * @phy: the phy of the association @@ -148,7 +118,6 @@ static struct phy *phy_find(struct device *dev, const char *con_id) { const char *dev_id = dev_name(dev); struct phy_lookup *p, *pl = NULL; - struct phy *phy; mutex_lock(&phy_provider_mutex); list_for_each_entry(p, &phys, node) @@ -158,12 +127,7 @@ static struct phy *phy_find(struct device *dev, const char *con_id) } mutex_unlock(&phy_provider_mutex); - phy = pl ? pl->phy : ERR_PTR(-ENODEV); - - /* fall-back to the old lookup method for now */ - if (IS_ERR(phy)) - phy = phy_lookup(dev, con_id); - return phy; + return pl ? pl->phy : ERR_PTR(-ENODEV); } static struct phy_provider *of_phy_provider_lookup(struct device_node *node) @@ -662,13 +626,11 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get); * @dev: device that is creating the new phy * @node: device node of the phy * @ops: function pointers for performing phy operations - * @init_data: contains the list of PHY consumers or NULL * * Called to create a phy using phy framework. */ struct phy *phy_create(struct device *dev, struct device_node *node, - const struct phy_ops *ops, - struct phy_init_data *init_data) + const struct phy_ops *ops) { int ret; int id; @@ -706,7 +668,6 @@ struct phy *phy_create(struct device *dev, struct device_node *node, phy->dev.of_node = node ?: dev->of_node; phy->id = id; phy->ops = ops; - phy->init_data = init_data; ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id); if (ret) @@ -741,7 +702,6 @@ EXPORT_SYMBOL_GPL(phy_create); * @dev: device that is creating the new phy * @node: device node of the phy * @ops: function pointers for performing phy operations - * @init_data: contains the list of PHY consumers or NULL * * Creates a new PHY device adding it to the PHY class. * While at that, it also associates the device with the phy using devres. @@ -749,8 +709,7 @@ EXPORT_SYMBOL_GPL(phy_create); * then, devres data is freed. */ struct phy *devm_phy_create(struct device *dev, struct device_node *node, - const struct phy_ops *ops, - struct phy_init_data *init_data) + const struct phy_ops *ops) { struct phy **ptr, *phy; @@ -758,7 +717,7 @@ struct phy *devm_phy_create(struct device *dev, struct device_node *node, if (!ptr) return ERR_PTR(-ENOMEM); - phy = phy_create(dev, node, ops, init_data); + phy = phy_create(dev, node, ops); if (!IS_ERR(phy)) { *ptr = phy; devres_add(dev, ptr); diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c index 84f49e5a3f24..f86cbe68ddaf 100644 --- a/drivers/phy/phy-exynos-dp-video.c +++ b/drivers/phy/phy-exynos-dp-video.c @@ -112,7 +112,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev) match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node); state->drvdata = match->data; - phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL); + phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create Display Port PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index 6a9bef138617..943e0f88a120 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -137,7 +137,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) { struct phy *phy = devm_phy_create(dev, NULL, - &exynos_mipi_video_phy_ops, NULL); + &exynos_mipi_video_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY %d\n", i); return PTR_ERR(phy); diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c index f756aca871db..b3ca3bc2314f 100644 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ b/drivers/phy/phy-exynos5-usbdrd.c @@ -637,8 +637,7 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { struct phy *phy = devm_phy_create(dev, NULL, - &exynos5_usbdrd_phy_ops, - NULL); + &exynos5_usbdrd_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "Failed to create usbdrd_phy phy\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c index 54cf4ae60d29..bc858cc800a1 100644 --- a/drivers/phy/phy-exynos5250-sata.c +++ b/drivers/phy/phy-exynos5250-sata.c @@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev) return ret; } - sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops, NULL); + sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops); if (IS_ERR(sata_phy->phy)) { clk_disable_unprepare(sata_phy->phyclk); dev_err(dev, "failed to create PHY\n"); diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c index d5d978085c6d..a80ff9d7fe15 100644 --- a/drivers/phy/phy-hix5hd2-sata.c +++ b/drivers/phy/phy-hix5hd2-sata.c @@ -156,7 +156,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev) if (IS_ERR(priv->peri_ctrl)) priv->peri_ctrl = NULL; - phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL); + phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index 87dcc9ab7f23..e34da13885e8 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -1231,7 +1231,7 @@ static int miphy28lp_probe(struct platform_device *pdev) miphy_dev->phys[port] = miphy_phy; - phy = devm_phy_create(&pdev->dev, child, &miphy28lp_ops, NULL); + phy = devm_phy_create(&pdev->dev, child, &miphy28lp_ops); if (IS_ERR(phy)) { dev_err(&pdev->dev, "failed to create PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 801afaf2d449..239930edfe1d 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -593,7 +593,7 @@ static int miphy365x_probe(struct platform_device *pdev) miphy_dev->phys[port] = miphy_phy; - phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL); + phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops); if (IS_ERR(phy)) { dev_err(&pdev->dev, "failed to create PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-mvebu-sata.c b/drivers/phy/phy-mvebu-sata.c index d395558cb12e..03b94f92e6f1 100644 --- a/drivers/phy/phy-mvebu-sata.c +++ b/drivers/phy/phy-mvebu-sata.c @@ -101,7 +101,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev) if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); - phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL); + phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops); if (IS_ERR(phy)) return PTR_ERR(phy); diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c index f091576b6449..5dab3ec71a19 100644 --- a/drivers/phy/phy-omap-usb2.c +++ b/drivers/phy/phy-omap-usb2.c @@ -260,7 +260,7 @@ static int omap_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); pm_runtime_enable(phy->dev); - generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL); + generic_phy = devm_phy_create(phy->dev, NULL, &ops); if (IS_ERR(generic_phy)) { pm_runtime_disable(phy->dev); return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c index 7b3ddfb65898..4b243f7a10e4 100644 --- a/drivers/phy/phy-qcom-apq8064-sata.c +++ b/drivers/phy/phy-qcom-apq8064-sata.c @@ -228,8 +228,7 @@ static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) if (IS_ERR(phy->mmio)) return PTR_ERR(phy->mmio); - generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops, - NULL); + generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops); if (IS_ERR(generic_phy)) { dev_err(dev, "%s: failed to create phy\n", __func__); return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c index 759b0bf5b6b3..6f2fe2627916 100644 --- a/drivers/phy/phy-qcom-ipq806x-sata.c +++ b/drivers/phy/phy-qcom-ipq806x-sata.c @@ -150,8 +150,7 @@ static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) if (IS_ERR(phy->mmio)) return PTR_ERR(phy->mmio); - generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops, - NULL); + generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops); if (IS_ERR(generic_phy)) { dev_err(dev, "%s: failed to create phy\n", __func__); return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c index 2793af17799f..778276aba3aa 100644 --- a/drivers/phy/phy-rcar-gen2.c +++ b/drivers/phy/phy-rcar-gen2.c @@ -304,7 +304,7 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev) phy->select_value = select_value[channel_num][n]; phy->phy = devm_phy_create(dev, NULL, - &rcar_gen2_phy_ops, NULL); + &rcar_gen2_phy_ops); if (IS_ERR(phy->phy)) { dev_err(dev, "Failed to create PHY\n"); return PTR_ERR(phy->phy); diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c index 908949dfb4db..4a12f66b7fb5 100644 --- a/drivers/phy/phy-samsung-usb2.c +++ b/drivers/phy/phy-samsung-usb2.c @@ -202,8 +202,7 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev) struct samsung_usb2_phy_instance *p = &drv->instances[i]; dev_dbg(dev, "Creating phy \"%s\"\n", label); - p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops, - NULL); + p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops); if (IS_ERR(p->phy)) { dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n", label); diff --git a/drivers/phy/phy-spear1310-miphy.c b/drivers/phy/phy-spear1310-miphy.c index 5f4c586ee951..9f47fae7eecb 100644 --- a/drivers/phy/phy-spear1310-miphy.c +++ b/drivers/phy/phy-spear1310-miphy.c @@ -227,7 +227,7 @@ static int spear1310_miphy_probe(struct platform_device *pdev) return -EINVAL; } - priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops, NULL); + priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops); if (IS_ERR(priv->phy)) { dev_err(dev, "failed to create SATA PCIe PHY\n"); return PTR_ERR(priv->phy); diff --git a/drivers/phy/phy-spear1340-miphy.c b/drivers/phy/phy-spear1340-miphy.c index 1ecd0945bad3..e42bc200275f 100644 --- a/drivers/phy/phy-spear1340-miphy.c +++ b/drivers/phy/phy-spear1340-miphy.c @@ -259,7 +259,7 @@ static int spear1340_miphy_probe(struct platform_device *pdev) return PTR_ERR(priv->misc); } - priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops, NULL); + priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops); if (IS_ERR(priv->phy)) { dev_err(dev, "failed to create SATA PCIe PHY\n"); return PTR_ERR(priv->phy); diff --git a/drivers/phy/phy-stih407-usb.c b/drivers/phy/phy-stih407-usb.c index 42428d4181ea..74f0fab3cd8a 100644 --- a/drivers/phy/phy-stih407-usb.c +++ b/drivers/phy/phy-stih407-usb.c @@ -137,7 +137,7 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev) } phy_dev->param = res->start; - phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data, NULL); + phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); if (IS_ERR(phy)) { dev_err(dev, "failed to create Display Port PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-stih41x-usb.c b/drivers/phy/phy-stih41x-usb.c index 9f16cb8e01f4..4ab581eadacf 100644 --- a/drivers/phy/phy-stih41x-usb.c +++ b/drivers/phy/phy-stih41x-usb.c @@ -148,7 +148,7 @@ static int stih41x_usb_phy_probe(struct platform_device *pdev) return PTR_ERR(phy_dev->clk); } - phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops, NULL); + phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create phy\n"); diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index 6bd2b0c972cc..fb02a67c9181 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -298,7 +298,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) return PTR_ERR(phy->pmu); } - phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL); + phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops); if (IS_ERR(phy->phy)) { dev_err(dev, "failed to create PHY %d\n", i); return PTR_ERR(phy->phy); diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index ab1e22d9a1e8..c297b7a10d30 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -399,7 +399,7 @@ static int ti_pipe3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); pm_runtime_enable(phy->dev); - generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL); + generic_phy = devm_phy_create(phy->dev, NULL, &ops); if (IS_ERR(generic_phy)) return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index c45a3aa4f2cc..d19e4a06b858 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -678,7 +678,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) otg->set_host = twl4030_set_host; otg->set_peripheral = twl4030_set_peripheral; - phy = devm_phy_create(twl->dev, NULL, &ops, NULL); + phy = devm_phy_create(twl->dev, NULL, &ops); if (IS_ERR(phy)) { dev_dbg(&pdev->dev, "Failed to create PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index f8a51b16ade8..29214a36ea28 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev) ctx->dev = &pdev->dev; platform_set_drvdata(pdev, ctx); - ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL); + ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops); if (IS_ERR(ctx->phy)) { dev_dbg(&pdev->dev, "Failed to create PHY\n"); rc = PTR_ERR(ctx->phy); diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c index 1631ec94fb02..a84299b922c8 100644 --- a/drivers/pinctrl/pinctrl-tegra-xusb.c +++ b/drivers/pinctrl/pinctrl-tegra-xusb.c @@ -910,7 +910,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev) goto reset; } - phy = devm_phy_create(&pdev->dev, NULL, &pcie_phy_ops, NULL); + phy = devm_phy_create(&pdev->dev, NULL, &pcie_phy_ops); if (IS_ERR(phy)) { err = PTR_ERR(phy); goto unregister; @@ -919,7 +919,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev) padctl->phys[TEGRA_XUSB_PADCTL_PCIE] = phy; phy_set_drvdata(phy, padctl); - phy = devm_phy_create(&pdev->dev, NULL, &sata_phy_ops, NULL); + phy = devm_phy_create(&pdev->dev, NULL, &sata_phy_ops); if (IS_ERR(phy)) { err = PTR_ERR(phy); goto unregister; diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 849284e5873f..a0197fa1b116 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -61,7 +61,6 @@ struct phy { struct device dev; int id; const struct phy_ops *ops; - struct phy_init_data *init_data; struct mutex mutex; int init_count; int power_count; @@ -84,32 +83,6 @@ struct phy_provider { struct of_phandle_args *args); }; -/** - * struct phy_consumer - represents the phy consumer - * @dev_name: the device name of the controller that will use this PHY device - * @port: name given to the consumer port - */ -struct phy_consumer { - const char *dev_name; - const char *port; -}; - -/** - * struct phy_init_data - contains the list of PHY consumers - * @num_consumers: number of consumers for this PHY device - * @consumers: list of PHY consumers - */ -struct phy_init_data { - unsigned int num_consumers; - struct phy_consumer *consumers; -}; - -#define PHY_CONSUMER(_dev_name, _port) \ -{ \ - .dev_name = _dev_name, \ - .port = _port, \ -} - struct phy_lookup { struct list_head node; const char *dev_id; @@ -166,10 +139,9 @@ struct phy *of_phy_get(struct device_node *np, const char *con_id); struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args *args); struct phy *phy_create(struct device *dev, struct device_node *node, - const struct phy_ops *ops, - struct phy_init_data *init_data); + const struct phy_ops *ops); struct phy *devm_phy_create(struct device *dev, struct device_node *node, - const struct phy_ops *ops, struct phy_init_data *init_data); + const struct phy_ops *ops); void phy_destroy(struct phy *phy); void devm_phy_destroy(struct device *dev, struct phy *phy); struct phy_provider *__of_phy_provider_register(struct device *dev, @@ -310,16 +282,14 @@ static inline struct phy *of_phy_simple_xlate(struct device *dev, static inline struct phy *phy_create(struct device *dev, struct device_node *node, - const struct phy_ops *ops, - struct phy_init_data *init_data) + const struct phy_ops *ops) { return ERR_PTR(-ENOSYS); } static inline struct phy *devm_phy_create(struct device *dev, struct device_node *node, - const struct phy_ops *ops, - struct phy_init_data *init_data) + const struct phy_ops *ops) { return ERR_PTR(-ENOSYS); } From 08f871a3aca252b15107fc37dedcdacbac80fdb5 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 19 Nov 2014 17:28:23 +0200 Subject: [PATCH 18/25] usb: dwc3: host: convey the PHYs to xhci On some platforms a PHY may need to be handled also in the host controller driver. Exynos5420 SoC requires some "PHY tuning" based on the USB speed. This patch delivers dwc3's PHYs to the xhci platform device when it's created. Signed-off-by: Heikki Krogerus Tested-by: Vivek Gautam Acked-by: Felipe Balbi Signed-off-by: Kishon Vijay Abraham I --- drivers/usb/dwc3/host.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index dcb8ca084598..12bfd3c5405e 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -29,8 +29,7 @@ int dwc3_host_init(struct dwc3 *dwc) xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); if (!xhci) { dev_err(dwc->dev, "couldn't allocate xHCI device\n"); - ret = -ENOMEM; - goto err0; + return -ENOMEM; } dma_set_coherent_mask(&xhci->dev, dwc->dev->coherent_dma_mask); @@ -60,22 +59,33 @@ int dwc3_host_init(struct dwc3 *dwc) goto err1; } + phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy", + dev_name(&xhci->dev)); + phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy", + dev_name(&xhci->dev)); + ret = platform_device_add(xhci); if (ret) { dev_err(dwc->dev, "failed to register xHCI device\n"); - goto err1; + goto err2; } return 0; - +err2: + phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy", + dev_name(&xhci->dev)); + phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy", + dev_name(&xhci->dev)); err1: platform_device_put(xhci); - -err0: return ret; } void dwc3_host_exit(struct dwc3 *dwc) { + phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy", + dev_name(&dwc->xhci->dev)); + phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy", + dev_name(&dwc->xhci->dev)); platform_device_unregister(dwc->xhci); } From 9bde18c1b5d2c9a1b90fc0f3bbe1a314194f6fdf Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Fri, 21 Nov 2014 19:05:48 +0530 Subject: [PATCH 19/25] phy: exynos5-usbdrd: Add pipe-clk, utmi-clk and itp-clk support Exynos7 SoC has now separate gate control for 125MHz pipe3 phy clock, as well as 60MHz utmi phy clock. Additionally, separate gate control is available for the clock used for ITP (Isochronous Transfer Packet) generation. So get the same and control in the phy-exynos5-usbdrd driver. Suggested-by: Anton Tikhomirov Signed-off-by: Vivek Gautam Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/samsung-phy.txt | 6 + drivers/phy/phy-exynos5-usbdrd.c | 104 +++++++++++++++--- 2 files changed, 92 insertions(+), 18 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt index 15e0f2c7130f..d5bad920827f 100644 --- a/Documentation/devicetree/bindings/phy/samsung-phy.txt +++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt @@ -128,6 +128,7 @@ Required properties: - compatible : Should be set to one of the following supported values: - "samsung,exynos5250-usbdrd-phy" - for exynos5250 SoC, - "samsung,exynos5420-usbdrd-phy" - for exynos5420 SoC. + - "samsung,exynos7-usbdrd-phy" - for exynos7 SoC. - reg : Register offset and length of USB DRD PHY register set; - clocks: Clock IDs array as required by the controller - clock-names: names of clocks correseponding to IDs in the clock property; @@ -138,6 +139,11 @@ Required properties: PHY operations, associated by phy name. It is used to determine bit values for clock settings register. For Exynos5420 this is given as 'sclk_usbphy30' in CMU. + - optional clocks: Exynos7 SoC has now following additional + gate clocks available: + - phy_pipe: for PIPE3 phy + - phy_utmi: for UTMI+ phy + - itp: for ITP generation - samsung,pmu-syscon: phandle for PMU system controller interface, used to control pmu registers for power isolation. - #phy-cells : from the generic PHY bindings, must be 1; diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c index b3ca3bc2314f..99ba56dc63dd 100644 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ b/drivers/phy/phy-exynos5-usbdrd.c @@ -141,6 +141,7 @@ struct exynos5_usbdrd_phy_drvdata { const struct exynos5_usbdrd_phy_config *phy_cfg; u32 pmu_offset_usbdrd0_phy; u32 pmu_offset_usbdrd1_phy; + bool has_common_clk_gate; }; /** @@ -148,6 +149,9 @@ struct exynos5_usbdrd_phy_drvdata { * @dev: pointer to device instance of this platform device * @reg_phy: usb phy controller register memory base * @clk: phy clock for register access + * @pipeclk: clock for pipe3 phy + * @utmiclk: clock for utmi+ phy + * @itpclk: clock for ITP generation * @drv_data: pointer to SoC level driver data structure * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY * instances each with its 'phy' and 'phy_cfg'. @@ -155,12 +159,14 @@ struct exynos5_usbdrd_phy_drvdata { * reference clocks' for SS and HS operations * @ref_clk: reference clock to PHY block from which PHY's * operational clocks are derived - * @ref_rate: rate of above reference clock */ struct exynos5_usbdrd_phy { struct device *dev; void __iomem *reg_phy; struct clk *clk; + struct clk *pipeclk; + struct clk *utmiclk; + struct clk *itpclk; const struct exynos5_usbdrd_phy_drvdata *drv_data; struct phy_usb_instance { struct phy *phy; @@ -447,6 +453,11 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy) dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); clk_prepare_enable(phy_drd->ref_clk); + if (!phy_drd->drv_data->has_common_clk_gate) { + clk_prepare_enable(phy_drd->pipeclk); + clk_prepare_enable(phy_drd->utmiclk); + clk_prepare_enable(phy_drd->itpclk); + } /* Enable VBUS supply */ if (phy_drd->vbus) { @@ -464,6 +475,11 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy) fail_vbus: clk_disable_unprepare(phy_drd->ref_clk); + if (!phy_drd->drv_data->has_common_clk_gate) { + clk_disable_unprepare(phy_drd->itpclk); + clk_disable_unprepare(phy_drd->utmiclk); + clk_disable_unprepare(phy_drd->pipeclk); + } return ret; } @@ -483,6 +499,11 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy) regulator_disable(phy_drd->vbus); clk_disable_unprepare(phy_drd->ref_clk); + if (!phy_drd->drv_data->has_common_clk_gate) { + clk_disable_unprepare(phy_drd->itpclk); + clk_disable_unprepare(phy_drd->pipeclk); + clk_disable_unprepare(phy_drd->utmiclk); + } return 0; } @@ -506,6 +527,57 @@ static struct phy_ops exynos5_usbdrd_phy_ops = { .owner = THIS_MODULE, }; +static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd) +{ + unsigned long ref_rate; + int ret; + + phy_drd->clk = devm_clk_get(phy_drd->dev, "phy"); + if (IS_ERR(phy_drd->clk)) { + dev_err(phy_drd->dev, "Failed to get phy clock\n"); + return PTR_ERR(phy_drd->clk); + } + + phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref"); + if (IS_ERR(phy_drd->ref_clk)) { + dev_err(phy_drd->dev, "Failed to get phy reference clock\n"); + return PTR_ERR(phy_drd->ref_clk); + } + ref_rate = clk_get_rate(phy_drd->ref_clk); + + ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); + if (ret) { + dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", + ref_rate); + return ret; + } + + if (!phy_drd->drv_data->has_common_clk_gate) { + phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe"); + if (IS_ERR(phy_drd->pipeclk)) { + dev_info(phy_drd->dev, + "PIPE3 phy operational clock not specified\n"); + phy_drd->pipeclk = NULL; + } + + phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi"); + if (IS_ERR(phy_drd->utmiclk)) { + dev_info(phy_drd->dev, + "UTMI phy operational clock not specified\n"); + phy_drd->utmiclk = NULL; + } + + phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp"); + if (IS_ERR(phy_drd->itpclk)) { + dev_info(phy_drd->dev, + "ITP clock from main OSC not specified\n"); + phy_drd->itpclk = NULL; + } + } + + return 0; +} + static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { { .id = EXYNOS5_DRDPHY_UTMI, @@ -525,11 +597,19 @@ static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, + .has_common_clk_gate = true, }; static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, + .has_common_clk_gate = true, +}; + +static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = { + .phy_cfg = phy_cfg_exynos5, + .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, + .has_common_clk_gate = false, }; static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { @@ -539,6 +619,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { }, { .compatible = "samsung,exynos5420-usbdrd-phy", .data = &exynos5420_usbdrd_phy + }, { + .compatible = "samsung,exynos7-usbdrd-phy", + .data = &exynos7_usbdrd_phy }, { }, }; @@ -555,7 +638,6 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) const struct exynos5_usbdrd_phy_drvdata *drv_data; struct regmap *reg_pmu; u32 pmu_offset; - unsigned long ref_rate; int i, ret; int channel; @@ -576,23 +658,9 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) drv_data = match->data; phy_drd->drv_data = drv_data; - phy_drd->clk = devm_clk_get(dev, "phy"); - if (IS_ERR(phy_drd->clk)) { - dev_err(dev, "Failed to get clock of phy controller\n"); - return PTR_ERR(phy_drd->clk); - } - - phy_drd->ref_clk = devm_clk_get(dev, "ref"); - if (IS_ERR(phy_drd->ref_clk)) { - dev_err(dev, "Failed to get reference clock of usbdrd phy\n"); - return PTR_ERR(phy_drd->ref_clk); - } - ref_rate = clk_get_rate(phy_drd->ref_clk); - - ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); + ret = exynos5_usbdrd_phy_clk_handle(phy_drd); if (ret) { - dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", - ref_rate); + dev_err(dev, "Failed to initialize clocks\n"); return ret; } From a6867836db35d1e178744166765b57288bf4e8e5 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Fri, 21 Nov 2014 19:05:49 +0530 Subject: [PATCH 20/25] phy: exynos5-usbdrd: Add facility for VBUS-BOOST-5V supply Some Exynos boards have a separate regulator controlling a Boost 5V supply which goes as input for VBUS regulator. So adding a control for the same in driver, to enable vbus supply on the port. Signed-off-by: Vivek Gautam Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-exynos5-usbdrd.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c index 99ba56dc63dd..04374018425f 100644 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ b/drivers/phy/phy-exynos5-usbdrd.c @@ -159,6 +159,8 @@ struct exynos5_usbdrd_phy_drvdata { * reference clocks' for SS and HS operations * @ref_clk: reference clock to PHY block from which PHY's * operational clocks are derived + * vbus: VBUS regulator for phy + * vbus_boost: Boost regulator for VBUS present on few Exynos boards */ struct exynos5_usbdrd_phy { struct device *dev; @@ -178,6 +180,7 @@ struct exynos5_usbdrd_phy { u32 extrefclk; struct clk *ref_clk; struct regulator *vbus; + struct regulator *vbus_boost; }; static inline @@ -460,11 +463,20 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy) } /* Enable VBUS supply */ + if (phy_drd->vbus_boost) { + ret = regulator_enable(phy_drd->vbus_boost); + if (ret) { + dev_err(phy_drd->dev, + "Failed to enable VBUS boost supply\n"); + goto fail_vbus; + } + } + if (phy_drd->vbus) { ret = regulator_enable(phy_drd->vbus); if (ret) { dev_err(phy_drd->dev, "Failed to enable VBUS supply\n"); - goto fail_vbus; + goto fail_vbus_boost; } } @@ -473,6 +485,10 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy) return 0; +fail_vbus_boost: + if (phy_drd->vbus_boost) + regulator_disable(phy_drd->vbus_boost); + fail_vbus: clk_disable_unprepare(phy_drd->ref_clk); if (!phy_drd->drv_data->has_common_clk_gate) { @@ -497,6 +513,8 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy) /* Disable VBUS supply */ if (phy_drd->vbus) regulator_disable(phy_drd->vbus); + if (phy_drd->vbus_boost) + regulator_disable(phy_drd->vbus_boost); clk_disable_unprepare(phy_drd->ref_clk); if (!phy_drd->drv_data->has_common_clk_gate) { @@ -690,7 +708,7 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) break; } - /* Get Vbus regulator */ + /* Get Vbus regulators */ phy_drd->vbus = devm_regulator_get(dev, "vbus"); if (IS_ERR(phy_drd->vbus)) { ret = PTR_ERR(phy_drd->vbus); @@ -701,6 +719,16 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) phy_drd->vbus = NULL; } + phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost"); + if (IS_ERR(phy_drd->vbus_boost)) { + ret = PTR_ERR(phy_drd->vbus_boost); + if (ret == -EPROBE_DEFER) + return ret; + + dev_warn(dev, "Failed to get VBUS boost supply regulator\n"); + phy_drd->vbus_boost = NULL; + } + dev_vdbg(dev, "Creating usbdrd_phy phy\n"); for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { From 556186a02c441e8fb03aa4c87d39bdc8ef95820e Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Fri, 21 Nov 2014 19:05:50 +0530 Subject: [PATCH 21/25] phy: exynos7-usbdrd: Update dependency for ARCH_EXYNOS This PHY controller is also present on Exynos7 platform in arch-exynos family. So PHY_EXYNOS5_USBDRD should now depend on ARCH_EXYNOS. Signed-off-by: Vivek Gautam Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 4144d250524f..96d43d5acc57 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -208,7 +208,7 @@ config PHY_EXYNOS5250_USB2 config PHY_EXYNOS5_USBDRD tristate "Exynos5 SoC series USB DRD PHY driver" - depends on ARCH_EXYNOS5 && OF + depends on ARCH_EXYNOS && OF depends on HAS_IOMEM depends on USB_DWC3_EXYNOS select GENERIC_PHY From c1fc0050102150481ad4ba7e6449b3aa6e88d6e5 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 13 Nov 2014 12:47:43 +0100 Subject: [PATCH 22/25] phy: Use PTR_ERR_OR_ZERO to fix warning raised by coccinelle Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR Generated by: coccinelle/api/ptr_ret.cocci Signed-off-by: Gregory CLEMENT Acked-by: Jason Cooper Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-berlin-sata.c | 5 +---- drivers/phy/phy-hix5hd2-sata.c | 5 +---- drivers/phy/phy-miphy365x.c | 5 +---- drivers/phy/phy-stih41x-usb.c | 5 +---- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c index 3e599dc96164..099eee8851e5 100644 --- a/drivers/phy/phy-berlin-sata.c +++ b/drivers/phy/phy-berlin-sata.c @@ -268,10 +268,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev) phy_provider = devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id phy_berlin_sata_of_match[] = { diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c index a80ff9d7fe15..34915b4202f1 100644 --- a/drivers/phy/phy-hix5hd2-sata.c +++ b/drivers/phy/phy-hix5hd2-sata.c @@ -164,10 +164,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev) phy_set_drvdata(phy, priv); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id hix5hd2_sata_phy_of_match[] = { diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 239930edfe1d..6ab43a814ad2 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -610,10 +610,7 @@ static int miphy365x_probe(struct platform_device *pdev) } provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate); - if (IS_ERR(provider)) - return PTR_ERR(provider); - - return 0; + return PTR_ERR_OR_ZERO(provider); } static const struct of_device_id miphy365x_of_match[] = { diff --git a/drivers/phy/phy-stih41x-usb.c b/drivers/phy/phy-stih41x-usb.c index 4ab581eadacf..a603801293ff 100644 --- a/drivers/phy/phy-stih41x-usb.c +++ b/drivers/phy/phy-stih41x-usb.c @@ -160,10 +160,7 @@ static int stih41x_usb_phy_probe(struct platform_device *pdev) phy_set_drvdata(phy, phy_dev); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id stih41x_usb_phy_of_match[] = { From aa1facbd2847263cf168ec7d66e6c8951c413905 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 13 Nov 2014 12:47:44 +0100 Subject: [PATCH 23/25] Phy: DT binding documentation for Marvell MVEBU SATA phy. Describe the binding for the Marvell MVEBU SATA phy. This driver can be used at least with Kirkwood, Dove and maybe others. Additionally, update the SATA binding with the properties to link to the phy nodes. Signed-off-by: Andrew Lunn Acked-by: Jason Cooper Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/ata/marvell.txt | 6 +++++ .../devicetree/bindings/phy/phy-mvebu.txt | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/phy-mvebu.txt diff --git a/Documentation/devicetree/bindings/ata/marvell.txt b/Documentation/devicetree/bindings/ata/marvell.txt index 1c8351604d38..b460edd12766 100644 --- a/Documentation/devicetree/bindings/ata/marvell.txt +++ b/Documentation/devicetree/bindings/ata/marvell.txt @@ -6,11 +6,17 @@ Required Properties: - interrupts : Interrupt controller is using - nr-ports : Number of SATA ports in use. +Optional Properties: +- phys : List of phandles to sata phys +- phy-names : Should be "0", "1", etc, one number per phandle + Example: sata@80000 { compatible = "marvell,orion-sata"; reg = <0x80000 0x5000>; interrupts = <21>; + phys = <&sata_phy0>, <&sata_phy1>; + phy-names = "0", "1"; nr-ports = <2>; } diff --git a/Documentation/devicetree/bindings/phy/phy-mvebu.txt b/Documentation/devicetree/bindings/phy/phy-mvebu.txt new file mode 100644 index 000000000000..6cb3364aeafb --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-mvebu.txt @@ -0,0 +1,22 @@ +* Marvell MVEBU SATA PHY + +Power control for the SATA phy found on Marvell MVEBU SoCs. + +This document extends the binding described in phy-bindings.txt + +Required properties : + + - reg : Offset and length of the register set for the SATA device + - compatible : Should be "marvell,mvebu-sata-phy" + - clocks : phandle of clock and specifier that supplies the device + - clock-names : Should be "sata" + +Example: + sata-phy@84000 { + compatible = "marvell,mvebu-sata-phy"; + reg = <0x84000 0x0334>; + clocks = <&gate_clk 15>; + clock-names = "sata"; + #phy-cells = <0>; + status = "ok"; + }; From 1a6ab1c0e844041524f8edd1acf0de94df991aaa Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 13 Nov 2014 12:47:45 +0100 Subject: [PATCH 24/25] Phy: DT binding documentation for the Armada 375 USB cluster binding Armada 375 comes with an USB2 host and device controller and an USB3 controller. The USB cluster control register allows to manage common features of both USB controllers. This commit adds the Device Tree binding documentation for this piece of hardware. Signed-off-by: Gregory CLEMENT Acked-by: Jason Cooper Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/phy-mvebu.txt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/phy-mvebu.txt b/Documentation/devicetree/bindings/phy/phy-mvebu.txt index 6cb3364aeafb..f95b6260a3b3 100644 --- a/Documentation/devicetree/bindings/phy/phy-mvebu.txt +++ b/Documentation/devicetree/bindings/phy/phy-mvebu.txt @@ -20,3 +20,24 @@ Example: #phy-cells = <0>; status = "ok"; }; + +Armada 375 USB cluster +---------------------- + +Armada 375 comes with an USB2 host and device controller and an USB3 +controller. The USB cluster control register allows to manage common +features of both USB controllers. + +Required properties: + +- compatible: "marvell,armada-375-usb-cluster" +- reg: Should contain usb cluster register location and length. +- #phy-cells : from the generic phy bindings, must be 1. Possible +values are 1 (USB2), 2 (USB3). + +Example: + usbcluster: usb-cluster@18400 { + compatible = "marvell,armada-375-usb-cluster"; + reg = <0x18400 0x4>; + #phy-cells = <1> + }; From eee47538ec1f26198cf5da675975b61d7f16135b Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 13 Nov 2014 12:47:46 +0100 Subject: [PATCH 25/25] phy: add support for USB cluster on the Armada 375 SoC The Armada 375 SoC comes with an USB2 host and device controller and an USB3 controller. The USB cluster control register allows to manage common features of both USB controllers. This commit adds a driver integrated in the generic PHY framework to control this USB cluster feature. Signed-off-by: Gregory CLEMENT Signed-off-by: Thomas Petazzoni [ kishon@ti.com : Made it to use the updated devm_phy_create API and soem cosmentic changes in Kconfig file.] Signed-off-by: Kishon Vijay Abraham I Acked-by: Jason Cooper --- drivers/phy/Kconfig | 6 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-armada375-usb2.c | 158 +++++++++++++++++++++++++++++++ include/dt-bindings/phy/phy.h | 1 + 4 files changed, 166 insertions(+) create mode 100644 drivers/phy/phy-armada375-usb2.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 96d43d5acc57..ccad8809ecb1 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -29,6 +29,12 @@ config PHY_BERLIN_SATA help Enable this to support the SATA PHY on Marvell Berlin SoCs. +config ARMADA375_USBCLUSTER_PHY + def_bool y + depends on MACH_ARMADA_375 || COMPILE_TEST + depends on OF + select GENERIC_PHY + config PHY_EXYNOS_MIPI_VIDEO tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" depends on HAS_IOMEM diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 68022f698f51..aa74f961e44e 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o +obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o diff --git a/drivers/phy/phy-armada375-usb2.c b/drivers/phy/phy-armada375-usb2.c new file mode 100644 index 000000000000..ac7d99d01cb3 --- /dev/null +++ b/drivers/phy/phy-armada375-usb2.c @@ -0,0 +1,158 @@ +/* + * USB cluster support for Armada 375 platform. + * + * Copyright (C) 2014 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2 or later. This program is licensed "as is" + * without any warranty of any kind, whether express or implied. + * + * Armada 375 comes with an USB2 host and device controller and an + * USB3 controller. The USB cluster control register allows to manage + * common features of both USB controllers. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB2_PHY_CONFIG_DISABLE BIT(0) + +struct armada375_cluster_phy { + struct phy *phy; + void __iomem *reg; + bool use_usb3; + int phy_provided; +}; + +static int armada375_usb_phy_init(struct phy *phy) +{ + struct armada375_cluster_phy *cluster_phy; + u32 reg; + + cluster_phy = dev_get_drvdata(phy->dev.parent); + if (!cluster_phy) + return -ENODEV; + + reg = readl(cluster_phy->reg); + if (cluster_phy->use_usb3) + reg |= USB2_PHY_CONFIG_DISABLE; + else + reg &= ~USB2_PHY_CONFIG_DISABLE; + writel(reg, cluster_phy->reg); + + return 0; +} + +static struct phy_ops armada375_usb_phy_ops = { + .init = armada375_usb_phy_init, + .owner = THIS_MODULE, +}; + +/* + * Only one controller can use this PHY. We shouldn't have the case + * when two controllers want to use this PHY. But if this case occurs + * then we provide a phy to the first one and return an error for the + * next one. This error has also to be an error returned by + * devm_phy_optional_get() so different from ENODEV for USB2. In the + * USB3 case it still optional and we use ENODEV. + */ +static struct phy *armada375_usb_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev); + + if (!cluster_phy) + return ERR_PTR(-ENODEV); + + /* + * Either the phy had never been requested and then the first + * usb claiming it can get it, or it had already been + * requested in this case, we only allow to use it with the + * same configuration. + */ + if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) && + (cluster_phy->phy_provided != args->args[0]))) { + dev_err(dev, "This PHY has already been provided!\n"); + dev_err(dev, "Check your device tree, only one controller can use it\n."); + if (args->args[0] == PHY_TYPE_USB2) + return ERR_PTR(-EBUSY); + else + return ERR_PTR(-ENODEV); + } + + if (args->args[0] == PHY_TYPE_USB2) + cluster_phy->use_usb3 = false; + else if (args->args[0] == PHY_TYPE_USB3) + cluster_phy->use_usb3 = true; + else { + dev_err(dev, "Invalid PHY mode\n"); + return ERR_PTR(-ENODEV); + } + + /* Store which phy mode is used for next test */ + cluster_phy->phy_provided = args->args[0]; + + return cluster_phy->phy; +} + +static int armada375_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *phy; + struct phy_provider *phy_provider; + void __iomem *usb_cluster_base; + struct resource *res; + struct armada375_cluster_phy *cluster_phy; + + cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL); + if (!cluster_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + usb_cluster_base = devm_ioremap_resource(&pdev->dev, res); + if (!usb_cluster_base) + return -ENOMEM; + + phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + cluster_phy->phy = phy; + cluster_phy->reg = usb_cluster_base; + + dev_set_drvdata(dev, cluster_phy); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, + armada375_usb_phy_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id of_usb_cluster_table[] = { + { .compatible = "marvell,armada-375-usb-cluster", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, of_usb_cluster_table); + +static struct platform_driver armada375_usb_phy_driver = { + .probe = armada375_usb_phy_probe, + .driver = { + .of_match_table = of_usb_cluster_table, + .name = "armada-375-usb-cluster", + .owner = THIS_MODULE, + } +}; +module_platform_driver(armada375_usb_phy_driver); + +MODULE_DESCRIPTION("Armada 375 USB cluster driver"); +MODULE_AUTHOR("Gregory CLEMENT "); +MODULE_LICENSE("GPL"); diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h index e8c6a3f04b85..6c901930eb3e 100644 --- a/include/dt-bindings/phy/phy.h +++ b/include/dt-bindings/phy/phy.h @@ -10,6 +10,7 @@ #ifndef _DT_BINDINGS_PHY #define _DT_BINDINGS_PHY +#define PHY_NONE 0 #define PHY_TYPE_SATA 1 #define PHY_TYPE_PCIE 2 #define PHY_TYPE_USB2 3