Commit afa4ba05 authored by Swapnil Jakhade's avatar Swapnil Jakhade Committed by Kishon Vijay Abraham I

phy: cadence-torrent: Add support for subnode bindings

Implement single link subnode support to the phy driver.
Add reset support including PHY reset and individual lane reset.
Signed-off-by: default avatarSwapnil Jakhade <sjakhade@cadence.com>
Signed-off-by: default avatarYuti Amonkar <yamonkar@cadence.com>
Signed-off-by: default avatarKishon Vijay Abraham I <kishon@ti.com>
parent 597bf3f1
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* *
*/ */
#include <dt-bindings/phy/phy.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
...@@ -18,6 +19,7 @@ ...@@ -18,6 +19,7 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#define REF_CLK_19_2MHz 19200000 #define REF_CLK_19_2MHz 19200000
...@@ -183,14 +185,24 @@ static const struct reg_field phy_reset_ctrl = ...@@ -183,14 +185,24 @@ static const struct reg_field phy_reset_ctrl =
static const struct of_device_id cdns_torrent_phy_of_match[]; static const struct of_device_id cdns_torrent_phy_of_match[];
struct cdns_torrent_inst {
struct phy *phy;
u32 mlane;
u32 phy_type;
u32 num_lanes;
struct reset_control *lnk_rst;
};
struct cdns_torrent_phy { struct cdns_torrent_phy {
void __iomem *base; /* DPTX registers base */ void __iomem *base; /* DPTX registers base */
void __iomem *sd_base; /* SD0801 registers base */ void __iomem *sd_base; /* SD0801 registers base */
u32 num_lanes; /* Number of lanes to use */
u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */ u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */
struct reset_control *phy_rst;
struct device *dev; struct device *dev;
struct clk *clk; struct clk *clk;
unsigned long ref_clk_rate; unsigned long ref_clk_rate;
struct cdns_torrent_inst phys[MAX_NUM_LANES];
int nsubnodes;
struct regmap *regmap; struct regmap *regmap;
struct regmap *regmap_common_cdb; struct regmap *regmap_common_cdb;
struct regmap *regmap_phy_pcs_common_cdb; struct regmap *regmap_phy_pcs_common_cdb;
...@@ -217,7 +229,8 @@ static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, ...@@ -217,7 +229,8 @@ static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy,
u32 num_lanes); u32 num_lanes);
static static
int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy); int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy);
static void cdns_torrent_dp_pma_cfg(struct cdns_torrent_phy *cdns_phy); static void cdns_torrent_dp_pma_cfg(struct cdns_torrent_phy *cdns_phy,
struct cdns_torrent_inst *inst);
static static
void cdns_torrent_dp_pma_cmn_cfg_19_2mhz(struct cdns_torrent_phy *cdns_phy); void cdns_torrent_dp_pma_cmn_cfg_19_2mhz(struct cdns_torrent_phy *cdns_phy);
static static
...@@ -237,11 +250,15 @@ static int cdns_torrent_dp_configure(struct phy *phy, ...@@ -237,11 +250,15 @@ static int cdns_torrent_dp_configure(struct phy *phy,
static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy, static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy,
u32 num_lanes, u32 num_lanes,
enum phy_powerstate powerstate); enum phy_powerstate powerstate);
static int cdns_torrent_phy_on(struct phy *phy);
static int cdns_torrent_phy_off(struct phy *phy);
static const struct phy_ops cdns_torrent_phy_ops = { static const struct phy_ops cdns_torrent_phy_ops = {
.init = cdns_torrent_dp_init, .init = cdns_torrent_dp_init,
.exit = cdns_torrent_dp_exit, .exit = cdns_torrent_dp_exit,
.configure = cdns_torrent_dp_configure, .configure = cdns_torrent_dp_configure,
.power_on = cdns_torrent_phy_on,
.power_off = cdns_torrent_phy_off,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
...@@ -564,7 +581,7 @@ static int cdns_torrent_dp_configure_rate(struct cdns_torrent_phy *cdns_phy, ...@@ -564,7 +581,7 @@ static int cdns_torrent_dp_configure_rate(struct cdns_torrent_phy *cdns_phy,
/* /*
* Verify, that parameters to configure PHY with are correct. * Verify, that parameters to configure PHY with are correct.
*/ */
static int cdns_torrent_dp_verify_config(struct cdns_torrent_phy *cdns_phy, static int cdns_torrent_dp_verify_config(struct cdns_torrent_inst *inst,
struct phy_configure_opts_dp *dp) struct phy_configure_opts_dp *dp)
{ {
u8 i; u8 i;
...@@ -599,7 +616,7 @@ static int cdns_torrent_dp_verify_config(struct cdns_torrent_phy *cdns_phy, ...@@ -599,7 +616,7 @@ static int cdns_torrent_dp_verify_config(struct cdns_torrent_phy *cdns_phy,
} }
/* Check against actual number of PHY's lanes. */ /* Check against actual number of PHY's lanes. */
if (dp->lanes > cdns_phy->num_lanes) if (dp->lanes > inst->num_lanes)
return -EINVAL; return -EINVAL;
/* /*
...@@ -791,10 +808,11 @@ static void cdns_torrent_dp_set_voltages(struct cdns_torrent_phy *cdns_phy, ...@@ -791,10 +808,11 @@ static void cdns_torrent_dp_set_voltages(struct cdns_torrent_phy *cdns_phy,
static int cdns_torrent_dp_configure(struct phy *phy, static int cdns_torrent_dp_configure(struct phy *phy,
union phy_configure_opts *opts) union phy_configure_opts *opts)
{ {
struct cdns_torrent_phy *cdns_phy = phy_get_drvdata(phy); struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
int ret; int ret;
ret = cdns_torrent_dp_verify_config(cdns_phy, &opts->dp); ret = cdns_torrent_dp_verify_config(inst, &opts->dp);
if (ret) { if (ret) {
dev_err(&phy->dev, "invalid params for phy configure\n"); dev_err(&phy->dev, "invalid params for phy configure\n");
return ret; return ret;
...@@ -826,8 +844,8 @@ static int cdns_torrent_dp_init(struct phy *phy) ...@@ -826,8 +844,8 @@ static int cdns_torrent_dp_init(struct phy *phy)
{ {
unsigned char lane_bits; unsigned char lane_bits;
int ret; int ret;
struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
struct cdns_torrent_phy *cdns_phy = phy_get_drvdata(phy); struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
ret = clk_prepare_enable(cdns_phy->clk); ret = clk_prepare_enable(cdns_phy->clk);
...@@ -856,19 +874,19 @@ static int cdns_torrent_dp_init(struct phy *phy) ...@@ -856,19 +874,19 @@ static int cdns_torrent_dp_init(struct phy *phy)
cdns_torrent_dp_write(regmap, PHY_AUX_CTRL, 0x0003); /* enable AUX */ cdns_torrent_dp_write(regmap, PHY_AUX_CTRL, 0x0003); /* enable AUX */
/* PHY PMA registers configuration function */ /* PHY PMA registers configuration function */
cdns_torrent_dp_pma_cfg(cdns_phy); cdns_torrent_dp_pma_cfg(cdns_phy, inst);
/* /*
* Set lines power state to A0 * Set lines power state to A0
* Set lines pll clk enable to 0 * Set lines pll clk enable to 0
*/ */
cdns_torrent_dp_set_a0_pll(cdns_phy, cdns_phy->num_lanes); cdns_torrent_dp_set_a0_pll(cdns_phy, inst->num_lanes);
/* /*
* release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on * release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on
* used lanes * used lanes
*/ */
lane_bits = (1 << cdns_phy->num_lanes) - 1; lane_bits = (1 << inst->num_lanes) - 1;
cdns_torrent_dp_write(regmap, PHY_RESET, cdns_torrent_dp_write(regmap, PHY_RESET,
((0xF & ~lane_bits) << 4) | (0xF & lane_bits)); ((0xF & ~lane_bits) << 4) | (0xF & lane_bits));
...@@ -886,23 +904,25 @@ static int cdns_torrent_dp_init(struct phy *phy) ...@@ -886,23 +904,25 @@ static int cdns_torrent_dp_init(struct phy *phy)
cdns_phy->max_bit_rate, cdns_phy->max_bit_rate,
false); false);
cdns_torrent_dp_pma_cmn_rate(cdns_phy, cdns_phy->max_bit_rate, cdns_torrent_dp_pma_cmn_rate(cdns_phy, cdns_phy->max_bit_rate,
cdns_phy->num_lanes); inst->num_lanes);
/* take out of reset */ /* take out of reset */
regmap_field_write(cdns_phy->phy_reset_ctrl, 0x1); regmap_field_write(cdns_phy->phy_reset_ctrl, 0x1);
cdns_torrent_phy_on(phy);
ret = cdns_torrent_dp_wait_pma_cmn_ready(cdns_phy); ret = cdns_torrent_dp_wait_pma_cmn_ready(cdns_phy);
if (ret) if (ret)
return ret; return ret;
ret = cdns_torrent_dp_run(cdns_phy, cdns_phy->num_lanes); ret = cdns_torrent_dp_run(cdns_phy, inst->num_lanes);
return ret; return ret;
} }
static int cdns_torrent_dp_exit(struct phy *phy) static int cdns_torrent_dp_exit(struct phy *phy)
{ {
struct cdns_torrent_phy *cdns_phy = phy_get_drvdata(phy); struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
clk_disable_unprepare(cdns_phy->clk); clk_disable_unprepare(cdns_phy->clk);
return 0; return 0;
...@@ -926,7 +946,8 @@ int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy) ...@@ -926,7 +946,8 @@ int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy)
return 0; return 0;
} }
static void cdns_torrent_dp_pma_cfg(struct cdns_torrent_phy *cdns_phy) static void cdns_torrent_dp_pma_cfg(struct cdns_torrent_phy *cdns_phy,
struct cdns_torrent_inst *inst)
{ {
unsigned int i; unsigned int i;
...@@ -938,7 +959,7 @@ static void cdns_torrent_dp_pma_cfg(struct cdns_torrent_phy *cdns_phy) ...@@ -938,7 +959,7 @@ static void cdns_torrent_dp_pma_cfg(struct cdns_torrent_phy *cdns_phy)
cdns_torrent_dp_pma_cmn_cfg_25mhz(cdns_phy); cdns_torrent_dp_pma_cmn_cfg_25mhz(cdns_phy);
/* PMA lane configuration to deal with multi-link operation */ /* PMA lane configuration to deal with multi-link operation */
for (i = 0; i < cdns_phy->num_lanes; i++) for (i = 0; i < inst->num_lanes; i++)
cdns_torrent_dp_pma_lane_cfg(cdns_phy, i); cdns_torrent_dp_pma_lane_cfg(cdns_phy, i);
} }
...@@ -1518,6 +1539,33 @@ static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes) ...@@ -1518,6 +1539,33 @@ static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes)
return ret; return ret;
} }
static int cdns_torrent_phy_on(struct phy *phy)
{
struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
int ret;
/* Take the PHY out of reset */
ret = reset_control_deassert(cdns_phy->phy_rst);
if (ret)
return ret;
/* Take the PHY lane group out of reset */
return reset_control_deassert(inst->lnk_rst);
}
static int cdns_torrent_phy_off(struct phy *phy)
{
struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
int ret;
ret = reset_control_assert(cdns_phy->phy_rst);
if (ret)
return ret;
return reset_control_assert(inst->lnk_rst);
}
static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base, static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base,
u32 block_offset, u32 block_offset,
...@@ -1664,8 +1712,8 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev) ...@@ -1664,8 +1712,8 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
struct phy_provider *phy_provider; struct phy_provider *phy_provider;
const struct of_device_id *match; const struct of_device_id *match;
struct cdns_torrent_data *data; struct cdns_torrent_data *data;
struct phy *phy; struct device_node *child;
int err, ret; int ret, subnodes, node = 0, i;
/* Get init data for this PHY */ /* Get init data for this PHY */
match = of_match_device(cdns_torrent_phy_of_match, dev); match = of_match_device(cdns_torrent_phy_of_match, dev);
...@@ -1678,12 +1726,20 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev) ...@@ -1678,12 +1726,20 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
if (!cdns_phy) if (!cdns_phy)
return -ENOMEM; return -ENOMEM;
cdns_phy->dev = &pdev->dev; dev_set_drvdata(dev, cdns_phy);
cdns_phy->dev = dev;
phy = devm_phy_create(dev, NULL, &cdns_torrent_phy_ops); cdns_phy->phy_rst = devm_reset_control_get_exclusive_by_index(dev, 0);
if (IS_ERR(phy)) { if (IS_ERR(cdns_phy->phy_rst)) {
dev_err(dev, "failed to create Torrent PHY\n"); dev_err(dev, "%s: failed to get reset\n",
return PTR_ERR(phy); dev->of_node->full_name);
return PTR_ERR(cdns_phy->phy_rst);
}
cdns_phy->clk = devm_clk_get(dev, "refclk");
if (IS_ERR(cdns_phy->clk)) {
dev_err(dev, "phy ref clock not found\n");
return PTR_ERR(cdns_phy->clk);
} }
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
...@@ -1691,18 +1747,57 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev) ...@@ -1691,18 +1747,57 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
if (IS_ERR(cdns_phy->sd_base)) if (IS_ERR(cdns_phy->sd_base))
return PTR_ERR(cdns_phy->sd_base); return PTR_ERR(cdns_phy->sd_base);
regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); subnodes = of_get_available_child_count(dev->of_node);
cdns_phy->base = devm_ioremap_resource(&pdev->dev, regs); if (subnodes == 0) {
if (IS_ERR(cdns_phy->base)) dev_err(dev, "No available link subnodes found\n");
return PTR_ERR(cdns_phy->base); return -EINVAL;
} else if (subnodes != 1) {
dev_err(dev, "Driver supports only one link subnode.\n");
return -EINVAL;
}
for_each_available_child_of_node(dev->of_node, child) {
struct phy *gphy;
err = device_property_read_u32(dev, "num_lanes", cdns_phy->phys[node].lnk_rst =
&cdns_phy->num_lanes); of_reset_control_array_get_exclusive(child);
if (err) if (IS_ERR(cdns_phy->phys[node].lnk_rst)) {
cdns_phy->num_lanes = DEFAULT_NUM_LANES; dev_err(dev, "%s: failed to get reset\n",
child->full_name);
ret = PTR_ERR(cdns_phy->phys[node].lnk_rst);
goto put_lnk_rst;
}
switch (cdns_phy->num_lanes) { if (of_property_read_u32(child, "reg",
&cdns_phy->phys[node].mlane)) {
dev_err(dev, "%s: No \"reg\"-property.\n",
child->full_name);
ret = -EINVAL;
goto put_child;
}
if (cdns_phy->phys[node].mlane != 0) {
dev_err(dev,
"%s: Driver supports only lane-0 as master lane.\n",
child->full_name);
ret = -EINVAL;
goto put_child;
}
if (of_property_read_u32(child, "cdns,phy-type",
&cdns_phy->phys[node].phy_type)) {
dev_err(dev, "%s: No \"cdns,phy-type\"-property.\n",
child->full_name);
ret = -EINVAL;
goto put_child;
}
cdns_phy->phys[node].num_lanes = DEFAULT_NUM_LANES;
of_property_read_u32(child, "cdns,num-lanes",
&cdns_phy->phys[node].num_lanes);
if (cdns_phy->phys[node].phy_type == PHY_TYPE_DP) {
switch (cdns_phy->phys[node].num_lanes) {
case 1: case 1:
case 2: case 2:
case 4: case 4:
...@@ -1710,14 +1805,14 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev) ...@@ -1710,14 +1805,14 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
break; break;
default: default:
dev_err(dev, "unsupported number of lanes: %d\n", dev_err(dev, "unsupported number of lanes: %d\n",
cdns_phy->num_lanes); cdns_phy->phys[node].num_lanes);
return -EINVAL; ret = -EINVAL;
goto put_child;
} }
err = device_property_read_u32(dev, "max_bit_rate",
&cdns_phy->max_bit_rate);
if (err)
cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE; cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE;
of_property_read_u32(child, "cdns,max-bit-rate",
&cdns_phy->max_bit_rate);
switch (cdns_phy->max_bit_rate) { switch (cdns_phy->max_bit_rate) {
case 1620: case 1620:
...@@ -1733,36 +1828,82 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev) ...@@ -1733,36 +1828,82 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
default: default:
dev_err(dev, "unsupported max bit rate: %dMbps\n", dev_err(dev, "unsupported max bit rate: %dMbps\n",
cdns_phy->max_bit_rate); cdns_phy->max_bit_rate);
return -EINVAL; ret = -EINVAL;
goto put_child;
} }
cdns_phy->clk = devm_clk_get(dev, "refclk"); /* DPTX registers */
if (IS_ERR(cdns_phy->clk)) { regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dev_err(dev, "phy ref clock not found\n"); cdns_phy->base = devm_ioremap_resource(&pdev->dev,
return PTR_ERR(cdns_phy->clk); regs);
if (IS_ERR(cdns_phy->base)) {
ret = PTR_ERR(cdns_phy->base);
goto put_child;
}
gphy = devm_phy_create(dev, child,
&cdns_torrent_phy_ops);
if (IS_ERR(gphy)) {
ret = PTR_ERR(gphy);
goto put_child;
}
dev_info(dev, "%d lanes, max bit rate %d.%03d Gbps\n",
cdns_phy->phys[node].num_lanes,
cdns_phy->max_bit_rate / 1000,
cdns_phy->max_bit_rate % 1000);
} else {
dev_err(dev, "Driver supports only PHY_TYPE_DP\n");
ret = -ENOTSUPP;
goto put_child;
} }
cdns_phy->phys[node].phy = gphy;
phy_set_drvdata(gphy, &cdns_phy->phys[node]);
phy_set_drvdata(phy, cdns_phy); node++;
}
cdns_phy->nsubnodes = node;
ret = cdns_regmap_init_torrent_dp(cdns_phy, cdns_phy->sd_base, ret = cdns_regmap_init_torrent_dp(cdns_phy, cdns_phy->sd_base,
cdns_phy->base, cdns_phy->base,
data->block_offset_shift, data->block_offset_shift,
data->reg_offset_shift); data->reg_offset_shift);
if (ret) if (ret)
return ret; goto put_lnk_rst;
ret = cdns_regfield_init(cdns_phy); ret = cdns_regfield_init(cdns_phy);
if (ret) if (ret)
return ret; goto put_lnk_rst;
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider)) {
ret = PTR_ERR(phy_provider);
goto put_lnk_rst;
}
dev_info(dev, "%d lanes, max bit rate %d.%03d Gbps\n", return 0;
cdns_phy->num_lanes,
cdns_phy->max_bit_rate / 1000,
cdns_phy->max_bit_rate % 1000);
return PTR_ERR_OR_ZERO(phy_provider); put_child:
node++;
put_lnk_rst:
for (i = 0; i < node; i++)
reset_control_put(cdns_phy->phys[i].lnk_rst);
of_node_put(child);
return ret;
}
static int cdns_torrent_phy_remove(struct platform_device *pdev)
{
struct cdns_torrent_phy *cdns_phy = platform_get_drvdata(pdev);
int i;
reset_control_assert(cdns_phy->phy_rst);
for (i = 0; i < cdns_phy->nsubnodes; i++) {
reset_control_assert(cdns_phy->phys[i].lnk_rst);
reset_control_put(cdns_phy->phys[i].lnk_rst);
}
return 0;
} }
static const struct cdns_torrent_data cdns_map_torrent = { static const struct cdns_torrent_data cdns_map_torrent = {
...@@ -1790,6 +1931,7 @@ MODULE_DEVICE_TABLE(of, cdns_torrent_phy_of_match); ...@@ -1790,6 +1931,7 @@ MODULE_DEVICE_TABLE(of, cdns_torrent_phy_of_match);
static struct platform_driver cdns_torrent_phy_driver = { static struct platform_driver cdns_torrent_phy_driver = {
.probe = cdns_torrent_phy_probe, .probe = cdns_torrent_phy_probe,
.remove = cdns_torrent_phy_remove,
.driver = { .driver = {
.name = "cdns-torrent-phy", .name = "cdns-torrent-phy",
.of_match_table = cdns_torrent_phy_of_match, .of_match_table = cdns_torrent_phy_of_match,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment