Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Phy drivers for Rockchip platforms
#
config PHY_ROCKCHIP_DP
tristate "Rockchip Display Port PHY Driver"
depends on ARCH_ROCKCHIP && OF
select GENERIC_PHY
help
Enable this to support the Rockchip Display Port PHY.
config PHY_ROCKCHIP_DPHY_RX0
tristate "Rockchip MIPI Synopsys DPHY RX0 driver"
depends on ARCH_ROCKCHIP || COMPILE_TEST
select GENERIC_PHY_MIPI_DPHY
select GENERIC_PHY
help
Enable this to support the Rockchip MIPI Synopsys DPHY RX0
associated to the Rockchip ISP module present in RK3399 SoCs.
To compile this driver as a module, choose M here: the module
will be called phy-rockchip-dphy-rx0.
config PHY_ROCKCHIP_EMMC
tristate "Rockchip EMMC PHY Driver"
depends on ARCH_ROCKCHIP && OF
select GENERIC_PHY
help
Enable this to support the Rockchip EMMC PHY.
config PHY_ROCKCHIP_INNO_HDMI
tristate "Rockchip INNO HDMI PHY Driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
depends on COMMON_CLK
depends on HAS_IOMEM
select GENERIC_PHY
help
Enable this to support the Rockchip Innosilicon HDMI PHY.
config PHY_ROCKCHIP_INNO_USB2
tristate "Rockchip INNO USB2PHY Driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
depends on COMMON_CLK
depends on EXTCON
depends on USB_SUPPORT
select GENERIC_PHY
select USB_COMMON
help
Support for Rockchip USB2.0 PHY with Innosilicon IP block.
config PHY_ROCKCHIP_INNO_CSIDPHY
tristate "Rockchip Innosilicon MIPI CSI PHY driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
select GENERIC_PHY
select GENERIC_PHY_MIPI_DPHY
help
Enable this to support the Rockchip MIPI CSI PHY with
Innosilicon IP block.
config PHY_ROCKCHIP_INNO_DSIDPHY
tristate "Rockchip Innosilicon MIPI/LVDS/TTL PHY driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
select GENERIC_PHY
select GENERIC_PHY_MIPI_DPHY
help
Enable this to support the Rockchip MIPI/LVDS/TTL PHY with
Innosilicon IP block.
config PHY_ROCKCHIP_NANENG_COMBO_PHY
tristate "Rockchip NANENG COMBO PHY Driver"
depends on ARCH_ROCKCHIP && OF
select GENERIC_PHY
help
Enable this to support the Rockchip PCIe/USB3.0/SATA/QSGMII
combo PHY with NaNeng IP block.
config PHY_ROCKCHIP_PCIE
tristate "Rockchip PCIe PHY Driver"
depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
depends on HAS_IOMEM
select GENERIC_PHY
select MFD_SYSCON
help
Enable this to support the Rockchip PCIe PHY.
config PHY_ROCKCHIP_SAMSUNG_DCPHY
tristate "Rockchip Samsung MIPI DCPHY driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST)
select GENERIC_PHY
select GENERIC_PHY_MIPI_DPHY
help
Enable this to support the Rockchip MIPI DCPHY with
Samsung IP block.
To compile this driver as a module, choose M here: the module
will be called phy-rockchip-samsung-dcphy
config PHY_ROCKCHIP_SAMSUNG_HDPTX
tristate "Rockchip Samsung HDMI/eDP Combo PHY driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
depends on COMMON_CLK
depends on HAS_IOMEM
select GENERIC_PHY
select MFD_SYSCON
select RATIONAL
help
Enable this to support the Rockchip HDMI/eDP Combo PHY
with Samsung IP block.
config PHY_ROCKCHIP_SNPS_PCIE3
tristate "Rockchip Snps PCIe3 PHY Driver"
depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
depends on HAS_IOMEM
select GENERIC_PHY
select MFD_SYSCON
help
Enable this to support the Rockchip snps PCIe3 PHY.
config PHY_ROCKCHIP_TYPEC
tristate "Rockchip TYPEC PHY Driver"
depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
select EXTCON
select GENERIC_PHY
select RESET_CONTROLLER
help
Enable this to support the Rockchip USB TYPEC PHY.
config PHY_ROCKCHIP_USB
tristate "Rockchip USB2 PHY Driver"
depends on ARCH_ROCKCHIP && OF
select GENERIC_PHY
help
Enable this to support the Rockchip USB 2.0 PHY.
config PHY_ROCKCHIP_USBDP
tristate "Rockchip USBDP COMBO PHY Driver"
depends on ARCH_ROCKCHIP && OF
depends on TYPEC
select GENERIC_PHY
select USB_COMMON
help
Enable this to support the Rockchip USB3.0/DP combo PHY with
Samsung IP block. This is required for USB3 support on RK3588.
To compile this driver as a module, choose M here: the module
will be called phy-rockchip-usbdp
+16
View File
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o
obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0.o
obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o
obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_DCPHY) += phy-rockchip-samsung-dcphy.o
obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o
obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o
obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
obj-$(CONFIG_PHY_ROCKCHIP_USBDP) += phy-rockchip-usbdp.o
@@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Rockchip DP PHY driver
*
* Copyright (C) 2016 FuZhou Rockchip Co., Ltd.
* Author: Yakir Yang <ykk@@rock-chips.com>
*/
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define GRF_SOC_CON12 0x0274
#define GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK BIT(20)
#define GRF_EDP_REF_CLK_SEL_INTER BIT(4)
#define GRF_EDP_PHY_SIDDQ_HIWORD_MASK BIT(21)
#define GRF_EDP_PHY_SIDDQ_ON 0
#define GRF_EDP_PHY_SIDDQ_OFF BIT(5)
struct rockchip_dp_phy {
struct device *dev;
struct regmap *grf;
struct clk *phy_24m;
};
static int rockchip_set_phy_state(struct phy *phy, bool enable)
{
struct rockchip_dp_phy *dp = phy_get_drvdata(phy);
int ret;
if (enable) {
ret = regmap_write(dp->grf, GRF_SOC_CON12,
GRF_EDP_PHY_SIDDQ_HIWORD_MASK |
GRF_EDP_PHY_SIDDQ_ON);
if (ret < 0) {
dev_err(dp->dev, "Can't enable PHY power %d\n", ret);
return ret;
}
ret = clk_prepare_enable(dp->phy_24m);
} else {
clk_disable_unprepare(dp->phy_24m);
ret = regmap_write(dp->grf, GRF_SOC_CON12,
GRF_EDP_PHY_SIDDQ_HIWORD_MASK |
GRF_EDP_PHY_SIDDQ_OFF);
}
return ret;
}
static int rockchip_dp_phy_power_on(struct phy *phy)
{
return rockchip_set_phy_state(phy, true);
}
static int rockchip_dp_phy_power_off(struct phy *phy)
{
return rockchip_set_phy_state(phy, false);
}
static const struct phy_ops rockchip_dp_phy_ops = {
.power_on = rockchip_dp_phy_power_on,
.power_off = rockchip_dp_phy_power_off,
.owner = THIS_MODULE,
};
static int rockchip_dp_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct phy_provider *phy_provider;
struct rockchip_dp_phy *dp;
struct phy *phy;
int ret;
if (!np)
return -ENODEV;
if (!dev->parent || !dev->parent->of_node)
return -ENODEV;
dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
if (!dp)
return -ENOMEM;
dp->dev = dev;
dp->phy_24m = devm_clk_get(dev, "24m");
if (IS_ERR(dp->phy_24m)) {
dev_err(dev, "cannot get clock 24m\n");
return PTR_ERR(dp->phy_24m);
}
ret = clk_set_rate(dp->phy_24m, 24000000);
if (ret < 0) {
dev_err(dp->dev, "cannot set clock phy_24m %d\n", ret);
return ret;
}
dp->grf = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(dp->grf)) {
dev_err(dev, "rk3288-dp needs the General Register Files syscon\n");
return PTR_ERR(dp->grf);
}
ret = regmap_write(dp->grf, GRF_SOC_CON12, GRF_EDP_REF_CLK_SEL_INTER |
GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK);
if (ret != 0) {
dev_err(dp->dev, "Could not config GRF edp ref clk: %d\n", ret);
return ret;
}
phy = devm_phy_create(dev, np, &rockchip_dp_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create phy\n");
return PTR_ERR(phy);
}
phy_set_drvdata(phy, dp);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id rockchip_dp_phy_dt_ids[] = {
{ .compatible = "rockchip,rk3288-dp-phy" },
{}
};
MODULE_DEVICE_TABLE(of, rockchip_dp_phy_dt_ids);
static struct platform_driver rockchip_dp_phy_driver = {
.probe = rockchip_dp_phy_probe,
.driver = {
.name = "rockchip-dp-phy",
.of_match_table = rockchip_dp_phy_dt_ids,
},
};
module_platform_driver(rockchip_dp_phy_driver);
MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip DP PHY driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,383 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Rockchip MIPI Synopsys DPHY RX0 driver
*
* Copyright (C) 2019 Collabora, Ltd.
*
* Based on:
*
* drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
* in https://chromium.googlesource.com/chromiumos/third_party/kernel,
* chromeos-4.4 branch.
*
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
* Jacob Chen <jacob2.chen@rock-chips.com>
* Shunqian Zheng <zhengsq@rock-chips.com>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define RK3399_GRF_SOC_CON9 0x6224
#define RK3399_GRF_SOC_CON21 0x6254
#define RK3399_GRF_SOC_CON22 0x6258
#define RK3399_GRF_SOC_CON23 0x625c
#define RK3399_GRF_SOC_CON24 0x6260
#define RK3399_GRF_SOC_CON25 0x6264
#define RK3399_GRF_SOC_STATUS1 0xe2a4
#define CLOCK_LANE_HS_RX_CONTROL 0x34
#define LANE0_HS_RX_CONTROL 0x44
#define LANE1_HS_RX_CONTROL 0x54
#define LANE2_HS_RX_CONTROL 0x84
#define LANE3_HS_RX_CONTROL 0x94
#define LANES_THS_SETTLE_CONTROL 0x75
#define THS_SETTLE_COUNTER_THRESHOLD 0x04
struct hsfreq_range {
u16 range_h;
u8 cfg_bit;
};
static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
{ 89, 0x00 }, { 99, 0x10 }, { 109, 0x20 }, { 129, 0x01 },
{ 139, 0x11 }, { 149, 0x21 }, { 169, 0x02 }, { 179, 0x12 },
{ 199, 0x22 }, { 219, 0x03 }, { 239, 0x13 }, { 249, 0x23 },
{ 269, 0x04 }, { 299, 0x14 }, { 329, 0x05 }, { 359, 0x15 },
{ 399, 0x25 }, { 449, 0x06 }, { 499, 0x16 }, { 549, 0x07 },
{ 599, 0x17 }, { 649, 0x08 }, { 699, 0x18 }, { 749, 0x09 },
{ 799, 0x19 }, { 849, 0x29 }, { 899, 0x39 }, { 949, 0x0a },
{ 999, 0x1a }, { 1049, 0x2a }, { 1099, 0x3a }, { 1149, 0x0b },
{ 1199, 0x1b }, { 1249, 0x2b }, { 1299, 0x3b }, { 1349, 0x0c },
{ 1399, 0x1c }, { 1449, 0x2c }, { 1500, 0x3c }
};
static const char * const rk3399_mipidphy_clks[] = {
"dphy-ref",
"dphy-cfg",
"grf",
};
enum dphy_reg_id {
GRF_DPHY_RX0_TURNDISABLE = 0,
GRF_DPHY_RX0_FORCERXMODE,
GRF_DPHY_RX0_FORCETXSTOPMODE,
GRF_DPHY_RX0_ENABLE,
GRF_DPHY_RX0_TESTCLR,
GRF_DPHY_RX0_TESTCLK,
GRF_DPHY_RX0_TESTEN,
GRF_DPHY_RX0_TESTDIN,
GRF_DPHY_RX0_TURNREQUEST,
GRF_DPHY_RX0_TESTDOUT,
GRF_DPHY_TX0_TURNDISABLE,
GRF_DPHY_TX0_FORCERXMODE,
GRF_DPHY_TX0_FORCETXSTOPMODE,
GRF_DPHY_TX0_TURNREQUEST,
GRF_DPHY_TX1RX1_TURNDISABLE,
GRF_DPHY_TX1RX1_FORCERXMODE,
GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
GRF_DPHY_TX1RX1_ENABLE,
GRF_DPHY_TX1RX1_MASTERSLAVEZ,
GRF_DPHY_TX1RX1_BASEDIR,
GRF_DPHY_TX1RX1_ENABLECLK,
GRF_DPHY_TX1RX1_TURNREQUEST,
GRF_DPHY_RX1_SRC_SEL,
/* rk3288 only */
GRF_CON_DISABLE_ISP,
GRF_CON_ISP_DPHY_SEL,
GRF_DSI_CSI_TESTBUS_SEL,
GRF_DVP_V18SEL,
/* below is for rk3399 only */
GRF_DPHY_RX0_CLK_INV_SEL,
GRF_DPHY_RX1_CLK_INV_SEL,
};
struct dphy_reg {
u16 offset;
u8 mask;
u8 shift;
};
#define PHY_REG(_offset, _width, _shift) \
{ .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
static const struct dphy_reg rk3399_grf_dphy_regs[] = {
[GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
[GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
[GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
[GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
[GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
[GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
[GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
[GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
[GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
[GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
[GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
[GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
[GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
[GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
[GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
[GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
[GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
[GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
[GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
[GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
[GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
[GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
[GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
[GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
[GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
};
struct rk_dphy_drv_data {
const char * const *clks;
unsigned int num_clks;
const struct hsfreq_range *hsfreq_ranges;
unsigned int num_hsfreq_ranges;
const struct dphy_reg *regs;
};
struct rk_dphy {
struct device *dev;
struct regmap *grf;
struct clk_bulk_data *clks;
const struct rk_dphy_drv_data *drv_data;
struct phy_configure_opts_mipi_dphy config;
u8 hsfreq;
};
static inline void rk_dphy_write_grf(struct rk_dphy *priv,
unsigned int index, u8 value)
{
const struct dphy_reg *reg = &priv->drv_data->regs[index];
/* Update high word */
unsigned int val = (value << reg->shift) |
(reg->mask << (reg->shift + 16));
if (WARN_ON(!reg->offset))
return;
regmap_write(priv->grf, reg->offset, val);
}
static void rk_dphy_write(struct rk_dphy *priv, u8 test_code, u8 test_data)
{
rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTDIN, test_code);
rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTEN, 1);
/*
* With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
* is latched internally as the current test code. Test data is
* programmed internally by rising edge on TESTCLK.
* This code assumes that TESTCLK is already 1.
*/
rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 0);
rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTEN, 0);
rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTDIN, test_data);
rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 1);
}
static void rk_dphy_enable(struct rk_dphy *priv)
{
rk_dphy_write_grf(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
rk_dphy_write_grf(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
/* Disable lane turn around, which is ignored in receive mode */
rk_dphy_write_grf(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
rk_dphy_write_grf(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
rk_dphy_write_grf(priv, GRF_DPHY_RX0_ENABLE,
GENMASK(priv->config.lanes - 1, 0));
/* dphy start */
rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 1);
rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLR, 1);
usleep_range(100, 150);
rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLR, 0);
usleep_range(100, 150);
/* set clock lane */
/* HS hsfreq_range & lane 0 settle bypass */
rk_dphy_write(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
/* HS RX Control of lane0 */
rk_dphy_write(priv, LANE0_HS_RX_CONTROL, priv->hsfreq << 1);
/* HS RX Control of lane1 */
rk_dphy_write(priv, LANE1_HS_RX_CONTROL, priv->hsfreq << 1);
/* HS RX Control of lane2 */
rk_dphy_write(priv, LANE2_HS_RX_CONTROL, priv->hsfreq << 1);
/* HS RX Control of lane3 */
rk_dphy_write(priv, LANE3_HS_RX_CONTROL, priv->hsfreq << 1);
/* HS RX Data Lanes Settle State Time Control */
rk_dphy_write(priv, LANES_THS_SETTLE_CONTROL,
THS_SETTLE_COUNTER_THRESHOLD);
/* Normal operation */
rk_dphy_write(priv, 0x0, 0);
}
static int rk_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
{
struct rk_dphy *priv = phy_get_drvdata(phy);
const struct rk_dphy_drv_data *drv_data = priv->drv_data;
struct phy_configure_opts_mipi_dphy *config = &opts->mipi_dphy;
unsigned int hsfreq = 0;
unsigned int i;
u64 data_rate_mbps;
int ret;
/* pass with phy_mipi_dphy_get_default_config (with pixel rate?) */
ret = phy_mipi_dphy_config_validate(config);
if (ret)
return ret;
data_rate_mbps = div_u64(config->hs_clk_rate, 1000 * 1000);
dev_dbg(priv->dev, "lanes %d - data_rate_mbps %llu\n",
config->lanes, data_rate_mbps);
for (i = 0; i < drv_data->num_hsfreq_ranges; i++) {
if (drv_data->hsfreq_ranges[i].range_h >= data_rate_mbps) {
hsfreq = drv_data->hsfreq_ranges[i].cfg_bit;
break;
}
}
if (!hsfreq)
return -EINVAL;
priv->hsfreq = hsfreq;
priv->config = *config;
return 0;
}
static int rk_dphy_power_on(struct phy *phy)
{
struct rk_dphy *priv = phy_get_drvdata(phy);
int ret;
ret = clk_bulk_enable(priv->drv_data->num_clks, priv->clks);
if (ret)
return ret;
rk_dphy_enable(priv);
return 0;
}
static int rk_dphy_power_off(struct phy *phy)
{
struct rk_dphy *priv = phy_get_drvdata(phy);
rk_dphy_write_grf(priv, GRF_DPHY_RX0_ENABLE, 0);
clk_bulk_disable(priv->drv_data->num_clks, priv->clks);
return 0;
}
static int rk_dphy_init(struct phy *phy)
{
struct rk_dphy *priv = phy_get_drvdata(phy);
return clk_bulk_prepare(priv->drv_data->num_clks, priv->clks);
}
static int rk_dphy_exit(struct phy *phy)
{
struct rk_dphy *priv = phy_get_drvdata(phy);
clk_bulk_unprepare(priv->drv_data->num_clks, priv->clks);
return 0;
}
static const struct phy_ops rk_dphy_ops = {
.power_on = rk_dphy_power_on,
.power_off = rk_dphy_power_off,
.init = rk_dphy_init,
.exit = rk_dphy_exit,
.configure = rk_dphy_configure,
.owner = THIS_MODULE,
};
static const struct rk_dphy_drv_data rk3399_mipidphy_drv_data = {
.clks = rk3399_mipidphy_clks,
.num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
.hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
.num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
.regs = rk3399_grf_dphy_regs,
};
static const struct of_device_id rk_dphy_dt_ids[] = {
{
.compatible = "rockchip,rk3399-mipi-dphy-rx0",
.data = &rk3399_mipidphy_drv_data,
},
{}
};
MODULE_DEVICE_TABLE(of, rk_dphy_dt_ids);
static int rk_dphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
const struct rk_dphy_drv_data *drv_data;
struct phy_provider *phy_provider;
struct rk_dphy *priv;
struct phy *phy;
unsigned int i;
int ret;
if (!dev->parent || !dev->parent->of_node)
return -ENODEV;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->grf = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(priv->grf)) {
dev_err(dev, "Can't find GRF syscon\n");
return -ENODEV;
}
drv_data = of_device_get_match_data(dev);
priv->drv_data = drv_data;
priv->clks = devm_kcalloc(&pdev->dev, drv_data->num_clks,
sizeof(*priv->clks), GFP_KERNEL);
if (!priv->clks)
return -ENOMEM;
for (i = 0; i < drv_data->num_clks; i++)
priv->clks[i].id = drv_data->clks[i];
ret = devm_clk_bulk_get(&pdev->dev, drv_data->num_clks, priv->clks);
if (ret)
return ret;
phy = devm_phy_create(dev, np, &rk_dphy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create phy\n");
return PTR_ERR(phy);
}
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static struct platform_driver rk_dphy_driver = {
.probe = rk_dphy_probe,
.driver = {
.name = "rockchip-mipi-dphy-rx0",
.of_match_table = rk_dphy_dt_ids,
},
};
module_platform_driver(rk_dphy_driver);
MODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>");
MODULE_DESCRIPTION("Rockchip MIPI Synopsys DPHY RX0 driver");
MODULE_LICENSE("Dual MIT/GPL");
@@ -0,0 +1,427 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Rockchip emmc PHY driver
*
* Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>
* Copyright (C) 2016 ROCKCHIP, Inc.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/hw_bitfield.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
/*
* The higher 16-bit of this register is used for write protection
* only if BIT(x + 16) set to 1 the BIT(x) can be written.
*/
#define HIWORD_UPDATE(val, mask, shift) \
(FIELD_PREP_WM16((mask) << (shift), (val)))
/* Register definition */
#define GRF_EMMCPHY_CON0 0x0
#define GRF_EMMCPHY_CON1 0x4
#define GRF_EMMCPHY_CON2 0x8
#define GRF_EMMCPHY_CON3 0xc
#define GRF_EMMCPHY_CON4 0x10
#define GRF_EMMCPHY_CON5 0x14
#define GRF_EMMCPHY_CON6 0x18
#define GRF_EMMCPHY_STATUS 0x20
#define PHYCTRL_PDB_MASK 0x1
#define PHYCTRL_PDB_SHIFT 0x0
#define PHYCTRL_PDB_PWR_ON 0x1
#define PHYCTRL_PDB_PWR_OFF 0x0
#define PHYCTRL_ENDLL_MASK 0x1
#define PHYCTRL_ENDLL_SHIFT 0x1
#define PHYCTRL_ENDLL_ENABLE 0x1
#define PHYCTRL_ENDLL_DISABLE 0x0
#define PHYCTRL_CALDONE_MASK 0x1
#define PHYCTRL_CALDONE_SHIFT 0x6
#define PHYCTRL_CALDONE_DONE 0x1
#define PHYCTRL_CALDONE_GOING 0x0
#define PHYCTRL_DLLRDY_MASK 0x1
#define PHYCTRL_DLLRDY_SHIFT 0x5
#define PHYCTRL_DLLRDY_DONE 0x1
#define PHYCTRL_DLLRDY_GOING 0x0
#define PHYCTRL_FREQSEL_200M 0x0
#define PHYCTRL_FREQSEL_50M 0x1
#define PHYCTRL_FREQSEL_100M 0x2
#define PHYCTRL_FREQSEL_150M 0x3
#define PHYCTRL_FREQSEL_MASK 0x3
#define PHYCTRL_FREQSEL_SHIFT 0xc
#define PHYCTRL_DR_MASK 0x7
#define PHYCTRL_DR_SHIFT 0x4
#define PHYCTRL_DR_50OHM 0x0
#define PHYCTRL_DR_33OHM 0x1
#define PHYCTRL_DR_66OHM 0x2
#define PHYCTRL_DR_100OHM 0x3
#define PHYCTRL_DR_40OHM 0x4
#define PHYCTRL_OTAPDLYENA 0x1
#define PHYCTRL_OTAPDLYENA_MASK 0x1
#define PHYCTRL_OTAPDLYENA_SHIFT 0xb
#define PHYCTRL_OTAPDLYSEL_DEFAULT 0x4
#define PHYCTRL_OTAPDLYSEL_MAXVALUE 0xf
#define PHYCTRL_OTAPDLYSEL_MASK 0xf
#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7
#define PHYCTRL_REN_STRB_DISABLE 0x0
#define PHYCTRL_REN_STRB_ENABLE 0x1
#define PHYCTRL_REN_STRB_MASK 0x1
#define PHYCTRL_REN_STRB_SHIFT 0x9
#define PHYCTRL_IS_CALDONE(x) \
((((x) >> PHYCTRL_CALDONE_SHIFT) & \
PHYCTRL_CALDONE_MASK) == PHYCTRL_CALDONE_DONE)
#define PHYCTRL_IS_DLLRDY(x) \
((((x) >> PHYCTRL_DLLRDY_SHIFT) & \
PHYCTRL_DLLRDY_MASK) == PHYCTRL_DLLRDY_DONE)
struct rockchip_emmc_phy {
unsigned int reg_offset;
struct regmap *reg_base;
struct clk *emmcclk;
unsigned int drive_impedance;
unsigned int enable_strobe_pulldown;
unsigned int output_tapdelay_select;
};
static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
{
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
unsigned int caldone;
unsigned int dllrdy;
unsigned int freqsel = PHYCTRL_FREQSEL_200M;
unsigned long rate;
int ret;
/*
* Keep phyctrl_pdb and phyctrl_endll low to allow
* initialization of CALIO state M/C DFFs
*/
regmap_write(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
PHYCTRL_PDB_MASK,
PHYCTRL_PDB_SHIFT));
regmap_write(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
PHYCTRL_ENDLL_MASK,
PHYCTRL_ENDLL_SHIFT));
/* Already finish power_off above */
if (on_off == PHYCTRL_PDB_PWR_OFF)
return 0;
rate = clk_get_rate(rk_phy->emmcclk);
if (rate != 0) {
unsigned long ideal_rate;
unsigned long diff;
switch (rate) {
case 1 ... 74999999:
ideal_rate = 50000000;
freqsel = PHYCTRL_FREQSEL_50M;
break;
case 75000000 ... 124999999:
ideal_rate = 100000000;
freqsel = PHYCTRL_FREQSEL_100M;
break;
case 125000000 ... 174999999:
ideal_rate = 150000000;
freqsel = PHYCTRL_FREQSEL_150M;
break;
default:
ideal_rate = 200000000;
break;
}
diff = (rate > ideal_rate) ?
rate - ideal_rate : ideal_rate - rate;
/*
* In order for tuning delays to be accurate we need to be
* pretty spot on for the DLL range, so warn if we're too
* far off. Also warn if we're above the 200 MHz max. Don't
* warn for really slow rates since we won't be tuning then.
*/
if ((rate > 50000000 && diff > 15000000) || (rate > 200000000))
dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate);
}
/*
* According to the user manual, calpad calibration
* cycle takes more than 2us without the minimal recommended
* value, so we may need a little margin here
*/
udelay(3);
regmap_write(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON,
PHYCTRL_PDB_MASK,
PHYCTRL_PDB_SHIFT));
/*
* According to the user manual, it asks driver to wait 5us for
* calpad busy trimming. However it is documented that this value is
* PVT(A.K.A process,voltage and temperature) relevant, so some
* failure cases are found which indicates we should be more tolerant
* to calpad busy trimming.
*/
ret = regmap_read_poll_timeout(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
caldone, PHYCTRL_IS_CALDONE(caldone),
0, 50);
if (ret) {
pr_err("%s: caldone failed, ret=%d\n", __func__, ret);
return ret;
}
/* Set the frequency of the DLL operation */
regmap_write(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_CON0,
HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK,
PHYCTRL_FREQSEL_SHIFT));
/* Turn on the DLL */
regmap_write(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
PHYCTRL_ENDLL_MASK,
PHYCTRL_ENDLL_SHIFT));
/*
* We turned on the DLL even though the rate was 0 because we the
* clock might be turned on later. ...but we can't wait for the DLL
* to lock when the rate is 0 because it will never lock with no
* input clock.
*
* Technically we should be checking the lock later when the clock
* is turned on, but for now we won't.
*/
if (rate == 0)
return 0;
/*
* After enabling analog DLL circuits docs say that we need 10.2 us if
* our source clock is at 50 MHz and that lock time scales linearly
* with clock speed. If we are powering on the PHY and the card clock
* is super slow (like 100 kHZ) this could take as long as 5.1 ms as
* per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms
* Hopefully we won't be running at 100 kHz, but we should still make
* sure we wait long enough.
*
* NOTE: There appear to be corner cases where the DLL seems to take
* extra long to lock for reasons that aren't understood. In some
* extreme cases we've seen it take up to over 10ms (!). We'll be
* generous and give it 50ms.
*/
ret = regmap_read_poll_timeout(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
dllrdy, PHYCTRL_IS_DLLRDY(dllrdy),
0, 50 * USEC_PER_MSEC);
if (ret) {
pr_err("%s: dllrdy failed. ret=%d\n", __func__, ret);
return ret;
}
return 0;
}
static int rockchip_emmc_phy_init(struct phy *phy)
{
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
int ret = 0;
/*
* We purposely get the clock here and not in probe to avoid the
* circular dependency problem. We expect:
* - PHY driver to probe
* - SDHCI driver to start probe
* - SDHCI driver to register it's clock
* - SDHCI driver to get the PHY
* - SDHCI driver to init the PHY
*
* The clock is optional, using clk_get_optional() to get the clock
* and do error processing if the return value != NULL
*
* NOTE: we don't do anything special for EPROBE_DEFER here. Given the
* above expected use case, EPROBE_DEFER isn't sensible to expect, so
* it's just like any other error.
*/
rk_phy->emmcclk = clk_get_optional(&phy->dev, "emmcclk");
if (IS_ERR(rk_phy->emmcclk)) {
ret = PTR_ERR(rk_phy->emmcclk);
dev_err(&phy->dev, "Error getting emmcclk: %d\n", ret);
rk_phy->emmcclk = NULL;
}
return ret;
}
static int rockchip_emmc_phy_exit(struct phy *phy)
{
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
clk_put(rk_phy->emmcclk);
return 0;
}
static int rockchip_emmc_phy_power_off(struct phy *phy)
{
/* Power down emmc phy analog blocks */
return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF);
}
static int rockchip_emmc_phy_power_on(struct phy *phy)
{
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
/* Drive impedance: from DTS */
regmap_write(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
HIWORD_UPDATE(rk_phy->drive_impedance,
PHYCTRL_DR_MASK,
PHYCTRL_DR_SHIFT));
/* Output tap delay: enable */
regmap_write(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_CON0,
HIWORD_UPDATE(PHYCTRL_OTAPDLYENA,
PHYCTRL_OTAPDLYENA_MASK,
PHYCTRL_OTAPDLYENA_SHIFT));
/* Output tap delay */
regmap_write(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_CON0,
HIWORD_UPDATE(rk_phy->output_tapdelay_select,
PHYCTRL_OTAPDLYSEL_MASK,
PHYCTRL_OTAPDLYSEL_SHIFT));
/* Internal pull-down for strobe line */
regmap_write(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_CON2,
HIWORD_UPDATE(rk_phy->enable_strobe_pulldown,
PHYCTRL_REN_STRB_MASK,
PHYCTRL_REN_STRB_SHIFT));
/* Power up emmc phy analog blocks */
return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON);
}
static const struct phy_ops ops = {
.init = rockchip_emmc_phy_init,
.exit = rockchip_emmc_phy_exit,
.power_on = rockchip_emmc_phy_power_on,
.power_off = rockchip_emmc_phy_power_off,
.owner = THIS_MODULE,
};
static u32 convert_drive_impedance_ohm(struct platform_device *pdev, u32 dr_ohm)
{
switch (dr_ohm) {
case 100:
return PHYCTRL_DR_100OHM;
case 66:
return PHYCTRL_DR_66OHM;
case 50:
return PHYCTRL_DR_50OHM;
case 40:
return PHYCTRL_DR_40OHM;
case 33:
return PHYCTRL_DR_33OHM;
}
dev_warn(&pdev->dev, "Invalid value %u for drive-impedance-ohm.\n",
dr_ohm);
return PHYCTRL_DR_50OHM;
}
static int rockchip_emmc_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rockchip_emmc_phy *rk_phy;
struct phy *generic_phy;
struct phy_provider *phy_provider;
struct regmap *grf;
unsigned int reg_offset;
u32 val;
if (!dev->parent || !dev->parent->of_node)
return -ENODEV;
grf = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(grf)) {
dev_err(dev, "Missing rockchip,grf property\n");
return PTR_ERR(grf);
}
rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
if (!rk_phy)
return -ENOMEM;
if (of_property_read_u32(dev->of_node, "reg", &reg_offset)) {
dev_err(dev, "missing reg property in node %pOFn\n",
dev->of_node);
return -EINVAL;
}
rk_phy->reg_offset = reg_offset;
rk_phy->reg_base = grf;
rk_phy->drive_impedance = PHYCTRL_DR_50OHM;
rk_phy->enable_strobe_pulldown = PHYCTRL_REN_STRB_DISABLE;
rk_phy->output_tapdelay_select = PHYCTRL_OTAPDLYSEL_DEFAULT;
if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val))
rk_phy->drive_impedance = convert_drive_impedance_ohm(pdev, val);
if (of_property_read_bool(dev->of_node, "rockchip,enable-strobe-pulldown"))
rk_phy->enable_strobe_pulldown = PHYCTRL_REN_STRB_ENABLE;
if (!of_property_read_u32(dev->of_node, "rockchip,output-tapdelay-select", &val)) {
if (val <= PHYCTRL_OTAPDLYSEL_MAXVALUE)
rk_phy->output_tapdelay_select = val;
else
dev_err(dev, "output-tapdelay-select exceeds limit, apply default\n");
}
generic_phy = devm_phy_create(dev, dev->of_node, &ops);
if (IS_ERR(generic_phy)) {
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(generic_phy);
}
phy_set_drvdata(generic_phy, rk_phy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id rockchip_emmc_phy_dt_ids[] = {
{ .compatible = "rockchip,rk3399-emmc-phy" },
{}
};
MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids);
static struct platform_driver rockchip_emmc_driver = {
.probe = rockchip_emmc_phy_probe,
.driver = {
.name = "rockchip-emmc-phy",
.of_match_table = rockchip_emmc_phy_dt_ids,
},
};
module_platform_driver(rockchip_emmc_driver);
MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip EMMC PHY driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,536 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip MIPI RX Innosilicon DPHY driver
*
* Copyright (C) 2021 Fuzhou Rockchip Electronics Co., Ltd.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
/* GRF */
#define RK1808_GRF_PD_VI_CON_OFFSET 0x0430
#define RK3326_GRF_PD_VI_CON_OFFSET 0x0430
#define RK3368_GRF_SOC_CON6_OFFSET 0x0418
#define RK3568_GRF_VI_CON0 0x0340
#define RK3568_GRF_VI_CON1 0x0344
#define RK3588_CSIDPHY_GRF_CON0 0x0000
/* PHY */
#define CSIDPHY_CTRL_LANE_ENABLE 0x00
#define CSIDPHY_CTRL_LANE_ENABLE_CK BIT(6)
#define CSIDPHY_CTRL_LANE_ENABLE_MASK GENMASK(5, 2)
#define CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED BIT(0)
/* not present on all variants */
#define CSIDPHY_CTRL_PWRCTL 0x04
#define CSIDPHY_CTRL_PWRCTL_UNDEFINED GENMASK(7, 5)
#define CSIDPHY_CTRL_PWRCTL_SYNCRST BIT(2)
#define CSIDPHY_CTRL_PWRCTL_LDO_PD BIT(1)
#define CSIDPHY_CTRL_PWRCTL_PLL_PD BIT(0)
#define CSIDPHY_CTRL_DIG_RST 0x80
#define CSIDPHY_CTRL_DIG_RST_UNDEFINED 0x1e
#define CSIDPHY_CTRL_DIG_RST_RESET BIT(0)
/* offset after ths_settle_offset */
#define CSIDPHY_CLK_THS_SETTLE 0
#define CSIDPHY_LANE_THS_SETTLE(n) (((n) + 1) * 0x80)
#define CSIDPHY_THS_SETTLE_MASK GENMASK(6, 0)
/* offset after calib_offset */
#define CSIDPHY_CLK_CALIB_EN 0
#define CSIDPHY_LANE_CALIB_EN(n) (((n) + 1) * 0x80)
#define CSIDPHY_CALIB_EN BIT(7)
/* Configure the count time of the THS-SETTLE by protocol. */
#define RK1808_CSIDPHY_CLK_WR_THS_SETTLE 0x160
#define RK3326_CSIDPHY_CLK_WR_THS_SETTLE 0x100
#define RK3368_CSIDPHY_CLK_WR_THS_SETTLE 0x100
#define RK3568_CSIDPHY_CLK_WR_THS_SETTLE 0x160
/* Calibration reception enable */
#define RK1808_CSIDPHY_CLK_CALIB_EN 0x168
#define RK3568_CSIDPHY_CLK_CALIB_EN 0x168
#define RESETS_MAX 2
/*
* The higher 16-bit of this register is used for write protection
* only if BIT(x + 16) set to 1 the BIT(x) can be written.
*/
#define HIWORD_UPDATE(val, mask, shift) \
((val) << (shift) | (mask) << ((shift) + 16))
#define HZ_TO_MHZ(freq) div_u64(freq, 1000 * 1000)
enum dphy_reg_id {
/* rk1808 & rk3326 */
GRF_DPHY_CSIPHY_FORCERXMODE,
GRF_DPHY_CSIPHY_CLKLANE_EN,
GRF_DPHY_CSIPHY_DATALANE_EN,
};
struct dphy_reg {
u32 offset;
u32 mask;
u32 shift;
u8 valid;
};
#define PHY_REG(_offset, _width, _shift) \
{ .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, .valid = 1, }
static const struct dphy_reg rk1808_grf_dphy_regs[] = {
[GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 4, 0),
[GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 1, 8),
[GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 4, 4),
};
static const struct dphy_reg rk3326_grf_dphy_regs[] = {
[GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 4, 0),
[GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 1, 8),
[GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 4, 4),
};
static const struct dphy_reg rk3368_grf_dphy_regs[] = {
[GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3368_GRF_SOC_CON6_OFFSET, 4, 8),
};
static const struct dphy_reg rk3568_grf_dphy_regs[] = {
[GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3568_GRF_VI_CON0, 4, 0),
[GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK3568_GRF_VI_CON0, 4, 4),
[GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK3568_GRF_VI_CON0, 1, 8),
};
static const struct dphy_reg rk3588_grf_dphy_regs[] = {
[GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3588_CSIDPHY_GRF_CON0, 4, 0),
[GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK3588_CSIDPHY_GRF_CON0, 4, 4),
[GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK3588_CSIDPHY_GRF_CON0, 1, 8),
};
struct hsfreq_range {
u32 range_h;
u8 cfg_bit;
};
struct dphy_drv_data {
int pwrctl_offset;
int ths_settle_offset;
int calib_offset;
const struct hsfreq_range *hsfreq_ranges;
int num_hsfreq_ranges;
const struct dphy_reg *grf_regs;
const char *const *resets;
unsigned int resets_num;
};
struct rockchip_inno_csidphy {
struct device *dev;
void __iomem *phy_base;
struct clk *pclk;
struct regmap *grf;
struct reset_control_bulk_data resets[RESETS_MAX];
unsigned int resets_num;
const struct dphy_drv_data *drv_data;
struct phy_configure_opts_mipi_dphy config;
u8 hsfreq;
};
static inline void write_grf_reg(struct rockchip_inno_csidphy *priv,
int index, u8 value)
{
const struct dphy_drv_data *drv_data = priv->drv_data;
const struct dphy_reg *reg = &drv_data->grf_regs[index];
if (reg->valid)
regmap_write(priv->grf, reg->offset,
HIWORD_UPDATE(value, reg->mask, reg->shift));
}
/* These tables must be sorted by .range_h ascending. */
static const struct hsfreq_range rk1808_mipidphy_hsfreq_ranges[] = {
{ 109, 0x02}, { 149, 0x03}, { 199, 0x06}, { 249, 0x06},
{ 299, 0x06}, { 399, 0x08}, { 499, 0x0b}, { 599, 0x0e},
{ 699, 0x10}, { 799, 0x12}, { 999, 0x16}, {1199, 0x1e},
{1399, 0x23}, {1599, 0x2d}, {1799, 0x32}, {1999, 0x37},
{2199, 0x3c}, {2399, 0x41}, {2499, 0x46}
};
static const struct hsfreq_range rk3326_mipidphy_hsfreq_ranges[] = {
{ 109, 0x00}, { 149, 0x01}, { 199, 0x02}, { 249, 0x03},
{ 299, 0x04}, { 399, 0x05}, { 499, 0x06}, { 599, 0x07},
{ 699, 0x08}, { 799, 0x09}, { 899, 0x0a}, {1099, 0x0b},
{1249, 0x0c}, {1349, 0x0d}, {1500, 0x0e}
};
static const struct hsfreq_range rk3368_mipidphy_hsfreq_ranges[] = {
{ 109, 0x00}, { 149, 0x01}, { 199, 0x02}, { 249, 0x03},
{ 299, 0x04}, { 399, 0x05}, { 499, 0x06}, { 599, 0x07},
{ 699, 0x08}, { 799, 0x09}, { 899, 0x0a}, {1099, 0x0b},
{1249, 0x0c}, {1349, 0x0d}, {1500, 0x0e}
};
static const char *const rk3368_reset_names[] = {
"apb"
};
static const char *const rk3588_reset_names[] = {
"apb",
"phy"
};
static void rockchip_inno_csidphy_ths_settle(struct rockchip_inno_csidphy *priv,
int hsfreq, int offset)
{
const struct dphy_drv_data *drv_data = priv->drv_data;
u32 val;
val = readl(priv->phy_base + drv_data->ths_settle_offset + offset);
val &= ~CSIDPHY_THS_SETTLE_MASK;
val |= hsfreq;
writel(val, priv->phy_base + drv_data->ths_settle_offset + offset);
}
static int rockchip_inno_csidphy_configure(struct phy *phy,
union phy_configure_opts *opts)
{
struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy);
const struct dphy_drv_data *drv_data = priv->drv_data;
struct phy_configure_opts_mipi_dphy *config = &opts->mipi_dphy;
unsigned int hsfreq = 0;
unsigned int i;
u64 data_rate_mbps;
int ret;
/* pass with phy_mipi_dphy_get_default_config (with pixel rate?) */
ret = phy_mipi_dphy_config_validate(config);
if (ret)
return ret;
data_rate_mbps = HZ_TO_MHZ(config->hs_clk_rate);
dev_dbg(priv->dev, "lanes %d - data_rate_mbps %llu\n",
config->lanes, data_rate_mbps);
for (i = 0; i < drv_data->num_hsfreq_ranges; i++) {
if (drv_data->hsfreq_ranges[i].range_h >= data_rate_mbps) {
hsfreq = drv_data->hsfreq_ranges[i].cfg_bit;
break;
}
}
if (!hsfreq)
return -EINVAL;
priv->hsfreq = hsfreq;
priv->config = *config;
return 0;
}
static int rockchip_inno_csidphy_power_on(struct phy *phy)
{
struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy);
const struct dphy_drv_data *drv_data = priv->drv_data;
u64 data_rate_mbps = HZ_TO_MHZ(priv->config.hs_clk_rate);
u32 val;
int ret, i;
ret = clk_enable(priv->pclk);
if (ret < 0)
return ret;
ret = pm_runtime_resume_and_get(priv->dev);
if (ret < 0) {
clk_disable(priv->pclk);
return ret;
}
/* phy start */
if (drv_data->pwrctl_offset >= 0)
writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED |
CSIDPHY_CTRL_PWRCTL_SYNCRST,
priv->phy_base + drv_data->pwrctl_offset);
/* set data lane num and enable clock lane */
val = FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_MASK, GENMASK(priv->config.lanes - 1, 0)) |
FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_CK, 1) |
FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED, 1);
writel(val, priv->phy_base + CSIDPHY_CTRL_LANE_ENABLE);
/* Reset dphy analog part */
if (drv_data->pwrctl_offset >= 0)
writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED,
priv->phy_base + drv_data->pwrctl_offset);
usleep_range(500, 1000);
/* Reset dphy digital part */
writel(CSIDPHY_CTRL_DIG_RST_UNDEFINED,
priv->phy_base + CSIDPHY_CTRL_DIG_RST);
writel(CSIDPHY_CTRL_DIG_RST_UNDEFINED + CSIDPHY_CTRL_DIG_RST_RESET,
priv->phy_base + CSIDPHY_CTRL_DIG_RST);
/* not into receive mode/wait stopstate */
write_grf_reg(priv, GRF_DPHY_CSIPHY_FORCERXMODE, 0x0);
/* enable calibration */
if (data_rate_mbps > 1500 && drv_data->calib_offset >= 0) {
writel(CSIDPHY_CALIB_EN,
priv->phy_base + drv_data->calib_offset +
CSIDPHY_CLK_CALIB_EN);
for (i = 0; i < priv->config.lanes; i++)
writel(CSIDPHY_CALIB_EN,
priv->phy_base + drv_data->calib_offset +
CSIDPHY_LANE_CALIB_EN(i));
}
rockchip_inno_csidphy_ths_settle(priv, priv->hsfreq,
CSIDPHY_CLK_THS_SETTLE);
for (i = 0; i < priv->config.lanes; i++)
rockchip_inno_csidphy_ths_settle(priv, priv->hsfreq,
CSIDPHY_LANE_THS_SETTLE(i));
write_grf_reg(priv, GRF_DPHY_CSIPHY_CLKLANE_EN, 0x1);
write_grf_reg(priv, GRF_DPHY_CSIPHY_DATALANE_EN,
GENMASK(priv->config.lanes - 1, 0));
return 0;
}
static int rockchip_inno_csidphy_power_off(struct phy *phy)
{
struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy);
const struct dphy_drv_data *drv_data = priv->drv_data;
/* disable all lanes */
writel(CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED,
priv->phy_base + CSIDPHY_CTRL_LANE_ENABLE);
/* disable pll and ldo */
if (drv_data->pwrctl_offset >= 0)
writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED |
CSIDPHY_CTRL_PWRCTL_LDO_PD |
CSIDPHY_CTRL_PWRCTL_PLL_PD,
priv->phy_base + drv_data->pwrctl_offset);
usleep_range(500, 1000);
pm_runtime_put(priv->dev);
clk_disable(priv->pclk);
return 0;
}
static int rockchip_inno_csidphy_init(struct phy *phy)
{
struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy);
return clk_prepare(priv->pclk);
}
static int rockchip_inno_csidphy_exit(struct phy *phy)
{
struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy);
clk_unprepare(priv->pclk);
return 0;
}
static const struct phy_ops rockchip_inno_csidphy_ops = {
.power_on = rockchip_inno_csidphy_power_on,
.power_off = rockchip_inno_csidphy_power_off,
.init = rockchip_inno_csidphy_init,
.exit = rockchip_inno_csidphy_exit,
.configure = rockchip_inno_csidphy_configure,
.owner = THIS_MODULE,
};
static const struct dphy_drv_data rk1808_mipidphy_drv_data = {
.pwrctl_offset = -1,
.ths_settle_offset = RK1808_CSIDPHY_CLK_WR_THS_SETTLE,
.calib_offset = RK1808_CSIDPHY_CLK_CALIB_EN,
.hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges,
.num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges),
.grf_regs = rk1808_grf_dphy_regs,
.resets = rk3368_reset_names,
.resets_num = ARRAY_SIZE(rk3368_reset_names),
};
static const struct dphy_drv_data rk3326_mipidphy_drv_data = {
.pwrctl_offset = CSIDPHY_CTRL_PWRCTL,
.ths_settle_offset = RK3326_CSIDPHY_CLK_WR_THS_SETTLE,
.calib_offset = -1,
.hsfreq_ranges = rk3326_mipidphy_hsfreq_ranges,
.num_hsfreq_ranges = ARRAY_SIZE(rk3326_mipidphy_hsfreq_ranges),
.grf_regs = rk3326_grf_dphy_regs,
.resets = rk3368_reset_names,
.resets_num = ARRAY_SIZE(rk3368_reset_names),
};
static const struct dphy_drv_data rk3368_mipidphy_drv_data = {
.pwrctl_offset = CSIDPHY_CTRL_PWRCTL,
.ths_settle_offset = RK3368_CSIDPHY_CLK_WR_THS_SETTLE,
.calib_offset = -1,
.hsfreq_ranges = rk3368_mipidphy_hsfreq_ranges,
.num_hsfreq_ranges = ARRAY_SIZE(rk3368_mipidphy_hsfreq_ranges),
.grf_regs = rk3368_grf_dphy_regs,
.resets = rk3368_reset_names,
.resets_num = ARRAY_SIZE(rk3368_reset_names),
};
static const struct dphy_drv_data rk3568_mipidphy_drv_data = {
.pwrctl_offset = -1,
.ths_settle_offset = RK3568_CSIDPHY_CLK_WR_THS_SETTLE,
.calib_offset = RK3568_CSIDPHY_CLK_CALIB_EN,
.hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges,
.num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges),
.grf_regs = rk3568_grf_dphy_regs,
.resets = rk3368_reset_names,
.resets_num = ARRAY_SIZE(rk3368_reset_names),
};
static const struct dphy_drv_data rk3588_mipidphy_drv_data = {
.pwrctl_offset = -1,
.ths_settle_offset = RK3568_CSIDPHY_CLK_WR_THS_SETTLE,
.calib_offset = RK3568_CSIDPHY_CLK_CALIB_EN,
.hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges,
.num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges),
.grf_regs = rk3588_grf_dphy_regs,
.resets = rk3588_reset_names,
.resets_num = ARRAY_SIZE(rk3588_reset_names),
};
static const struct of_device_id rockchip_inno_csidphy_match_id[] = {
{
.compatible = "rockchip,px30-csi-dphy",
.data = &rk3326_mipidphy_drv_data,
},
{
.compatible = "rockchip,rk1808-csi-dphy",
.data = &rk1808_mipidphy_drv_data,
},
{
.compatible = "rockchip,rk3326-csi-dphy",
.data = &rk3326_mipidphy_drv_data,
},
{
.compatible = "rockchip,rk3368-csi-dphy",
.data = &rk3368_mipidphy_drv_data,
},
{
.compatible = "rockchip,rk3568-csi-dphy",
.data = &rk3568_mipidphy_drv_data,
},
{
.compatible = "rockchip,rk3588-csi-dphy",
.data = &rk3588_mipidphy_drv_data,
},
{}
};
MODULE_DEVICE_TABLE(of, rockchip_inno_csidphy_match_id);
static int rockchip_inno_csidphy_probe(struct platform_device *pdev)
{
struct rockchip_inno_csidphy *priv;
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
struct phy *phy;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
platform_set_drvdata(pdev, priv);
priv->drv_data = of_device_get_match_data(dev);
if (!priv->drv_data) {
dev_err(dev, "Can't find device data\n");
return -ENODEV;
}
priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
if (IS_ERR(priv->grf)) {
dev_err(dev, "Can't find GRF syscon\n");
return PTR_ERR(priv->grf);
}
priv->phy_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->phy_base))
return PTR_ERR(priv->phy_base);
priv->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(priv->pclk)) {
dev_err(dev, "failed to get pclk\n");
return PTR_ERR(priv->pclk);
}
if (priv->drv_data->resets_num > RESETS_MAX) {
dev_err(dev, "invalid number of resets\n");
return -EINVAL;
}
priv->resets_num = priv->drv_data->resets_num;
for (unsigned int i = 0; i < priv->resets_num; i++)
priv->resets[i].id = priv->drv_data->resets[i];
ret = devm_reset_control_bulk_get_exclusive(dev, priv->resets_num,
priv->resets);
if (ret) {
dev_err(dev, "failed to get system reset control\n");
return ret;
}
phy = devm_phy_create(dev, NULL, &rockchip_inno_csidphy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create phy\n");
return PTR_ERR(phy);
}
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider)) {
dev_err(dev, "failed to register phy provider\n");
return PTR_ERR(phy_provider);
}
pm_runtime_enable(dev);
return 0;
}
static void rockchip_inno_csidphy_remove(struct platform_device *pdev)
{
struct rockchip_inno_csidphy *priv = platform_get_drvdata(pdev);
pm_runtime_disable(priv->dev);
}
static struct platform_driver rockchip_inno_csidphy_driver = {
.driver = {
.name = "rockchip-inno-csidphy",
.of_match_table = rockchip_inno_csidphy_match_id,
},
.probe = rockchip_inno_csidphy_probe,
.remove = rockchip_inno_csidphy_remove,
};
module_platform_driver(rockchip_inno_csidphy_driver);
MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
MODULE_DESCRIPTION("Rockchip MIPI Innosilicon CSI-DPHY driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,878 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018 Rockchip Electronics Co. Ltd.
*
* Author: Wyon Bi <bivvy.bi@rock-chips.com>
*/
#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/time64.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
/*
* The offset address[7:0] is distributed two parts, one from the bit7 to bit5
* is the first address, the other from the bit4 to bit0 is the second address.
* when you configure the registers, you must set both of them. The Clock Lane
* and Data Lane use the same registers with the same second address, but the
* first address is different.
*/
#define FIRST_ADDRESS(x) (((x) & 0x7) << 5)
#define SECOND_ADDRESS(x) (((x) & 0x1f) << 0)
#define PHY_REG(first, second) (FIRST_ADDRESS(first) | \
SECOND_ADDRESS(second))
/* Analog Register Part: reg00 */
#define BANDGAP_POWER_MASK BIT(7)
#define BANDGAP_POWER_DOWN BIT(7)
#define BANDGAP_POWER_ON 0
#define LANE_EN_MASK GENMASK(6, 2)
#define LANE_EN_CK BIT(6)
#define LANE_EN_3 BIT(5)
#define LANE_EN_2 BIT(4)
#define LANE_EN_1 BIT(3)
#define LANE_EN_0 BIT(2)
#define POWER_WORK_MASK GENMASK(1, 0)
#define POWER_WORK_ENABLE UPDATE(1, 1, 0)
#define POWER_WORK_DISABLE UPDATE(2, 1, 0)
/* Analog Register Part: reg01 */
#define REG_SYNCRST_MASK BIT(2)
#define REG_SYNCRST_RESET BIT(2)
#define REG_SYNCRST_NORMAL 0
#define REG_LDOPD_MASK BIT(1)
#define REG_LDOPD_POWER_DOWN BIT(1)
#define REG_LDOPD_POWER_ON 0
#define REG_PLLPD_MASK BIT(0)
#define REG_PLLPD_POWER_DOWN BIT(0)
#define REG_PLLPD_POWER_ON 0
/* Analog Register Part: reg03 */
#define REG_FBDIV_HI_MASK BIT(5)
#define REG_FBDIV_HI(x) UPDATE((x >> 8), 5, 5)
#define REG_PREDIV_MASK GENMASK(4, 0)
#define REG_PREDIV(x) UPDATE(x, 4, 0)
/* Analog Register Part: reg04 */
#define REG_FBDIV_LO_MASK GENMASK(7, 0)
#define REG_FBDIV_LO(x) UPDATE(x, 7, 0)
/* Analog Register Part: reg05 */
#define SAMPLE_CLOCK_PHASE_MASK GENMASK(6, 4)
#define SAMPLE_CLOCK_PHASE(x) UPDATE(x, 6, 4)
#define CLOCK_LANE_SKEW_PHASE_MASK GENMASK(2, 0)
#define CLOCK_LANE_SKEW_PHASE(x) UPDATE(x, 2, 0)
/* Analog Register Part: reg06 */
#define DATA_LANE_3_SKEW_PHASE_MASK GENMASK(6, 4)
#define DATA_LANE_3_SKEW_PHASE(x) UPDATE(x, 6, 4)
#define DATA_LANE_2_SKEW_PHASE_MASK GENMASK(2, 0)
#define DATA_LANE_2_SKEW_PHASE(x) UPDATE(x, 2, 0)
/* Analog Register Part: reg07 */
#define DATA_LANE_1_SKEW_PHASE_MASK GENMASK(6, 4)
#define DATA_LANE_1_SKEW_PHASE(x) UPDATE(x, 6, 4)
#define DATA_LANE_0_SKEW_PHASE_MASK GENMASK(2, 0)
#define DATA_LANE_0_SKEW_PHASE(x) UPDATE(x, 2, 0)
/* Analog Register Part: reg08 */
#define PLL_POST_DIV_ENABLE_MASK BIT(5)
#define PLL_POST_DIV_ENABLE BIT(5)
#define SAMPLE_CLOCK_DIRECTION_MASK BIT(4)
#define SAMPLE_CLOCK_DIRECTION_REVERSE BIT(4)
#define SAMPLE_CLOCK_DIRECTION_FORWARD 0
#define LOWFRE_EN_MASK BIT(5)
#define PLL_OUTPUT_FREQUENCY_DIV_BY_1 0
#define PLL_OUTPUT_FREQUENCY_DIV_BY_2 1
/* Analog Register Part: reg0b */
#define CLOCK_LANE_VOD_RANGE_SET_MASK GENMASK(3, 0)
#define CLOCK_LANE_VOD_RANGE_SET(x) UPDATE(x, 3, 0)
#define VOD_MIN_RANGE 0x1
#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)
#define REG_DIG_RSTN_RESET 0
/* Digital Register Part: reg01 */
#define INVERT_TXCLKESC_MASK BIT(1)
#define INVERT_TXCLKESC_ENABLE BIT(1)
#define INVERT_TXCLKESC_DISABLE 0
#define INVERT_TXBYTECLKHS_MASK BIT(0)
#define INVERT_TXBYTECLKHS_ENABLE BIT(0)
#define INVERT_TXBYTECLKHS_DISABLE 0
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg05 */
#define T_LPX_CNT_MASK GENMASK(5, 0)
#define T_LPX_CNT(x) UPDATE(x, 5, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg06 */
#define T_HS_ZERO_CNT_HI_MASK BIT(7)
#define T_HS_ZERO_CNT_HI(x) UPDATE(x, 7, 7)
#define T_HS_PREPARE_CNT_MASK GENMASK(6, 0)
#define T_HS_PREPARE_CNT(x) UPDATE(x, 6, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg07 */
#define T_HS_ZERO_CNT_LO_MASK GENMASK(5, 0)
#define T_HS_ZERO_CNT_LO(x) UPDATE(x, 5, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg08 */
#define T_HS_TRAIL_CNT_MASK GENMASK(6, 0)
#define T_HS_TRAIL_CNT(x) UPDATE(x, 6, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg09 */
#define T_HS_EXIT_CNT_LO_MASK GENMASK(4, 0)
#define T_HS_EXIT_CNT_LO(x) UPDATE(x, 4, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0a */
#define T_CLK_POST_CNT_LO_MASK GENMASK(3, 0)
#define T_CLK_POST_CNT_LO(x) UPDATE(x, 3, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0c */
#define LPDT_TX_PPI_SYNC_MASK BIT(2)
#define LPDT_TX_PPI_SYNC_ENABLE BIT(2)
#define LPDT_TX_PPI_SYNC_DISABLE 0
#define T_WAKEUP_CNT_HI_MASK GENMASK(1, 0)
#define T_WAKEUP_CNT_HI(x) UPDATE(x, 1, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0d */
#define T_WAKEUP_CNT_LO_MASK GENMASK(7, 0)
#define T_WAKEUP_CNT_LO(x) UPDATE(x, 7, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0e */
#define T_CLK_PRE_CNT_MASK GENMASK(3, 0)
#define T_CLK_PRE_CNT(x) UPDATE(x, 3, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg10 */
#define T_CLK_POST_CNT_HI_MASK GENMASK(7, 6)
#define T_CLK_POST_CNT_HI(x) UPDATE(x, 7, 6)
#define T_TA_GO_CNT_MASK GENMASK(5, 0)
#define T_TA_GO_CNT(x) UPDATE(x, 5, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg11 */
#define T_HS_EXIT_CNT_HI_MASK BIT(6)
#define T_HS_EXIT_CNT_HI(x) UPDATE(x, 6, 6)
#define T_TA_SURE_CNT_MASK GENMASK(5, 0)
#define T_TA_SURE_CNT(x) UPDATE(x, 5, 0)
/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg12 */
#define T_TA_WAIT_CNT_MASK GENMASK(5, 0)
#define T_TA_WAIT_CNT(x) UPDATE(x, 5, 0)
/* LVDS Register Part: reg00 */
#define LVDS_DIGITAL_INTERNAL_RESET_MASK BIT(2)
#define LVDS_DIGITAL_INTERNAL_RESET_DISABLE BIT(2)
#define LVDS_DIGITAL_INTERNAL_RESET_ENABLE 0
/* LVDS Register Part: reg01 */
#define LVDS_DIGITAL_INTERNAL_ENABLE_MASK BIT(7)
#define LVDS_DIGITAL_INTERNAL_ENABLE BIT(7)
#define LVDS_DIGITAL_INTERNAL_DISABLE 0
/* LVDS Register Part: reg03 */
#define MODE_ENABLE_MASK GENMASK(2, 0)
#define TTL_MODE_ENABLE BIT(2)
#define LVDS_MODE_ENABLE BIT(1)
#define MIPI_MODE_ENABLE BIT(0)
/* LVDS Register Part: reg0b */
#define LVDS_LANE_EN_MASK GENMASK(7, 3)
#define LVDS_DATA_LANE0_EN BIT(7)
#define LVDS_DATA_LANE1_EN BIT(6)
#define LVDS_DATA_LANE2_EN BIT(5)
#define LVDS_DATA_LANE3_EN BIT(4)
#define LVDS_CLK_LANE_EN BIT(3)
#define LVDS_PLL_POWER_MASK BIT(2)
#define LVDS_PLL_POWER_OFF BIT(2)
#define LVDS_PLL_POWER_ON 0
#define LVDS_BANDGAP_POWER_MASK BIT(0)
#define LVDS_BANDGAP_POWER_DOWN BIT(0)
#define LVDS_BANDGAP_POWER_ON 0
#define DSI_PHY_RSTZ 0xa0
#define PHY_ENABLECLK BIT(2)
#define DSI_PHY_STATUS 0xb0
#define PHY_LOCK BIT(0)
enum phy_max_rate {
MAX_1GHZ,
MAX_1_5GHZ,
MAX_2_5GHZ,
};
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 {
struct device *dev;
struct clk *ref_clk;
struct clk *pclk_phy;
struct clk *pclk_host;
const struct inno_video_phy_plat_data *pdata;
void __iomem *phy_base;
void __iomem *host_base;
struct reset_control *rst;
enum phy_mode mode;
struct phy_configure_opts_mipi_dphy dphy_cfg;
struct clk *pll_clk;
struct {
struct clk_hw hw;
u8 prediv;
u16 fbdiv;
unsigned long rate;
} pll;
};
enum {
REGISTER_PART_ANALOG,
REGISTER_PART_DIGITAL,
REGISTER_PART_CLOCK_LANE,
REGISTER_PART_DATA0_LANE,
REGISTER_PART_DATA1_LANE,
REGISTER_PART_DATA2_LANE,
REGISTER_PART_DATA3_LANE,
REGISTER_PART_LVDS,
};
struct inno_mipi_dphy_timing {
unsigned long rate;
u8 lpx;
u8 hs_prepare;
u8 clk_lane_hs_zero;
u8 data_lane_hs_zero;
u8 hs_trail;
};
static const
struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table_max_1ghz[] = {
{ 110000000, 0x0, 0x20, 0x16, 0x02, 0x22},
{ 150000000, 0x0, 0x06, 0x16, 0x03, 0x45},
{ 200000000, 0x0, 0x18, 0x17, 0x04, 0x0b},
{ 250000000, 0x0, 0x05, 0x17, 0x05, 0x16},
{ 300000000, 0x0, 0x51, 0x18, 0x06, 0x2c},
{ 400000000, 0x0, 0x64, 0x19, 0x07, 0x33},
{ 500000000, 0x0, 0x20, 0x1b, 0x07, 0x4e},
{ 600000000, 0x0, 0x6a, 0x1d, 0x08, 0x3a},
{ 700000000, 0x0, 0x3e, 0x1e, 0x08, 0x6a},
{ 800000000, 0x0, 0x21, 0x1f, 0x09, 0x29},
{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},
{ 150000000, 0x02, 0x7f, 0x16, 0x03, 0x02},
{ 200000000, 0x02, 0x7f, 0x17, 0x04, 0x02},
{ 250000000, 0x02, 0x7f, 0x17, 0x05, 0x04},
{ 300000000, 0x02, 0x7f, 0x18, 0x06, 0x04},
{ 400000000, 0x03, 0x7e, 0x19, 0x07, 0x04},
{ 500000000, 0x03, 0x7c, 0x1b, 0x07, 0x08},
{ 600000000, 0x03, 0x70, 0x1d, 0x08, 0x10},
{ 700000000, 0x05, 0x40, 0x1e, 0x08, 0x30},
{ 800000000, 0x05, 0x02, 0x1f, 0x09, 0x30},
{1000000000, 0x05, 0x08, 0x20, 0x09, 0x30},
{1200000000, 0x06, 0x03, 0x32, 0x14, 0x0f},
{1400000000, 0x09, 0x03, 0x32, 0x14, 0x0f},
{1600000000, 0x0d, 0x42, 0x36, 0x0e, 0x0f},
{1800000000, 0x0e, 0x47, 0x7a, 0x0e, 0x0f},
{2000000000, 0x11, 0x64, 0x7a, 0x0e, 0x0b},
{2200000000, 0x13, 0x64, 0x7e, 0x15, 0x0b},
{2400000000, 0x13, 0x33, 0x7f, 0x15, 0x6a},
{2500000000, 0x15, 0x54, 0x7f, 0x15, 0x6a},
};
static void phy_update_bits(struct inno_dsidphy *inno,
u8 first, u8 second, u8 mask, u8 val)
{
u32 reg = PHY_REG(first, second) << 2;
unsigned int tmp, orig;
orig = readl(inno->phy_base + reg);
tmp = orig & ~mask;
tmp |= val & mask;
writel(tmp, inno->phy_base + reg);
}
static unsigned long inno_dsidphy_pll_calc_rate(struct inno_dsidphy *inno,
unsigned long rate)
{
unsigned long prate = clk_get_rate(inno->ref_clk);
unsigned long best_freq = 0;
unsigned long fref, fout;
u8 min_prediv, max_prediv;
u8 _prediv, best_prediv = 1;
u16 _fbdiv, best_fbdiv = 1;
u32 min_delta = UINT_MAX;
/*
* The PLL output frequency can be calculated using a simple formula:
* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2
* PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2
*/
fref = prate / 2;
if (rate > 1000000000UL)
fout = 1000000000UL;
else
fout = rate;
/* 5Mhz < Fref / prediv < 40MHz */
min_prediv = DIV_ROUND_UP(fref, 40000000);
max_prediv = fref / 5000000;
for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
u64 tmp;
u32 delta;
tmp = (u64)fout * _prediv;
do_div(tmp, fref);
_fbdiv = tmp;
/*
* The possible settings of feedback divider are
* 12, 13, 14, 16, ~ 511
*/
if (_fbdiv == 15)
continue;
if (_fbdiv < 12 || _fbdiv > 511)
continue;
tmp = (u64)_fbdiv * fref;
do_div(tmp, _prediv);
delta = abs(fout - tmp);
if (!delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = tmp;
break;
} else if (delta < min_delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = tmp;
min_delta = delta;
}
}
if (best_freq) {
inno->pll.prediv = best_prediv;
inno->pll.fbdiv = best_fbdiv;
inno->pll.rate = best_freq;
}
return best_freq;
}
static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
{
struct phy_configure_opts_mipi_dphy *cfg = &inno->dphy_cfg;
const struct inno_mipi_dphy_timing *timings;
u32 t_txbyteclkhs, t_txclkesc;
u32 txbyteclkhs, txclkesc, esc_clk_div;
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;
inno_dsidphy_pll_calc_rate(inno, cfg->hs_clk_rate);
/* Select MIPI mode */
phy_update_bits(inno, REGISTER_PART_LVDS, 0x03,
MODE_ENABLE_MASK, MIPI_MODE_ENABLE);
/* Configure PLL */
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x03,
REG_PREDIV_MASK, REG_PREDIV(inno->pll.prediv));
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x03,
REG_FBDIV_HI_MASK, REG_FBDIV_HI(inno->pll.fbdiv));
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x04,
REG_FBDIV_LO_MASK, REG_FBDIV_LO(inno->pll.fbdiv));
if (inno->pdata->max_rate == MAX_2_5GHZ) {
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x08,
PLL_POST_DIV_ENABLE_MASK, PLL_POST_DIV_ENABLE);
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,
REG_LDOPD_MASK | REG_PLLPD_MASK,
REG_LDOPD_POWER_ON | REG_PLLPD_POWER_ON);
/* Reset analog */
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01,
REG_SYNCRST_MASK, REG_SYNCRST_RESET);
udelay(1);
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01,
REG_SYNCRST_MASK, REG_SYNCRST_NORMAL);
/* Reset digital */
phy_update_bits(inno, REGISTER_PART_DIGITAL, 0x00,
REG_DIG_RSTN_MASK, REG_DIG_RSTN_RESET);
udelay(1);
phy_update_bits(inno, REGISTER_PART_DIGITAL, 0x00,
REG_DIG_RSTN_MASK, REG_DIG_RSTN_NORMAL);
txbyteclkhs = inno->pll.rate / 8;
t_txbyteclkhs = div_u64(PSEC_PER_SEC, txbyteclkhs);
esc_clk_div = DIV_ROUND_UP(txbyteclkhs, 20000000);
txclkesc = txbyteclkhs / esc_clk_div;
t_txclkesc = div_u64(PSEC_PER_SEC, txclkesc);
/*
* The value of counter for HS Ths-exit
* Ths-exit = Tpin_txbyteclkhs * value
*/
hs_exit = DIV_ROUND_UP(cfg->hs_exit, t_txbyteclkhs);
/*
* The value of counter for HS Tclk-post
* Tclk-post = Tpin_txbyteclkhs * value
*/
clk_post = DIV_ROUND_UP(cfg->clk_post, t_txbyteclkhs);
/*
* The value of counter for HS Tclk-pre
* Tclk-pre = Tpin_txbyteclkhs * value
*/
clk_pre = DIV_ROUND_UP(cfg->clk_pre, BITS_PER_BYTE);
/*
* The value of counter for HS Tta-go
* Tta-go for turnaround
* Tta-go = Ttxclkesc * value
*/
ta_go = DIV_ROUND_UP(cfg->ta_go, t_txclkesc);
/*
* The value of counter for HS Tta-sure
* Tta-sure for turnaround
* Tta-sure = Ttxclkesc * value
*/
ta_sure = DIV_ROUND_UP(cfg->ta_sure, t_txclkesc);
/*
* The value of counter for HS Tta-wait
* Tta-wait for turnaround
* Tta-wait = Ttxclkesc * value
*/
ta_wait = DIV_ROUND_UP(cfg->ta_get, t_txclkesc);
for (i = 0; i < inno->pdata->num_timings; i++)
if (inno->pll.rate <= timings[i].rate)
break;
if (i == inno->pdata->num_timings)
--i;
/*
* The value of counter for HS Tlpx Time
* Tlpx = Tpin_txbyteclkhs * (2 + value)
*/
if (inno->pdata->max_rate == MAX_1GHZ) {
lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs);
if (lpx >= 2)
lpx -= 2;
} else
lpx = timings[i].lpx;
hs_prepare = timings[i].hs_prepare;
hs_trail = timings[i].hs_trail;
clk_lane_hs_zero = timings[i].clk_lane_hs_zero;
data_lane_hs_zero = timings[i].data_lane_hs_zero;
wakeup = 0x3ff;
for (i = REGISTER_PART_CLOCK_LANE; i <= REGISTER_PART_DATA3_LANE; i++) {
if (i == REGISTER_PART_CLOCK_LANE)
hs_zero = clk_lane_hs_zero;
else
hs_zero = data_lane_hs_zero;
phy_update_bits(inno, i, 0x05, T_LPX_CNT_MASK,
T_LPX_CNT(lpx));
phy_update_bits(inno, i, 0x06, T_HS_PREPARE_CNT_MASK,
T_HS_PREPARE_CNT(hs_prepare));
if (inno->pdata->max_rate == MAX_2_5GHZ)
phy_update_bits(inno, i, 0x06, T_HS_ZERO_CNT_HI_MASK,
T_HS_ZERO_CNT_HI(hs_zero >> 6));
phy_update_bits(inno, i, 0x07, T_HS_ZERO_CNT_LO_MASK,
T_HS_ZERO_CNT_LO(hs_zero));
phy_update_bits(inno, i, 0x08, T_HS_TRAIL_CNT_MASK,
T_HS_TRAIL_CNT(hs_trail));
if (inno->pdata->max_rate == MAX_2_5GHZ)
phy_update_bits(inno, i, 0x11, T_HS_EXIT_CNT_HI_MASK,
T_HS_EXIT_CNT_HI(hs_exit >> 5));
phy_update_bits(inno, i, 0x09, T_HS_EXIT_CNT_LO_MASK,
T_HS_EXIT_CNT_LO(hs_exit));
if (inno->pdata->max_rate == MAX_2_5GHZ)
phy_update_bits(inno, i, 0x10, T_CLK_POST_CNT_HI_MASK,
T_CLK_POST_CNT_HI(clk_post >> 4));
phy_update_bits(inno, i, 0x0a, T_CLK_POST_CNT_LO_MASK,
T_CLK_POST_CNT_LO(clk_post));
phy_update_bits(inno, i, 0x0e, T_CLK_PRE_CNT_MASK,
T_CLK_PRE_CNT(clk_pre));
phy_update_bits(inno, i, 0x0c, T_WAKEUP_CNT_HI_MASK,
T_WAKEUP_CNT_HI(wakeup >> 8));
phy_update_bits(inno, i, 0x0d, T_WAKEUP_CNT_LO_MASK,
T_WAKEUP_CNT_LO(wakeup));
phy_update_bits(inno, i, 0x10, T_TA_GO_CNT_MASK,
T_TA_GO_CNT(ta_go));
phy_update_bits(inno, i, 0x11, T_TA_SURE_CNT_MASK,
T_TA_SURE_CNT(ta_sure));
phy_update_bits(inno, i, 0x12, T_TA_WAIT_CNT_MASK,
T_TA_WAIT_CNT(ta_wait));
}
/* 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 | val);
}
static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno)
{
u8 prediv = 2;
u16 fbdiv = 28;
/* Sample clock reverse direction */
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x08,
SAMPLE_CLOCK_DIRECTION_MASK | LOWFRE_EN_MASK,
SAMPLE_CLOCK_DIRECTION_REVERSE |
PLL_OUTPUT_FREQUENCY_DIV_BY_1);
/* Select LVDS mode */
phy_update_bits(inno, REGISTER_PART_LVDS, 0x03,
MODE_ENABLE_MASK, LVDS_MODE_ENABLE);
/* Configure PLL */
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x03,
REG_PREDIV_MASK, REG_PREDIV(prediv));
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x03,
REG_FBDIV_HI_MASK, REG_FBDIV_HI(fbdiv));
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x04,
REG_FBDIV_LO_MASK, REG_FBDIV_LO(fbdiv));
phy_update_bits(inno, REGISTER_PART_LVDS, 0x08, 0xff, 0xfc);
/* Enable PLL and Bandgap */
phy_update_bits(inno, REGISTER_PART_LVDS, 0x0b,
LVDS_PLL_POWER_MASK | LVDS_BANDGAP_POWER_MASK,
LVDS_PLL_POWER_ON | LVDS_BANDGAP_POWER_ON);
msleep(20);
/* Select PLL mode */
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x1e,
PLL_MODE_SEL_MASK, PLL_MODE_SEL_LVDS_MODE);
/* Reset LVDS digital logic */
phy_update_bits(inno, REGISTER_PART_LVDS, 0x00,
LVDS_DIGITAL_INTERNAL_RESET_MASK,
LVDS_DIGITAL_INTERNAL_RESET_ENABLE);
udelay(1);
phy_update_bits(inno, REGISTER_PART_LVDS, 0x00,
LVDS_DIGITAL_INTERNAL_RESET_MASK,
LVDS_DIGITAL_INTERNAL_RESET_DISABLE);
/* Enable LVDS digital logic */
phy_update_bits(inno, REGISTER_PART_LVDS, 0x01,
LVDS_DIGITAL_INTERNAL_ENABLE_MASK,
LVDS_DIGITAL_INTERNAL_ENABLE);
/* Enable LVDS analog driver */
phy_update_bits(inno, REGISTER_PART_LVDS, 0x0b,
LVDS_LANE_EN_MASK, LVDS_CLK_LANE_EN |
LVDS_DATA_LANE0_EN | LVDS_DATA_LANE1_EN |
LVDS_DATA_LANE2_EN | LVDS_DATA_LANE3_EN);
}
static int inno_dsidphy_power_on(struct phy *phy)
{
struct inno_dsidphy *inno = phy_get_drvdata(phy);
clk_prepare_enable(inno->pclk_phy);
clk_prepare_enable(inno->ref_clk);
pm_runtime_get_sync(inno->dev);
/* Bandgap power on */
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00,
BANDGAP_POWER_MASK, BANDGAP_POWER_ON);
/* Enable power work */
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00,
POWER_WORK_MASK, POWER_WORK_ENABLE);
switch (inno->mode) {
case PHY_MODE_MIPI_DPHY:
inno_dsidphy_mipi_mode_enable(inno);
break;
case PHY_MODE_LVDS:
inno_dsidphy_lvds_mode_enable(inno);
break;
default:
return -EINVAL;
}
return 0;
}
static int inno_dsidphy_power_off(struct phy *phy)
{
struct inno_dsidphy *inno = phy_get_drvdata(phy);
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00, LANE_EN_MASK, 0);
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01,
REG_LDOPD_MASK | REG_PLLPD_MASK,
REG_LDOPD_POWER_DOWN | REG_PLLPD_POWER_DOWN);
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00,
POWER_WORK_MASK, POWER_WORK_DISABLE);
phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00,
BANDGAP_POWER_MASK, BANDGAP_POWER_DOWN);
phy_update_bits(inno, REGISTER_PART_LVDS, 0x0b, LVDS_LANE_EN_MASK, 0);
phy_update_bits(inno, REGISTER_PART_LVDS, 0x01,
LVDS_DIGITAL_INTERNAL_ENABLE_MASK,
LVDS_DIGITAL_INTERNAL_DISABLE);
phy_update_bits(inno, REGISTER_PART_LVDS, 0x0b,
LVDS_PLL_POWER_MASK | LVDS_BANDGAP_POWER_MASK,
LVDS_PLL_POWER_OFF | LVDS_BANDGAP_POWER_DOWN);
pm_runtime_put(inno->dev);
clk_disable_unprepare(inno->ref_clk);
clk_disable_unprepare(inno->pclk_phy);
return 0;
}
static int inno_dsidphy_set_mode(struct phy *phy, enum phy_mode mode,
int submode)
{
struct inno_dsidphy *inno = phy_get_drvdata(phy);
switch (mode) {
case PHY_MODE_MIPI_DPHY:
case PHY_MODE_LVDS:
inno->mode = mode;
break;
default:
return -EINVAL;
}
return 0;
}
static int inno_dsidphy_configure(struct phy *phy,
union phy_configure_opts *opts)
{
struct inno_dsidphy *inno = phy_get_drvdata(phy);
int ret;
if (inno->mode != PHY_MODE_MIPI_DPHY)
return -EINVAL;
ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
if (ret)
return ret;
memcpy(&inno->dphy_cfg, &opts->mipi_dphy, sizeof(inno->dphy_cfg));
return 0;
}
static const struct phy_ops inno_dsidphy_ops = {
.configure = inno_dsidphy_configure,
.set_mode = inno_dsidphy_set_mode,
.power_on = inno_dsidphy_power_on,
.power_off = inno_dsidphy_power_off,
.owner = THIS_MODULE,
};
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)
{
struct device *dev = &pdev->dev;
struct inno_dsidphy *inno;
struct phy_provider *phy_provider;
struct phy *phy;
int ret;
inno = devm_kzalloc(dev, sizeof(*inno), GFP_KERNEL);
if (!inno)
return -ENOMEM;
inno->dev = dev;
inno->pdata = of_device_get_match_data(inno->dev);
platform_set_drvdata(pdev, inno);
inno->phy_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(inno->phy_base))
return PTR_ERR(inno->phy_base);
inno->ref_clk = devm_clk_get(dev, "ref");
if (IS_ERR(inno->ref_clk)) {
ret = PTR_ERR(inno->ref_clk);
dev_err(dev, "failed to get ref clock: %d\n", ret);
return ret;
}
inno->pclk_phy = devm_clk_get(dev, "pclk");
if (IS_ERR(inno->pclk_phy)) {
ret = PTR_ERR(inno->pclk_phy);
dev_err(dev, "failed to get phy pclk: %d\n", ret);
return ret;
}
inno->rst = devm_reset_control_get(dev, "apb");
if (IS_ERR(inno->rst)) {
ret = PTR_ERR(inno->rst);
dev_err(dev, "failed to get system reset control: %d\n", ret);
return ret;
}
phy = devm_phy_create(dev, NULL, &inno_dsidphy_ops);
if (IS_ERR(phy)) {
ret = PTR_ERR(phy);
dev_err(dev, "failed to create phy: %d\n", ret);
return ret;
}
phy_set_drvdata(phy, inno);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider)) {
ret = PTR_ERR(phy_provider);
dev_err(dev, "failed to register phy provider: %d\n", ret);
return ret;
}
pm_runtime_enable(dev);
return 0;
}
static void inno_dsidphy_remove(struct platform_device *pdev)
{
struct inno_dsidphy *inno = platform_get_drvdata(pdev);
pm_runtime_disable(inno->dev);
}
static const struct of_device_id inno_dsidphy_of_match[] = {
{
.compatible = "rockchip,px30-dsi-dphy",
.data = &max_1ghz_video_phy_plat_data,
}, {
.compatible = "rockchip,rk3128-dsi-dphy",
.data = &max_1ghz_video_phy_plat_data,
}, {
.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,
}, {
.compatible = "rockchip,rv1126-dsi-dphy",
.data = &max_2_5ghz_video_phy_plat_data,
},
{}
};
MODULE_DEVICE_TABLE(of, inno_dsidphy_of_match);
static struct platform_driver inno_dsidphy_driver = {
.driver = {
.name = "inno-dsidphy",
.of_match_table = of_match_ptr(inno_dsidphy_of_match),
},
.probe = inno_dsidphy_probe,
.remove = inno_dsidphy_remove,
};
module_platform_driver(inno_dsidphy_driver);
MODULE_AUTHOR("Wyon Bi <bivvy.bi@rock-chips.com>");
MODULE_DESCRIPTION("Innosilicon MIPI/LVDS/TTL Video Combo PHY driver");
MODULE_LICENSE("GPL v2");
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,338 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Rockchip PCIe PHY driver
*
* Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>
* Copyright (C) 2016 ROCKCHIP, Inc.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/hw_bitfield.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#define PHY_MAX_LANE_NUM 4
#define PHY_CFG_DATA_MASK GENMASK(10, 7)
#define PHY_CFG_ADDR_MASK GENMASK(6, 1)
#define PHY_CFG_WR_ENABLE 1
#define PHY_CFG_WR_DISABLE 0
#define PHY_CFG_WR_MASK BIT(0)
#define PHY_CFG_PLL_LOCK 0x10
#define PHY_CFG_CLK_TEST 0x10
#define PHY_CFG_CLK_SCC 0x12
#define PHY_CFG_SEPE_RATE BIT(3)
#define PHY_CFG_PLL_100M BIT(3)
#define PHY_PLL_LOCKED BIT(9)
#define PHY_PLL_OUTPUT BIT(10)
#define PHY_LANE_A_STATUS 0x30
#define PHY_LANE_B_STATUS 0x31
#define PHY_LANE_C_STATUS 0x32
#define PHY_LANE_D_STATUS 0x33
#define PHY_LANE_RX_DET_SHIFT 11
#define PHY_LANE_RX_DET_TH 0x1
#define PHY_LANE_IDLE_OFF 0x1
#define PHY_LANE_IDLE_MASK BIT(3)
struct rockchip_pcie_data {
unsigned int pcie_conf;
unsigned int pcie_status;
unsigned int pcie_laneoff;
};
struct rockchip_pcie_phy {
const struct rockchip_pcie_data *phy_data;
struct regmap *reg_base;
struct phy_pcie_instance {
struct phy *phy;
u32 index;
} phys[PHY_MAX_LANE_NUM];
struct mutex pcie_mutex;
struct reset_control *phy_rst;
struct clk *clk_pciephy_ref;
int pwr_cnt;
int init_cnt;
};
static struct rockchip_pcie_phy *to_pcie_phy(struct phy_pcie_instance *inst)
{
return container_of(inst, struct rockchip_pcie_phy,
phys[inst->index]);
}
static struct phy *rockchip_pcie_phy_of_xlate(struct device *dev,
const struct of_phandle_args *args)
{
struct rockchip_pcie_phy *rk_phy = dev_get_drvdata(dev);
if (args->args_count == 0)
return rk_phy->phys[0].phy;
if (WARN_ON(args->args[0] >= PHY_MAX_LANE_NUM))
return ERR_PTR(-ENODEV);
return rk_phy->phys[args->args[0]].phy;
}
static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy,
u32 addr, u32 data)
{
regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
FIELD_PREP_WM16(PHY_CFG_DATA_MASK, data) |
FIELD_PREP_WM16(PHY_CFG_ADDR_MASK, addr));
udelay(1);
regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
FIELD_PREP_WM16(PHY_CFG_WR_MASK, PHY_CFG_WR_ENABLE));
udelay(1);
regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
FIELD_PREP_WM16(PHY_CFG_WR_MASK, PHY_CFG_WR_DISABLE));
}
static int rockchip_pcie_phy_power_off(struct phy *phy)
{
struct phy_pcie_instance *inst = phy_get_drvdata(phy);
struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst);
int err = 0;
guard(mutex)(&rk_phy->pcie_mutex);
regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_laneoff,
FIELD_PREP_WM16(PHY_LANE_IDLE_MASK,
PHY_LANE_IDLE_OFF) << inst->index);
if (--rk_phy->pwr_cnt) {
return 0;
}
err = reset_control_assert(rk_phy->phy_rst);
if (err) {
dev_err(&phy->dev, "assert phy_rst err %d\n", err);
rk_phy->pwr_cnt++;
regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_laneoff,
FIELD_PREP_WM16(PHY_LANE_IDLE_MASK,
!PHY_LANE_IDLE_OFF) << inst->index);
return err;
}
return err;
}
static int rockchip_pcie_phy_power_on(struct phy *phy)
{
struct phy_pcie_instance *inst = phy_get_drvdata(phy);
struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst);
int err = 0;
u32 status;
guard(mutex)(&rk_phy->pcie_mutex);
regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_laneoff,
FIELD_PREP_WM16(PHY_LANE_IDLE_MASK,
!PHY_LANE_IDLE_OFF) << inst->index);
if (rk_phy->pwr_cnt++) {
return 0;
}
err = reset_control_deassert(rk_phy->phy_rst);
if (err) {
dev_err(&phy->dev, "deassert phy_rst err %d\n", err);
rk_phy->pwr_cnt--;
return err;
}
regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
FIELD_PREP_WM16(PHY_CFG_ADDR_MASK, PHY_CFG_PLL_LOCK));
/*
* No documented timeout value for phy operation below,
* so we make it large enough here. And we use loop-break
* method which should not be harmful.
*/
err = regmap_read_poll_timeout(rk_phy->reg_base,
rk_phy->phy_data->pcie_status,
status,
status & PHY_PLL_LOCKED,
200, 100000);
if (err) {
dev_err(&phy->dev, "pll lock timeout!\n");
goto err_pll_lock;
}
phy_wr_cfg(rk_phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE);
phy_wr_cfg(rk_phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M);
err = regmap_read_poll_timeout(rk_phy->reg_base,
rk_phy->phy_data->pcie_status,
status,
!(status & PHY_PLL_OUTPUT),
200, 100000);
if (err) {
dev_err(&phy->dev, "pll output enable timeout!\n");
goto err_pll_lock;
}
regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
FIELD_PREP_WM16(PHY_CFG_ADDR_MASK, PHY_CFG_PLL_LOCK));
err = regmap_read_poll_timeout(rk_phy->reg_base,
rk_phy->phy_data->pcie_status,
status,
status & PHY_PLL_LOCKED,
200, 100000);
if (err) {
dev_err(&phy->dev, "pll relock timeout!\n");
goto err_pll_lock;
}
return err;
err_pll_lock:
reset_control_assert(rk_phy->phy_rst);
rk_phy->pwr_cnt--;
return err;
}
static int rockchip_pcie_phy_init(struct phy *phy)
{
struct phy_pcie_instance *inst = phy_get_drvdata(phy);
struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst);
int err = 0;
guard(mutex)(&rk_phy->pcie_mutex);
if (rk_phy->init_cnt++) {
return 0;
}
err = reset_control_assert(rk_phy->phy_rst);
if (err) {
dev_err(&phy->dev, "assert phy_rst err %d\n", err);
rk_phy->init_cnt--;
return err;
}
return err;
}
static int rockchip_pcie_phy_exit(struct phy *phy)
{
struct phy_pcie_instance *inst = phy_get_drvdata(phy);
struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst);
guard(mutex)(&rk_phy->pcie_mutex);
if (--rk_phy->init_cnt)
goto err_init_cnt;
err_init_cnt:
return 0;
}
static const struct phy_ops ops = {
.init = rockchip_pcie_phy_init,
.exit = rockchip_pcie_phy_exit,
.power_on = rockchip_pcie_phy_power_on,
.power_off = rockchip_pcie_phy_power_off,
.owner = THIS_MODULE,
};
static const struct rockchip_pcie_data rk3399_pcie_data = {
.pcie_conf = 0xe220,
.pcie_status = 0xe2a4,
.pcie_laneoff = 0xe214,
};
static const struct of_device_id rockchip_pcie_phy_dt_ids[] = {
{
.compatible = "rockchip,rk3399-pcie-phy",
.data = &rk3399_pcie_data,
},
{}
};
MODULE_DEVICE_TABLE(of, rockchip_pcie_phy_dt_ids);
static int rockchip_pcie_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rockchip_pcie_phy *rk_phy;
struct phy_provider *phy_provider;
struct regmap *grf;
int i;
u32 phy_num;
grf = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(grf)) {
dev_err(dev, "Cannot find GRF syscon\n");
return PTR_ERR(grf);
}
rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
if (!rk_phy)
return -ENOMEM;
rk_phy->phy_data = device_get_match_data(&pdev->dev);
if (!rk_phy->phy_data)
return -EINVAL;
rk_phy->reg_base = grf;
mutex_init(&rk_phy->pcie_mutex);
rk_phy->phy_rst = devm_reset_control_get(dev, "phy");
if (IS_ERR(rk_phy->phy_rst))
return dev_err_probe(&pdev->dev, PTR_ERR(rk_phy->phy_rst),
"missing phy property for reset controller\n");
rk_phy->clk_pciephy_ref = devm_clk_get_enabled(dev, "refclk");
if (IS_ERR(rk_phy->clk_pciephy_ref))
return dev_err_probe(&pdev->dev, PTR_ERR(rk_phy->clk_pciephy_ref),
"failed to get phyclk\n");
/* parse #phy-cells to see if it's legacy PHY model */
if (of_property_read_u32(dev->of_node, "#phy-cells", &phy_num))
return -ENOENT;
phy_num = (phy_num == 0) ? 1 : PHY_MAX_LANE_NUM;
dev_dbg(dev, "phy number is %d\n", phy_num);
for (i = 0; i < phy_num; i++) {
rk_phy->phys[i].phy = devm_phy_create(dev, dev->of_node, &ops);
if (IS_ERR(rk_phy->phys[i].phy)) {
dev_err(dev, "failed to create PHY%d\n", i);
return PTR_ERR(rk_phy->phys[i].phy);
}
rk_phy->phys[i].index = i;
phy_set_drvdata(rk_phy->phys[i].phy, &rk_phy->phys[i]);
}
platform_set_drvdata(pdev, rk_phy);
phy_provider = devm_of_phy_provider_register(dev,
rockchip_pcie_phy_of_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static struct platform_driver rockchip_pcie_driver = {
.probe = rockchip_pcie_phy_probe,
.driver = {
.name = "rockchip-pcie-phy",
.of_match_table = rockchip_pcie_phy_dt_ids,
},
};
module_platform_driver(rockchip_pcie_driver);
MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip PCIe PHY driver");
MODULE_LICENSE("GPL v2");
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,355 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip PCIE3.0 phy driver
*
* Copyright (C) 2022 Rockchip Electronics Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/pcie.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
/* Register for RK3568 */
#define GRF_PCIE30PHY_CON1 0x4
#define GRF_PCIE30PHY_CON6 0x18
#define GRF_PCIE30PHY_CON9 0x24
#define GRF_PCIE30PHY_DA_OCM (BIT(15) | BIT(31))
#define GRF_PCIE30PHY_STATUS0 0x80
#define GRF_PCIE30PHY_WR_EN (0xf << 16)
#define SRAM_INIT_DONE(reg) (reg & BIT(14))
#define RK3568_BIFURCATION_LANE_0_1 BIT(0)
/* Register for RK3588 */
#define PHP_GRF_PCIESEL_CON 0x100
#define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0
#define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904
#define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04
#define RK3588_PCIE3PHY_GRF_PHY0_LN0_CON1 0x1004
#define RK3588_PCIE3PHY_GRF_PHY0_LN1_CON1 0x1104
#define RK3588_PCIE3PHY_GRF_PHY1_LN0_CON1 0x2004
#define RK3588_PCIE3PHY_GRF_PHY1_LN1_CON1 0x2104
#define RK3588_SRAM_INIT_DONE(reg) (reg & BIT(0))
#define RK3588_BIFURCATION_LANE_0_1 BIT(0)
#define RK3588_BIFURCATION_LANE_2_3 BIT(1)
#define RK3588_LANE_AGGREGATION BIT(2)
#define RK3588_RX_CMN_REFCLK_MODE_EN ((BIT(7) << 16) | BIT(7))
#define RK3588_RX_CMN_REFCLK_MODE_DIS (BIT(7) << 16)
#define RK3588_PCIE1LN_SEL_EN (GENMASK(1, 0) << 16)
#define RK3588_PCIE30_PHY_MODE_EN (GENMASK(2, 0) << 16)
struct rockchip_p3phy_ops;
struct rockchip_p3phy_priv {
const struct rockchip_p3phy_ops *ops;
void __iomem *mmio;
/* mode: RC, EP */
int mode;
/* pcie30_phymode: Aggregation, Bifurcation */
int pcie30_phymode;
struct regmap *phy_grf;
struct regmap *pipe_grf;
struct reset_control *p30phy;
struct phy *phy;
struct clk_bulk_data *clks;
int num_clks;
int num_lanes;
u32 lanes[4];
u32 rx_cmn_refclk_mode[4];
};
struct rockchip_p3phy_ops {
int (*phy_init)(struct rockchip_p3phy_priv *priv);
};
static int rockchip_p3phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
/* Actually We don't care EP/RC mode, but just record it */
switch (submode) {
case PHY_MODE_PCIE_RC:
priv->mode = PHY_MODE_PCIE_RC;
break;
case PHY_MODE_PCIE_EP:
priv->mode = PHY_MODE_PCIE_EP;
break;
default:
dev_err(&phy->dev, "%s, invalid mode\n", __func__);
return -EINVAL;
}
return 0;
}
static int rockchip_p3phy_rk3568_init(struct rockchip_p3phy_priv *priv)
{
struct phy *phy = priv->phy;
bool bifurcation = false;
int ret;
u32 reg;
/* Deassert PCIe PMA output clamp mode */
regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, GRF_PCIE30PHY_DA_OCM);
for (int i = 0; i < priv->num_lanes; i++) {
dev_info(&phy->dev, "lane number %d, val %d\n", i, priv->lanes[i]);
if (priv->lanes[i] > 1)
bifurcation = true;
}
/* Set bifurcation if needed, and it doesn't care RC/EP */
if (bifurcation) {
dev_info(&phy->dev, "bifurcation enabled\n");
regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6,
GRF_PCIE30PHY_WR_EN | RK3568_BIFURCATION_LANE_0_1);
regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON1,
GRF_PCIE30PHY_DA_OCM);
} else {
dev_dbg(&phy->dev, "bifurcation disabled\n");
regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6,
GRF_PCIE30PHY_WR_EN & ~RK3568_BIFURCATION_LANE_0_1);
}
reset_control_deassert(priv->p30phy);
ret = regmap_read_poll_timeout(priv->phy_grf,
GRF_PCIE30PHY_STATUS0,
reg, SRAM_INIT_DONE(reg),
0, 500);
if (ret)
dev_err(&priv->phy->dev, "%s: lock failed 0x%x, check input refclk and power supply\n",
__func__, reg);
return ret;
}
static const struct rockchip_p3phy_ops rk3568_ops = {
.phy_init = rockchip_p3phy_rk3568_init,
};
static int rockchip_p3phy_rk3588_init(struct rockchip_p3phy_priv *priv)
{
u32 reg = 0;
u8 mode = RK3588_LANE_AGGREGATION; /* default */
int ret;
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY0_LN0_CON1,
priv->rx_cmn_refclk_mode[0] ? RK3588_RX_CMN_REFCLK_MODE_EN :
RK3588_RX_CMN_REFCLK_MODE_DIS);
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY0_LN1_CON1,
priv->rx_cmn_refclk_mode[1] ? RK3588_RX_CMN_REFCLK_MODE_EN :
RK3588_RX_CMN_REFCLK_MODE_DIS);
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY1_LN0_CON1,
priv->rx_cmn_refclk_mode[2] ? RK3588_RX_CMN_REFCLK_MODE_EN :
RK3588_RX_CMN_REFCLK_MODE_DIS);
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY1_LN1_CON1,
priv->rx_cmn_refclk_mode[3] ? RK3588_RX_CMN_REFCLK_MODE_EN :
RK3588_RX_CMN_REFCLK_MODE_DIS);
/* Deassert PCIe PMA output clamp mode */
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, BIT(8) | BIT(24));
/* Set bifurcation if needed */
for (int i = 0; i < priv->num_lanes; i++) {
if (priv->lanes[i] > 1)
mode &= ~RK3588_LANE_AGGREGATION;
if (priv->lanes[i] == 3)
mode |= RK3588_BIFURCATION_LANE_0_1;
if (priv->lanes[i] == 4)
mode |= RK3588_BIFURCATION_LANE_2_3;
}
reg = mode;
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0,
RK3588_PCIE30_PHY_MODE_EN | reg);
/* Set pcie1ln_sel in PHP_GRF_PCIESEL_CON */
if (!IS_ERR(priv->pipe_grf)) {
reg = mode & (RK3588_BIFURCATION_LANE_0_1 | RK3588_BIFURCATION_LANE_2_3);
if (reg)
regmap_write(priv->pipe_grf, PHP_GRF_PCIESEL_CON,
RK3588_PCIE1LN_SEL_EN | reg);
}
reset_control_deassert(priv->p30phy);
ret = regmap_read_poll_timeout(priv->phy_grf,
RK3588_PCIE3PHY_GRF_PHY0_STATUS1,
reg, RK3588_SRAM_INIT_DONE(reg),
0, 500);
ret |= regmap_read_poll_timeout(priv->phy_grf,
RK3588_PCIE3PHY_GRF_PHY1_STATUS1,
reg, RK3588_SRAM_INIT_DONE(reg),
0, 500);
if (ret)
dev_err(&priv->phy->dev, "lock failed 0x%x, check input refclk and power supply\n",
reg);
return ret;
}
static const struct rockchip_p3phy_ops rk3588_ops = {
.phy_init = rockchip_p3phy_rk3588_init,
};
static int rockchip_p3phy_init(struct phy *phy)
{
struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
int ret;
ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
if (ret) {
dev_err(&priv->phy->dev, "failed to enable PCIe bulk clks %d\n", ret);
return ret;
}
reset_control_assert(priv->p30phy);
udelay(1);
if (priv->ops->phy_init) {
ret = priv->ops->phy_init(priv);
if (ret)
clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
}
return ret;
}
static int rockchip_p3phy_exit(struct phy *phy)
{
struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
reset_control_assert(priv->p30phy);
return 0;
}
static const struct phy_ops rockchip_p3phy_ops = {
.init = rockchip_p3phy_init,
.exit = rockchip_p3phy_exit,
.set_mode = rockchip_p3phy_set_mode,
.owner = THIS_MODULE,
};
static int rockchip_p3phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct rockchip_p3phy_priv *priv;
struct device_node *np = dev->of_node;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(priv->mmio)) {
ret = PTR_ERR(priv->mmio);
return ret;
}
priv->ops = of_device_get_match_data(&pdev->dev);
if (!priv->ops) {
dev_err(dev, "no of match data provided\n");
return -EINVAL;
}
priv->phy_grf = syscon_regmap_lookup_by_phandle(np, "rockchip,phy-grf");
if (IS_ERR(priv->phy_grf)) {
dev_err(dev, "failed to find rockchip,phy_grf regmap\n");
return PTR_ERR(priv->phy_grf);
}
if (of_device_is_compatible(np, "rockchip,rk3588-pcie3-phy")) {
priv->pipe_grf =
syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,pipe-grf");
if (IS_ERR(priv->pipe_grf))
dev_info(dev, "failed to find rockchip,pipe_grf regmap\n");
} else {
priv->pipe_grf = NULL;
}
priv->num_lanes = of_property_read_variable_u32_array(dev->of_node, "data-lanes",
priv->lanes, 2,
ARRAY_SIZE(priv->lanes));
/* if no data-lanes assume aggregation */
if (priv->num_lanes == -EINVAL) {
dev_dbg(dev, "no data-lanes property found\n");
priv->num_lanes = 1;
priv->lanes[0] = 1;
} else if (priv->num_lanes < 0) {
dev_err(dev, "failed to read data-lanes property %d\n", priv->num_lanes);
return priv->num_lanes;
}
ret = of_property_read_variable_u32_array(dev->of_node,
"rockchip,rx-common-refclk-mode",
priv->rx_cmn_refclk_mode, 1,
ARRAY_SIZE(priv->rx_cmn_refclk_mode));
/*
* if no rockchip,rx-common-refclk-mode, assume enabled for all lanes in
* order to be DT backwards compatible. (Since HW reset val is enabled.)
*/
if (ret == -EINVAL) {
for (int i = 0; i < ARRAY_SIZE(priv->rx_cmn_refclk_mode); i++)
priv->rx_cmn_refclk_mode[i] = 1;
} else if (ret < 0) {
dev_err(dev, "failed to read rockchip,rx-common-refclk-mode property %d\n",
ret);
return ret;
}
priv->phy = devm_phy_create(dev, NULL, &rockchip_p3phy_ops);
if (IS_ERR(priv->phy)) {
dev_err(dev, "failed to create combphy\n");
return PTR_ERR(priv->phy);
}
priv->p30phy = devm_reset_control_get_optional_exclusive(dev, "phy");
if (IS_ERR(priv->p30phy)) {
return dev_err_probe(dev, PTR_ERR(priv->p30phy),
"failed to get phy reset control\n");
}
if (!priv->p30phy)
dev_info(dev, "no phy reset control specified\n");
priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks);
if (priv->num_clks < 1)
return -ENODEV;
dev_set_drvdata(dev, priv);
phy_set_drvdata(priv->phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id rockchip_p3phy_of_match[] = {
{ .compatible = "rockchip,rk3568-pcie3-phy", .data = &rk3568_ops },
{ .compatible = "rockchip,rk3588-pcie3-phy", .data = &rk3588_ops },
{ },
};
MODULE_DEVICE_TABLE(of, rockchip_p3phy_of_match);
static struct platform_driver rockchip_p3phy_driver = {
.probe = rockchip_p3phy_probe,
.driver = {
.name = "rockchip-snps-pcie3-phy",
.of_match_table = rockchip_p3phy_of_match,
},
};
module_platform_driver(rockchip_p3phy_driver);
MODULE_DESCRIPTION("Rockchip Synopsys PCIe 3.0 PHY driver");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,563 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Rockchip usb PHY driver
*
* Copyright (C) 2014 Yunzhi Li <lyz@rock-chips.com>
* Copyright (C) 2014 ROCKCHIP, Inc.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/hw_bitfield.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/delay.h>
static int enable_usb_uart;
#define UOC_CON0 0x00
#define UOC_CON0_SIDDQ BIT(13)
#define UOC_CON0_DISABLE BIT(4)
#define UOC_CON0_COMMON_ON_N BIT(0)
#define UOC_CON2 0x08
#define UOC_CON2_SOFT_CON_SEL BIT(2)
#define UOC_CON3 0x0c
/* bits present on rk3188 and rk3288 phys */
#define UOC_CON3_UTMI_TERMSEL_FULLSPEED BIT(5)
#define UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC 1UL
#define UOC_CON3_UTMI_XCVRSEELCT_MASK GENMASK(4, 3)
#define UOC_CON3_UTMI_OPMODE_NODRIVING 1UL
#define UOC_CON3_UTMI_OPMODE_MASK GENMASK(2, 1)
#define UOC_CON3_UTMI_SUSPENDN BIT(0)
struct rockchip_usb_phys {
int reg;
const char *pll_name;
};
struct rockchip_usb_phy_base;
struct rockchip_usb_phy_pdata {
struct rockchip_usb_phys *phys;
int (*init_usb_uart)(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata);
int usb_uart_phy;
};
struct rockchip_usb_phy_base {
struct device *dev;
struct regmap *reg_base;
const struct rockchip_usb_phy_pdata *pdata;
};
struct rockchip_usb_phy {
struct rockchip_usb_phy_base *base;
struct device_node *np;
unsigned int reg_offset;
struct clk *clk;
struct clk *clk480m;
struct clk_hw clk480m_hw;
struct phy *phy;
bool uart_enabled;
struct reset_control *reset;
struct regulator *vbus;
};
static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
bool siddq)
{
u32 val = FIELD_PREP_WM16(UOC_CON0_SIDDQ, siddq);
return regmap_write(phy->base->reg_base, phy->reg_offset, val);
}
static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
return 480000000;
}
static void rockchip_usb_phy480m_disable(struct clk_hw *hw)
{
struct rockchip_usb_phy *phy = container_of(hw,
struct rockchip_usb_phy,
clk480m_hw);
if (phy->vbus)
regulator_disable(phy->vbus);
/* Power down usb phy analog blocks by set siddq 1 */
rockchip_usb_phy_power(phy, 1);
}
static int rockchip_usb_phy480m_enable(struct clk_hw *hw)
{
struct rockchip_usb_phy *phy = container_of(hw,
struct rockchip_usb_phy,
clk480m_hw);
/* Power up usb phy analog blocks by set siddq 0 */
return rockchip_usb_phy_power(phy, 0);
}
static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw)
{
struct rockchip_usb_phy *phy = container_of(hw,
struct rockchip_usb_phy,
clk480m_hw);
int ret;
u32 val;
ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val);
if (ret < 0)
return ret;
return (val & UOC_CON0_SIDDQ) ? 0 : 1;
}
static const struct clk_ops rockchip_usb_phy480m_ops = {
.enable = rockchip_usb_phy480m_enable,
.disable = rockchip_usb_phy480m_disable,
.is_enabled = rockchip_usb_phy480m_is_enabled,
.recalc_rate = rockchip_usb_phy480m_recalc_rate,
};
static int rockchip_usb_phy_power_off(struct phy *_phy)
{
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
if (phy->uart_enabled)
return -EBUSY;
clk_disable_unprepare(phy->clk480m);
return 0;
}
static int rockchip_usb_phy_power_on(struct phy *_phy)
{
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
if (phy->uart_enabled)
return -EBUSY;
if (phy->vbus) {
int ret;
ret = regulator_enable(phy->vbus);
if (ret)
return ret;
}
return clk_prepare_enable(phy->clk480m);
}
static int rockchip_usb_phy_reset(struct phy *_phy)
{
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
if (phy->reset) {
reset_control_assert(phy->reset);
udelay(10);
reset_control_deassert(phy->reset);
}
return 0;
}
static const struct phy_ops ops = {
.power_on = rockchip_usb_phy_power_on,
.power_off = rockchip_usb_phy_power_off,
.reset = rockchip_usb_phy_reset,
.owner = THIS_MODULE,
};
static void rockchip_usb_phy_action(void *data)
{
struct rockchip_usb_phy *rk_phy = data;
if (!rk_phy->uart_enabled) {
of_clk_del_provider(rk_phy->np);
clk_unregister(rk_phy->clk480m);
}
if (rk_phy->clk)
clk_put(rk_phy->clk);
}
static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
struct device_node *child)
{
struct rockchip_usb_phy *rk_phy;
unsigned int reg_offset;
const char *clk_name;
struct clk_init_data init;
int err, i;
rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL);
if (!rk_phy)
return -ENOMEM;
rk_phy->base = base;
rk_phy->np = child;
if (of_property_read_u32(child, "reg", &reg_offset)) {
dev_err(base->dev, "missing reg property in node %pOFn\n",
child);
return -EINVAL;
}
rk_phy->reset = of_reset_control_get(child, "phy-reset");
if (IS_ERR(rk_phy->reset))
rk_phy->reset = NULL;
rk_phy->reg_offset = reg_offset;
rk_phy->clk = of_clk_get_by_name(child, "phyclk");
if (IS_ERR(rk_phy->clk))
rk_phy->clk = NULL;
i = 0;
init.name = NULL;
while (base->pdata->phys[i].reg) {
if (base->pdata->phys[i].reg == reg_offset) {
init.name = base->pdata->phys[i].pll_name;
break;
}
i++;
}
if (!init.name) {
dev_err(base->dev, "phy data not found\n");
return -EINVAL;
}
if (enable_usb_uart && base->pdata->usb_uart_phy == i) {
dev_dbg(base->dev, "phy%d used as uart output\n", i);
rk_phy->uart_enabled = true;
} else {
if (rk_phy->clk) {
clk_name = __clk_get_name(rk_phy->clk);
init.flags = 0;
init.parent_names = &clk_name;
init.num_parents = 1;
} else {
init.flags = 0;
init.parent_names = NULL;
init.num_parents = 0;
}
init.ops = &rockchip_usb_phy480m_ops;
rk_phy->clk480m_hw.init = &init;
rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
if (IS_ERR(rk_phy->clk480m)) {
err = PTR_ERR(rk_phy->clk480m);
goto err_clk;
}
err = of_clk_add_provider(child, of_clk_src_simple_get,
rk_phy->clk480m);
if (err < 0)
goto err_clk_prov;
}
err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action,
rk_phy);
if (err)
return err;
rk_phy->phy = devm_phy_create(base->dev, child, &ops);
if (IS_ERR(rk_phy->phy)) {
dev_err(base->dev, "failed to create PHY\n");
return PTR_ERR(rk_phy->phy);
}
phy_set_drvdata(rk_phy->phy, rk_phy);
rk_phy->vbus = devm_regulator_get_optional(&rk_phy->phy->dev, "vbus");
if (IS_ERR(rk_phy->vbus)) {
if (PTR_ERR(rk_phy->vbus) == -EPROBE_DEFER)
return PTR_ERR(rk_phy->vbus);
rk_phy->vbus = NULL;
}
/*
* When acting as uart-pipe, just keep clock on otherwise
* only power up usb phy when it use, so disable it when init
*/
if (rk_phy->uart_enabled)
return clk_prepare_enable(rk_phy->clk);
else
return rockchip_usb_phy_power(rk_phy, 1);
err_clk_prov:
if (!rk_phy->uart_enabled)
clk_unregister(rk_phy->clk480m);
err_clk:
if (rk_phy->clk)
clk_put(rk_phy->clk);
return err;
}
static const struct rockchip_usb_phy_pdata rk3066a_pdata = {
.phys = (struct rockchip_usb_phys[]){
{ .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" },
{ .reg = 0x188, .pll_name = "sclk_otgphy1_480m" },
{ /* sentinel */ }
},
};
static int __init rockchip_init_usb_uart_common(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata)
{
int regoffs = pdata->phys[pdata->usb_uart_phy].reg;
int ret;
u32 val;
/*
* COMMON_ON and DISABLE settings are described in the TRM,
* but were not present in the original code.
* Also disable the analog phy components to save power.
*/
val = FIELD_PREP_WM16(UOC_CON0_COMMON_ON_N, 1) |
FIELD_PREP_WM16(UOC_CON0_DISABLE, 1) |
FIELD_PREP_WM16(UOC_CON0_SIDDQ, 1);
ret = regmap_write(grf, regoffs + UOC_CON0, val);
if (ret)
return ret;
val = FIELD_PREP_WM16(UOC_CON2_SOFT_CON_SEL, 1);
ret = regmap_write(grf, regoffs + UOC_CON2, val);
if (ret)
return ret;
val = FIELD_PREP_WM16(UOC_CON3_UTMI_SUSPENDN, 0) |
FIELD_PREP_WM16(UOC_CON3_UTMI_OPMODE_MASK,
UOC_CON3_UTMI_OPMODE_NODRIVING) |
FIELD_PREP_WM16(UOC_CON3_UTMI_XCVRSEELCT_MASK,
UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC) |
FIELD_PREP_WM16(UOC_CON3_UTMI_TERMSEL_FULLSPEED, 1);
ret = regmap_write(grf, UOC_CON3, val);
if (ret)
return ret;
return 0;
}
#define RK3188_UOC0_CON0 0x10c
#define RK3188_UOC0_CON0_BYPASSSEL BIT(9)
#define RK3188_UOC0_CON0_BYPASSDMEN BIT(8)
/*
* Enable the bypass of uart2 data through the otg usb phy.
* See description of rk3288-variant for details.
*/
static int __init rk3188_init_usb_uart(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata)
{
u32 val;
int ret;
ret = rockchip_init_usb_uart_common(grf, pdata);
if (ret)
return ret;
val = FIELD_PREP_WM16(RK3188_UOC0_CON0_BYPASSSEL, 1) |
FIELD_PREP_WM16(RK3188_UOC0_CON0_BYPASSDMEN, 1);
ret = regmap_write(grf, RK3188_UOC0_CON0, val);
if (ret)
return ret;
return 0;
}
static const struct rockchip_usb_phy_pdata rk3188_pdata = {
.phys = (struct rockchip_usb_phys[]){
{ .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" },
{ .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" },
{ /* sentinel */ }
},
.init_usb_uart = rk3188_init_usb_uart,
.usb_uart_phy = 0,
};
#define RK3288_UOC0_CON3 0x32c
#define RK3288_UOC0_CON3_BYPASSDMEN BIT(6)
#define RK3288_UOC0_CON3_BYPASSSEL BIT(7)
/*
* Enable the bypass of uart2 data through the otg usb phy.
* Original description in the TRM.
* 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1.
* 2. Disable the pull-up resistance on the D+ line by setting
* OPMODE0[1:0] to 2’b01.
* 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend
* mode, set COMMONONN to 1’b1.
* 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0.
* 5. Set BYPASSSEL0 to 1’b1.
* 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0.
* To receive data, monitor FSVPLUS0.
*
* The actual code in the vendor kernel does some things differently.
*/
static int __init rk3288_init_usb_uart(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata)
{
u32 val;
int ret;
ret = rockchip_init_usb_uart_common(grf, pdata);
if (ret)
return ret;
val = FIELD_PREP_WM16(RK3288_UOC0_CON3_BYPASSSEL, 1) |
FIELD_PREP_WM16(RK3288_UOC0_CON3_BYPASSDMEN, 1);
ret = regmap_write(grf, RK3288_UOC0_CON3, val);
if (ret)
return ret;
return 0;
}
static const struct rockchip_usb_phy_pdata rk3288_pdata = {
.phys = (struct rockchip_usb_phys[]){
{ .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
{ .reg = 0x334, .pll_name = "sclk_otgphy1_480m" },
{ .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
{ /* sentinel */ }
},
.init_usb_uart = rk3288_init_usb_uart,
.usb_uart_phy = 0,
};
static int rockchip_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rockchip_usb_phy_base *phy_base;
struct phy_provider *phy_provider;
int err;
phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL);
if (!phy_base)
return -ENOMEM;
phy_base->pdata = device_get_match_data(dev);
if (!phy_base->pdata) {
dev_err(dev, "missing phy data\n");
return -EINVAL;
}
phy_base->dev = dev;
phy_base->reg_base = ERR_PTR(-ENODEV);
if (dev->parent && dev->parent->of_node)
phy_base->reg_base = syscon_node_to_regmap(
dev->parent->of_node);
if (IS_ERR(phy_base->reg_base))
phy_base->reg_base = syscon_regmap_lookup_by_phandle(
dev->of_node, "rockchip,grf");
if (IS_ERR(phy_base->reg_base)) {
dev_err(&pdev->dev, "Missing rockchip,grf property\n");
return PTR_ERR(phy_base->reg_base);
}
for_each_available_child_of_node_scoped(dev->of_node, child) {
err = rockchip_usb_phy_init(phy_base, child);
if (err)
return err;
}
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
{ .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata },
{ .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata },
{ .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata },
{}
};
MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids);
static struct platform_driver rockchip_usb_driver = {
.probe = rockchip_usb_phy_probe,
.driver = {
.name = "rockchip-usb-phy",
.of_match_table = rockchip_usb_phy_dt_ids,
},
};
module_platform_driver(rockchip_usb_driver);
#ifndef MODULE
static int __init rockchip_init_usb_uart(void)
{
const struct of_device_id *match;
const struct rockchip_usb_phy_pdata *data;
struct device_node *np;
struct regmap *grf;
int ret;
if (!enable_usb_uart)
return 0;
np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids,
&match);
if (!np) {
pr_err("%s: failed to find usbphy node\n", __func__);
return -ENOTSUPP;
}
pr_debug("%s: using settings for %s\n", __func__, match->compatible);
data = match->data;
if (!data->init_usb_uart) {
pr_err("%s: usb-uart not available on %s\n",
__func__, match->compatible);
return -ENOTSUPP;
}
grf = ERR_PTR(-ENODEV);
if (np->parent)
grf = syscon_node_to_regmap(np->parent);
if (IS_ERR(grf))
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(grf)) {
pr_err("%s: Missing rockchip,grf property, %lu\n",
__func__, PTR_ERR(grf));
return PTR_ERR(grf);
}
ret = data->init_usb_uart(grf, data);
if (ret) {
pr_err("%s: could not init usb_uart, %d\n", __func__, ret);
enable_usb_uart = 0;
return ret;
}
return 0;
}
early_initcall(rockchip_init_usb_uart);
static int __init rockchip_usb_uart(char *buf)
{
enable_usb_uart = true;
return 0;
}
early_param("rockchip.usb_uart", rockchip_usb_uart);
#endif
MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
MODULE_LICENSE("GPL v2");
File diff suppressed because it is too large Load Diff