phy-for-6.19

- Core
   - Drop Kishon as maintainer, thanks to him for helping, move to credits and
     add Neil to help with reviews.
   - Add new phy_notify_stat to notify phy from controllers during the
     runtime transitions and usage in samsung phy
 
  - New Support
   - Renesas RZ/G3E USB3.0 driver
   - NXP Support TJA1048/TJA1051 CAN phy
   - Rockchip support for rk3506 dsi dphy
   - Qualcomm Glymur QMP PCIe PHY support
 
 - Updates
   - PM support for rcar-gen3-usb2 driver
   - Samsung HDMI/eDP Transmitter Combo PHY updates
   - Freescale imx8mq support for alternate reference clock
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmk2fUUACgkQfBQHDyUj
 g0dBWA/9G7FZahHvVj40hS721fYJ8RYVWkRwaPaUg+DBd8rRgU0lE+rYXC60Y+Hp
 lILhtwb9NvzjR9+i9fuVmrMWRdYCb7aLzwOjRSzxApnB1s3S7Nl7M33/xrtZ6n6K
 KL978REzaJgQzRICZfjrky65OfUA942eqmgtTmMpfdnJCHI8QOzIwQApQt1Zubjt
 Gu+R4D1iujarDM0J+J6UudixTdnYk5UnY+UqpsR/e6g9E5ERk5g7xC/NG49Q7oim
 L2TXB755ZXAJdlMP6KQGFAS8bq44qvudrOaiy2PHVy1yxmhmcAeh1GmR51WJKOmM
 lVypGbKgsce2eFWwCDe3fVtA2aJD9urdWtn5MAXQRdC8Cwz7Q7P8ne7Q9FXhHGr8
 GgGSXd1iQho7zOwm3LGRJ4ItSb2dK3sypldjskD2lMXGrm53y7DMfwsDsK3ZzCrZ
 YV3+klzeeQrA0jVTRD4CS2eZ62GdsGRx/8XrvKe4eMd4EBsgDTLG/894PQU/VOte
 V/rSv6d4CW/UfRVychlEW9+4f9gqpgsfgdl39u/cVS5c/VPTV7XtMOd/9bFM+k1Q
 qOVPLwBcz24hO8e+3jbYouiISbrvxGpLVK4PomyesOm/5AWNM+30vGFWjR5cXngi
 DekaYa4nD2m1Dk0h+wD5gvgdfzhUuhNOnWggPRmUSywGirrnj+I=
 =mvDM
 -----END PGP SIGNATURE-----

Merge tag 'phy-for-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy

Pull phy updates from Vinod Koul:
 "Core:
   - Drop Kishon as maintainer, thanks to him for helping, move to
     credits and add Neil to help with reviews.
   - Add new phy_notify_stat to notify phy from controllers during the
     runtime transitions and usage in samsung phy

  New hardware support:
   - Renesas RZ/G3E USB3.0 driver
   - NXP Support TJA1048/TJA1051 CAN phy
   - Rockchip support for rk3506 dsi dphy
   - Qualcomm Glymur QMP PCIe PHY support

  Updates:
   - PM support for rcar-gen3-usb2 driver
   - Samsung HDMI/eDP Transmitter Combo PHY updates
   - Freescale imx8mq support for alternate reference clock"

* tag 'phy-for-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy: (40 commits)
  MAINTAINERS: phy: Add Neil Armstrong as reviewers for phy subsystem
  MAINTAINERS: phy: Move Kishon Vijay Abraham I to credits
  phy: fsl-imx8mq-usb: support alternate reference clock
  dt-bindings: phy: imx8mq-usb: add alternate reference clock
  phy: rockchip: samsung-hdptx: Prevent Inter-Pair Skew from exceeding the limits
  phy: rockchip: samsung-hdptx: Reduce ROPLL loop bandwidth
  phy: rockchip: samsung-hdptx: Fix reported clock rate in high bpc mode
  phy: ti: gmii-sel: Add a sanity check on the phy_id
  phy: qcom: qmp-pcie: Add support for Glymur PCIe Gen5x4 PHY
  phy: qcom-qmp: pcs: Add v8.50 register offsets
  dt-bindings: phy: qcom,sc8280xp-qmp-pcie-phy: Document the Glymur QMP PCIe PHY
  dt-bindings: phy: qcom,sc8280xp-qmp-pcie-phy: Restrict resets per each device
  phy: freescale: Initialize priv->lock
  phy: renesas: Remove unneeded semicolons
  phy: qcom: m31-eusb2: Update init sequence to set PHY_ENABLE
  phy: qcom: qmp-combo: get the USB3 & DisplayPort lanes mapping from DT
  dt-bindings: phy: qcom,sc8280xp-qmp-usb43dp-phy: Document lanes mapping when not using in USB-C complex
  phy: rockchip: naneng-combphy: Fix PCIe L1ss support RK3562
  phy: rockchip: naneng-combphy: Fix PCIe L1ss support RK3528
  phy: renesas: rcar-gen3-usb2: Add suspend/resume support
  ...
master
Linus Torvalds 2025-12-09 06:31:47 +09:00
commit 0623fdf30b
36 changed files with 1119 additions and 143 deletions

View File

@ -16,6 +16,10 @@ D: One of assisting postmasters for vger.kernel.org's lists
S: (ask for current address)
S: Finland
N: Kishon Vijay Abraham I
E: kishon@kernel.org
D: Generic Phy Framework
N: Thomas Abraham
E: thomas.ab@samsung.com
D: Samsung pin controller driver

View File

@ -27,11 +27,16 @@ properties:
const: 0
clocks:
maxItems: 1
minItems: 1
items:
- description: PHY configuration clock
- description: Alternate PHY reference clock
clock-names:
minItems: 1
items:
- const: phy
- const: alt
power-domains:
maxItems: 1

View File

@ -80,6 +80,7 @@ properties:
- mediatek,mt2712-tphy
- mediatek,mt6893-tphy
- mediatek,mt7629-tphy
- mediatek,mt7981-tphy
- mediatek,mt7986-tphy
- mediatek,mt8183-tphy
- mediatek,mt8186-tphy

View File

@ -16,6 +16,7 @@ description:
properties:
compatible:
enum:
- qcom,glymur-qmp-gen5x4-pcie-phy
- qcom,qcs615-qmp-gen3x1-pcie-phy
- qcom,qcs8300-qmp-gen4x2-pcie-phy
- qcom,sa8775p-qmp-gen4x2-pcie-phy
@ -178,6 +179,7 @@ allOf:
compatible:
contains:
enum:
- qcom,glymur-qmp-gen5x4-pcie-phy
- qcom,sa8775p-qmp-gen4x2-pcie-phy
- qcom,sa8775p-qmp-gen4x4-pcie-phy
- qcom,sc8280xp-qmp-gen3x1-pcie-phy
@ -213,17 +215,26 @@ allOf:
compatible:
contains:
enum:
- qcom,glymur-qmp-gen5x4-pcie-phy
- qcom,sm8550-qmp-gen4x2-pcie-phy
- qcom,sm8650-qmp-gen4x2-pcie-phy
- qcom,x1e80100-qmp-gen3x2-pcie-phy
- qcom,x1e80100-qmp-gen4x2-pcie-phy
- qcom,x1e80100-qmp-gen4x4-pcie-phy
- qcom,x1e80100-qmp-gen4x8-pcie-phy
- qcom,x1p42100-qmp-gen4x4-pcie-phy
then:
properties:
resets:
minItems: 2
reset-names:
minItems: 2
else:
properties:
resets:
maxItems: 1
reset-names:
maxItems: 1
- if:
properties:

View File

@ -78,10 +78,77 @@ properties:
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
$ref: /schemas/graph.yaml#/$defs/port-base
description: Output endpoint of the PHY
unevaluatedProperties: false
properties:
endpoint:
$ref: /schemas/graph.yaml#/$defs/endpoint-base
unevaluatedProperties: false
endpoint@0:
$ref: /schemas/graph.yaml#/$defs/endpoint-base
description: Display Port Output lanes of the PHY when used with static mapping,
The entry index is the DP lanes index, and the number is the PHY
signal in the order RX0, TX0, TX1, RX1.
unevaluatedProperties: false
properties:
# Static lane mappings are mutually exclusive with typec-mux/orientation-mux
data-lanes:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 2
maxItems: 4
oneOf:
- items: # DisplayPort 1 lane, normal orientation
- const: 3
- items: # DisplayPort 1 lane, flipped orientation
- const: 0
- items: # DisplayPort 2 lanes, normal orientation
- const: 3
- const: 2
- items: # DisplayPort 2 lanes, flipped orientation
- const: 0
- const: 1
- items: # DisplayPort 4 lanes, normal orientation
- const: 3
- const: 2
- const: 1
- const: 0
- items: # DisplayPort 4 lanes, flipped orientation
- const: 0
- const: 1
- const: 2
- const: 3
required:
- data-lanes
endpoint@1:
$ref: /schemas/graph.yaml#/$defs/endpoint-base
description: USB Output lanes of the PHY when used with static mapping.
The entry index is the USB3 lane in the order TX then RX, and the
number is the PHY signal in the order RX0, TX0, TX1, RX1.
unevaluatedProperties: false
properties:
# Static lane mappings are mutually exclusive with typec-mux/orientation-mux
data-lanes:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 2
oneOf:
- items: # USB3, normal orientation
- const: 1
- const: 0
- items: # USB3, flipped orientation
- const: 2
- const: 3
required:
- data-lanes
port@1:
$ref: /schemas/graph.yaml#/properties/port

View File

@ -0,0 +1,63 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/renesas,rzg3e-usb3-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/G3E USB 3.0 PHY
maintainers:
- Biju Das <biju.das.jz@bp.renesas.com>
properties:
compatible:
const: renesas,r9a09g047-usb3-phy
reg:
maxItems: 1
clocks:
items:
- description: APB bus clock
- description: USB 2.0 PHY reference clock
- description: USB 3.0 PHY reference clock
clock-names:
items:
- const: pclk
- const: core
- const: ref_alt_clk_p
power-domains:
maxItems: 1
resets:
maxItems: 1
'#phy-cells':
const: 0
required:
- compatible
- reg
- clocks
- clock-names
- power-domains
- resets
- '#phy-cells'
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/renesas,r9a09g047-cpg.h>
usb-phy@15870000 {
compatible = "renesas,r9a09g047-usb3-phy";
reg = <0x15870000 0x10000>;
clocks = <&cpg CPG_MOD 0xb0>, <&cpg CPG_CORE 13>, <&cpg CPG_CORE 12>;
clock-names = "pclk", "core", "ref_alt_clk_p";
power-domains = <&cpg>;
resets = <&cpg 0xaa>;
#phy-cells = <0>;
};

View File

@ -118,6 +118,7 @@ allOf:
contains:
enum:
- renesas,usb2-phy-r9a09g057
- renesas,usb2-phy-r9a08g045
- renesas,rzg2l-usb2-phy
then:
properties:

View File

@ -18,6 +18,7 @@ properties:
- rockchip,px30-dsi-dphy
- rockchip,rk3128-dsi-dphy
- rockchip,rk3368-dsi-dphy
- rockchip,rk3506-dsi-dphy
- rockchip,rk3568-dsi-dphy
- rockchip,rv1126-dsi-dphy

View File

@ -23,15 +23,25 @@ properties:
- enum:
- ti,tcan1042
- ti,tcan1043
- nxp,tja1048
- nxp,tja1051
- nxp,tja1057
- nxp,tjr1443
'#phy-cells':
const: 0
enum: [0, 1]
silent-gpios:
description:
gpio node to toggle silent signal on transceiver
maxItems: 1
standby-gpios:
description:
gpio node to toggle standby signal on transceiver
maxItems: 1
gpio node to toggle standby signal on transceiver. For two Items, item 1
is for stbn1, item 2 is for stbn2.
minItems: 1
maxItems: 2
enable-gpios:
description:
@ -54,6 +64,59 @@ required:
- compatible
- '#phy-cells'
allOf:
- if:
properties:
compatible:
enum:
- nxp,tjr1443
- ti,tcan1042
- ti,tcan1043
then:
properties:
'#phy-cells':
const: 0
silent-gpios: false
standby-gpios:
maxItems: 1
- if:
properties:
compatible:
contains:
const: nxp,tja1048
then:
properties:
'#phy-cells':
const: 1
enable-gpios: false
silent-gpios: false
standby-gpios:
minItems: 2
- if:
properties:
compatible:
contains:
const: nxp,tja1051
then:
properties:
'#phy-cells':
const: 0
standby-gpios: false
- if:
properties:
compatible:
contains:
const: nxp,tja1057
then:
properties:
'#phy-cells':
const: 0
enable-gpios: false
standby-gpios: false
additionalProperties: false
examples:

View File

@ -10547,7 +10547,7 @@ F: include/uapi/asm-generic/
GENERIC PHY FRAMEWORK
M: Vinod Koul <vkoul@kernel.org>
M: Kishon Vijay Abraham I <kishon@kernel.org>
R: Neil Armstrong <neil.armstrong@linaro.org>
L: linux-phy@lists.infradead.org
S: Supported
Q: https://patchwork.kernel.org/project/linux-phy/list/

View File

@ -375,7 +375,7 @@ static struct phy *bcm63xx_usbh_phy_xlate(struct device *dev,
return of_phy_simple_xlate(dev, args);
}
static int __init bcm63xx_usbh_phy_probe(struct platform_device *pdev)
static int bcm63xx_usbh_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct bcm63xx_usbh_phy *usbh;
@ -432,7 +432,7 @@ static int __init bcm63xx_usbh_phy_probe(struct platform_device *pdev)
return 0;
}
static const struct of_device_id bcm63xx_usbh_phy_ids[] __initconst = {
static const struct of_device_id bcm63xx_usbh_phy_ids[] = {
{ .compatible = "brcm,bcm6318-usbh-phy", .data = &usbh_bcm6318 },
{ .compatible = "brcm,bcm6328-usbh-phy", .data = &usbh_bcm6328 },
{ .compatible = "brcm,bcm6358-usbh-phy", .data = &usbh_bcm6358 },
@ -443,7 +443,7 @@ static const struct of_device_id bcm63xx_usbh_phy_ids[] __initconst = {
};
MODULE_DEVICE_TABLE(of, bcm63xx_usbh_phy_ids);
static struct platform_driver bcm63xx_usbh_phy_driver __refdata = {
static struct platform_driver bcm63xx_usbh_phy_driver = {
.driver = {
.name = "bcm63xx-usbh-phy",
.of_match_table = bcm63xx_usbh_phy_ids,

View File

@ -16,6 +16,7 @@
#define PHY_CTRL0_REF_SSP_EN BIT(2)
#define PHY_CTRL0_FSEL_MASK GENMASK(10, 5)
#define PHY_CTRL0_FSEL_24M 0x2a
#define PHY_CTRL0_FSEL_100M 0x27
#define PHY_CTRL1 0x4
#define PHY_CTRL1_RESET BIT(0)
@ -108,6 +109,7 @@ struct tca_blk {
struct imx8mq_usb_phy {
struct phy *phy;
struct clk *clk;
struct clk *alt_clk;
void __iomem *base;
struct regulator *vbus;
struct tca_blk *tca;
@ -582,7 +584,8 @@ static int imx8mp_usb_phy_init(struct phy *phy)
/* USB3.0 PHY signal fsel for 24M ref */
value = readl(imx_phy->base + PHY_CTRL0);
value &= ~PHY_CTRL0_FSEL_MASK;
value |= FIELD_PREP(PHY_CTRL0_FSEL_MASK, PHY_CTRL0_FSEL_24M);
value |= FIELD_PREP(PHY_CTRL0_FSEL_MASK, imx_phy->alt_clk ?
PHY_CTRL0_FSEL_100M : PHY_CTRL0_FSEL_24M);
writel(value, imx_phy->base + PHY_CTRL0);
/* Disable alt_clk_en and use internal MPLL clocks */
@ -626,13 +629,24 @@ static int imx8mq_phy_power_on(struct phy *phy)
if (ret)
return ret;
return clk_prepare_enable(imx_phy->clk);
ret = clk_prepare_enable(imx_phy->clk);
if (ret)
return ret;
ret = clk_prepare_enable(imx_phy->alt_clk);
if (ret) {
clk_disable_unprepare(imx_phy->clk);
return ret;
}
return ret;
}
static int imx8mq_phy_power_off(struct phy *phy)
{
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
clk_disable_unprepare(imx_phy->alt_clk);
clk_disable_unprepare(imx_phy->clk);
regulator_disable(imx_phy->vbus);
@ -681,6 +695,11 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR(imx_phy->clk);
}
imx_phy->alt_clk = devm_clk_get_optional(dev, "alt");
if (IS_ERR(imx_phy->alt_clk))
return dev_err_probe(dev, PTR_ERR(imx_phy->alt_clk),
"Failed to get alt clk\n");
imx_phy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(imx_phy->base))
return PTR_ERR(imx_phy->base);

View File

@ -533,7 +533,7 @@ static struct phy *imx_hsio_xlate(struct device *dev,
static int imx_hsio_probe(struct platform_device *pdev)
{
int i;
int i, ret;
void __iomem *off;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
@ -545,6 +545,9 @@ static int imx_hsio_probe(struct platform_device *pdev)
return -ENOMEM;
priv->dev = &pdev->dev;
priv->drvdata = of_device_get_match_data(dev);
ret = devm_mutex_init(dev, &priv->lock);
if (ret)
return ret;
/* Get HSIO configuration mode */
if (of_property_read_string(np, "fsl,hsio-cfg", &priv->hsio_cfg))

View File

@ -17,31 +17,40 @@ struct can_transceiver_data {
u32 flags;
#define CAN_TRANSCEIVER_STB_PRESENT BIT(0)
#define CAN_TRANSCEIVER_EN_PRESENT BIT(1)
#define CAN_TRANSCEIVER_DUAL_CH BIT(2)
#define CAN_TRANSCEIVER_SILENT_PRESENT BIT(3)
};
struct can_transceiver_phy {
struct phy *generic_phy;
struct gpio_desc *silent_gpio;
struct gpio_desc *standby_gpio;
struct gpio_desc *enable_gpio;
struct can_transceiver_priv *priv;
};
struct can_transceiver_priv {
struct mux_state *mux_state;
int num_ch;
struct can_transceiver_phy can_transceiver_phy[] __counted_by(num_ch);
};
/* Power on function */
static int can_transceiver_phy_power_on(struct phy *phy)
{
struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy);
struct can_transceiver_priv *priv = can_transceiver_phy->priv;
int ret;
if (can_transceiver_phy->mux_state) {
ret = mux_state_select(can_transceiver_phy->mux_state);
if (priv->mux_state) {
ret = mux_state_select(priv->mux_state);
if (ret) {
dev_err(&phy->dev, "Failed to select CAN mux: %d\n", ret);
return ret;
}
}
if (can_transceiver_phy->standby_gpio)
gpiod_set_value_cansleep(can_transceiver_phy->silent_gpio, 0);
gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 0);
if (can_transceiver_phy->enable_gpio)
gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 1);
return 0;
@ -51,13 +60,13 @@ static int can_transceiver_phy_power_on(struct phy *phy)
static int can_transceiver_phy_power_off(struct phy *phy)
{
struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy);
struct can_transceiver_priv *priv = can_transceiver_phy->priv;
if (can_transceiver_phy->standby_gpio)
gpiod_set_value_cansleep(can_transceiver_phy->silent_gpio, 1);
gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 1);
if (can_transceiver_phy->enable_gpio)
gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 0);
if (can_transceiver_phy->mux_state)
mux_state_deselect(can_transceiver_phy->mux_state);
if (priv->mux_state)
mux_state_deselect(priv->mux_state);
return 0;
}
@ -76,6 +85,18 @@ static const struct can_transceiver_data tcan1043_drvdata = {
.flags = CAN_TRANSCEIVER_STB_PRESENT | CAN_TRANSCEIVER_EN_PRESENT,
};
static const struct can_transceiver_data tja1048_drvdata = {
.flags = CAN_TRANSCEIVER_STB_PRESENT | CAN_TRANSCEIVER_DUAL_CH,
};
static const struct can_transceiver_data tja1051_drvdata = {
.flags = CAN_TRANSCEIVER_SILENT_PRESENT | CAN_TRANSCEIVER_EN_PRESENT,
};
static const struct can_transceiver_data tja1057_drvdata = {
.flags = CAN_TRANSCEIVER_SILENT_PRESENT,
};
static const struct of_device_id can_transceiver_phy_ids[] = {
{
.compatible = "ti,tcan1042",
@ -85,6 +106,18 @@ static const struct of_device_id can_transceiver_phy_ids[] = {
.compatible = "ti,tcan1043",
.data = &tcan1043_drvdata
},
{
.compatible = "nxp,tja1048",
.data = &tja1048_drvdata
},
{
.compatible = "nxp,tja1051",
.data = &tja1051_drvdata
},
{
.compatible = "nxp,tja1057",
.data = &tja1057_drvdata
},
{
.compatible = "nxp,tjr1443",
.data = &tcan1043_drvdata
@ -103,64 +136,107 @@ devm_mux_state_get_optional(struct device *dev, const char *mux_name)
return devm_mux_state_get(dev, mux_name);
}
static struct phy *can_transceiver_phy_xlate(struct device *dev,
const struct of_phandle_args *args)
{
struct can_transceiver_priv *priv = dev_get_drvdata(dev);
u32 idx;
if (priv->num_ch == 1)
return priv->can_transceiver_phy[0].generic_phy;
if (args->args_count != 1)
return ERR_PTR(-EINVAL);
idx = args->args[0];
if (idx >= priv->num_ch)
return ERR_PTR(-EINVAL);
return priv->can_transceiver_phy[idx].generic_phy;
}
static int can_transceiver_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct can_transceiver_phy *can_transceiver_phy;
struct can_transceiver_priv *priv;
const struct can_transceiver_data *drvdata;
const struct of_device_id *match;
struct phy *phy;
struct gpio_desc *silent_gpio;
struct gpio_desc *standby_gpio;
struct gpio_desc *enable_gpio;
struct mux_state *mux_state;
u32 max_bitrate = 0;
int err;
can_transceiver_phy = devm_kzalloc(dev, sizeof(struct can_transceiver_phy), GFP_KERNEL);
if (!can_transceiver_phy)
return -ENOMEM;
int err, i, num_ch = 1;
match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node);
drvdata = match->data;
if (drvdata->flags & CAN_TRANSCEIVER_DUAL_CH)
num_ch = 2;
priv = devm_kzalloc(dev, struct_size(priv, can_transceiver_phy, num_ch), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->num_ch = num_ch;
platform_set_drvdata(pdev, priv);
mux_state = devm_mux_state_get_optional(dev, NULL);
if (IS_ERR(mux_state))
return PTR_ERR(mux_state);
can_transceiver_phy->mux_state = mux_state;
priv->mux_state = mux_state;
phy = devm_phy_create(dev, dev->of_node,
&can_transceiver_phy_ops);
err = device_property_read_u32(dev, "max-bitrate", &max_bitrate);
if ((err != -EINVAL) && !max_bitrate)
dev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit\n");
for (i = 0; i < num_ch; i++) {
can_transceiver_phy = &priv->can_transceiver_phy[i];
can_transceiver_phy->priv = priv;
phy = devm_phy_create(dev, dev->of_node, &can_transceiver_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create can transceiver phy\n");
return PTR_ERR(phy);
}
err = device_property_read_u32(dev, "max-bitrate", &max_bitrate);
if ((err != -EINVAL) && !max_bitrate)
dev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit\n");
phy->attrs.max_link_rate = max_bitrate;
can_transceiver_phy->generic_phy = phy;
can_transceiver_phy->priv = priv;
if (drvdata->flags & CAN_TRANSCEIVER_STB_PRESENT) {
standby_gpio = devm_gpiod_get_optional(dev, "standby", GPIOD_OUT_HIGH);
standby_gpio = devm_gpiod_get_index_optional(dev, "standby", i,
GPIOD_OUT_HIGH);
if (IS_ERR(standby_gpio))
return PTR_ERR(standby_gpio);
can_transceiver_phy->standby_gpio = standby_gpio;
}
if (drvdata->flags & CAN_TRANSCEIVER_EN_PRESENT) {
enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
enable_gpio = devm_gpiod_get_index_optional(dev, "enable", i,
GPIOD_OUT_LOW);
if (IS_ERR(enable_gpio))
return PTR_ERR(enable_gpio);
can_transceiver_phy->enable_gpio = enable_gpio;
}
if (drvdata->flags & CAN_TRANSCEIVER_SILENT_PRESENT) {
silent_gpio = devm_gpiod_get_index_optional(dev, "silent", i,
GPIOD_OUT_LOW);
if (IS_ERR(silent_gpio))
return PTR_ERR(silent_gpio);
can_transceiver_phy->silent_gpio = silent_gpio;
}
phy_set_drvdata(can_transceiver_phy->generic_phy, can_transceiver_phy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
}
phy_provider = devm_of_phy_provider_register(dev, can_transceiver_phy_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}

View File

@ -520,6 +520,31 @@ int phy_notify_disconnect(struct phy *phy, int port)
}
EXPORT_SYMBOL_GPL(phy_notify_disconnect);
/**
* phy_notify_state() - phy state notification
* @phy: the PHY returned by phy_get()
* @state: the PHY state
*
* Notify the PHY of a state transition. Used to notify and
* configure the PHY accordingly.
*
* Returns: %0 if successful, a negative error code otherwise
*/
int phy_notify_state(struct phy *phy, union phy_notify state)
{
int ret;
if (!phy || !phy->ops->notify_phystate)
return 0;
mutex_lock(&phy->mutex);
ret = phy->ops->notify_phystate(phy, state);
mutex_unlock(&phy->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(phy_notify_state);
/**
* phy_configure() - Changes the phy parameters
* @phy: the phy returned by phy_get()

View File

@ -25,6 +25,7 @@
#define POR BIT(1)
#define USB_PHY_HS_PHY_CTRL_COMMON0 (0x54)
#define PHY_ENABLE BIT(0)
#define SIDDQ_SEL BIT(1)
#define SIDDQ BIT(2)
#define FSEL GENMASK(6, 4)
@ -81,6 +82,7 @@ struct m31_eusb2_priv_data {
static const struct m31_phy_tbl_entry m31_eusb2_setup_tbl[] = {
M31_EUSB_PHY_INIT_CFG(USB_PHY_CFG0, UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 1),
M31_EUSB_PHY_INIT_CFG(USB_PHY_UTMI_CTRL5, POR, 1),
M31_EUSB_PHY_INIT_CFG(USB_PHY_HS_PHY_CTRL_COMMON0, PHY_ENABLE, 1),
M31_EUSB_PHY_INIT_CFG(USB_PHY_CFG1, PLL_EN, 1),
M31_EUSB_PHY_INIT_CFG(USB_PHY_FSEL_SEL, FSEL_SEL, 1),
};

View File

@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_graph.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
@ -1643,14 +1644,9 @@ static const struct qmp_phy_init_tbl x1e80100_usb43dp_pcs_usb_tbl[] = {
};
/* list of regulators */
struct qmp_regulator_data {
const char *name;
unsigned int enable_load;
};
static struct qmp_regulator_data qmp_phy_vreg_l[] = {
{ .name = "vdda-phy", .enable_load = 21800 },
{ .name = "vdda-pll", .enable_load = 36000 },
static struct regulator_bulk_data qmp_phy_vreg_l[] = {
{ .supply = "vdda-phy", .init_load_uA = 21800, },
{ .supply = "vdda-pll", .init_load_uA = 36000, },
};
static const u8 qmp_dp_v3_pre_emphasis_hbr3_hbr2[4][4] = {
@ -1744,6 +1740,26 @@ static const u8 qmp_dp_v6_pre_emphasis_hbr_rbr[4][4] = {
{ 0x22, 0xff, 0xff, 0xff }
};
struct qmp_combo_lane_mapping {
unsigned int lanes_count;
enum typec_orientation orientation;
u32 lanes[4];
};
static const struct qmp_combo_lane_mapping usb3_data_lanes[] = {
{ 2, TYPEC_ORIENTATION_NORMAL, { 1, 0 }},
{ 2, TYPEC_ORIENTATION_REVERSE, { 2, 3 }},
};
static const struct qmp_combo_lane_mapping dp_data_lanes[] = {
{ 1, TYPEC_ORIENTATION_NORMAL, { 3 }},
{ 1, TYPEC_ORIENTATION_REVERSE, { 0 }},
{ 2, TYPEC_ORIENTATION_NORMAL, { 3, 2 }},
{ 2, TYPEC_ORIENTATION_REVERSE, { 0, 1 }},
{ 4, TYPEC_ORIENTATION_NORMAL, { 3, 2, 1, 0 }},
{ 4, TYPEC_ORIENTATION_REVERSE, { 0, 1, 2, 3 }},
};
struct qmp_combo;
struct qmp_combo_offsets {
@ -1808,7 +1824,7 @@ struct qmp_phy_cfg {
const char * const *reset_list;
int num_resets;
/* regulators to be requested */
const struct qmp_regulator_data *vreg_list;
const struct regulator_bulk_data *vreg_list;
int num_vregs;
/* array of registers with different offsets */
@ -3439,39 +3455,6 @@ static const struct dev_pm_ops qmp_combo_pm_ops = {
qmp_combo_runtime_resume, NULL)
};
static int qmp_combo_vreg_init(struct qmp_combo *qmp)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
struct device *dev = qmp->dev;
int num = cfg->num_vregs;
int ret, i;
qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL);
if (!qmp->vregs)
return -ENOMEM;
for (i = 0; i < num; i++)
qmp->vregs[i].supply = cfg->vreg_list[i].name;
ret = devm_regulator_bulk_get(dev, num, qmp->vregs);
if (ret) {
dev_err(dev, "failed at devm_regulator_bulk_get\n");
return ret;
}
for (i = 0; i < num; i++) {
ret = regulator_set_load(qmp->vregs[i].consumer,
cfg->vreg_list[i].enable_load);
if (ret) {
dev_err(dev, "failed to set load at %s\n",
qmp->vregs[i].supply);
return ret;
}
}
return 0;
}
static int qmp_combo_reset_init(struct qmp_combo *qmp)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
@ -4117,6 +4100,84 @@ static struct phy *qmp_combo_phy_xlate(struct device *dev, const struct of_phand
return ERR_PTR(-EINVAL);
}
static void qmp_combo_find_lanes_orientation(const struct qmp_combo_lane_mapping *mapping,
unsigned int mapping_count,
u32 *lanes, unsigned int lanes_count,
enum typec_orientation *orientation)
{
int i;
for (i = 0; i < mapping_count; i++) {
if (mapping[i].lanes_count != lanes_count)
continue;
if (!memcmp(mapping[i].lanes, lanes, sizeof(u32) * lanes_count)) {
*orientation = mapping[i].orientation;
return;
}
}
}
static int qmp_combo_get_dt_lanes_mapping(struct device *dev, unsigned int endpoint,
u32 *data_lanes, unsigned int max,
unsigned int *count)
{
struct device_node *ep __free(device_node) = NULL;
int ret;
ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, endpoint);
if (!ep)
return -EINVAL;
ret = of_property_count_u32_elems(ep, "data-lanes");
if (ret < 0)
return ret;
*count = ret;
if (*count > max)
return -EINVAL;
return of_property_read_u32_array(ep, "data-lanes", data_lanes,
min_t(unsigned int, *count, max));
}
static int qmp_combo_get_dt_dp_orientation(struct device *dev,
enum typec_orientation *orientation)
{
unsigned int count;
u32 data_lanes[4];
int ret;
/* DP is described on the first endpoint of the first port */
ret = qmp_combo_get_dt_lanes_mapping(dev, 0, data_lanes, 4, &count);
if (ret < 0)
return ret == -EINVAL ? 0 : ret;
/* Search for a match and only update orientation if found */
qmp_combo_find_lanes_orientation(dp_data_lanes, ARRAY_SIZE(dp_data_lanes),
data_lanes, count, orientation);
return 0;
}
static int qmp_combo_get_dt_usb3_orientation(struct device *dev,
enum typec_orientation *orientation)
{
unsigned int count;
u32 data_lanes[2];
int ret;
/* USB3 is described on the second endpoint of the first port */
ret = qmp_combo_get_dt_lanes_mapping(dev, 1, data_lanes, 2, &count);
if (ret < 0)
return ret == -EINVAL ? 0 : ret;
/* Search for a match and only update orientation if found */
qmp_combo_find_lanes_orientation(usb3_data_lanes, ARRAY_SIZE(usb3_data_lanes),
data_lanes, count, orientation);
return 0;
}
static int qmp_combo_probe(struct platform_device *pdev)
{
struct qmp_combo *qmp;
@ -4144,7 +4205,8 @@ static int qmp_combo_probe(struct platform_device *pdev)
if (ret)
return ret;
ret = qmp_combo_vreg_init(qmp);
ret = devm_regulator_bulk_get_const(dev, qmp->cfg->num_vregs,
qmp->cfg->vreg_list, &qmp->vregs);
if (ret)
return ret;
@ -4167,9 +4229,41 @@ static int qmp_combo_probe(struct platform_device *pdev)
if (ret)
goto err_node_put;
qmp->qmpphy_mode = QMPPHY_MODE_USB3DP;
if (of_property_present(dev->of_node, "mode-switch") ||
of_property_present(dev->of_node, "orientation-switch")) {
ret = qmp_combo_typec_register(qmp);
if (ret)
goto err_node_put;
} else {
enum typec_orientation dp_orientation = TYPEC_ORIENTATION_NONE;
enum typec_orientation usb3_orientation = TYPEC_ORIENTATION_NONE;
ret = qmp_combo_get_dt_dp_orientation(dev, &dp_orientation);
if (ret)
goto err_node_put;
ret = qmp_combo_get_dt_usb3_orientation(dev, &usb3_orientation);
if (ret)
goto err_node_put;
if (dp_orientation == TYPEC_ORIENTATION_NONE &&
usb3_orientation != TYPEC_ORIENTATION_NONE) {
qmp->qmpphy_mode = QMPPHY_MODE_USB3_ONLY;
qmp->orientation = usb3_orientation;
} else if (usb3_orientation == TYPEC_ORIENTATION_NONE &&
dp_orientation != TYPEC_ORIENTATION_NONE) {
qmp->qmpphy_mode = QMPPHY_MODE_DP_ONLY;
qmp->orientation = dp_orientation;
} else if (dp_orientation != TYPEC_ORIENTATION_NONE &&
dp_orientation == usb3_orientation) {
qmp->qmpphy_mode = QMPPHY_MODE_USB3DP;
qmp->orientation = dp_orientation;
} else {
dev_warn(dev, "unable to determine orientation & mode from data-lanes");
}
}
ret = drm_aux_bridge_register(dev);
if (ret)
@ -4189,11 +4283,6 @@ static int qmp_combo_probe(struct platform_device *pdev)
if (ret)
goto err_node_put;
/*
* The hw default is USB3_ONLY, but USB3+DP mode lets us more easily
* check both sub-blocks' init tables for blunders at probe time.
*/
qmp->qmpphy_mode = QMPPHY_MODE_USB3DP;
qmp->usb_phy = devm_phy_create(dev, usb_np, &qmp_combo_usb_phy_ops);
if (IS_ERR(qmp->usb_phy)) {

View File

@ -100,6 +100,12 @@ static const unsigned int pciephy_v7_regs_layout[QPHY_LAYOUT_SIZE] = {
[QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V7_PCS_POWER_DOWN_CONTROL,
};
static const unsigned int pciephy_v8_50_regs_layout[QPHY_LAYOUT_SIZE] = {
[QPHY_START_CTRL] = QPHY_V8_50_PCS_START_CONTROL,
[QPHY_PCS_STATUS] = QPHY_V8_50_PCS_STATUS1,
[QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V8_50_PCS_POWER_DOWN_CONTROL,
};
static const struct qmp_phy_init_tbl msm8998_pcie_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
@ -3072,6 +3078,7 @@ struct qmp_pcie_offsets {
u16 rx2;
u16 txz;
u16 rxz;
u16 txrxz;
u16 ln_shrd;
};
@ -3356,6 +3363,12 @@ static const struct qmp_pcie_offsets qmp_pcie_offsets_v6_30 = {
.ln_shrd = 0x8000,
};
static const struct qmp_pcie_offsets qmp_pcie_offsets_v8_50 = {
.serdes = 0x8000,
.pcs = 0x9000,
.txrxz = 0xd000,
};
static const struct qmp_phy_cfg ipq8074_pciephy_cfg = {
.lanes = 1,
@ -4412,6 +4425,22 @@ static const struct qmp_phy_cfg qmp_v6_gen4x4_pciephy_cfg = {
.phy_status = PHYSTATUS_4_20,
};
static const struct qmp_phy_cfg glymur_qmp_gen5x4_pciephy_cfg = {
.lanes = 4,
.offsets = &qmp_pcie_offsets_v8_50,
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = pciephy_v8_50_regs_layout,
.pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
.phy_status = PHYSTATUS_4_20,
};
static void qmp_pcie_init_port_b(struct qmp_pcie *qmp, const struct qmp_phy_cfg_tbls *tbls)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
@ -5163,6 +5192,9 @@ err_node_put:
static const struct of_device_id qmp_pcie_of_match_table[] = {
{
.compatible = "qcom,glymur-qmp-gen5x4-pcie-phy",
.data = &glymur_qmp_gen5x4_pciephy_cfg,
}, {
.compatible = "qcom,ipq6018-qmp-pcie-phy",
.data = &ipq6018_pciephy_cfg,
}, {

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef QCOM_PHY_QMP_PCS_V8_50_H_
#define QCOM_PHY_QMP_PCS_V8_50_H_
#define QPHY_V8_50_PCS_STATUS1 0x010
#define QPHY_V8_50_PCS_START_CONTROL 0x05c
#define QPHY_V8_50_PCS_POWER_DOWN_CONTROL 0x64
#endif

View File

@ -58,6 +58,8 @@
#include "phy-qcom-qmp-pcs-v8.h"
#include "phy-qcom-qmp-pcs-v8_50.h"
/* QPHY_SW_RESET bit */
#define SW_RESET BIT(0)
/* QPHY_POWER_DOWN_CONTROL */

View File

@ -40,3 +40,10 @@ config PHY_RCAR_GEN3_USB3
select GENERIC_PHY
help
Support for USB 3.0 PHY found on Renesas R-Car generation 3 SoCs.
config PHY_RZ_G3E_USB3
tristate "Renesas RZ/G3E USB 3.0 PHY driver"
depends on ARCH_RENESAS || COMPILE_TEST
select GENERIC_PHY
help
Support for USB 3.0 PHY found on Renesas RZ/G3E SoCs.

View File

@ -4,3 +4,4 @@ obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
obj-$(CONFIG_PHY_RCAR_GEN3_PCIE) += phy-rcar-gen3-pcie.o
obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o
obj-$(CONFIG_PHY_RCAR_GEN3_USB3) += phy-rcar-gen3-usb3.o
obj-$(CONFIG_PHY_RZ_G3E_USB3) += phy-rzg3e-usb3.o

View File

@ -128,7 +128,7 @@ error:
static void rcar_gen3_phy_pcie_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
};
}
static struct platform_driver rcar_gen3_phy_driver = {
.driver = {

View File

@ -132,9 +132,9 @@ struct rcar_gen3_chan {
struct device *dev; /* platform_device's device */
const struct rcar_gen3_phy_drv_data *phy_data;
struct extcon_dev *extcon;
struct reset_control *rstc;
struct rcar_gen3_phy rphys[NUM_OF_PHYS];
struct regulator *vbus;
struct reset_control *rstc;
struct work_struct work;
spinlock_t lock; /* protects access to hardware and driver data structure. */
enum usb_dr_mode dr_mode;
@ -771,33 +771,32 @@ static enum usb_dr_mode rcar_gen3_get_dr_mode(struct device_node *np)
return candidate;
}
static void rcar_gen3_reset_assert(void *data)
{
reset_control_assert(data);
}
static int rcar_gen3_phy_usb2_init_bus(struct rcar_gen3_chan *channel)
{
struct device *dev = channel->dev;
int ret;
u32 val;
channel->rstc = devm_reset_control_array_get_shared(dev);
if (IS_ERR(channel->rstc))
return PTR_ERR(channel->rstc);
if (!channel->phy_data->init_bus)
return 0;
ret = pm_runtime_resume_and_get(dev);
if (ret)
return ret;
ret = reset_control_deassert(channel->rstc);
if (ret)
goto rpm_put;
val = readl(channel->base + USB2_AHB_BUS_CTR);
val &= ~USB2_AHB_BUS_CTR_MBL_MASK;
val |= USB2_AHB_BUS_CTR_MBL_INCR4;
writel(val, channel->base + USB2_AHB_BUS_CTR);
rpm_put:
pm_runtime_put(dev);
return ret;
return 0;
}
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
@ -837,6 +836,18 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
}
}
channel->rstc = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(channel->rstc))
return PTR_ERR(channel->rstc);
ret = reset_control_deassert(channel->rstc);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, rcar_gen3_reset_assert, channel->rstc);
if (ret)
return ret;
/*
* devm_phy_create() will call pm_runtime_enable(&phy->dev);
* And then, phy-core will manage runtime pm for this device.
@ -852,11 +863,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, channel);
channel->dev = dev;
if (channel->phy_data->init_bus) {
ret = rcar_gen3_phy_usb2_init_bus(channel);
if (ret)
goto error;
}
spin_lock_init(&channel->lock);
for (i = 0; i < NUM_OF_PHYS; i++) {
@ -924,14 +933,41 @@ static void rcar_gen3_phy_usb2_remove(struct platform_device *pdev)
if (channel->is_otg_channel)
device_remove_file(&pdev->dev, &dev_attr_role);
reset_control_assert(channel->rstc);
pm_runtime_disable(&pdev->dev);
};
}
static int rcar_gen3_phy_usb2_suspend(struct device *dev)
{
struct rcar_gen3_chan *channel = dev_get_drvdata(dev);
return reset_control_assert(channel->rstc);
}
static int rcar_gen3_phy_usb2_resume(struct device *dev)
{
struct rcar_gen3_chan *channel = dev_get_drvdata(dev);
int ret;
ret = reset_control_deassert(channel->rstc);
if (ret)
return ret;
ret = rcar_gen3_phy_usb2_init_bus(channel);
if (ret)
reset_control_assert(channel->rstc);
return ret;
}
static DEFINE_SIMPLE_DEV_PM_OPS(rcar_gen3_phy_usb2_pm_ops,
rcar_gen3_phy_usb2_suspend,
rcar_gen3_phy_usb2_resume);
static struct platform_driver rcar_gen3_phy_usb2_driver = {
.driver = {
.name = "phy_rcar_gen3_usb2",
.of_match_table = rcar_gen3_phy_usb2_match_table,
.pm = pm_ptr(&rcar_gen3_phy_usb2_pm_ops),
},
.probe = rcar_gen3_phy_usb2_probe,
.remove = rcar_gen3_phy_usb2_remove,

View File

@ -202,7 +202,7 @@ error:
static void rcar_gen3_phy_usb3_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
};
}
static struct platform_driver rcar_gen3_phy_usb3_driver = {
.driver = {

View File

@ -0,0 +1,259 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/G3E USB3.0 PHY driver
*
* Copyright (C) 2025 Renesas Electronics Corporation
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#define USB3_TEST_RESET 0x0000
#define USB3_TEST_UTMICTRL2 0x0b04
#define USB3_TEST_PRMCTRL5_R 0x0c10
#define USB3_TEST_PRMCTRL6_R 0x0c14
#define USB3_TEST_RSTCTRL 0x1000
#define USB3_TEST_CLKCTRL 0x1004
#define USB3_TEST_RAMCTRL 0x100c
#define USB3_TEST_CREGCTRL 0x1010
#define USB3_TEST_LANECONFIG0 0x1030
#define USB3_TEST_RESET_PORTRESET0_CTRL BIT(9)
#define USB3_TEST_RESET_SIDDQ BIT(3)
#define USB3_TEST_RESET_PHY_RESET BIT(2)
#define USB3_TEST_RESET_PORTRESET0 BIT(1)
#define USB3_TEST_RESET_RELEASE_OVERRIDE (0)
#define USB3_TEST_UTMICTRL2_CTRL_MASK GENMASK(9, 8)
#define USB3_TEST_UTMICTRL2_MODE_MASK GENMASK(1, 0)
#define USB3_TEST_PRMCTRL5_R_TXPREEMPAMPTUNE0_MASK GENMASK(2, 1)
#define USB3_TEST_PRMCTRL6_R_OTGTUNE0_MASK GENMASK(2, 0)
#define USB3_TEST_RSTCTRL_HARDRESET_ODEN BIT(9)
#define USB3_TEST_RSTCTRL_PIPERESET_ODEN BIT(8)
#define USB3_TEST_RSTCTRL_HARDRESET BIT(1)
#define USB3_TEST_RSTCTRL_PIPERESET BIT(0)
#define USB3_TEST_RSTCTRL_ASSERT \
(USB3_TEST_RSTCTRL_HARDRESET_ODEN | USB3_TEST_RSTCTRL_PIPERESET_ODEN | \
USB3_TEST_RSTCTRL_HARDRESET | USB3_TEST_RSTCTRL_PIPERESET)
#define USB3_TEST_RSTCTRL_RELEASE_HARDRESET \
(USB3_TEST_RSTCTRL_HARDRESET_ODEN | USB3_TEST_RSTCTRL_PIPERESET_ODEN | \
USB3_TEST_RSTCTRL_PIPERESET)
#define USB3_TEST_RSTCTRL_DEASSERT \
(USB3_TEST_RSTCTRL_HARDRESET_ODEN | USB3_TEST_RSTCTRL_PIPERESET_ODEN)
#define USB3_TEST_RSTCTRL_RELEASE_OVERRIDE (0)
#define USB3_TEST_CLKCTRL_MPLLA_SSC_EN BIT(2)
#define USB3_TEST_RAMCTRL_SRAM_INIT_DONE BIT(2)
#define USB3_TEST_RAMCTRL_SRAM_EXT_LD_DONE BIT(0)
#define USB3_TEST_CREGCTRL_PARA_SEL BIT(8)
#define USB3_TEST_LANECONFIG0_DEFAULT (0xd)
struct rz_usb3 {
void __iomem *base;
struct reset_control *rstc;
bool skip_reinit;
};
static void rzg3e_phy_usb2test_phy_init(void __iomem *base)
{
u32 val;
val = readl(base + USB3_TEST_UTMICTRL2);
val |= USB3_TEST_UTMICTRL2_CTRL_MASK | USB3_TEST_UTMICTRL2_MODE_MASK;
writel(val, base + USB3_TEST_UTMICTRL2);
val = readl(base + USB3_TEST_PRMCTRL5_R);
val &= ~USB3_TEST_PRMCTRL5_R_TXPREEMPAMPTUNE0_MASK;
val |= FIELD_PREP(USB3_TEST_PRMCTRL5_R_TXPREEMPAMPTUNE0_MASK, 2);
writel(val, base + USB3_TEST_PRMCTRL5_R);
val = readl(base + USB3_TEST_PRMCTRL6_R);
val &= ~USB3_TEST_PRMCTRL6_R_OTGTUNE0_MASK;
val |= FIELD_PREP(USB3_TEST_PRMCTRL6_R_OTGTUNE0_MASK, 7);
writel(val, base + USB3_TEST_PRMCTRL6_R);
val = readl(base + USB3_TEST_RESET);
val &= ~USB3_TEST_RESET_SIDDQ;
val |= USB3_TEST_RESET_PORTRESET0_CTRL | USB3_TEST_RESET_PHY_RESET |
USB3_TEST_RESET_PORTRESET0;
writel(val, base + USB3_TEST_RESET);
fsleep(10);
val &= ~(USB3_TEST_RESET_PHY_RESET | USB3_TEST_RESET_PORTRESET0);
writel(val, base + USB3_TEST_RESET);
fsleep(10);
val = readl(base + USB3_TEST_UTMICTRL2);
val &= ~USB3_TEST_UTMICTRL2_CTRL_MASK;
writel(val, base + USB3_TEST_UTMICTRL2);
writel(USB3_TEST_RESET_RELEASE_OVERRIDE, base + USB3_TEST_RESET);
}
static int rzg3e_phy_usb3test_phy_init(void __iomem *base)
{
int ret;
u32 val;
writel(USB3_TEST_CREGCTRL_PARA_SEL, base + USB3_TEST_CREGCTRL);
writel(USB3_TEST_RSTCTRL_ASSERT, base + USB3_TEST_RSTCTRL);
fsleep(20);
writel(USB3_TEST_CLKCTRL_MPLLA_SSC_EN, base + USB3_TEST_CLKCTRL);
writel(USB3_TEST_LANECONFIG0_DEFAULT, base + USB3_TEST_LANECONFIG0);
writel(USB3_TEST_RSTCTRL_RELEASE_HARDRESET, base + USB3_TEST_RSTCTRL);
ret = readl_poll_timeout_atomic(base + USB3_TEST_RAMCTRL, val,
val & USB3_TEST_RAMCTRL_SRAM_INIT_DONE, 1, 10000);
if (ret)
return ret;
writel(USB3_TEST_RSTCTRL_DEASSERT, base + USB3_TEST_RSTCTRL);
writel(USB3_TEST_RAMCTRL_SRAM_EXT_LD_DONE, base + USB3_TEST_RAMCTRL);
writel(USB3_TEST_RSTCTRL_RELEASE_OVERRIDE, base + USB3_TEST_RSTCTRL);
return 0;
}
static int rzg3e_phy_usb3_init_helper(void __iomem *base)
{
rzg3e_phy_usb2test_phy_init(base);
return rzg3e_phy_usb3test_phy_init(base);
}
static int rzg3e_phy_usb3_init(struct phy *p)
{
struct rz_usb3 *r = phy_get_drvdata(p);
int ret = 0;
if (!r->skip_reinit)
ret = rzg3e_phy_usb3_init_helper(r->base);
return ret;
}
static const struct phy_ops rzg3e_phy_usb3_ops = {
.init = rzg3e_phy_usb3_init,
.owner = THIS_MODULE,
};
static int rzg3e_phy_usb3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *provider;
struct rz_usb3 *r;
struct phy *phy;
int ret;
r = devm_kzalloc(dev, sizeof(*r), GFP_KERNEL);
if (!r)
return -ENOMEM;
r->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(r->base))
return PTR_ERR(r->base);
r->rstc = devm_reset_control_get_shared_deasserted(dev, NULL);
if (IS_ERR(r->rstc))
return dev_err_probe(dev, PTR_ERR(r->rstc), "failed to get deasserted reset\n");
/*
* devm_phy_create() will call pm_runtime_enable(&phy->dev);
* And then, phy-core will manage runtime pm for this device.
*/
ret = devm_pm_runtime_enable(dev);
if (ret < 0)
return ret;
phy = devm_phy_create(dev, NULL, &rzg3e_phy_usb3_ops);
if (IS_ERR(phy))
return dev_err_probe(dev, PTR_ERR(phy), "failed to create USB3 PHY\n");
platform_set_drvdata(pdev, r);
phy_set_drvdata(phy, r);
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider))
return dev_err_probe(dev, PTR_ERR(provider), "failed to register PHY provider\n");
return 0;
}
static int rzg3e_phy_usb3_suspend(struct device *dev)
{
struct rz_usb3 *r = dev_get_drvdata(dev);
pm_runtime_put(dev);
reset_control_assert(r->rstc);
r->skip_reinit = false;
return 0;
}
static int rzg3e_phy_usb3_resume(struct device *dev)
{
struct rz_usb3 *r = dev_get_drvdata(dev);
int ret;
ret = reset_control_deassert(r->rstc);
if (ret)
return ret;
ret = pm_runtime_resume_and_get(dev);
if (ret)
goto reset_assert;
ret = rzg3e_phy_usb3_init_helper(r->base);
if (ret)
goto pm_put;
r->skip_reinit = true;
return 0;
pm_put:
pm_runtime_put(dev);
reset_assert:
reset_control_assert(r->rstc);
return ret;
}
static const struct dev_pm_ops rzg3e_phy_usb3_pm = {
NOIRQ_SYSTEM_SLEEP_PM_OPS(rzg3e_phy_usb3_suspend, rzg3e_phy_usb3_resume)
};
static const struct of_device_id rzg3e_phy_usb3_match_table[] = {
{ .compatible = "renesas,r9a09g047-usb3-phy" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzg3e_phy_usb3_match_table);
static struct platform_driver rzg3e_phy_usb3_driver = {
.driver = {
.name = "phy_rzg3e_usb3",
.of_match_table = rzg3e_phy_usb3_match_table,
.pm = pm_sleep_ptr(&rzg3e_phy_usb3_pm),
},
.probe = rzg3e_phy_usb3_probe,
};
module_platform_driver(rzg3e_phy_usb3_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Renesas RZ/G3E USB3.0 PHY Driver");
MODULE_AUTHOR("biju.das.jz@bp.renesas.com>");

View File

@ -99,10 +99,30 @@
#define VOD_MID_RANGE 0x3
#define VOD_BIG_RANGE 0x7
#define VOD_MAX_RANGE 0xf
/* Analog Register Part: reg18 */
#define LANE0_PRE_EMPHASIS_ENABLE_MASK BIT(6)
#define LANE0_PRE_EMPHASIS_ENABLE BIT(6)
#define LANE0_PRE_EMPHASIS_DISABLE 0
#define LANE1_PRE_EMPHASIS_ENABLE_MASK BIT(5)
#define LANE1_PRE_EMPHASIS_ENABLE BIT(5)
#define LANE1_PRE_EMPHASIS_DISABLE 0
/* Analog Register Part: reg19 */
#define PRE_EMPHASIS_RANGE_SET_MASK GENMASK(7, 6)
#define PRE_EMPHASIS_RANGE_SET(x) UPDATE(x, 7, 6)
/* Analog Register Part: reg1E */
#define PLL_MODE_SEL_MASK GENMASK(6, 5)
#define PLL_MODE_SEL_LVDS_MODE 0
#define PLL_MODE_SEL_MIPI_MODE BIT(5)
/* Analog Register Part: reg20 */
#define LANE0_PRE_EMPHASIS_RANGE_SET_MASK GENMASK(7, 6)
#define LANE0_PRE_EMPHASIS_RANGE_SET(x) UPDATE(x, 7, 6)
/* Analog Register Part: reg21 */
#define LANE1_PRE_EMPHASIS_RANGE_SET_MASK GENMASK(7, 6)
#define LANE1_PRE_EMPHASIS_RANGE_SET(x) UPDATE(x, 7, 6)
#define PRE_EMPHASIS_MIN_RANGE 0x0
#define PRE_EMPHASIS_MID_RANGE 0x1
#define PRE_EMPHASIS_MAX_RANGE 0x2
#define PRE_EMPHASIS_RESERVED_RANGE 0x3
/* Digital Register Part: reg00 */
#define REG_DIG_RSTN_MASK BIT(0)
#define REG_DIG_RSTN_NORMAL BIT(0)
@ -193,6 +213,7 @@
enum phy_max_rate {
MAX_1GHZ,
MAX_1_5GHZ,
MAX_2_5GHZ,
};
@ -200,6 +221,7 @@ struct inno_video_phy_plat_data {
const struct inno_mipi_dphy_timing *inno_mipi_dphy_timing_table;
const unsigned int num_timings;
enum phy_max_rate max_rate;
unsigned int max_lanes;
};
struct inno_dsidphy {
@ -258,6 +280,24 @@ struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table_max_1ghz[] = {
{1000000000, 0x0, 0x09, 0x20, 0x09, 0x27},
};
static const
struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table_max_1_5ghz[] = {
{ 110, 0x02, 0x7f, 0x16, 0x02, 0x02},
{ 150, 0x02, 0x7f, 0x16, 0x03, 0x02},
{ 200, 0x02, 0x7f, 0x17, 0x04, 0x02},
{ 250, 0x02, 0x7f, 0x17, 0x05, 0x04},
{ 300, 0x02, 0x7f, 0x18, 0x06, 0x04},
{ 400, 0x03, 0x7e, 0x19, 0x07, 0x04},
{ 500, 0x03, 0x7c, 0x1b, 0x07, 0x08},
{ 600, 0x03, 0x70, 0x1d, 0x08, 0x10},
{ 700, 0x05, 0x40, 0x1e, 0x08, 0x30},
{ 800, 0x05, 0x02, 0x1f, 0x09, 0x30},
{1000, 0x05, 0x08, 0x20, 0x09, 0x30},
{1200, 0x06, 0x03, 0x32, 0x14, 0x0f},
{1400, 0x09, 0x03, 0x32, 0x14, 0x0f},
{1500, 0x0d, 0x42, 0x36, 0x0e, 0x0f},
};
static const
struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table_max_2_5ghz[] = {
{ 110000000, 0x02, 0x7f, 0x16, 0x02, 0x02},
@ -372,6 +412,7 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait;
u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero;
unsigned int i;
u32 val;
timings = inno->pdata->inno_mipi_dphy_timing_table;
@ -393,6 +434,23 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x0b,
CLOCK_LANE_VOD_RANGE_SET_MASK,
CLOCK_LANE_VOD_RANGE_SET(VOD_MAX_RANGE));
} else if (inno->pdata->max_rate == MAX_1_5GHZ) {
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x18,
LANE0_PRE_EMPHASIS_ENABLE_MASK, LANE0_PRE_EMPHASIS_ENABLE);
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x18,
LANE1_PRE_EMPHASIS_ENABLE_MASK, LANE1_PRE_EMPHASIS_ENABLE);
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x19,
PRE_EMPHASIS_RANGE_SET_MASK,
PRE_EMPHASIS_RANGE_SET(PRE_EMPHASIS_MID_RANGE));
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x1a,
LANE0_PRE_EMPHASIS_RANGE_SET_MASK,
LANE0_PRE_EMPHASIS_RANGE_SET(PRE_EMPHASIS_MID_RANGE));
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x1b,
LANE1_PRE_EMPHASIS_RANGE_SET_MASK,
LANE1_PRE_EMPHASIS_RANGE_SET(PRE_EMPHASIS_MID_RANGE));
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x0b,
CLOCK_LANE_VOD_RANGE_SET_MASK,
CLOCK_LANE_VOD_RANGE_SET(VOD_MAX_RANGE));
}
/* Enable PLL and LDO */
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01,
@ -518,10 +576,25 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
T_TA_WAIT_CNT(ta_wait));
}
/* Enable all lanes on analog part */
/* Enable lanes on analog part */
switch (inno->pdata->max_lanes) {
case 1:
val = LANE_EN_0;
break;
case 2:
val = LANE_EN_0 | LANE_EN_1;
break;
case 3:
val = LANE_EN_0 | LANE_EN_1 | LANE_EN_2;
break;
case 4:
default:
val = LANE_EN_0 | LANE_EN_1 | LANE_EN_2 | LANE_EN_3;
break;
}
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00,
LANE_EN_MASK, LANE_EN_CK | LANE_EN_3 | LANE_EN_2 |
LANE_EN_1 | LANE_EN_0);
LANE_EN_MASK, LANE_EN_CK | val);
}
static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno)
@ -680,12 +753,21 @@ static const struct inno_video_phy_plat_data max_1ghz_video_phy_plat_data = {
.inno_mipi_dphy_timing_table = inno_mipi_dphy_timing_table_max_1ghz,
.num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table_max_1ghz),
.max_rate = MAX_1GHZ,
.max_lanes = 4,
};
static const struct inno_video_phy_plat_data max_1_5ghz_video_phy_plat_data = {
.inno_mipi_dphy_timing_table = inno_mipi_dphy_timing_table_max_1_5ghz,
.num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table_max_1_5ghz),
.max_rate = MAX_1_5GHZ,
.max_lanes = 2,
};
static const struct inno_video_phy_plat_data max_2_5ghz_video_phy_plat_data = {
.inno_mipi_dphy_timing_table = inno_mipi_dphy_timing_table_max_2_5ghz,
.num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table_max_2_5ghz),
.max_rate = MAX_2_5GHZ,
.max_lanes = 4,
};
static int inno_dsidphy_probe(struct platform_device *pdev)
@ -767,6 +849,9 @@ static const struct of_device_id inno_dsidphy_of_match[] = {
}, {
.compatible = "rockchip,rk3368-dsi-dphy",
.data = &max_1ghz_video_phy_plat_data,
}, {
.compatible = "rockchip,rk3506-dsi-dphy",
.data = &max_1_5ghz_video_phy_plat_data,
}, {
.compatible = "rockchip,rk3568-dsi-dphy",
.data = &max_2_5ghz_video_phy_plat_data,

View File

@ -21,6 +21,9 @@
#define REF_CLOCK_100MHz (100 * HZ_PER_MHZ)
/* RK3528 COMBO PHY REG */
#define RK3528_PHYREG5 0x14
#define RK3528_PHYREG5_GATE_TX_PCK_SEL BIT(3)
#define RK3528_PHYREG5_GATE_TX_PCK_DLY_PLL_OFF BIT(3)
#define RK3528_PHYREG6 0x18
#define RK3528_PHYREG6_PLL_KVCO GENMASK(12, 10)
#define RK3528_PHYREG6_PLL_KVCO_VALUE 0x2
@ -103,6 +106,10 @@
#define RK3568_PHYREG18 0x44
#define RK3568_PHYREG18_PLL_LOOP 0x32
#define RK3568_PHYREG30 0x74
#define RK3568_PHYREG30_GATE_TX_PCK_SEL BIT(7)
#define RK3568_PHYREG30_GATE_TX_PCK_DLY_PLL_OFF BIT(7)
#define RK3568_PHYREG32 0x7C
#define RK3568_PHYREG32_SSC_MASK GENMASK(7, 4)
#define RK3568_PHYREG32_SSC_DIR_MASK GENMASK(5, 4)
@ -504,6 +511,10 @@ static int rk3528_combphy_cfg(struct rockchip_combphy_priv *priv)
case REF_CLOCK_100MHz:
rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true);
if (priv->type == PHY_TYPE_PCIE) {
/* Gate_tx_pck_sel length select for L1ss support */
rockchip_combphy_updatel(priv, RK3528_PHYREG5_GATE_TX_PCK_SEL,
RK3528_PHYREG5_GATE_TX_PCK_DLY_PLL_OFF, RK3528_PHYREG5);
/* PLL KVCO tuning fine */
val = FIELD_PREP(RK3528_PHYREG6_PLL_KVCO, RK3528_PHYREG6_PLL_KVCO_VALUE);
rockchip_combphy_updatel(priv, RK3528_PHYREG6_PLL_KVCO, val,
@ -657,6 +668,10 @@ static int rk3562_combphy_cfg(struct rockchip_combphy_priv *priv)
case REF_CLOCK_100MHz:
rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true);
if (priv->type == PHY_TYPE_PCIE) {
/* Gate_tx_pck_sel length select for L1ss support */
rockchip_combphy_updatel(priv, RK3568_PHYREG30_GATE_TX_PCK_SEL,
RK3568_PHYREG30_GATE_TX_PCK_DLY_PLL_OFF,
RK3568_PHYREG30);
/* PLL KVCO tuning fine */
val = FIELD_PREP(RK3568_PHYREG33_PLL_KVCO_MASK,
RK3568_PHYREG33_PLL_KVCO_VALUE);

View File

@ -500,9 +500,7 @@ static const struct reg_sequence rk_hdtpx_common_cmn_init_seq[] = {
REG_SEQ0(CMN_REG(0043), 0x00),
REG_SEQ0(CMN_REG(0044), 0x46),
REG_SEQ0(CMN_REG(0045), 0x24),
REG_SEQ0(CMN_REG(0046), 0xff),
REG_SEQ0(CMN_REG(0047), 0x00),
REG_SEQ0(CMN_REG(0048), 0x44),
REG_SEQ0(CMN_REG(0049), 0xfa),
REG_SEQ0(CMN_REG(004a), 0x08),
REG_SEQ0(CMN_REG(004b), 0x00),
@ -575,6 +573,8 @@ static const struct reg_sequence rk_hdtpx_tmds_cmn_init_seq[] = {
REG_SEQ0(CMN_REG(0034), 0x00),
REG_SEQ0(CMN_REG(003d), 0x40),
REG_SEQ0(CMN_REG(0042), 0x78),
REG_SEQ0(CMN_REG(0046), 0xdd),
REG_SEQ0(CMN_REG(0048), 0x11),
REG_SEQ0(CMN_REG(004e), 0x34),
REG_SEQ0(CMN_REG(005c), 0x25),
REG_SEQ0(CMN_REG(005e), 0x4f),
@ -668,13 +668,9 @@ static const struct reg_sequence rk_hdtpx_common_lane_init_seq[] = {
static const struct reg_sequence rk_hdtpx_tmds_lane_init_seq[] = {
REG_SEQ0(LANE_REG(0312), 0x00),
REG_SEQ0(LANE_REG(031e), 0x00),
REG_SEQ0(LANE_REG(0412), 0x00),
REG_SEQ0(LANE_REG(041e), 0x00),
REG_SEQ0(LANE_REG(0512), 0x00),
REG_SEQ0(LANE_REG(051e), 0x00),
REG_SEQ0(LANE_REG(0612), 0x00),
REG_SEQ0(LANE_REG(061e), 0x08),
REG_SEQ0(LANE_REG(0303), 0x2f),
REG_SEQ0(LANE_REG(0403), 0x2f),
REG_SEQ0(LANE_REG(0503), 0x2f),
@ -687,6 +683,11 @@ static const struct reg_sequence rk_hdtpx_tmds_lane_init_seq[] = {
REG_SEQ0(LANE_REG(0406), 0x1c),
REG_SEQ0(LANE_REG(0506), 0x1c),
REG_SEQ0(LANE_REG(0606), 0x1c),
/* Keep Inter-Pair Skew in the limits */
REG_SEQ0(LANE_REG(031e), 0x02),
REG_SEQ0(LANE_REG(041e), 0x02),
REG_SEQ0(LANE_REG(051e), 0x02),
REG_SEQ0(LANE_REG(061e), 0x0a),
};
static struct tx_drv_ctrl tx_drv_ctrl_rbr[4][4] = {
@ -1037,7 +1038,8 @@ static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx)
ret = rk_hdptx_post_enable_pll(hdptx);
if (!ret)
hdptx->hw_rate = hdptx->hdmi_cfg.tmds_char_rate;
hdptx->hw_rate = DIV_ROUND_CLOSEST_ULL(hdptx->hdmi_cfg.tmds_char_rate * 8,
hdptx->hdmi_cfg.bpc);
return ret;
}
@ -1895,19 +1897,20 @@ static long rk_hdptx_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate,
* hence ensure rk_hdptx_phy_clk_set_rate() won't be invoked with
* a different rate argument.
*/
return hdptx->hdmi_cfg.tmds_char_rate;
return DIV_ROUND_CLOSEST_ULL(hdptx->hdmi_cfg.tmds_char_rate * 8, hdptx->hdmi_cfg.bpc);
}
static int rk_hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
unsigned long long tmds_rate = DIV_ROUND_CLOSEST_ULL(rate * hdptx->hdmi_cfg.bpc, 8);
/* Revert any unlikely TMDS char rate change since round_rate() */
if (hdptx->hdmi_cfg.tmds_char_rate != rate) {
dev_warn(hdptx->dev, "Reverting unexpected rate change from %lu to %llu\n",
rate, hdptx->hdmi_cfg.tmds_char_rate);
hdptx->hdmi_cfg.tmds_char_rate = rate;
if (hdptx->hdmi_cfg.tmds_char_rate != tmds_rate) {
dev_warn(hdptx->dev, "Reverting unexpected rate change from %llu to %llu\n",
tmds_rate, hdptx->hdmi_cfg.tmds_char_rate);
hdptx->hdmi_cfg.tmds_char_rate = tmds_rate;
}
/*

View File

@ -1823,7 +1823,7 @@ static int exynos5_usbdrd_orien_sw_set(struct typec_switch_dev *sw,
phy_drd->orientation = orientation;
}
clk_bulk_disable(phy_drd->drv_data->n_clks, phy_drd->clks);
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
return 0;
}

View File

@ -108,12 +108,39 @@ static const struct samsung_ufs_phy_cfg tensor_gs101_post_pwr_hs_config[] = {
END_UFS_PHY_CFG,
};
static const struct samsung_ufs_phy_cfg tensor_gs101_post_h8_enter[] = {
PHY_TRSV_REG_CFG_GS101(0x262, 0x08, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x265, 0x0A, PWR_MODE_ANY),
PHY_COMN_REG_CFG(0x1, 0x8, PWR_MODE_ANY),
PHY_COMN_REG_CFG(0x0, 0x86, PWR_MODE_ANY),
PHY_COMN_REG_CFG(0x8, 0x60, PWR_MODE_HS_ANY),
PHY_TRSV_REG_CFG_GS101(0x222, 0x08, PWR_MODE_HS_ANY),
PHY_TRSV_REG_CFG_GS101(0x246, 0x01, PWR_MODE_HS_ANY),
END_UFS_PHY_CFG,
};
static const struct samsung_ufs_phy_cfg tensor_gs101_pre_h8_exit[] = {
PHY_COMN_REG_CFG(0x0, 0xC6, PWR_MODE_ANY),
PHY_COMN_REG_CFG(0x1, 0x0C, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x262, 0x00, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x265, 0x00, PWR_MODE_ANY),
PHY_COMN_REG_CFG(0x8, 0xE0, PWR_MODE_HS_ANY),
PHY_TRSV_REG_CFG_GS101(0x246, 0x03, PWR_MODE_HS_ANY),
PHY_TRSV_REG_CFG_GS101(0x222, 0x18, PWR_MODE_HS_ANY),
END_UFS_PHY_CFG,
};
static const struct samsung_ufs_phy_cfg *tensor_gs101_ufs_phy_cfgs[CFG_TAG_MAX] = {
[CFG_PRE_INIT] = tensor_gs101_pre_init_cfg,
[CFG_PRE_PWR_HS] = tensor_gs101_pre_pwr_hs_config,
[CFG_POST_PWR_HS] = tensor_gs101_post_pwr_hs_config,
};
static const struct samsung_ufs_phy_cfg *tensor_gs101_hibern8_cfgs[] = {
[CFG_POST_HIBERN8_ENTER] = tensor_gs101_post_h8_enter,
[CFG_PRE_HIBERN8_EXIT] = tensor_gs101_pre_h8_exit,
};
static const char * const tensor_gs101_ufs_phy_clks[] = {
"ref_clk",
};
@ -170,6 +197,7 @@ static int gs101_phy_wait_for_cdr_lock(struct phy *phy, u8 lane)
const struct samsung_ufs_phy_drvdata tensor_gs101_ufs_phy = {
.cfgs = tensor_gs101_ufs_phy_cfgs,
.cfgs_hibern8 = tensor_gs101_hibern8_cfgs,
.isol = {
.offset = TENSOR_GS101_PHY_CTRL,
.mask = TENSOR_GS101_PHY_CTRL_MASK,

View File

@ -217,6 +217,44 @@ static int samsung_ufs_phy_set_mode(struct phy *generic_phy,
return 0;
}
static int samsung_ufs_phy_notify_state(struct phy *phy,
union phy_notify state)
{
struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
const struct samsung_ufs_phy_cfg *cfg;
int i, err = -EINVAL;
if (!ufs_phy->cfgs_hibern8)
return 0;
if (state.ufs_state == PHY_UFS_HIBERN8_ENTER)
cfg = ufs_phy->cfgs_hibern8[CFG_POST_HIBERN8_ENTER];
else if (state.ufs_state == PHY_UFS_HIBERN8_EXIT)
cfg = ufs_phy->cfgs_hibern8[CFG_PRE_HIBERN8_EXIT];
else
goto err_out;
for_each_phy_cfg(cfg) {
for_each_phy_lane(ufs_phy, i) {
samsung_ufs_phy_config(ufs_phy, cfg, i);
}
}
if (state.ufs_state == PHY_UFS_HIBERN8_EXIT) {
for_each_phy_lane(ufs_phy, i) {
if (ufs_phy->drvdata->wait_for_cdr) {
err = ufs_phy->drvdata->wait_for_cdr(phy, i);
if (err)
goto err_out;
}
}
}
return 0;
err_out:
return err;
}
static int samsung_ufs_phy_exit(struct phy *phy)
{
struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
@ -233,6 +271,7 @@ static const struct phy_ops samsung_ufs_phy_ops = {
.power_off = samsung_ufs_phy_power_off,
.calibrate = samsung_ufs_phy_calibrate,
.set_mode = samsung_ufs_phy_set_mode,
.notify_phystate = samsung_ufs_phy_notify_state,
.owner = THIS_MODULE,
};
@ -287,6 +326,7 @@ static int samsung_ufs_phy_probe(struct platform_device *pdev)
phy->dev = dev;
phy->drvdata = drvdata;
phy->cfgs = drvdata->cfgs;
phy->cfgs_hibern8 = drvdata->cfgs_hibern8;
memcpy(&phy->isol, &drvdata->isol, sizeof(phy->isol));
if (!of_property_read_u32_index(dev->of_node, "samsung,pmu-syscon", 1,

View File

@ -92,6 +92,11 @@ enum {
CFG_TAG_MAX,
};
enum {
CFG_POST_HIBERN8_ENTER,
CFG_PRE_HIBERN8_EXIT,
};
struct samsung_ufs_phy_cfg {
u32 off_0;
u32 off_1;
@ -108,6 +113,7 @@ struct samsung_ufs_phy_pmu_isol {
struct samsung_ufs_phy_drvdata {
const struct samsung_ufs_phy_cfg **cfgs;
const struct samsung_ufs_phy_cfg **cfgs_hibern8;
struct samsung_ufs_phy_pmu_isol isol;
const char * const *clk_list;
int num_clks;
@ -124,6 +130,7 @@ struct samsung_ufs_phy {
struct clk_bulk_data *clks;
const struct samsung_ufs_phy_drvdata *drvdata;
const struct samsung_ufs_phy_cfg * const *cfgs;
const struct samsung_ufs_phy_cfg * const *cfgs_hibern8;
struct samsung_ufs_phy_pmu_isol isol;
u8 lane_cnt;
int ufs_phy_state;

View File

@ -11,7 +11,6 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>

View File

@ -341,7 +341,7 @@ static struct phy *phy_gmii_sel_of_xlate(struct device *dev,
if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) &&
args->args_count < 2)
return ERR_PTR(-EINVAL);
if (phy_id > priv->num_ports)
if (phy_id < 1 || phy_id > priv->num_ports)
return ERR_PTR(-EINVAL);
if (phy_id != priv->if_phys[phy_id - 1].id)
return ERR_PTR(-EINVAL);

View File

@ -53,6 +53,15 @@ enum phy_media {
PHY_MEDIA_DAC,
};
enum phy_ufs_state {
PHY_UFS_HIBERN8_ENTER,
PHY_UFS_HIBERN8_EXIT,
};
union phy_notify {
enum phy_ufs_state ufs_state;
};
/**
* union phy_configure_opts - Opaque generic phy configuration
*
@ -83,6 +92,7 @@ union phy_configure_opts {
* @set_speed: set the speed of the phy (optional)
* @reset: resetting the phy
* @calibrate: calibrate the phy
* @notify_phystate: notify and configure the phy for a particular state
* @release: ops to be performed while the consumer relinquishes the PHY
* @owner: the module owner containing the ops
*/
@ -132,6 +142,7 @@ struct phy_ops {
int (*connect)(struct phy *phy, int port);
int (*disconnect)(struct phy *phy, int port);
int (*notify_phystate)(struct phy *phy, union phy_notify state);
void (*release)(struct phy *phy);
struct module *owner;
};
@ -255,6 +266,7 @@ int phy_reset(struct phy *phy);
int phy_calibrate(struct phy *phy);
int phy_notify_connect(struct phy *phy, int port);
int phy_notify_disconnect(struct phy *phy, int port);
int phy_notify_state(struct phy *phy, union phy_notify state);
static inline int phy_get_bus_width(struct phy *phy)
{
return phy->attrs.bus_width;
@ -412,6 +424,13 @@ static inline int phy_notify_disconnect(struct phy *phy, int index)
return -ENOSYS;
}
static inline int phy_notify_state(struct phy *phy, union phy_notify state)
{
if (!phy)
return 0;
return -ENOSYS;
}
static inline int phy_configure(struct phy *phy,
union phy_configure_opts *opts)
{