phy: for 4.21
*) Change phy set_mode ops to take both mode and setmode as arguments *) Add phy_configure() and phy_validate() API's mostly used for MIPI D-PHY *) Add helpers to get default values of parameters define in MIPI D-PHY spec *) Add driver for TI's CPSW Port PHY Interface Mode selection *) Add driver for Cadence Sierra PHY used with USB and PCIe *) Add driver for Freescale i.MX8MQ USB3 PHY *) Fixes QMP PHY bindings to allow the clocks provided by the PHY to be pointed at in device tree *) Fix for using fully specified regions (in device tree) for configuring the second lane in dual lane PHYs in QMP PHY *) Add support for Allwinner H6 USB2 PHY in phy-sun4i-usb driver *) Update phy-rcar-gen3-usb driver to follow the hardware manual *) Add support for fine grained power management in mapphone-mdm6600 driver Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> -----BEGIN PGP SIGNATURE----- iQJCBAABCgAsFiEEUXMr/TfP2p4suIY5Dlx4XIBNgtkFAlwQj94OHGtpc2hvbkB0 aS5jb20ACgkQDlx4XIBNgtmYrA/9HK7yfOWfNLJ7DB8bi4yPx5hd657sLohSdDEX 0Pxx9PRuxjLYlaHQh8XbHEdvnLIbbGgnHbfvkkHjxJx272G+QAWMi7/SwF3a2ZpY ev7U1gvWvCTqS/3RsEmxmwiX9esszcOgya2ia5TcyvTIHLla0QXTOUCmOQBejWhJ Wfe3siSzxn6O4BITsoRUu+0Ek3bScW/cFdby5B62zBrs912p1qlGx20ihvjywB6I uR5q1w5DxCbC9KZLTkQgfPXki69r1EaCYzdcEKnUON59OXceIy/TKDx/ewZBOjtR /d+lWybsLMF5MXIy2UKlaKxLrk4AVdsQ6zmfU23uTqCT3rWn7OPEvbzSj3nBXpgi SNzBTt9wHT4ZAGOq4fmhgF1Gn1gx4tEcarvAfeyyzv1riVgGbwlxSaUpWWvkya/E zDBqROi/9VU+WX+De+E2KTCAOjJpcnVpLx8euA7uSD2m0nF4Kp4YiyAJiH/ZDQba ghhR4DiD1xhjO77U+tM7mD+68ZyKsQubqzO+h4rztjZ/QRCWqGtOCg9sSQBxSz86 llEklLB3156dkLnHRMd0NIuSqYGOkHLWdTBrXNr2HaK7ZWvN6jxhC2puziIlmIZl 8m94QyY5pmuTJdQsWA/MEoWFglQqLBDxAVuoMH4d/XFx7PBNxiaTGOOEgX7MhfVM 9di9hak= =JLIJ -----END PGP SIGNATURE----- Merge tag 'phy-for-4.21_v1' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next Kishon writes: phy: for 4.21 *) Change phy set_mode ops to take both mode and setmode as arguments *) Add phy_configure() and phy_validate() API's mostly used for MIPI D-PHY *) Add helpers to get default values of parameters define in MIPI D-PHY spec *) Add driver for TI's CPSW Port PHY Interface Mode selection *) Add driver for Cadence Sierra PHY used with USB and PCIe *) Add driver for Freescale i.MX8MQ USB3 PHY *) Fixes QMP PHY bindings to allow the clocks provided by the PHY to be pointed at in device tree *) Fix for using fully specified regions (in device tree) for configuring the second lane in dual lane PHYs in QMP PHY *) Add support for Allwinner H6 USB2 PHY in phy-sun4i-usb driver *) Update phy-rcar-gen3-usb driver to follow the hardware manual *) Add support for fine grained power management in mapphone-mdm6600 driver Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> * tag 'phy-for-4.21_v1' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy: (30 commits) phy: qcom-qmp: Expose provided clocks to DT dt-bindings: phy-qcom-qmp: Move #clock-cells to child phy: qcom-qmp: Utilize fully-specified DT registers dt-bindings: phy-qcom-qmp: Fix register underspecification phy: ti: fix semicolon.cocci warnings phy: dphy: Add configuration helpers phy: Add MIPI D-PHY configuration options phy: Add configuration interface phy: Add MIPI D-PHY mode phy: add driver for Freescale i.MX8MQ USB3 PHY dt-bindings: phy: add binding for Freescale i.MX8MQ USB3 PHY phy: Use of_node_name_eq for node name comparisons net: ethernet: ti: cpsw: add support for port interface mode selection phy dt-bindings: net: ti: cpsw: switch to use phy-gmii-sel phy phy: ti: introduce phy-gmii-sel driver dt-bindings: phy: add cpsw port interface mode selection phy bindings phy: mvebu-cp110-comphy: fix spelling in structure name phy: mapphone-mdm6600: Improve phy related runtime PM calls phy: renesas: rcar-gen3-usb2: follow the hardware manual procedure phy: cadence: Add driver for Sierra PHY ...
This commit is contained in:
commit
ed0a773bff
|
@ -22,7 +22,8 @@ Required properties:
|
|||
- cpsw-phy-sel : Specifies the phandle to the CPSW phy mode selection
|
||||
device. See also cpsw-phy-sel.txt for it's binding.
|
||||
Note that in legacy cases cpsw-phy-sel may be
|
||||
a child device instead of a phandle.
|
||||
a child device instead of a phandle
|
||||
(DEPRECATED, use phys property instead).
|
||||
|
||||
Optional properties:
|
||||
- ti,hwmods : Must be "cpgmac0"
|
||||
|
@ -44,6 +45,7 @@ Optional properties:
|
|||
Slave Properties:
|
||||
Required properties:
|
||||
- phy-mode : See ethernet.txt file in the same directory
|
||||
- phys : phandle on phy-gmii-sel PHY (see phy/ti-phy-gmii-sel.txt)
|
||||
|
||||
Optional properties:
|
||||
- dual_emac_res_vlan : Specifies VID to be used to segregate the ports
|
||||
|
@ -85,12 +87,14 @@ Examples:
|
|||
phy-mode = "rgmii-txid";
|
||||
/* Filled in by U-Boot */
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 1 0>;
|
||||
};
|
||||
cpsw_emac1: slave@1 {
|
||||
phy_id = <&davinci_mdio>, <1>;
|
||||
phy-mode = "rgmii-txid";
|
||||
/* Filled in by U-Boot */
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 2 0>;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -114,11 +118,13 @@ Examples:
|
|||
phy-mode = "rgmii-txid";
|
||||
/* Filled in by U-Boot */
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 1 0>;
|
||||
};
|
||||
cpsw_emac1: slave@1 {
|
||||
phy_id = <&davinci_mdio>, <1>;
|
||||
phy-mode = "rgmii-txid";
|
||||
/* Filled in by U-Boot */
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 2 0>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
* Freescale i.MX8MQ USB3 PHY binding
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx8mq-usb-phy"
|
||||
- #phys-cells: must be 0 (see phy-bindings.txt in this directory)
|
||||
- reg: The base address and length of the registers
|
||||
- clocks: phandles to the clocks for each clock listed in clock-names
|
||||
- clock-names: must contain "phy"
|
||||
|
||||
Example:
|
||||
usb3_phy0: phy@381f0040 {
|
||||
compatible = "fsl,imx8mq-usb-phy";
|
||||
reg = <0x381f0040 0x40>;
|
||||
clocks = <&clk IMX8MQ_CLK_USB1_PHY_ROOT>;
|
||||
clock-names = "phy";
|
||||
#phy-cells = <0>;
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
Cadence Sierra PHY
|
||||
-----------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: cdns,sierra-phy-t0
|
||||
- clocks: Must contain an entry in clock-names.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names: Must be "phy_clk"
|
||||
- resets: Must contain an entry for each in reset-names.
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names: Must include "sierra_reset" and "sierra_apb".
|
||||
"sierra_reset" must control the reset line to the PHY.
|
||||
"sierra_apb" must control the reset line to the APB PHY
|
||||
interface.
|
||||
- reg: register range for the PHY.
|
||||
- #address-cells: Must be 1
|
||||
- #size-cells: Must be 0
|
||||
|
||||
Optional properties:
|
||||
- cdns,autoconf: A boolean property whose presence indicates that the
|
||||
PHY registers will be configured by hardware. If not
|
||||
present, all sub-node optional properties must be
|
||||
provided.
|
||||
|
||||
Sub-nodes:
|
||||
Each group of PHY lanes with a single master lane should be represented as
|
||||
a sub-node. Note that the actual configuration of each lane is determined by
|
||||
hardware strapping, and must match the configuration specified here.
|
||||
|
||||
Sub-node required properties:
|
||||
- #phy-cells: Generic PHY binding; must be 0.
|
||||
- reg: The master lane number. This is the lowest numbered lane
|
||||
in the lane group.
|
||||
- resets: Must contain one entry which controls the reset line for the
|
||||
master lane of the sub-node.
|
||||
See ../reset/reset.txt for details.
|
||||
|
||||
Sub-node optional properties:
|
||||
- cdns,num-lanes: Number of lanes in this group. From 1 to 4. The
|
||||
group is made up of consecutive lanes.
|
||||
- cdns,phy-type: Can be PHY_TYPE_PCIE or PHY_TYPE_USB3, depending on
|
||||
configuration of lanes.
|
||||
|
||||
Example:
|
||||
pcie_phy4: pcie-phy@fd240000 {
|
||||
compatible = "cdns,sierra-phy-t0";
|
||||
reg = <0x0 0xfd240000 0x0 0x40000>;
|
||||
resets = <&phyrst 0>, <&phyrst 1>;
|
||||
reset-names = "sierra_reset", "sierra_apb";
|
||||
clocks = <&phyclock>;
|
||||
clock-names = "phy_clk";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pcie0_phy0: pcie-phy@0 {
|
||||
reg = <0>;
|
||||
resets = <&phyrst 2>;
|
||||
cdns,num-lanes = <2>;
|
||||
#phy-cells = <0>;
|
||||
cdns,phy-type = <PHY_TYPE_PCIE>;
|
||||
};
|
||||
pcie0_phy1: pcie-phy@2 {
|
||||
reg = <2>;
|
||||
resets = <&phyrst 4>;
|
||||
cdns,num-lanes = <1>;
|
||||
#phy-cells = <0>;
|
||||
cdns,phy-type = <PHY_TYPE_PCIE>;
|
||||
};
|
|
@ -25,10 +25,6 @@ Required properties:
|
|||
- For all others:
|
||||
- The reg-names property shouldn't be defined.
|
||||
|
||||
- #clock-cells: must be 1
|
||||
- Phy pll outputs a bunch of clocks for Tx, Rx and Pipe
|
||||
interface (for pipe based PHYs). These clock are then gate-controlled
|
||||
by gcc.
|
||||
- #address-cells: must be 1
|
||||
- #size-cells: must be 1
|
||||
- ranges: must be present
|
||||
|
@ -82,27 +78,33 @@ Required nodes:
|
|||
- Each device node of QMP phy is required to have as many child nodes as
|
||||
the number of lanes the PHY has.
|
||||
|
||||
Required properties for child node:
|
||||
Required properties for child nodes of PCIe PHYs (one child per lane):
|
||||
- reg: list of offset and length pairs of register sets for PHY blocks -
|
||||
- index 0: tx
|
||||
- index 1: rx
|
||||
- index 2: pcs
|
||||
- index 3: pcs_misc (optional)
|
||||
|
||||
tx, rx, pcs, and pcs_misc (optional).
|
||||
- #phy-cells: must be 0
|
||||
|
||||
Required properties child node of pcie and usb3 qmp phys:
|
||||
Required properties for a single "lanes" child node of non-PCIe PHYs:
|
||||
- reg: list of offset and length pairs of register sets for PHY blocks
|
||||
For 1-lane devices:
|
||||
tx, rx, pcs, and (optionally) pcs_misc
|
||||
For 2-lane devices:
|
||||
tx0, rx0, pcs, tx1, rx1, and (optionally) pcs_misc
|
||||
- #phy-cells: must be 0
|
||||
|
||||
Required properties for child node of PCIe and USB3 qmp phys:
|
||||
- clocks: a list of phandles and clock-specifier pairs,
|
||||
one for each entry in clock-names.
|
||||
- clock-names: Must contain following:
|
||||
"pipe<lane-number>" for pipe clock specific to each lane.
|
||||
- clock-output-names: Name of the PHY clock that will be the parent for
|
||||
the above pipe clock.
|
||||
|
||||
For "qcom,ipq8074-qmp-pcie-phy":
|
||||
- "pcie20_phy0_pipe_clk" Pipe Clock parent
|
||||
(or)
|
||||
"pcie20_phy1_pipe_clk"
|
||||
- #clock-cells: must be 0
|
||||
- Phy pll outputs pipe clocks for pipe based PHYs. These clocks are then
|
||||
gate-controlled by the gcc.
|
||||
|
||||
Required properties for child node of PHYs with lane reset, AKA:
|
||||
"qcom,msm8996-qmp-pcie-phy"
|
||||
|
@ -115,7 +117,6 @@ Example:
|
|||
phy@34000 {
|
||||
compatible = "qcom,msm8996-qmp-pcie-phy";
|
||||
reg = <0x34000 0x488>;
|
||||
#clock-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
@ -137,6 +138,7 @@ Example:
|
|||
reg = <0x35000 0x130>,
|
||||
<0x35200 0x200>,
|
||||
<0x35400 0x1dc>;
|
||||
#clock-cells = <0>;
|
||||
#phy-cells = <0>;
|
||||
|
||||
clocks = <&gcc GCC_PCIE_0_PIPE_CLK>;
|
||||
|
@ -150,3 +152,54 @@ Example:
|
|||
...
|
||||
...
|
||||
};
|
||||
|
||||
phy@88eb000 {
|
||||
compatible = "qcom,sdm845-qmp-usb3-uni-phy";
|
||||
reg = <0x88eb000 0x18c>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
clocks = <&gcc GCC_USB3_SEC_PHY_AUX_CLK>,
|
||||
<&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
|
||||
<&gcc GCC_USB3_SEC_CLKREF_CLK>,
|
||||
<&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>;
|
||||
clock-names = "aux", "cfg_ahb", "ref", "com_aux";
|
||||
|
||||
resets = <&gcc GCC_USB3PHY_PHY_SEC_BCR>,
|
||||
<&gcc GCC_USB3_PHY_SEC_BCR>;
|
||||
reset-names = "phy", "common";
|
||||
|
||||
lane@88eb200 {
|
||||
reg = <0x88eb200 0x128>,
|
||||
<0x88eb400 0x1fc>,
|
||||
<0x88eb800 0x218>,
|
||||
<0x88eb600 0x70>;
|
||||
#clock-cells = <0>;
|
||||
#phy-cells = <0>;
|
||||
clocks = <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>;
|
||||
clock-names = "pipe0";
|
||||
clock-output-names = "usb3_uni_phy_pipe_clk_src";
|
||||
};
|
||||
};
|
||||
|
||||
phy@1d87000 {
|
||||
compatible = "qcom,sdm845-qmp-ufs-phy";
|
||||
reg = <0x1d87000 0x18c>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
clock-names = "ref",
|
||||
"ref_aux";
|
||||
clocks = <&gcc GCC_UFS_MEM_CLKREF_CLK>,
|
||||
<&gcc GCC_UFS_PHY_PHY_AUX_CLK>;
|
||||
|
||||
lanes@1d87400 {
|
||||
reg = <0x1d87400 0x108>,
|
||||
<0x1d87600 0x1e0>,
|
||||
<0x1d87c00 0x1dc>,
|
||||
<0x1d87800 0x108>,
|
||||
<0x1d87a00 0x1e0>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -14,13 +14,14 @@ Required properties:
|
|||
* allwinner,sun8i-r40-usb-phy
|
||||
* allwinner,sun8i-v3s-usb-phy
|
||||
* allwinner,sun50i-a64-usb-phy
|
||||
* allwinner,sun50i-h6-usb-phy
|
||||
- reg : a list of offset + length pairs
|
||||
- reg-names :
|
||||
* "phy_ctrl"
|
||||
* "pmu0" for H3, V3s and A64
|
||||
* "pmu0" for H3, V3s, A64 or H6
|
||||
* "pmu1"
|
||||
* "pmu2" for sun4i, sun6i, sun7i, sun8i-a83t or sun8i-h3
|
||||
* "pmu3" for sun8i-h3
|
||||
* "pmu3" for sun8i-h3 or sun50i-h6
|
||||
- #phy-cells : from the generic phy bindings, must be 1
|
||||
- clocks : phandle + clock specifier for the phy clocks
|
||||
- clock-names :
|
||||
|
@ -29,12 +30,13 @@ Required properties:
|
|||
* "usb0_phy", "usb1_phy" for sun8i
|
||||
* "usb0_phy", "usb1_phy", "usb2_phy" and "usb2_hsic_12M" for sun8i-a83t
|
||||
* "usb0_phy", "usb1_phy", "usb2_phy" and "usb3_phy" for sun8i-h3
|
||||
* "usb0_phy" and "usb3_phy" for sun50i-h6
|
||||
- resets : a list of phandle + reset specifier pairs
|
||||
- reset-names :
|
||||
* "usb0_reset"
|
||||
* "usb1_reset"
|
||||
* "usb2_reset" for sun4i, sun6i, sun7i, sun8i-a83t or sun8i-h3
|
||||
* "usb3_reset" for sun8i-h3
|
||||
* "usb3_reset" for sun8i-h3 and sun50i-h6
|
||||
|
||||
Optional properties:
|
||||
- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
CPSW Port's Interface Mode Selection PHY Tree Bindings
|
||||
-----------------------------------------------
|
||||
|
||||
TI am335x/am437x/dra7(am5)/dm814x CPSW3G Ethernet Subsystem supports
|
||||
two 10/100/1000 Ethernet ports with selectable G/MII, RMII, and RGMII interfaces.
|
||||
The interface mode is selected by configuring the MII mode selection register(s)
|
||||
(GMII_SEL) in the System Control Module chapter (SCM). GMII_SEL register(s) and
|
||||
bit fields placement in SCM are different between SoCs while fields meaning
|
||||
is the same.
|
||||
+--------------+
|
||||
+-------------------------------+ |SCM |
|
||||
| CPSW | | +---------+ |
|
||||
| +--------------------------------+gmii_sel | |
|
||||
| | | | +---------+ |
|
||||
| +----v---+ +--------+ | +--------------+
|
||||
| |Port 1..<--+-->GMII/MII<------->
|
||||
| | | | | | |
|
||||
| +--------+ | +--------+ |
|
||||
| | |
|
||||
| | +--------+ |
|
||||
| | | RMII <------->
|
||||
| +--> | |
|
||||
| | +--------+ |
|
||||
| | |
|
||||
| | +--------+ |
|
||||
| | | RGMII <------->
|
||||
| +--> | |
|
||||
| +--------+ |
|
||||
+-------------------------------+
|
||||
|
||||
CPSW Port's Interface Mode Selection PHY describes MII interface mode between
|
||||
CPSW Port and Ethernet PHY which depends on Eth PHY and board configuration.
|
||||
|
||||
CPSW Port's Interface Mode Selection PHY device should defined as child device
|
||||
of SCM node (scm_conf) and can be attached to each CPSW port node using standard
|
||||
PHY bindings (See phy/phy-bindings.txt).
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "ti,am3352-phy-gmii-sel" for am335x platform
|
||||
"ti,dra7xx-phy-gmii-sel" for dra7xx/am57xx platform
|
||||
"ti,am43xx-phy-gmii-sel" for am43xx platform
|
||||
"ti,dm814-phy-gmii-sel" for dm814x platform
|
||||
- reg : Address and length of the register set for the device
|
||||
- #phy-cells : must be 2.
|
||||
cell 1 - CPSW port number (starting from 1)
|
||||
cell 2 - RMII refclk mode
|
||||
|
||||
Examples:
|
||||
phy_gmii_sel: phy-gmii-sel {
|
||||
compatible = "ti,am3352-phy-gmii-sel";
|
||||
reg = <0x650 0x4>;
|
||||
#phy-cells = <2>;
|
||||
};
|
||||
|
||||
mac: ethernet@4a100000 {
|
||||
compatible = "ti,am335x-cpsw","ti,cpsw";
|
||||
...
|
||||
|
||||
cpsw_emac0: slave@4a100200 {
|
||||
...
|
||||
phys = <&phy_gmii_sel 1 1>;
|
||||
};
|
||||
|
||||
cpsw_emac1: slave@4a100300 {
|
||||
...
|
||||
phys = <&phy_gmii_sel 2 1>;
|
||||
};
|
||||
};
|
|
@ -6261,6 +6261,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy.git
|
|||
S: Supported
|
||||
F: drivers/phy/
|
||||
F: include/linux/phy/
|
||||
F: Documentation/devicetree/bindings/phy/
|
||||
|
||||
GENERIC PINCTRL I2C DEMULTIPLEXER DRIVER
|
||||
M: Wolfram Sang <wsa+renesas@sang-engineering.com>
|
||||
|
|
|
@ -1165,28 +1165,13 @@ static void mvpp22_gop_setup_irq(struct mvpp2_port *port)
|
|||
*/
|
||||
static int mvpp22_comphy_init(struct mvpp2_port *port)
|
||||
{
|
||||
enum phy_mode mode;
|
||||
int ret;
|
||||
|
||||
if (!port->comphy)
|
||||
return 0;
|
||||
|
||||
switch (port->phy_interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
mode = PHY_MODE_SGMII;
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
mode = PHY_MODE_2500SGMII;
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_10GKR:
|
||||
mode = PHY_MODE_10GKR;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = phy_set_mode(port->comphy, mode);
|
||||
ret = phy_set_mode_ext(port->comphy, PHY_MODE_ETHERNET,
|
||||
port->phy_interface);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -472,7 +472,6 @@ static int ocelot_port_open(struct net_device *dev)
|
|||
{
|
||||
struct ocelot_port *port = netdev_priv(dev);
|
||||
struct ocelot *ocelot = port->ocelot;
|
||||
enum phy_mode phy_mode;
|
||||
int err;
|
||||
|
||||
/* Enable receiving frames on the port, and activate auto-learning of
|
||||
|
@ -484,12 +483,8 @@ static int ocelot_port_open(struct net_device *dev)
|
|||
ANA_PORT_PORT_CFG, port->chip_port);
|
||||
|
||||
if (port->serdes) {
|
||||
if (port->phy_mode == PHY_INTERFACE_MODE_SGMII)
|
||||
phy_mode = PHY_MODE_SGMII;
|
||||
else
|
||||
phy_mode = PHY_MODE_QSGMII;
|
||||
|
||||
err = phy_set_mode(port->serdes, phy_mode);
|
||||
err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET,
|
||||
port->phy_mode);
|
||||
if (err) {
|
||||
netdev_err(dev, "Could not set mode of SerDes\n");
|
||||
return err;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/netdevice.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
@ -387,6 +388,7 @@ struct cpsw_slave_data {
|
|||
int phy_if;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */
|
||||
struct phy *ifphy;
|
||||
};
|
||||
|
||||
struct cpsw_platform_data {
|
||||
|
@ -1510,7 +1512,12 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
|
|||
phy_start(slave->phy);
|
||||
|
||||
/* Configure GMII_SEL register */
|
||||
cpsw_phy_sel(cpsw->dev, slave->phy->interface, slave->slave_num);
|
||||
if (!IS_ERR(slave->data->ifphy))
|
||||
phy_set_mode_ext(slave->data->ifphy, PHY_MODE_ETHERNET,
|
||||
slave->data->phy_if);
|
||||
else
|
||||
cpsw_phy_sel(cpsw->dev, slave->phy->interface,
|
||||
slave->slave_num);
|
||||
}
|
||||
|
||||
static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
|
||||
|
@ -3147,6 +3154,16 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
|
|||
if (strcmp(slave_node->name, "slave"))
|
||||
continue;
|
||||
|
||||
slave_data->ifphy = devm_of_phy_get(&pdev->dev, slave_node,
|
||||
NULL);
|
||||
if (!IS_ENABLED(CONFIG_TI_CPSW_PHY_SEL) &&
|
||||
IS_ERR(slave_data->ifphy)) {
|
||||
ret = PTR_ERR(slave_data->ifphy);
|
||||
dev_err(&pdev->dev,
|
||||
"%d: Error retrieving port phy: %d\n", i, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
slave_data->phy_node = of_parse_phandle(slave_node,
|
||||
"phy-handle", 0);
|
||||
parp = of_get_property(slave_node, "phy_id", &lenp);
|
||||
|
|
|
@ -15,6 +15,14 @@ config GENERIC_PHY
|
|||
phy users can obtain reference to the PHY. All the users of this
|
||||
framework should select this config.
|
||||
|
||||
config GENERIC_PHY_MIPI_DPHY
|
||||
bool
|
||||
help
|
||||
Generic MIPI D-PHY support.
|
||||
|
||||
Provides a number of helpers a core functions for MIPI D-PHY
|
||||
drivers to us.
|
||||
|
||||
config PHY_LPC18XX_USB_OTG
|
||||
tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver"
|
||||
depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
|
||||
|
@ -44,6 +52,7 @@ source "drivers/phy/allwinner/Kconfig"
|
|||
source "drivers/phy/amlogic/Kconfig"
|
||||
source "drivers/phy/broadcom/Kconfig"
|
||||
source "drivers/phy/cadence/Kconfig"
|
||||
source "drivers/phy/freescale/Kconfig"
|
||||
source "drivers/phy/hisilicon/Kconfig"
|
||||
source "drivers/phy/lantiq/Kconfig"
|
||||
source "drivers/phy/marvell/Kconfig"
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
|
||||
obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o
|
||||
obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o
|
||||
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
|
||||
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
|
||||
|
@ -16,6 +17,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
|||
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||
obj-y += broadcom/ \
|
||||
cadence/ \
|
||||
freescale/ \
|
||||
hisilicon/ \
|
||||
marvell/ \
|
||||
motorola/ \
|
||||
|
|
|
@ -115,6 +115,7 @@ enum sun4i_usb_phy_type {
|
|||
sun8i_r40_phy,
|
||||
sun8i_v3s_phy,
|
||||
sun50i_a64_phy,
|
||||
sun50i_h6_phy,
|
||||
};
|
||||
|
||||
struct sun4i_usb_phy_cfg {
|
||||
|
@ -126,6 +127,7 @@ struct sun4i_usb_phy_cfg {
|
|||
bool dedicated_clocks;
|
||||
bool enable_pmu_unk1;
|
||||
bool phy0_dual_route;
|
||||
int missing_phys;
|
||||
};
|
||||
|
||||
struct sun4i_usb_phy_data {
|
||||
|
@ -294,7 +296,8 @@ static int sun4i_usb_phy_init(struct phy *_phy)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (data->cfg->type == sun8i_a83t_phy) {
|
||||
if (data->cfg->type == sun8i_a83t_phy ||
|
||||
data->cfg->type == sun50i_h6_phy) {
|
||||
if (phy->index == 0) {
|
||||
val = readl(data->base + data->cfg->phyctl_offset);
|
||||
val |= PHY_CTL_VBUSVLDEXT;
|
||||
|
@ -343,7 +346,8 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
|
|||
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
|
||||
|
||||
if (phy->index == 0) {
|
||||
if (data->cfg->type == sun8i_a83t_phy) {
|
||||
if (data->cfg->type == sun8i_a83t_phy ||
|
||||
data->cfg->type == sun50i_h6_phy) {
|
||||
void __iomem *phyctl = data->base +
|
||||
data->cfg->phyctl_offset;
|
||||
|
||||
|
@ -474,7 +478,8 @@ static int sun4i_usb_phy_power_off(struct phy *_phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode)
|
||||
static int sun4i_usb_phy_set_mode(struct phy *_phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
|
||||
|
@ -646,6 +651,9 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
|
|||
if (args->args[0] >= data->cfg->num_phys)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (data->cfg->missing_phys & BIT(args->args[0]))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return data->phys[args->args[0]].phy;
|
||||
}
|
||||
|
||||
|
@ -741,6 +749,9 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
|||
struct sun4i_usb_phy *phy = data->phys + i;
|
||||
char name[16];
|
||||
|
||||
if (data->cfg->missing_phys & BIT(i))
|
||||
continue;
|
||||
|
||||
snprintf(name, sizeof(name), "usb%d_vbus", i);
|
||||
phy->vbus = devm_regulator_get_optional(dev, name);
|
||||
if (IS_ERR(phy->vbus)) {
|
||||
|
@ -952,6 +963,17 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
|
|||
.phy0_dual_route = true,
|
||||
};
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = {
|
||||
.num_phys = 4,
|
||||
.type = sun50i_h6_phy,
|
||||
.disc_thresh = 3,
|
||||
.phyctl_offset = REG_PHYCTL_A33,
|
||||
.dedicated_clocks = true,
|
||||
.enable_pmu_unk1 = true,
|
||||
.phy0_dual_route = true,
|
||||
.missing_phys = BIT(1) | BIT(2),
|
||||
};
|
||||
|
||||
static const struct of_device_id sun4i_usb_phy_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg },
|
||||
{ .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg },
|
||||
|
@ -965,6 +987,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
|
|||
{ .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg },
|
||||
{ .compatible = "allwinner,sun50i-a64-usb-phy",
|
||||
.data = &sun50i_a64_cfg},
|
||||
{ .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
|
||||
|
|
|
@ -152,7 +152,8 @@ static int phy_meson_gxl_usb2_reset(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int phy_meson_gxl_usb2_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int phy_meson_gxl_usb2_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
|
@ -209,7 +210,7 @@ static int phy_meson_gxl_usb2_power_on(struct phy *phy)
|
|||
/* power on the PHY by taking it out of reset mode */
|
||||
regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
|
||||
|
||||
ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode);
|
||||
ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode, 0);
|
||||
if (ret) {
|
||||
phy_meson_gxl_usb2_power_off(phy);
|
||||
|
||||
|
|
|
@ -119,7 +119,8 @@ static int phy_meson_gxl_usb3_power_off(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int phy_meson_gxl_usb3_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int phy_meson_gxl_usb3_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
|
@ -164,7 +165,7 @@ static int phy_meson_gxl_usb3_init(struct phy *phy)
|
|||
if (ret)
|
||||
goto err_disable_clk_phy;
|
||||
|
||||
ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode);
|
||||
ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode, 0);
|
||||
if (ret)
|
||||
goto err_disable_clk_peripheral;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Phy driver for Cadence MHDP DisplayPort controller
|
||||
# Phy drivers for Cadence PHYs
|
||||
#
|
||||
config PHY_CADENCE_DP
|
||||
tristate "Cadence MHDP DisplayPort PHY driver"
|
||||
|
@ -8,3 +8,10 @@ config PHY_CADENCE_DP
|
|||
select GENERIC_PHY
|
||||
help
|
||||
Support for Cadence MHDP DisplayPort PHY.
|
||||
|
||||
config PHY_CADENCE_SIERRA
|
||||
tristate "Cadence Sierra PHY Driver"
|
||||
depends on OF && HAS_IOMEM && RESET_CONTROLLER
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the Cadence Sierra PHY driver
|
|
@ -1 +1,2 @@
|
|||
obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o
|
||||
obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o
|
||||
|
|
|
@ -0,0 +1,395 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence Sierra PHY Driver
|
||||
*
|
||||
* Copyright (c) 2018 Cadence Design Systems
|
||||
* Author: Alan Douglas <adouglas@cadence.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
/* PHY register offsets */
|
||||
#define SIERRA_PHY_PLL_CFG (0xc00e << 2)
|
||||
#define SIERRA_DET_STANDEC_A (0x4000 << 2)
|
||||
#define SIERRA_DET_STANDEC_B (0x4001 << 2)
|
||||
#define SIERRA_DET_STANDEC_C (0x4002 << 2)
|
||||
#define SIERRA_DET_STANDEC_D (0x4003 << 2)
|
||||
#define SIERRA_DET_STANDEC_E (0x4004 << 2)
|
||||
#define SIERRA_PSM_LANECAL (0x4008 << 2)
|
||||
#define SIERRA_PSM_DIAG (0x4015 << 2)
|
||||
#define SIERRA_PSC_TX_A0 (0x4028 << 2)
|
||||
#define SIERRA_PSC_TX_A1 (0x4029 << 2)
|
||||
#define SIERRA_PSC_TX_A2 (0x402A << 2)
|
||||
#define SIERRA_PSC_TX_A3 (0x402B << 2)
|
||||
#define SIERRA_PSC_RX_A0 (0x4030 << 2)
|
||||
#define SIERRA_PSC_RX_A1 (0x4031 << 2)
|
||||
#define SIERRA_PSC_RX_A2 (0x4032 << 2)
|
||||
#define SIERRA_PSC_RX_A3 (0x4033 << 2)
|
||||
#define SIERRA_PLLCTRL_SUBRATE (0x403A << 2)
|
||||
#define SIERRA_PLLCTRL_GEN_D (0x403E << 2)
|
||||
#define SIERRA_DRVCTRL_ATTEN (0x406A << 2)
|
||||
#define SIERRA_CLKPATHCTRL_TMR (0x4081 << 2)
|
||||
#define SIERRA_RX_CREQ_FLTR_A_MODE1 (0x4087 << 2)
|
||||
#define SIERRA_RX_CREQ_FLTR_A_MODE0 (0x4088 << 2)
|
||||
#define SIERRA_CREQ_CCLKDET_MODE01 (0x408E << 2)
|
||||
#define SIERRA_RX_CTLE_MAINTENANCE (0x4091 << 2)
|
||||
#define SIERRA_CREQ_FSMCLK_SEL (0x4092 << 2)
|
||||
#define SIERRA_CTLELUT_CTRL (0x4098 << 2)
|
||||
#define SIERRA_DFE_ECMP_RATESEL (0x40C0 << 2)
|
||||
#define SIERRA_DFE_SMP_RATESEL (0x40C1 << 2)
|
||||
#define SIERRA_DEQ_VGATUNE_CTRL (0x40E1 << 2)
|
||||
#define SIERRA_TMRVAL_MODE3 (0x416E << 2)
|
||||
#define SIERRA_TMRVAL_MODE2 (0x416F << 2)
|
||||
#define SIERRA_TMRVAL_MODE1 (0x4170 << 2)
|
||||
#define SIERRA_TMRVAL_MODE0 (0x4171 << 2)
|
||||
#define SIERRA_PICNT_MODE1 (0x4174 << 2)
|
||||
#define SIERRA_CPI_OUTBUF_RATESEL (0x417C << 2)
|
||||
#define SIERRA_LFPSFILT_NS (0x418A << 2)
|
||||
#define SIERRA_LFPSFILT_RD (0x418B << 2)
|
||||
#define SIERRA_LFPSFILT_MP (0x418C << 2)
|
||||
#define SIERRA_SDFILT_H2L_A (0x4191 << 2)
|
||||
|
||||
#define SIERRA_MACRO_ID 0x00007364
|
||||
#define SIERRA_MAX_LANES 4
|
||||
|
||||
struct cdns_sierra_inst {
|
||||
struct phy *phy;
|
||||
u32 phy_type;
|
||||
u32 num_lanes;
|
||||
u32 mlane;
|
||||
struct reset_control *lnk_rst;
|
||||
};
|
||||
|
||||
struct cdns_reg_pairs {
|
||||
u16 val;
|
||||
u32 off;
|
||||
};
|
||||
|
||||
struct cdns_sierra_data {
|
||||
u32 id_value;
|
||||
u32 pcie_regs;
|
||||
u32 usb_regs;
|
||||
struct cdns_reg_pairs *pcie_vals;
|
||||
struct cdns_reg_pairs *usb_vals;
|
||||
};
|
||||
|
||||
struct cdns_sierra_phy {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct cdns_sierra_data *init_data;
|
||||
struct cdns_sierra_inst phys[SIERRA_MAX_LANES];
|
||||
struct reset_control *phy_rst;
|
||||
struct reset_control *apb_rst;
|
||||
struct clk *clk;
|
||||
int nsubnodes;
|
||||
bool autoconf;
|
||||
};
|
||||
|
||||
static void cdns_sierra_phy_init(struct phy *gphy)
|
||||
{
|
||||
struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
|
||||
struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent);
|
||||
int i, j;
|
||||
struct cdns_reg_pairs *vals;
|
||||
u32 num_regs;
|
||||
|
||||
if (ins->phy_type == PHY_TYPE_PCIE) {
|
||||
num_regs = phy->init_data->pcie_regs;
|
||||
vals = phy->init_data->pcie_vals;
|
||||
} else if (ins->phy_type == PHY_TYPE_USB3) {
|
||||
num_regs = phy->init_data->usb_regs;
|
||||
vals = phy->init_data->usb_vals;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < ins->num_lanes; i++)
|
||||
for (j = 0; j < num_regs ; j++)
|
||||
writel(vals[j].val, phy->base +
|
||||
vals[j].off + (i + ins->mlane) * 0x800);
|
||||
}
|
||||
|
||||
static int cdns_sierra_phy_on(struct phy *gphy)
|
||||
{
|
||||
struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
|
||||
|
||||
/* Take the PHY lane group out of reset */
|
||||
return reset_control_deassert(ins->lnk_rst);
|
||||
}
|
||||
|
||||
static int cdns_sierra_phy_off(struct phy *gphy)
|
||||
{
|
||||
struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
|
||||
|
||||
return reset_control_assert(ins->lnk_rst);
|
||||
}
|
||||
|
||||
static const struct phy_ops ops = {
|
||||
.power_on = cdns_sierra_phy_on,
|
||||
.power_off = cdns_sierra_phy_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst,
|
||||
struct device_node *child)
|
||||
{
|
||||
if (of_property_read_u32(child, "reg", &inst->mlane))
|
||||
return -EINVAL;
|
||||
|
||||
if (of_property_read_u32(child, "cdns,num-lanes", &inst->num_lanes))
|
||||
return -EINVAL;
|
||||
|
||||
if (of_property_read_u32(child, "cdns,phy-type", &inst->phy_type))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cdns_sierra_id_table[];
|
||||
|
||||
static int cdns_sierra_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_sierra_phy *sp;
|
||||
struct phy_provider *phy_provider;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *match;
|
||||
struct resource *res;
|
||||
int i, ret, node = 0;
|
||||
struct device_node *dn = dev->of_node, *child;
|
||||
|
||||
if (of_get_child_count(dn) == 0)
|
||||
return -ENODEV;
|
||||
|
||||
sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);
|
||||
if (!sp)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, sp);
|
||||
sp->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
sp->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(sp->base)) {
|
||||
dev_err(dev, "missing \"reg\"\n");
|
||||
return PTR_ERR(sp->base);
|
||||
}
|
||||
|
||||
/* Get init data for this PHY */
|
||||
match = of_match_device(cdns_sierra_id_table, dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
sp->init_data = (struct cdns_sierra_data *)match->data;
|
||||
|
||||
platform_set_drvdata(pdev, sp);
|
||||
|
||||
sp->clk = devm_clk_get(dev, "phy_clk");
|
||||
if (IS_ERR(sp->clk)) {
|
||||
dev_err(dev, "failed to get clock phy_clk\n");
|
||||
return PTR_ERR(sp->clk);
|
||||
}
|
||||
|
||||
sp->phy_rst = devm_reset_control_get(dev, "sierra_reset");
|
||||
if (IS_ERR(sp->phy_rst)) {
|
||||
dev_err(dev, "failed to get reset\n");
|
||||
return PTR_ERR(sp->phy_rst);
|
||||
}
|
||||
|
||||
sp->apb_rst = devm_reset_control_get(dev, "sierra_apb");
|
||||
if (IS_ERR(sp->apb_rst)) {
|
||||
dev_err(dev, "failed to get apb reset\n");
|
||||
return PTR_ERR(sp->apb_rst);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sp->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable APB */
|
||||
reset_control_deassert(sp->apb_rst);
|
||||
|
||||
/* Check that PHY is present */
|
||||
if (sp->init_data->id_value != readl(sp->base)) {
|
||||
ret = -EINVAL;
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
sp->autoconf = of_property_read_bool(dn, "cdns,autoconf");
|
||||
|
||||
for_each_available_child_of_node(dn, child) {
|
||||
struct phy *gphy;
|
||||
|
||||
sp->phys[node].lnk_rst =
|
||||
of_reset_control_get_exclusive_by_index(child, 0);
|
||||
|
||||
if (IS_ERR(sp->phys[node].lnk_rst)) {
|
||||
dev_err(dev, "failed to get reset %s\n",
|
||||
child->full_name);
|
||||
ret = PTR_ERR(sp->phys[node].lnk_rst);
|
||||
goto put_child2;
|
||||
}
|
||||
|
||||
if (!sp->autoconf) {
|
||||
ret = cdns_sierra_get_optional(&sp->phys[node], child);
|
||||
if (ret) {
|
||||
dev_err(dev, "missing property in node %s\n",
|
||||
child->name);
|
||||
goto put_child;
|
||||
}
|
||||
}
|
||||
|
||||
gphy = devm_phy_create(dev, child, &ops);
|
||||
|
||||
if (IS_ERR(gphy)) {
|
||||
ret = PTR_ERR(gphy);
|
||||
goto put_child;
|
||||
}
|
||||
sp->phys[node].phy = gphy;
|
||||
phy_set_drvdata(gphy, &sp->phys[node]);
|
||||
|
||||
/* Initialise the PHY registers, unless auto configured */
|
||||
if (!sp->autoconf)
|
||||
cdns_sierra_phy_init(gphy);
|
||||
|
||||
node++;
|
||||
}
|
||||
sp->nsubnodes = node;
|
||||
|
||||
/* If more than one subnode, configure the PHY as multilink */
|
||||
if (!sp->autoconf && sp->nsubnodes > 1)
|
||||
writel(2, sp->base + SIERRA_PHY_PLL_CFG);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
reset_control_deassert(sp->phy_rst);
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
|
||||
put_child:
|
||||
node++;
|
||||
put_child2:
|
||||
for (i = 0; i < node; i++)
|
||||
reset_control_put(sp->phys[i].lnk_rst);
|
||||
of_node_put(child);
|
||||
clk_disable:
|
||||
clk_disable_unprepare(sp->clk);
|
||||
reset_control_assert(sp->apb_rst);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns_sierra_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_sierra_phy *phy = dev_get_drvdata(pdev->dev.parent);
|
||||
int i;
|
||||
|
||||
reset_control_assert(phy->phy_rst);
|
||||
reset_control_assert(phy->apb_rst);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
/*
|
||||
* The device level resets will be put automatically.
|
||||
* Need to put the subnode resets here though.
|
||||
*/
|
||||
for (i = 0; i < phy->nsubnodes; i++) {
|
||||
reset_control_assert(phy->phys[i].lnk_rst);
|
||||
reset_control_put(phy->phys[i].lnk_rst);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cdns_reg_pairs cdns_usb_regs[] = {
|
||||
/*
|
||||
* Write USB configuration parameters to the PHY.
|
||||
* These values are specific to this specific hardware
|
||||
* configuration.
|
||||
*/
|
||||
{0xFE0A, SIERRA_DET_STANDEC_A},
|
||||
{0x000F, SIERRA_DET_STANDEC_B},
|
||||
{0x55A5, SIERRA_DET_STANDEC_C},
|
||||
{0x69AD, SIERRA_DET_STANDEC_D},
|
||||
{0x0241, SIERRA_DET_STANDEC_E},
|
||||
{0x0110, SIERRA_PSM_LANECAL},
|
||||
{0xCF00, SIERRA_PSM_DIAG},
|
||||
{0x001F, SIERRA_PSC_TX_A0},
|
||||
{0x0007, SIERRA_PSC_TX_A1},
|
||||
{0x0003, SIERRA_PSC_TX_A2},
|
||||
{0x0003, SIERRA_PSC_TX_A3},
|
||||
{0x0FFF, SIERRA_PSC_RX_A0},
|
||||
{0x0003, SIERRA_PSC_RX_A1},
|
||||
{0x0003, SIERRA_PSC_RX_A2},
|
||||
{0x0001, SIERRA_PSC_RX_A3},
|
||||
{0x0001, SIERRA_PLLCTRL_SUBRATE},
|
||||
{0x0406, SIERRA_PLLCTRL_GEN_D},
|
||||
{0x0000, SIERRA_DRVCTRL_ATTEN},
|
||||
{0x823E, SIERRA_CLKPATHCTRL_TMR},
|
||||
{0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1},
|
||||
{0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0},
|
||||
{0x7B3C, SIERRA_CREQ_CCLKDET_MODE01},
|
||||
{0x023C, SIERRA_RX_CTLE_MAINTENANCE},
|
||||
{0x3232, SIERRA_CREQ_FSMCLK_SEL},
|
||||
{0x8452, SIERRA_CTLELUT_CTRL},
|
||||
{0x4121, SIERRA_DFE_ECMP_RATESEL},
|
||||
{0x4121, SIERRA_DFE_SMP_RATESEL},
|
||||
{0x9999, SIERRA_DEQ_VGATUNE_CTRL},
|
||||
{0x0330, SIERRA_TMRVAL_MODE0},
|
||||
{0x01FF, SIERRA_PICNT_MODE1},
|
||||
{0x0009, SIERRA_CPI_OUTBUF_RATESEL},
|
||||
{0x000F, SIERRA_LFPSFILT_NS},
|
||||
{0x0009, SIERRA_LFPSFILT_RD},
|
||||
{0x0001, SIERRA_LFPSFILT_MP},
|
||||
{0x8013, SIERRA_SDFILT_H2L_A},
|
||||
{0x0400, SIERRA_TMRVAL_MODE1},
|
||||
};
|
||||
|
||||
static struct cdns_reg_pairs cdns_pcie_regs[] = {
|
||||
/*
|
||||
* Write PCIe configuration parameters to the PHY.
|
||||
* These values are specific to this specific hardware
|
||||
* configuration.
|
||||
*/
|
||||
{0x891f, SIERRA_DET_STANDEC_D},
|
||||
{0x0053, SIERRA_DET_STANDEC_E},
|
||||
{0x0400, SIERRA_TMRVAL_MODE2},
|
||||
{0x0200, SIERRA_TMRVAL_MODE3},
|
||||
};
|
||||
|
||||
static const struct cdns_sierra_data cdns_map_sierra = {
|
||||
SIERRA_MACRO_ID,
|
||||
ARRAY_SIZE(cdns_pcie_regs),
|
||||
ARRAY_SIZE(cdns_usb_regs),
|
||||
cdns_pcie_regs,
|
||||
cdns_usb_regs
|
||||
};
|
||||
|
||||
static const struct of_device_id cdns_sierra_id_table[] = {
|
||||
{
|
||||
.compatible = "cdns,sierra-phy-t0",
|
||||
.data = &cdns_map_sierra,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cdns_sierra_id_table);
|
||||
|
||||
static struct platform_driver cdns_sierra_driver = {
|
||||
.probe = cdns_sierra_phy_probe,
|
||||
.remove = cdns_sierra_phy_remove,
|
||||
.driver = {
|
||||
.name = "cdns-sierra-phy",
|
||||
.of_match_table = cdns_sierra_id_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(cdns_sierra_driver);
|
||||
|
||||
MODULE_ALIAS("platform:cdns_sierra");
|
||||
MODULE_AUTHOR("Cadence Design Systems");
|
||||
MODULE_DESCRIPTION("CDNS sierra phy driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,5 @@
|
|||
config PHY_FSL_IMX8MQ_USB
|
||||
tristate "Freescale i.MX8M USB3 PHY"
|
||||
depends on OF && HAS_IOMEM
|
||||
select GENERIC_PHY
|
||||
default SOC_IMX8MQ
|
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o
|
|
@ -0,0 +1,127 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Copyright (c) 2017 NXP. */
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define PHY_CTRL0 0x0
|
||||
#define PHY_CTRL0_REF_SSP_EN BIT(2)
|
||||
|
||||
#define PHY_CTRL1 0x4
|
||||
#define PHY_CTRL1_RESET BIT(0)
|
||||
#define PHY_CTRL1_COMMONONN BIT(1)
|
||||
#define PHY_CTRL1_ATERESET BIT(3)
|
||||
#define PHY_CTRL1_VDATSRCENB0 BIT(19)
|
||||
#define PHY_CTRL1_VDATDETENB0 BIT(20)
|
||||
|
||||
#define PHY_CTRL2 0x8
|
||||
#define PHY_CTRL2_TXENABLEN0 BIT(8)
|
||||
|
||||
struct imx8mq_usb_phy {
|
||||
struct phy *phy;
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static int imx8mq_usb_phy_init(struct phy *phy)
|
||||
{
|
||||
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
|
||||
u32 value;
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL1);
|
||||
value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 |
|
||||
PHY_CTRL1_COMMONONN);
|
||||
value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;
|
||||
writel(value, imx_phy->base + PHY_CTRL1);
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL0);
|
||||
value |= PHY_CTRL0_REF_SSP_EN;
|
||||
writel(value, imx_phy->base + PHY_CTRL0);
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL2);
|
||||
value |= PHY_CTRL2_TXENABLEN0;
|
||||
writel(value, imx_phy->base + PHY_CTRL2);
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL1);
|
||||
value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
|
||||
writel(value, imx_phy->base + PHY_CTRL1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8mq_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
|
||||
|
||||
return clk_prepare_enable(imx_phy->clk);
|
||||
}
|
||||
|
||||
static int imx8mq_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
|
||||
|
||||
clk_disable_unprepare(imx_phy->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops imx8mq_usb_phy_ops = {
|
||||
.init = imx8mq_usb_phy_init,
|
||||
.power_on = imx8mq_phy_power_on,
|
||||
.power_off = imx8mq_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int imx8mq_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct imx8mq_usb_phy *imx_phy;
|
||||
struct resource *res;
|
||||
|
||||
imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL);
|
||||
if (!imx_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
imx_phy->clk = devm_clk_get(dev, "phy");
|
||||
if (IS_ERR(imx_phy->clk)) {
|
||||
dev_err(dev, "failed to get imx8mq usb phy clock\n");
|
||||
return PTR_ERR(imx_phy->clk);
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
imx_phy->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(imx_phy->base))
|
||||
return PTR_ERR(imx_phy->base);
|
||||
|
||||
imx_phy->phy = devm_phy_create(dev, NULL, &imx8mq_usb_phy_ops);
|
||||
if (IS_ERR(imx_phy->phy))
|
||||
return PTR_ERR(imx_phy->phy);
|
||||
|
||||
phy_set_drvdata(imx_phy->phy, imx_phy);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id imx8mq_usb_phy_of_match[] = {
|
||||
{.compatible = "fsl,imx8mq-usb-phy",},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
|
||||
|
||||
static struct platform_driver imx8mq_usb_phy_driver = {
|
||||
.probe = imx8mq_usb_phy_probe,
|
||||
.driver = {
|
||||
.name = "imx8mq-usb-phy",
|
||||
.of_match_table = imx8mq_usb_phy_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(imx8mq_usb_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("FSL IMX8MQ USB PHY driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/iopoll.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
@ -114,43 +115,45 @@
|
|||
#define MVEBU_COMPHY_LANES 6
|
||||
#define MVEBU_COMPHY_PORTS 3
|
||||
|
||||
struct mvebu_comhy_conf {
|
||||
struct mvebu_comphy_conf {
|
||||
enum phy_mode mode;
|
||||
int submode;
|
||||
unsigned lane;
|
||||
unsigned port;
|
||||
u32 mux;
|
||||
};
|
||||
|
||||
#define MVEBU_COMPHY_CONF(_lane, _port, _mode, _mux) \
|
||||
#define MVEBU_COMPHY_CONF(_lane, _port, _submode, _mux) \
|
||||
{ \
|
||||
.lane = _lane, \
|
||||
.port = _port, \
|
||||
.mode = _mode, \
|
||||
.mode = PHY_MODE_ETHERNET, \
|
||||
.submode = _submode, \
|
||||
.mux = _mux, \
|
||||
}
|
||||
|
||||
static const struct mvebu_comhy_conf mvebu_comphy_cp110_modes[] = {
|
||||
static const struct mvebu_comphy_conf mvebu_comphy_cp110_modes[] = {
|
||||
/* lane 0 */
|
||||
MVEBU_COMPHY_CONF(0, 1, PHY_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(0, 1, PHY_MODE_2500SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1),
|
||||
/* lane 1 */
|
||||
MVEBU_COMPHY_CONF(1, 2, PHY_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(1, 2, PHY_MODE_2500SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1),
|
||||
/* lane 2 */
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_MODE_2500SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_MODE_10GKR, 0x1),
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1),
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_10GKR, 0x1),
|
||||
/* lane 3 */
|
||||
MVEBU_COMPHY_CONF(3, 1, PHY_MODE_SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(3, 1, PHY_MODE_2500SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2),
|
||||
/* lane 4 */
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_MODE_SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_MODE_2500SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_MODE_10GKR, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 1, PHY_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_10GKR, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 1, PHY_INTERFACE_MODE_SGMII, 0x1),
|
||||
/* lane 5 */
|
||||
MVEBU_COMPHY_CONF(5, 2, PHY_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(5, 2, PHY_MODE_2500SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1),
|
||||
};
|
||||
|
||||
struct mvebu_comphy_priv {
|
||||
|
@ -163,10 +166,12 @@ struct mvebu_comphy_lane {
|
|||
struct mvebu_comphy_priv *priv;
|
||||
unsigned id;
|
||||
enum phy_mode mode;
|
||||
int submode;
|
||||
int port;
|
||||
};
|
||||
|
||||
static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode)
|
||||
static int mvebu_comphy_get_mux(int lane, int port,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
int i, n = ARRAY_SIZE(mvebu_comphy_cp110_modes);
|
||||
|
||||
|
@ -177,7 +182,8 @@ static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode)
|
|||
for (i = 0; i < n; i++) {
|
||||
if (mvebu_comphy_cp110_modes[i].lane == lane &&
|
||||
mvebu_comphy_cp110_modes[i].port == port &&
|
||||
mvebu_comphy_cp110_modes[i].mode == mode)
|
||||
mvebu_comphy_cp110_modes[i].mode == mode &&
|
||||
mvebu_comphy_cp110_modes[i].submode == submode)
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -187,8 +193,7 @@ static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode)
|
|||
return mvebu_comphy_cp110_modes[i].mux;
|
||||
}
|
||||
|
||||
static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
|
||||
enum phy_mode mode)
|
||||
static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane)
|
||||
{
|
||||
struct mvebu_comphy_priv *priv = lane->priv;
|
||||
u32 val;
|
||||
|
@ -206,14 +211,14 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
|
|||
MVEBU_COMPHY_SERDES_CFG0_HALF_BUS |
|
||||
MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xf) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xf));
|
||||
if (mode == PHY_MODE_10GKR)
|
||||
if (lane->submode == PHY_INTERFACE_MODE_10GKR)
|
||||
val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xe) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xe);
|
||||
else if (mode == PHY_MODE_2500SGMII)
|
||||
else if (lane->submode == PHY_INTERFACE_MODE_2500BASEX)
|
||||
val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x8) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x8) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_HALF_BUS;
|
||||
else if (mode == PHY_MODE_SGMII)
|
||||
else if (lane->submode == PHY_INTERFACE_MODE_SGMII)
|
||||
val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x6) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x6) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_HALF_BUS;
|
||||
|
@ -243,7 +248,7 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
|
|||
/* refclk selection */
|
||||
val = readl(priv->base + MVEBU_COMPHY_MISC_CTRL0(lane->id));
|
||||
val &= ~MVEBU_COMPHY_MISC_CTRL0_REFCLK_SEL;
|
||||
if (mode == PHY_MODE_10GKR)
|
||||
if (lane->submode == PHY_INTERFACE_MODE_10GKR)
|
||||
val |= MVEBU_COMPHY_MISC_CTRL0_ICP_FORCE;
|
||||
writel(val, priv->base + MVEBU_COMPHY_MISC_CTRL0(lane->id));
|
||||
|
||||
|
@ -261,8 +266,7 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
|
|||
writel(val, priv->base + MVEBU_COMPHY_LOOPBACK(lane->id));
|
||||
}
|
||||
|
||||
static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane,
|
||||
enum phy_mode mode)
|
||||
static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane)
|
||||
{
|
||||
struct mvebu_comphy_priv *priv = lane->priv;
|
||||
u32 val;
|
||||
|
@ -303,13 +307,13 @@ static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_comphy_set_mode_sgmii(struct phy *phy, enum phy_mode mode)
|
||||
static int mvebu_comphy_set_mode_sgmii(struct phy *phy)
|
||||
{
|
||||
struct mvebu_comphy_lane *lane = phy_get_drvdata(phy);
|
||||
struct mvebu_comphy_priv *priv = lane->priv;
|
||||
u32 val;
|
||||
|
||||
mvebu_comphy_ethernet_init_reset(lane, mode);
|
||||
mvebu_comphy_ethernet_init_reset(lane);
|
||||
|
||||
val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id));
|
||||
val &= ~MVEBU_COMPHY_RX_CTRL1_CLK8T_EN;
|
||||
|
@ -330,7 +334,7 @@ static int mvebu_comphy_set_mode_sgmii(struct phy *phy, enum phy_mode mode)
|
|||
val |= MVEBU_COMPHY_GEN1_S0_TX_EMPH(0x1);
|
||||
writel(val, priv->base + MVEBU_COMPHY_GEN1_S0(lane->id));
|
||||
|
||||
return mvebu_comphy_init_plls(lane, PHY_MODE_SGMII);
|
||||
return mvebu_comphy_init_plls(lane);
|
||||
}
|
||||
|
||||
static int mvebu_comphy_set_mode_10gkr(struct phy *phy)
|
||||
|
@ -339,7 +343,7 @@ static int mvebu_comphy_set_mode_10gkr(struct phy *phy)
|
|||
struct mvebu_comphy_priv *priv = lane->priv;
|
||||
u32 val;
|
||||
|
||||
mvebu_comphy_ethernet_init_reset(lane, PHY_MODE_10GKR);
|
||||
mvebu_comphy_ethernet_init_reset(lane);
|
||||
|
||||
val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id));
|
||||
val |= MVEBU_COMPHY_RX_CTRL1_RXCLK2X_SEL |
|
||||
|
@ -469,7 +473,7 @@ static int mvebu_comphy_set_mode_10gkr(struct phy *phy)
|
|||
val |= MVEBU_COMPHY_EXT_SELV_RX_SAMPL(0x1a);
|
||||
writel(val, priv->base + MVEBU_COMPHY_EXT_SELV(lane->id));
|
||||
|
||||
return mvebu_comphy_init_plls(lane, PHY_MODE_10GKR);
|
||||
return mvebu_comphy_init_plls(lane);
|
||||
}
|
||||
|
||||
static int mvebu_comphy_power_on(struct phy *phy)
|
||||
|
@ -479,7 +483,8 @@ static int mvebu_comphy_power_on(struct phy *phy)
|
|||
int ret, mux;
|
||||
u32 val;
|
||||
|
||||
mux = mvebu_comphy_get_mux(lane->id, lane->port, lane->mode);
|
||||
mux = mvebu_comphy_get_mux(lane->id, lane->port,
|
||||
lane->mode, lane->submode);
|
||||
if (mux < 0)
|
||||
return -ENOTSUPP;
|
||||
|
||||
|
@ -492,12 +497,12 @@ static int mvebu_comphy_power_on(struct phy *phy)
|
|||
val |= mux << MVEBU_COMPHY_SELECTOR_PHY(lane->id);
|
||||
regmap_write(priv->regmap, MVEBU_COMPHY_SELECTOR, val);
|
||||
|
||||
switch (lane->mode) {
|
||||
case PHY_MODE_SGMII:
|
||||
case PHY_MODE_2500SGMII:
|
||||
ret = mvebu_comphy_set_mode_sgmii(phy, lane->mode);
|
||||
switch (lane->submode) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
ret = mvebu_comphy_set_mode_sgmii(phy);
|
||||
break;
|
||||
case PHY_MODE_10GKR:
|
||||
case PHY_INTERFACE_MODE_10GKR:
|
||||
ret = mvebu_comphy_set_mode_10gkr(phy);
|
||||
break;
|
||||
default:
|
||||
|
@ -512,14 +517,22 @@ static int mvebu_comphy_power_on(struct phy *phy)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int mvebu_comphy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int mvebu_comphy_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct mvebu_comphy_lane *lane = phy_get_drvdata(phy);
|
||||
|
||||
if (mvebu_comphy_get_mux(lane->id, lane->port, mode) < 0)
|
||||
if (mode != PHY_MODE_ETHERNET)
|
||||
return -EINVAL;
|
||||
|
||||
if (submode == PHY_INTERFACE_MODE_1000BASEX)
|
||||
submode = PHY_INTERFACE_MODE_SGMII;
|
||||
|
||||
if (mvebu_comphy_get_mux(lane->id, lane->port, mode, submode) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
lane->mode = mode;
|
||||
lane->submode = submode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -971,7 +971,7 @@ static int mtk_phy_exit(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
struct mtk_phy_instance *instance = phy_get_drvdata(phy);
|
||||
struct mtk_tphy *tphy = dev_get_drvdata(phy->dev.parent);
|
||||
|
|
|
@ -426,7 +426,7 @@ static int mtk_phy_exit(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
struct xsphy_instance *inst = phy_get_drvdata(phy);
|
||||
struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
#define PHY_MDM6600_PHY_DELAY_MS 4000 /* PHY enable 2.2s to 3.5s */
|
||||
#define PHY_MDM6600_ENABLED_DELAY_MS 8000 /* 8s more total for MDM6600 */
|
||||
|
@ -120,12 +121,22 @@ static int phy_mdm6600_power_on(struct phy *x)
|
|||
{
|
||||
struct phy_mdm6600 *ddata = phy_get_drvdata(x);
|
||||
struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
|
||||
int error;
|
||||
|
||||
if (!ddata->enabled)
|
||||
return -ENODEV;
|
||||
|
||||
error = pinctrl_pm_select_default_state(ddata->dev);
|
||||
if (error)
|
||||
dev_warn(ddata->dev, "%s: error with default_state: %i\n",
|
||||
__func__, error);
|
||||
|
||||
gpiod_set_value_cansleep(enable_gpio, 1);
|
||||
|
||||
/* Allow aggressive PM for USB, it's only needed for n_gsm port */
|
||||
if (pm_runtime_enabled(&x->dev))
|
||||
phy_pm_runtime_put(x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -133,12 +144,26 @@ static int phy_mdm6600_power_off(struct phy *x)
|
|||
{
|
||||
struct phy_mdm6600 *ddata = phy_get_drvdata(x);
|
||||
struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
|
||||
int error;
|
||||
|
||||
if (!ddata->enabled)
|
||||
return -ENODEV;
|
||||
|
||||
/* Paired with phy_pm_runtime_put() in phy_mdm6600_power_on() */
|
||||
if (pm_runtime_enabled(&x->dev)) {
|
||||
error = phy_pm_runtime_get(x);
|
||||
if (error < 0 && error != -EINPROGRESS)
|
||||
dev_warn(ddata->dev, "%s: phy_pm_runtime_get: %i\n",
|
||||
__func__, error);
|
||||
}
|
||||
|
||||
gpiod_set_value_cansleep(enable_gpio, 0);
|
||||
|
||||
error = pinctrl_pm_select_sleep_state(ddata->dev);
|
||||
if (error)
|
||||
dev_warn(ddata->dev, "%s: error with sleep_state: %i\n",
|
||||
__func__, error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -529,28 +554,17 @@ static int phy_mdm6600_probe(struct platform_device *pdev)
|
|||
ddata->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
/* Active state selected in phy_mdm6600_power_on() */
|
||||
error = pinctrl_pm_select_sleep_state(ddata->dev);
|
||||
if (error)
|
||||
dev_warn(ddata->dev, "%s: error with sleep_state: %i\n",
|
||||
__func__, error);
|
||||
|
||||
error = phy_mdm6600_init_lines(ddata);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
phy_mdm6600_init_irq(ddata);
|
||||
|
||||
ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops);
|
||||
if (IS_ERR(ddata->generic_phy)) {
|
||||
error = PTR_ERR(ddata->generic_phy);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
phy_set_drvdata(ddata->generic_phy, ddata);
|
||||
|
||||
ddata->phy_provider =
|
||||
devm_of_phy_provider_register(ddata->dev,
|
||||
of_phy_simple_xlate);
|
||||
if (IS_ERR(ddata->phy_provider)) {
|
||||
error = PTR_ERR(ddata->phy_provider);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
schedule_delayed_work(&ddata->bootup_work, 0);
|
||||
|
||||
/*
|
||||
|
@ -574,14 +588,31 @@ static int phy_mdm6600_probe(struct platform_device *pdev)
|
|||
if (error < 0) {
|
||||
dev_warn(ddata->dev, "failed to wake modem: %i\n", error);
|
||||
pm_runtime_put_noidle(ddata->dev);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops);
|
||||
if (IS_ERR(ddata->generic_phy)) {
|
||||
error = PTR_ERR(ddata->generic_phy);
|
||||
goto idle;
|
||||
}
|
||||
|
||||
phy_set_drvdata(ddata->generic_phy, ddata);
|
||||
|
||||
ddata->phy_provider =
|
||||
devm_of_phy_provider_register(ddata->dev,
|
||||
of_phy_simple_xlate);
|
||||
if (IS_ERR(ddata->phy_provider))
|
||||
error = PTR_ERR(ddata->phy_provider);
|
||||
|
||||
idle:
|
||||
pm_runtime_mark_last_busy(ddata->dev);
|
||||
pm_runtime_put_autosuspend(ddata->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
if (error < 0)
|
||||
phy_mdm6600_device_power_off(ddata);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
@ -104,20 +105,24 @@ struct serdes_mux {
|
|||
u8 idx;
|
||||
u8 port;
|
||||
enum phy_mode mode;
|
||||
int submode;
|
||||
u32 mask;
|
||||
u32 mux;
|
||||
};
|
||||
|
||||
#define SERDES_MUX(_idx, _port, _mode, _mask, _mux) { \
|
||||
#define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \
|
||||
.idx = _idx, \
|
||||
.port = _port, \
|
||||
.mode = _mode, \
|
||||
.submode = _submode, \
|
||||
.mask = _mask, \
|
||||
.mux = _mux, \
|
||||
}
|
||||
|
||||
#define SERDES_MUX_SGMII(i, p, m, c) SERDES_MUX(i, p, PHY_MODE_SGMII, m, c)
|
||||
#define SERDES_MUX_QSGMII(i, p, m, c) SERDES_MUX(i, p, PHY_MODE_QSGMII, m, c)
|
||||
#define SERDES_MUX_SGMII(i, p, m, c) \
|
||||
SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c)
|
||||
#define SERDES_MUX_QSGMII(i, p, m, c) \
|
||||
SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c)
|
||||
|
||||
static const struct serdes_mux ocelot_serdes_muxes[] = {
|
||||
SERDES_MUX_SGMII(SERDES1G(0), 0, 0, 0),
|
||||
|
@ -154,22 +159,27 @@ static const struct serdes_mux ocelot_serdes_muxes[] = {
|
|||
SERDES_MUX_SGMII(SERDES6G(1), 8, 0, 0),
|
||||
SERDES_MUX_SGMII(SERDES6G(2), 10, HSIO_HW_CFG_PCIE_ENA |
|
||||
HSIO_HW_CFG_DEV2G5_10_MODE, 0),
|
||||
SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, HSIO_HW_CFG_PCIE_ENA,
|
||||
SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, 0, HSIO_HW_CFG_PCIE_ENA,
|
||||
HSIO_HW_CFG_PCIE_ENA),
|
||||
};
|
||||
|
||||
static int serdes_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
struct serdes_macro *macro = phy_get_drvdata(phy);
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
/* As of now only PHY_MODE_ETHERNET is supported */
|
||||
if (mode != PHY_MODE_ETHERNET)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ocelot_serdes_muxes); i++) {
|
||||
if (macro->idx != ocelot_serdes_muxes[i].idx ||
|
||||
mode != ocelot_serdes_muxes[i].mode)
|
||||
mode != ocelot_serdes_muxes[i].mode ||
|
||||
submode != ocelot_serdes_muxes[i].submode)
|
||||
continue;
|
||||
|
||||
if (mode != PHY_MODE_QSGMII &&
|
||||
if (submode != PHY_INTERFACE_MODE_QSGMII &&
|
||||
macro->port != ocelot_serdes_muxes[i].port)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2013 NVIDIA Corporation
|
||||
* Copyright (C) 2018 Cadence Design Systems Inc.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/time64.h>
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/phy/phy-mipi-dphy.h>
|
||||
|
||||
#define PSEC_PER_SEC 1000000000000LL
|
||||
|
||||
/*
|
||||
* Minimum D-PHY timings based on MIPI D-PHY specification. Derived
|
||||
* from the valid ranges specified in Section 6.9, Table 14, Page 41
|
||||
* of the D-PHY specification (v2.1).
|
||||
*/
|
||||
int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
|
||||
unsigned int bpp,
|
||||
unsigned int lanes,
|
||||
struct phy_configure_opts_mipi_dphy *cfg)
|
||||
{
|
||||
unsigned long long hs_clk_rate;
|
||||
unsigned long long ui;
|
||||
|
||||
if (!cfg)
|
||||
return -EINVAL;
|
||||
|
||||
hs_clk_rate = pixel_clock * bpp;
|
||||
do_div(hs_clk_rate, lanes);
|
||||
|
||||
ui = ALIGN(PSEC_PER_SEC, hs_clk_rate);
|
||||
do_div(ui, hs_clk_rate);
|
||||
|
||||
cfg->clk_miss = 0;
|
||||
cfg->clk_post = 60000 + 52 * ui;
|
||||
cfg->clk_pre = 8000;
|
||||
cfg->clk_prepare = 38000;
|
||||
cfg->clk_settle = 95000;
|
||||
cfg->clk_term_en = 0;
|
||||
cfg->clk_trail = 60000;
|
||||
cfg->clk_zero = 262000;
|
||||
cfg->d_term_en = 0;
|
||||
cfg->eot = 0;
|
||||
cfg->hs_exit = 100000;
|
||||
cfg->hs_prepare = 40000 + 4 * ui;
|
||||
cfg->hs_zero = 105000 + 6 * ui;
|
||||
cfg->hs_settle = 85000 + 6 * ui;
|
||||
cfg->hs_skip = 40000;
|
||||
|
||||
/*
|
||||
* The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40)
|
||||
* contains this formula as:
|
||||
*
|
||||
* T_HS-TRAIL = max(n * 8 * ui, 60 + n * 4 * ui)
|
||||
*
|
||||
* where n = 1 for forward-direction HS mode and n = 4 for reverse-
|
||||
* direction HS mode. There's only one setting and this function does
|
||||
* not parameterize on anything other that ui, so this code will
|
||||
* assumes that reverse-direction HS mode is supported and uses n = 4.
|
||||
*/
|
||||
cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui);
|
||||
|
||||
cfg->init = 100000000;
|
||||
cfg->lpx = 60000;
|
||||
cfg->ta_get = 5 * cfg->lpx;
|
||||
cfg->ta_go = 4 * cfg->lpx;
|
||||
cfg->ta_sure = 2 * cfg->lpx;
|
||||
cfg->wakeup = 1000000000;
|
||||
|
||||
cfg->hs_clk_rate = hs_clk_rate;
|
||||
cfg->lanes = lanes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(phy_mipi_dphy_get_default_config);
|
||||
|
||||
/*
|
||||
* Validate D-PHY configuration according to MIPI D-PHY specification
|
||||
* (v1.2, Section Section 6.9 "Global Operation Timing Parameters").
|
||||
*/
|
||||
int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
|
||||
{
|
||||
unsigned long long ui;
|
||||
|
||||
if (!cfg)
|
||||
return -EINVAL;
|
||||
|
||||
ui = ALIGN(PSEC_PER_SEC, cfg->hs_clk_rate);
|
||||
do_div(ui, cfg->hs_clk_rate);
|
||||
|
||||
if (cfg->clk_miss > 60000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_post < (60000 + 52 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_pre < 8000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_prepare < 38000 || cfg->clk_prepare > 95000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_settle < 95000 || cfg->clk_settle > 300000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_term_en > 38000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_trail < 60000)
|
||||
return -EINVAL;
|
||||
|
||||
if ((cfg->clk_prepare + cfg->clk_zero) < 300000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->d_term_en > (35000 + 4 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->eot > (105000 + 12 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->hs_exit < 100000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->hs_prepare < (40000 + 4 * ui) ||
|
||||
cfg->hs_prepare > (85000 + 6 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if ((cfg->hs_prepare + cfg->hs_zero) < (145000 + 10 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if ((cfg->hs_settle < (85000 + 6 * ui)) ||
|
||||
(cfg->hs_settle > (145000 + 10 * ui)))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->hs_skip < 40000 || cfg->hs_skip > (55000 + 4 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->hs_trail < max(8 * ui, 60000 + 4 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->init < 100000000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->lpx < 50000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->ta_get != (5 * cfg->lpx))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->ta_go != (4 * cfg->lpx))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > (2 * cfg->lpx))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->wakeup < 1000000000)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(phy_mipi_dphy_config_validate);
|
|
@ -360,7 +360,7 @@ int phy_power_off(struct phy *phy)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(phy_power_off);
|
||||
|
||||
int phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -368,14 +368,14 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode)
|
|||
return 0;
|
||||
|
||||
mutex_lock(&phy->mutex);
|
||||
ret = phy->ops->set_mode(phy, mode);
|
||||
ret = phy->ops->set_mode(phy, mode, submode);
|
||||
if (!ret)
|
||||
phy->attrs.mode = mode;
|
||||
mutex_unlock(&phy->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_set_mode);
|
||||
EXPORT_SYMBOL_GPL(phy_set_mode_ext);
|
||||
|
||||
int phy_reset(struct phy *phy)
|
||||
{
|
||||
|
@ -407,6 +407,70 @@ int phy_calibrate(struct phy *phy)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(phy_calibrate);
|
||||
|
||||
/**
|
||||
* phy_configure() - Changes the phy parameters
|
||||
* @phy: the phy returned by phy_get()
|
||||
* @opts: New configuration to apply
|
||||
*
|
||||
* Used to change the PHY parameters. phy_init() must have been called
|
||||
* on the phy. The configuration will be applied on the current phy
|
||||
* mode, that can be changed using phy_set_mode().
|
||||
*
|
||||
* Returns: 0 if successful, an negative error code otherwise
|
||||
*/
|
||||
int phy_configure(struct phy *phy, union phy_configure_opts *opts)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!phy)
|
||||
return -EINVAL;
|
||||
|
||||
if (!phy->ops->configure)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&phy->mutex);
|
||||
ret = phy->ops->configure(phy, opts);
|
||||
mutex_unlock(&phy->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_configure);
|
||||
|
||||
/**
|
||||
* phy_validate() - Checks the phy parameters
|
||||
* @phy: the phy returned by phy_get()
|
||||
* @mode: phy_mode the configuration is applicable to.
|
||||
* @submode: PHY submode the configuration is applicable to.
|
||||
* @opts: Configuration to check
|
||||
*
|
||||
* Used to check that the current set of parameters can be handled by
|
||||
* the phy. Implementations are free to tune the parameters passed as
|
||||
* arguments if needed by some implementation detail or
|
||||
* constraints. It will not change any actual configuration of the
|
||||
* PHY, so calling it as many times as deemed fit will have no side
|
||||
* effect.
|
||||
*
|
||||
* Returns: 0 if successful, an negative error code otherwise
|
||||
*/
|
||||
int phy_validate(struct phy *phy, enum phy_mode mode, int submode,
|
||||
union phy_configure_opts *opts)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!phy)
|
||||
return -EINVAL;
|
||||
|
||||
if (!phy->ops->validate)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&phy->mutex);
|
||||
ret = phy->ops->validate(phy, mode, submode, opts);
|
||||
mutex_unlock(&phy->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_validate);
|
||||
|
||||
/**
|
||||
* _of_phy_get() - lookup and obtain a reference to a phy by phandle
|
||||
* @np: device_node for which to get the phy
|
||||
|
|
|
@ -72,6 +72,9 @@
|
|||
|
||||
#define MAX_PROP_NAME 32
|
||||
|
||||
/* Define the assumed distance between lanes for underspecified device trees. */
|
||||
#define QMP_PHY_LEGACY_LANE_STRIDE 0x400
|
||||
|
||||
struct qmp_phy_init_tbl {
|
||||
unsigned int offset;
|
||||
unsigned int val;
|
||||
|
@ -733,9 +736,6 @@ struct qmp_phy_cfg {
|
|||
bool has_phy_dp_com_ctrl;
|
||||
/* true, if PHY has secondary tx/rx lanes to be configured */
|
||||
bool is_dual_lane_phy;
|
||||
/* Register offset of secondary tx/rx lanes for USB DP combo PHY */
|
||||
unsigned int tx_b_lane_offset;
|
||||
unsigned int rx_b_lane_offset;
|
||||
|
||||
/* true, if PCS block has no separate SW_RESET register */
|
||||
bool no_pcs_sw_reset;
|
||||
|
@ -748,6 +748,8 @@ struct qmp_phy_cfg {
|
|||
* @tx: iomapped memory space for lane's tx
|
||||
* @rx: iomapped memory space for lane's rx
|
||||
* @pcs: iomapped memory space for lane's pcs
|
||||
* @tx2: iomapped memory space for second lane's tx (in dual lane PHYs)
|
||||
* @rx2: iomapped memory space for second lane's rx (in dual lane PHYs)
|
||||
* @pcs_misc: iomapped memory space for lane's pcs_misc
|
||||
* @pipe_clk: pipe lock
|
||||
* @index: lane index
|
||||
|
@ -759,6 +761,8 @@ struct qmp_phy {
|
|||
void __iomem *tx;
|
||||
void __iomem *rx;
|
||||
void __iomem *pcs;
|
||||
void __iomem *tx2;
|
||||
void __iomem *rx2;
|
||||
void __iomem *pcs_misc;
|
||||
struct clk *pipe_clk;
|
||||
unsigned int index;
|
||||
|
@ -975,8 +979,6 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
|
|||
|
||||
.has_phy_dp_com_ctrl = true,
|
||||
.is_dual_lane_phy = true,
|
||||
.tx_b_lane_offset = 0x400,
|
||||
.rx_b_lane_offset = 0x400,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = {
|
||||
|
@ -1031,9 +1033,6 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
|
|||
.mask_pcs_ready = PCS_READY,
|
||||
|
||||
.is_dual_lane_phy = true,
|
||||
.tx_b_lane_offset = 0x400,
|
||||
.rx_b_lane_offset = 0x400,
|
||||
|
||||
.no_pcs_sw_reset = true,
|
||||
};
|
||||
|
||||
|
@ -1238,12 +1237,12 @@ static int qcom_qmp_phy_init(struct phy *phy)
|
|||
qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num);
|
||||
/* Configuration for other LANE for USB-DP combo PHY */
|
||||
if (cfg->is_dual_lane_phy)
|
||||
qcom_qmp_phy_configure(tx + cfg->tx_b_lane_offset, cfg->regs,
|
||||
qcom_qmp_phy_configure(qphy->tx2, cfg->regs,
|
||||
cfg->tx_tbl, cfg->tx_tbl_num);
|
||||
|
||||
qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num);
|
||||
if (cfg->is_dual_lane_phy)
|
||||
qcom_qmp_phy_configure(rx + cfg->rx_b_lane_offset, cfg->regs,
|
||||
qcom_qmp_phy_configure(qphy->rx2, cfg->regs,
|
||||
cfg->rx_tbl, cfg->rx_tbl_num);
|
||||
|
||||
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
|
||||
|
@ -1365,7 +1364,8 @@ static int qcom_qmp_phy_poweron(struct phy *phy)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_qmp_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int qcom_qmp_phy_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct qmp_phy *qphy = phy_get_drvdata(phy);
|
||||
struct qcom_qmp *qmp = qphy->qmp;
|
||||
|
@ -1542,6 +1542,11 @@ static int qcom_qmp_phy_clk_init(struct device *dev)
|
|||
return devm_clk_bulk_get(dev, num, qmp->clks);
|
||||
}
|
||||
|
||||
static void phy_pipe_clk_release_provider(void *res)
|
||||
{
|
||||
of_clk_del_provider(res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a fixed rate pipe clock.
|
||||
*
|
||||
|
@ -1588,7 +1593,23 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
|
|||
fixed->fixed_rate = 125000000;
|
||||
fixed->hw.init = &init;
|
||||
|
||||
return devm_clk_hw_register(qmp->dev, &fixed->hw);
|
||||
ret = devm_clk_hw_register(qmp->dev, &fixed->hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Roll a devm action because the clock provider is the child node, but
|
||||
* the child node is not actually a device.
|
||||
*/
|
||||
ret = devm_add_action(qmp->dev, phy_pipe_clk_release_provider, np);
|
||||
if (ret)
|
||||
phy_pipe_clk_release_provider(np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct phy_ops qcom_qmp_phy_gen_ops = {
|
||||
|
@ -1614,8 +1635,9 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
|
|||
|
||||
/*
|
||||
* Get memory resources for each phy lane:
|
||||
* Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2; and
|
||||
* pcs_misc (optional) -> 3.
|
||||
* Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
|
||||
* For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5
|
||||
* For single lane PHYs: pcs_misc (optional) -> 3.
|
||||
*/
|
||||
qphy->tx = of_iomap(np, 0);
|
||||
if (!qphy->tx)
|
||||
|
@ -1629,7 +1651,32 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
|
|||
if (!qphy->pcs)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* If this is a dual-lane PHY, then there should be registers for the
|
||||
* second lane. Some old device trees did not specify this, so fall
|
||||
* back to old legacy behavior of assuming they can be reached at an
|
||||
* offset from the first lane.
|
||||
*/
|
||||
if (qmp->cfg->is_dual_lane_phy) {
|
||||
qphy->tx2 = of_iomap(np, 3);
|
||||
qphy->rx2 = of_iomap(np, 4);
|
||||
if (!qphy->tx2 || !qphy->rx2) {
|
||||
dev_warn(dev,
|
||||
"Underspecified device tree, falling back to legacy register regions\n");
|
||||
|
||||
/* In the old version, pcs_misc is at index 3. */
|
||||
qphy->pcs_misc = qphy->tx2;
|
||||
qphy->tx2 = qphy->tx + QMP_PHY_LEGACY_LANE_STRIDE;
|
||||
qphy->rx2 = qphy->rx + QMP_PHY_LEGACY_LANE_STRIDE;
|
||||
|
||||
} else {
|
||||
qphy->pcs_misc = of_iomap(np, 5);
|
||||
}
|
||||
|
||||
} else {
|
||||
qphy->pcs_misc = of_iomap(np, 3);
|
||||
}
|
||||
|
||||
if (!qphy->pcs_misc)
|
||||
dev_vdbg(dev, "PHY pcs_misc-reg not used\n");
|
||||
|
||||
|
|
|
@ -425,7 +425,8 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
|
|||
HSTX_TRIM_MASK);
|
||||
}
|
||||
|
||||
static int qusb2_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int qusb2_phy_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct qusb2_phy *qphy = phy_get_drvdata(phy);
|
||||
|
||||
|
|
|
@ -65,7 +65,8 @@ static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy)
|
|||
}
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, enum phy_mode mode)
|
||||
int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
|
||||
|
|
|
@ -84,7 +84,8 @@ static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy)
|
|||
}
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, enum phy_mode mode)
|
||||
int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
|
||||
|
|
|
@ -42,7 +42,8 @@ struct qcom_usb_hs_phy {
|
|||
struct notifier_block vbus_notify;
|
||||
};
|
||||
|
||||
static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int qcom_usb_hs_phy_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
|
||||
u8 addr;
|
||||
|
|
|
@ -307,16 +307,21 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
|
|||
void __iomem *usb2_base = ch->base;
|
||||
u32 val;
|
||||
|
||||
/* Should not use functions of read-modify-write a register */
|
||||
val = readl(usb2_base + USB2_LINECTRL1);
|
||||
val = (val & ~USB2_LINECTRL1_DP_RPD) | USB2_LINECTRL1_DPRPD_EN |
|
||||
USB2_LINECTRL1_DMRPD_EN | USB2_LINECTRL1_DM_RPD;
|
||||
writel(val, usb2_base + USB2_LINECTRL1);
|
||||
|
||||
val = readl(usb2_base + USB2_VBCTRL);
|
||||
writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL);
|
||||
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
|
||||
rcar_gen3_control_otg_irq(ch, 1);
|
||||
val = readl(usb2_base + USB2_ADPCTRL);
|
||||
writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
|
||||
val = readl(usb2_base + USB2_LINECTRL1);
|
||||
rcar_gen3_set_linectrl(ch, 0, 0);
|
||||
writel(val | USB2_LINECTRL1_DPRPD_EN | USB2_LINECTRL1_DMRPD_EN,
|
||||
usb2_base + USB2_LINECTRL1);
|
||||
|
||||
msleep(20);
|
||||
|
||||
writel(0xffffffff, usb2_base + USB2_OBINTSTA);
|
||||
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
|
||||
|
||||
rcar_gen3_device_recognition(ch);
|
||||
}
|
||||
|
|
|
@ -1168,8 +1168,8 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
|
|||
struct phy *phy;
|
||||
|
||||
/* This driver aims to support both otg-port and host-port */
|
||||
if (of_node_cmp(child_np->name, "host-port") &&
|
||||
of_node_cmp(child_np->name, "otg-port"))
|
||||
if (!of_node_name_eq(child_np, "host-port") &&
|
||||
!of_node_name_eq(child_np, "otg-port"))
|
||||
goto next_child;
|
||||
|
||||
phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
|
||||
|
@ -1183,7 +1183,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
|
|||
phy_set_drvdata(rport->phy, rport);
|
||||
|
||||
/* initialize otg/host port separately */
|
||||
if (!of_node_cmp(child_np->name, "host-port")) {
|
||||
if (of_node_name_eq(child_np, "host-port")) {
|
||||
ret = rockchip_usb2phy_host_port_init(rphy, rport,
|
||||
child_np);
|
||||
if (ret)
|
||||
|
|
|
@ -1176,10 +1176,10 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
|
|||
for_each_available_child_of_node(np, child_np) {
|
||||
struct phy *phy;
|
||||
|
||||
if (!of_node_cmp(child_np->name, "dp-port"))
|
||||
if (of_node_name_eq(child_np, "dp-port"))
|
||||
phy = devm_phy_create(dev, child_np,
|
||||
&rockchip_dp_phy_ops);
|
||||
else if (!of_node_cmp(child_np->name, "usb3-port"))
|
||||
else if (of_node_name_eq(child_np, "usb3-port"))
|
||||
phy = devm_phy_create(dev, child_np,
|
||||
&rockchip_usb3_phy_ops);
|
||||
else
|
||||
|
|
|
@ -76,3 +76,13 @@ config TWL4030_USB
|
|||
family chips (including the TWL5030 and TPS659x0 devices).
|
||||
This transceiver supports high and full speed devices plus,
|
||||
in host mode, low speed.
|
||||
|
||||
config PHY_TI_GMII_SEL
|
||||
tristate
|
||||
default y if TI_CPSW=y
|
||||
depends on TI_CPSW || COMPILE_TEST
|
||||
select GENERIC_PHY
|
||||
default m
|
||||
help
|
||||
This driver supports configuring of the TI CPSW Port mode depending on
|
||||
the Ethernet PHY connected to the CPSW Port.
|
||||
|
|
|
@ -6,3 +6,4 @@ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
|
|||
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
|
||||
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
|
||||
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
|
||||
obj-$(CONFIG_PHY_TI_GMII_SEL) += phy-gmii-sel.o
|
||||
|
|
|
@ -93,7 +93,8 @@ static int da8xx_usb20_phy_power_off(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int da8xx_usb20_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int da8xx_usb20_phy_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
|
||||
u32 val;
|
||||
|
|
|
@ -0,0 +1,349 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Texas Instruments CPSW Port's PHY Interface Mode selection Driver
|
||||
*
|
||||
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
* Based on cpsw-phy-sel.c driver created by Mugunthan V N <mugunthanvnm@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* AM33xx SoC specific definitions for the CONTROL port */
|
||||
#define AM33XX_GMII_SEL_MODE_MII 0
|
||||
#define AM33XX_GMII_SEL_MODE_RMII 1
|
||||
#define AM33XX_GMII_SEL_MODE_RGMII 2
|
||||
|
||||
enum {
|
||||
PHY_GMII_SEL_PORT_MODE,
|
||||
PHY_GMII_SEL_RGMII_ID_MODE,
|
||||
PHY_GMII_SEL_RMII_IO_CLK_EN,
|
||||
PHY_GMII_SEL_LAST,
|
||||
};
|
||||
|
||||
struct phy_gmii_sel_phy_priv {
|
||||
struct phy_gmii_sel_priv *priv;
|
||||
u32 id;
|
||||
struct phy *if_phy;
|
||||
int rmii_clock_external;
|
||||
int phy_if_mode;
|
||||
struct regmap_field *fields[PHY_GMII_SEL_LAST];
|
||||
};
|
||||
|
||||
struct phy_gmii_sel_soc_data {
|
||||
u32 num_ports;
|
||||
u32 features;
|
||||
const struct reg_field (*regfields)[PHY_GMII_SEL_LAST];
|
||||
};
|
||||
|
||||
struct phy_gmii_sel_priv {
|
||||
struct device *dev;
|
||||
const struct phy_gmii_sel_soc_data *soc_data;
|
||||
struct regmap *regmap;
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy_gmii_sel_phy_priv *if_phys;
|
||||
};
|
||||
|
||||
static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
struct phy_gmii_sel_phy_priv *if_phy = phy_get_drvdata(phy);
|
||||
const struct phy_gmii_sel_soc_data *soc_data = if_phy->priv->soc_data;
|
||||
struct device *dev = if_phy->priv->dev;
|
||||
struct regmap_field *regfield;
|
||||
int ret, rgmii_id = 0;
|
||||
u32 gmii_sel_mode = 0;
|
||||
|
||||
if (mode != PHY_MODE_ETHERNET)
|
||||
return -EINVAL;
|
||||
|
||||
switch (submode) {
|
||||
case PHY_INTERFACE_MODE_RMII:
|
||||
gmii_sel_mode = AM33XX_GMII_SEL_MODE_RMII;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
gmii_sel_mode = AM33XX_GMII_SEL_MODE_RGMII;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_RGMII_ID:
|
||||
case PHY_INTERFACE_MODE_RGMII_RXID:
|
||||
case PHY_INTERFACE_MODE_RGMII_TXID:
|
||||
gmii_sel_mode = AM33XX_GMII_SEL_MODE_RGMII;
|
||||
rgmii_id = 1;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_MII:
|
||||
mode = AM33XX_GMII_SEL_MODE_MII;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(dev,
|
||||
"port%u: unsupported mode: \"%s\". Defaulting to MII.\n",
|
||||
if_phy->id, phy_modes(rgmii_id));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if_phy->phy_if_mode = submode;
|
||||
|
||||
dev_dbg(dev, "%s id:%u mode:%u rgmii_id:%d rmii_clk_ext:%d\n",
|
||||
__func__, if_phy->id, mode, rgmii_id,
|
||||
if_phy->rmii_clock_external);
|
||||
|
||||
regfield = if_phy->fields[PHY_GMII_SEL_PORT_MODE];
|
||||
ret = regmap_field_write(regfield, gmii_sel_mode);
|
||||
if (ret) {
|
||||
dev_err(dev, "port%u: set mode fail %d", if_phy->id, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (soc_data->features & BIT(PHY_GMII_SEL_RGMII_ID_MODE) &&
|
||||
if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE]) {
|
||||
regfield = if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE];
|
||||
ret = regmap_field_write(regfield, rgmii_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) &&
|
||||
if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN]) {
|
||||
regfield = if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN];
|
||||
ret = regmap_field_write(regfield,
|
||||
if_phy->rmii_clock_external);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const
|
||||
struct reg_field phy_gmii_sel_fields_am33xx[][PHY_GMII_SEL_LAST] = {
|
||||
{
|
||||
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x650, 0, 1),
|
||||
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD(0x650, 4, 4),
|
||||
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD(0x650, 6, 6),
|
||||
},
|
||||
{
|
||||
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x650, 2, 3),
|
||||
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD(0x650, 5, 5),
|
||||
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD(0x650, 7, 7),
|
||||
},
|
||||
};
|
||||
|
||||
static const
|
||||
struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am33xx = {
|
||||
.num_ports = 2,
|
||||
.features = BIT(PHY_GMII_SEL_RGMII_ID_MODE) |
|
||||
BIT(PHY_GMII_SEL_RMII_IO_CLK_EN),
|
||||
.regfields = phy_gmii_sel_fields_am33xx,
|
||||
};
|
||||
|
||||
static const
|
||||
struct reg_field phy_gmii_sel_fields_dra7[][PHY_GMII_SEL_LAST] = {
|
||||
{
|
||||
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 0, 1),
|
||||
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
|
||||
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
|
||||
},
|
||||
{
|
||||
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 4, 5),
|
||||
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
|
||||
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
|
||||
},
|
||||
};
|
||||
|
||||
static const
|
||||
struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dra7 = {
|
||||
.num_ports = 2,
|
||||
.regfields = phy_gmii_sel_fields_dra7,
|
||||
};
|
||||
|
||||
static const
|
||||
struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dm814 = {
|
||||
.num_ports = 2,
|
||||
.features = BIT(PHY_GMII_SEL_RGMII_ID_MODE),
|
||||
.regfields = phy_gmii_sel_fields_am33xx,
|
||||
};
|
||||
|
||||
static const struct of_device_id phy_gmii_sel_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,am3352-phy-gmii-sel",
|
||||
.data = &phy_gmii_sel_soc_am33xx,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,dra7xx-phy-gmii-sel",
|
||||
.data = &phy_gmii_sel_soc_dra7,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,am43xx-phy-gmii-sel",
|
||||
.data = &phy_gmii_sel_soc_am33xx,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,dm814-phy-gmii-sel",
|
||||
.data = &phy_gmii_sel_soc_dm814,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, phy_gmii_sel_id_table);
|
||||
|
||||
static const struct phy_ops phy_gmii_sel_ops = {
|
||||
.set_mode = phy_gmii_sel_mode,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct phy *phy_gmii_sel_of_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct phy_gmii_sel_priv *priv = dev_get_drvdata(dev);
|
||||
int phy_id = args->args[0];
|
||||
|
||||
if (args->args_count < 1)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) &&
|
||||
args->args_count < 2)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (!priv || !priv->if_phys)
|
||||
return ERR_PTR(-ENODEV);
|
||||
if (phy_id > priv->soc_data->num_ports)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (phy_id != priv->if_phys[phy_id - 1].id)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
phy_id--;
|
||||
if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN))
|
||||
priv->if_phys[phy_id].rmii_clock_external = args->args[1];
|
||||
dev_dbg(dev, "%s id:%u ext:%d\n", __func__,
|
||||
priv->if_phys[phy_id].id, args->args[1]);
|
||||
|
||||
return priv->if_phys[phy_id].if_phy;
|
||||
}
|
||||
|
||||
static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv)
|
||||
{
|
||||
const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data;
|
||||
struct device *dev = priv->dev;
|
||||
struct phy_gmii_sel_phy_priv *if_phys;
|
||||
int i, num_ports, ret;
|
||||
|
||||
num_ports = priv->soc_data->num_ports;
|
||||
|
||||
if_phys = devm_kcalloc(priv->dev, num_ports,
|
||||
sizeof(*if_phys), GFP_KERNEL);
|
||||
if (!if_phys)
|
||||
return -ENOMEM;
|
||||
dev_dbg(dev, "%s %d\n", __func__, num_ports);
|
||||
|
||||
for (i = 0; i < num_ports; i++) {
|
||||
const struct reg_field *field;
|
||||
struct regmap_field *regfield;
|
||||
|
||||
if_phys[i].id = i + 1;
|
||||
if_phys[i].priv = priv;
|
||||
|
||||
field = &soc_data->regfields[i][PHY_GMII_SEL_PORT_MODE];
|
||||
dev_dbg(dev, "%s field %x %d %d\n", __func__,
|
||||
field->reg, field->msb, field->lsb);
|
||||
|
||||
regfield = devm_regmap_field_alloc(dev, priv->regmap, *field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phys[i].fields[PHY_GMII_SEL_PORT_MODE] = regfield;
|
||||
|
||||
field = &soc_data->regfields[i][PHY_GMII_SEL_RGMII_ID_MODE];
|
||||
if (field->reg != (~0)) {
|
||||
regfield = devm_regmap_field_alloc(dev,
|
||||
priv->regmap,
|
||||
*field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phys[i].fields[PHY_GMII_SEL_RGMII_ID_MODE] =
|
||||
regfield;
|
||||
}
|
||||
|
||||
field = &soc_data->regfields[i][PHY_GMII_SEL_RMII_IO_CLK_EN];
|
||||
if (field->reg != (~0)) {
|
||||
regfield = devm_regmap_field_alloc(dev,
|
||||
priv->regmap,
|
||||
*field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phys[i].fields[PHY_GMII_SEL_RMII_IO_CLK_EN] =
|
||||
regfield;
|
||||
}
|
||||
|
||||
if_phys[i].if_phy = devm_phy_create(dev,
|
||||
priv->dev->of_node,
|
||||
&phy_gmii_sel_ops);
|
||||
if (IS_ERR(if_phys[i].if_phy)) {
|
||||
ret = PTR_ERR(if_phys[i].if_phy);
|
||||
dev_err(dev, "Failed to create phy%d %d\n", i, ret);
|
||||
return ret;
|
||||
}
|
||||
phy_set_drvdata(if_phys[i].if_phy, &if_phys[i]);
|
||||
}
|
||||
|
||||
priv->if_phys = if_phys;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_gmii_sel_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
const struct of_device_id *of_id;
|
||||
struct phy_gmii_sel_priv *priv;
|
||||
int ret;
|
||||
|
||||
of_id = of_match_node(phy_gmii_sel_id_table, pdev->dev.of_node);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
priv->soc_data = of_id->data;
|
||||
|
||||
priv->regmap = syscon_node_to_regmap(node->parent);
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
ret = PTR_ERR(priv->regmap);
|
||||
dev_err(dev, "Failed to get syscon %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = phy_gmii_sel_init_ports(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
priv->phy_provider =
|
||||
devm_of_phy_provider_register(dev,
|
||||
phy_gmii_sel_of_xlate);
|
||||
if (IS_ERR(priv->phy_provider)) {
|
||||
ret = PTR_ERR(priv->phy_provider);
|
||||
dev_err(dev, "Failed to create phy provider %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver phy_gmii_sel_driver = {
|
||||
.probe = phy_gmii_sel_probe,
|
||||
.driver = {
|
||||
.name = "phy-gmii-sel",
|
||||
.of_match_table = phy_gmii_sel_id_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(phy_gmii_sel_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Grygorii Strashko <grygorii.strashko@ti.com>");
|
||||
MODULE_DESCRIPTION("TI CPSW Port's PHY Interface Mode selection Driver");
|
|
@ -53,7 +53,7 @@ static int tusb1210_power_off(struct phy *phy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
struct tusb1210 *tusb = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
|
|
@ -0,0 +1,285 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2018 Cadence Design Systems Inc.
|
||||
*/
|
||||
|
||||
#ifndef __PHY_MIPI_DPHY_H_
|
||||
#define __PHY_MIPI_DPHY_H_
|
||||
|
||||
#include <video/videomode.h>
|
||||
|
||||
/**
|
||||
* struct phy_configure_opts_mipi_dphy - MIPI D-PHY configuration set
|
||||
*
|
||||
* This structure is used to represent the configuration state of a
|
||||
* MIPI D-PHY phy.
|
||||
*/
|
||||
struct phy_configure_opts_mipi_dphy {
|
||||
/**
|
||||
* @clk_miss:
|
||||
*
|
||||
* Timeout, in picoseconds, for receiver to detect absence of
|
||||
* Clock transitions and disable the Clock Lane HS-RX.
|
||||
*
|
||||
* Maximum value: 60000 ps
|
||||
*/
|
||||
unsigned int clk_miss;
|
||||
|
||||
/**
|
||||
* @clk_post:
|
||||
*
|
||||
* Time, in picoseconds, that the transmitter continues to
|
||||
* send HS clock after the last associated Data Lane has
|
||||
* transitioned to LP Mode. Interval is defined as the period
|
||||
* from the end of @hs_trail to the beginning of @clk_trail.
|
||||
*
|
||||
* Minimum value: 60000 ps + 52 * @hs_clk_rate period in ps
|
||||
*/
|
||||
unsigned int clk_post;
|
||||
|
||||
/**
|
||||
* @clk_pre:
|
||||
*
|
||||
* Time, in UI, that the HS clock shall be driven by
|
||||
* the transmitter prior to any associated Data Lane beginning
|
||||
* the transition from LP to HS mode.
|
||||
*
|
||||
* Minimum value: 8 UI
|
||||
*/
|
||||
unsigned int clk_pre;
|
||||
|
||||
/**
|
||||
* @clk_prepare:
|
||||
*
|
||||
* Time, in picoseconds, that the transmitter drives the Clock
|
||||
* Lane LP-00 Line state immediately before the HS-0 Line
|
||||
* state starting the HS transmission.
|
||||
*
|
||||
* Minimum value: 38000 ps
|
||||
* Maximum value: 95000 ps
|
||||
*/
|
||||
unsigned int clk_prepare;
|
||||
|
||||
/**
|
||||
* @clk_settle:
|
||||
*
|
||||
* Time interval, in picoseconds, during which the HS receiver
|
||||
* should ignore any Clock Lane HS transitions, starting from
|
||||
* the beginning of @clk_prepare.
|
||||
*
|
||||
* Minimum value: 95000 ps
|
||||
* Maximum value: 300000 ps
|
||||
*/
|
||||
unsigned int clk_settle;
|
||||
|
||||
/**
|
||||
* @clk_term_en:
|
||||
*
|
||||
* Time, in picoseconds, for the Clock Lane receiver to enable
|
||||
* the HS line termination.
|
||||
*
|
||||
* Maximum value: 38000 ps
|
||||
*/
|
||||
unsigned int clk_term_en;
|
||||
|
||||
/**
|
||||
* @clk_trail:
|
||||
*
|
||||
* Time, in picoseconds, that the transmitter drives the HS-0
|
||||
* state after the last payload clock bit of a HS transmission
|
||||
* burst.
|
||||
*
|
||||
* Minimum value: 60000 ps
|
||||
*/
|
||||
unsigned int clk_trail;
|
||||
|
||||
/**
|
||||
* @clk_zero:
|
||||
*
|
||||
* Time, in picoseconds, that the transmitter drives the HS-0
|
||||
* state prior to starting the Clock.
|
||||
*/
|
||||
unsigned int clk_zero;
|
||||
|
||||
/**
|
||||
* @d_term_en:
|
||||
*
|
||||
* Time, in picoseconds, for the Data Lane receiver to enable
|
||||
* the HS line termination.
|
||||
*
|
||||
* Maximum value: 35000 ps + 4 * @hs_clk_rate period in ps
|
||||
*/
|
||||
unsigned int d_term_en;
|
||||
|
||||
/**
|
||||
* @eot:
|
||||
*
|
||||
* Transmitted time interval, in picoseconds, from the start
|
||||
* of @hs_trail or @clk_trail, to the start of the LP- 11
|
||||
* state following a HS burst.
|
||||
*
|
||||
* Maximum value: 105000 ps + 12 * @hs_clk_rate period in ps
|
||||
*/
|
||||
unsigned int eot;
|
||||
|
||||
/**
|
||||
* @hs_exit:
|
||||
*
|
||||
* Time, in picoseconds, that the transmitter drives LP-11
|
||||
* following a HS burst.
|
||||
*
|
||||
* Minimum value: 100000 ps
|
||||
*/
|
||||
unsigned int hs_exit;
|
||||
|
||||
/**
|
||||
* @hs_prepare:
|
||||
*
|
||||
* Time, in picoseconds, that the transmitter drives the Data
|
||||
* Lane LP-00 Line state immediately before the HS-0 Line
|
||||
* state starting the HS transmission.
|
||||
*
|
||||
* Minimum value: 40000 ps + 4 * @hs_clk_rate period in ps
|
||||
* Maximum value: 85000 ps + 6 * @hs_clk_rate period in ps
|
||||
*/
|
||||
unsigned int hs_prepare;
|
||||
|
||||
/**
|
||||
* @hs_settle:
|
||||
*
|
||||
* Time interval, in picoseconds, during which the HS receiver
|
||||
* shall ignore any Data Lane HS transitions, starting from
|
||||
* the beginning of @hs_prepare.
|
||||
*
|
||||
* Minimum value: 85000 ps + 6 * @hs_clk_rate period in ps
|
||||
* Maximum value: 145000 ps + 10 * @hs_clk_rate period in ps
|
||||
*/
|
||||
unsigned int hs_settle;
|
||||
|
||||
/**
|
||||
* @hs_skip:
|
||||
*
|
||||
* Time interval, in picoseconds, during which the HS-RX
|
||||
* should ignore any transitions on the Data Lane, following a
|
||||
* HS burst. The end point of the interval is defined as the
|
||||
* beginning of the LP-11 state following the HS burst.
|
||||
*
|
||||
* Minimum value: 40000 ps
|
||||
* Maximum value: 55000 ps + 4 * @hs_clk_rate period in ps
|
||||
*/
|
||||
unsigned int hs_skip;
|
||||
|
||||
/**
|
||||
* @hs_trail:
|
||||
*
|
||||
* Time, in picoseconds, that the transmitter drives the
|
||||
* flipped differential state after last payload data bit of a
|
||||
* HS transmission burst
|
||||
*
|
||||
* Minimum value: max(8 * @hs_clk_rate period in ps,
|
||||
* 60000 ps + 4 * @hs_clk_rate period in ps)
|
||||
*/
|
||||
unsigned int hs_trail;
|
||||
|
||||
/**
|
||||
* @hs_zero:
|
||||
*
|
||||
* Time, in picoseconds, that the transmitter drives the HS-0
|
||||
* state prior to transmitting the Sync sequence.
|
||||
*/
|
||||
unsigned int hs_zero;
|
||||
|
||||
/**
|
||||
* @init:
|
||||
*
|
||||
* Time, in picoseconds for the initialization period to
|
||||
* complete.
|
||||
*
|
||||
* Minimum value: 100000000 ps
|
||||
*/
|
||||
unsigned int init;
|
||||
|
||||
/**
|
||||
* @lpx:
|
||||
*
|
||||
* Transmitted length, in picoseconds, of any Low-Power state
|
||||
* period.
|
||||
*
|
||||
* Minimum value: 50000 ps
|
||||
*/
|
||||
unsigned int lpx;
|
||||
|
||||
/**
|
||||
* @ta_get:
|
||||
*
|
||||
* Time, in picoseconds, that the new transmitter drives the
|
||||
* Bridge state (LP-00) after accepting control during a Link
|
||||
* Turnaround.
|
||||
*
|
||||
* Value: 5 * @lpx
|
||||
*/
|
||||
unsigned int ta_get;
|
||||
|
||||
/**
|
||||
* @ta_go:
|
||||
*
|
||||
* Time, in picoseconds, that the transmitter drives the
|
||||
* Bridge state (LP-00) before releasing control during a Link
|
||||
* Turnaround.
|
||||
*
|
||||
* Value: 4 * @lpx
|
||||
*/
|
||||
unsigned int ta_go;
|
||||
|
||||
/**
|
||||
* @ta_sure:
|
||||
*
|
||||
* Time, in picoseconds, that the new transmitter waits after
|
||||
* the LP-10 state before transmitting the Bridge state
|
||||
* (LP-00) during a Link Turnaround.
|
||||
*
|
||||
* Minimum value: @lpx
|
||||
* Maximum value: 2 * @lpx
|
||||
*/
|
||||
unsigned int ta_sure;
|
||||
|
||||
/**
|
||||
* @wakeup:
|
||||
*
|
||||
* Time, in picoseconds, that a transmitter drives a Mark-1
|
||||
* state prior to a Stop state in order to initiate an exit
|
||||
* from ULPS.
|
||||
*
|
||||
* Minimum value: 1000000000 ps
|
||||
*/
|
||||
unsigned int wakeup;
|
||||
|
||||
/**
|
||||
* @hs_clk_rate:
|
||||
*
|
||||
* Clock rate, in Hertz, of the high-speed clock.
|
||||
*/
|
||||
unsigned long hs_clk_rate;
|
||||
|
||||
/**
|
||||
* @lp_clk_rate:
|
||||
*
|
||||
* Clock rate, in Hertz, of the low-power clock.
|
||||
*/
|
||||
unsigned long lp_clk_rate;
|
||||
|
||||
/**
|
||||
* @lanes:
|
||||
*
|
||||
* Number of active data lanes used for the transmissions.
|
||||
*/
|
||||
unsigned char lanes;
|
||||
};
|
||||
|
||||
int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
|
||||
unsigned int bpp,
|
||||
unsigned int lanes,
|
||||
struct phy_configure_opts_mipi_dphy *cfg);
|
||||
int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg);
|
||||
|
||||
#endif /* __PHY_MIPI_DPHY_H_ */
|
|
@ -20,6 +20,8 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/phy/phy-mipi-dphy.h>
|
||||
|
||||
struct phy;
|
||||
|
||||
enum phy_mode {
|
||||
|
@ -35,13 +37,21 @@ enum phy_mode {
|
|||
PHY_MODE_USB_DEVICE_HS,
|
||||
PHY_MODE_USB_DEVICE_SS,
|
||||
PHY_MODE_USB_OTG,
|
||||
PHY_MODE_SGMII,
|
||||
PHY_MODE_2500SGMII,
|
||||
PHY_MODE_QSGMII,
|
||||
PHY_MODE_10GKR,
|
||||
PHY_MODE_UFS_HS_A,
|
||||
PHY_MODE_UFS_HS_B,
|
||||
PHY_MODE_PCIE,
|
||||
PHY_MODE_ETHERNET,
|
||||
PHY_MODE_MIPI_DPHY,
|
||||
};
|
||||
|
||||
/**
|
||||
* union phy_configure_opts - Opaque generic phy configuration
|
||||
*
|
||||
* @mipi_dphy: Configuration set applicable for phys supporting
|
||||
* the MIPI_DPHY phy mode.
|
||||
*/
|
||||
union phy_configure_opts {
|
||||
struct phy_configure_opts_mipi_dphy mipi_dphy;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -60,7 +70,38 @@ struct phy_ops {
|
|||
int (*exit)(struct phy *phy);
|
||||
int (*power_on)(struct phy *phy);
|
||||
int (*power_off)(struct phy *phy);
|
||||
int (*set_mode)(struct phy *phy, enum phy_mode mode);
|
||||
int (*set_mode)(struct phy *phy, enum phy_mode mode, int submode);
|
||||
|
||||
/**
|
||||
* @configure:
|
||||
*
|
||||
* Optional.
|
||||
*
|
||||
* Used to change the PHY parameters. phy_init() must have
|
||||
* been called on the phy.
|
||||
*
|
||||
* Returns: 0 if successful, an negative error code otherwise
|
||||
*/
|
||||
int (*configure)(struct phy *phy, union phy_configure_opts *opts);
|
||||
|
||||
/**
|
||||
* @validate:
|
||||
*
|
||||
* Optional.
|
||||
*
|
||||
* Used to check that the current set of parameters can be
|
||||
* handled by the phy. Implementations are free to tune the
|
||||
* parameters passed as arguments if needed by some
|
||||
* implementation detail or constraints. It must not change
|
||||
* any actual configuration of the PHY, so calling it as many
|
||||
* times as deemed fit by the consumer must have no side
|
||||
* effect.
|
||||
*
|
||||
* Returns: 0 if the configuration can be applied, an negative
|
||||
* error code otherwise
|
||||
*/
|
||||
int (*validate)(struct phy *phy, enum phy_mode mode, int submode,
|
||||
union phy_configure_opts *opts);
|
||||
int (*reset)(struct phy *phy);
|
||||
int (*calibrate)(struct phy *phy);
|
||||
struct module *owner;
|
||||
|
@ -164,7 +205,13 @@ int phy_init(struct phy *phy);
|
|||
int phy_exit(struct phy *phy);
|
||||
int phy_power_on(struct phy *phy);
|
||||
int phy_power_off(struct phy *phy);
|
||||
int phy_set_mode(struct phy *phy, enum phy_mode mode);
|
||||
int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode);
|
||||
#define phy_set_mode(phy, mode) \
|
||||
phy_set_mode_ext(phy, mode, 0)
|
||||
int phy_configure(struct phy *phy, union phy_configure_opts *opts);
|
||||
int phy_validate(struct phy *phy, enum phy_mode mode, int submode,
|
||||
union phy_configure_opts *opts);
|
||||
|
||||
static inline enum phy_mode phy_get_mode(struct phy *phy)
|
||||
{
|
||||
return phy->attrs.mode;
|
||||
|
@ -278,13 +325,17 @@ static inline int phy_power_off(struct phy *phy)
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static inline int phy_set_mode_ext(struct phy *phy, enum phy_mode mode,
|
||||
int submode)
|
||||
{
|
||||
if (!phy)
|
||||
return 0;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
#define phy_set_mode(phy, mode) \
|
||||
phy_set_mode_ext(phy, mode, 0)
|
||||
|
||||
static inline enum phy_mode phy_get_mode(struct phy *phy)
|
||||
{
|
||||
return PHY_MODE_INVALID;
|
||||
|
@ -304,6 +355,24 @@ static inline int phy_calibrate(struct phy *phy)
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int phy_configure(struct phy *phy,
|
||||
union phy_configure_opts *opts)
|
||||
{
|
||||
if (!phy)
|
||||
return 0;
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int phy_validate(struct phy *phy, enum phy_mode mode, int submode,
|
||||
union phy_configure_opts *opts)
|
||||
{
|
||||
if (!phy)
|
||||
return 0;
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int phy_get_bus_width(struct phy *phy)
|
||||
{
|
||||
return -ENOSYS;
|
||||
|
|
Loading…
Reference in New Issue