phy: miphy365x: Represent each PHY channel as a DT subnode

This has the added advantages of being able to enable/disable each
of the channels as simply as enabling/disabling the DT node.

Suggested-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
Lee Jones 2014-07-09 12:41:13 +01:00 committed by Kishon Vijay Abraham I
parent 6e877fedb1
commit 7ebdb52e19
1 changed files with 138 additions and 118 deletions

View File

@ -18,6 +18,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/delay.h> #include <linux/delay.h>
@ -28,10 +29,8 @@
#define HFC_TIMEOUT 100 #define HFC_TIMEOUT 100
#define SYSCFG_2521 0x824 #define SYSCFG_SELECT_SATA_MASK BIT(1)
#define SYSCFG_2522 0x828 #define SYSCFG_SELECT_SATA_POS 1
#define SYSCFG_PCIE_SATA_MASK BIT(1)
#define SYSCFG_PCIE_SATA_POS 1
/* MiPHY365x register definitions */ /* MiPHY365x register definitions */
#define RESET_REG 0x00 #define RESET_REG 0x00
@ -136,25 +135,21 @@
#define BIT_LOCK_LEVEL 0x01 #define BIT_LOCK_LEVEL 0x01
#define BIT_LOCK_CNT_512 (0x03 << 5) #define BIT_LOCK_CNT_512 (0x03 << 5)
static u8 ports[] = { MIPHY_PORT_0, MIPHY_PORT_1 };
struct miphy365x_phy { struct miphy365x_phy {
struct phy *phy; struct phy *phy;
void __iomem *base; void __iomem *base;
void __iomem *sata; bool pcie_tx_pol_inv;
void __iomem *pcie; bool sata_tx_pol_inv;
u32 sata_gen;
u64 ctrlreg;
u8 type; u8 type;
u8 port;
}; };
struct miphy365x_dev { struct miphy365x_dev {
struct device *dev; struct device *dev;
struct regmap *regmap; struct regmap *regmap;
struct mutex miphy_mutex; struct mutex miphy_mutex;
struct miphy365x phys[ARRAY_SIZE(ports)]; struct miphy365x_phy **phys;
bool pcie_tx_pol_inv;
bool sata_tx_pol_inv;
u32 sata_gen;
}; };
/* /*
@ -180,27 +175,12 @@ static u8 rx_tx_spd[] = {
static int miphy365x_set_path(struct miphy365x_phy *miphy_phy, static int miphy365x_set_path(struct miphy365x_phy *miphy_phy,
struct miphy365x_dev *miphy_dev) struct miphy365x_dev *miphy_dev)
{ {
u8 config = miphy_phy->type | miphy_phy->port; bool sata = (miphy_phy->type == MIPHY_TYPE_SATA);
u32 mask = SYSCFG_PCIE_SATA_MASK;
u32 reg;
bool sata;
switch (config) { return regmap_update_bits(miphy_dev->regmap,
case MIPHY_SATA_PORT0: (unsigned int)miphy_phy->ctrlreg,
reg = SYSCFG_2521; SYSCFG_SELECT_SATA_MASK,
sata = true; sata << SYSCFG_SELECT_SATA_POS);
break;
case MIPHY_PCIE_PORT1:
reg = SYSCFG_2522;
sata = false;
break;
default:
dev_err(miphy_dev->dev, "Configuration not supported\n");
return -EINVAL;
}
return regmap_update_bits(miphy_dev->regmap, reg, mask,
sata << SYSCFG_PCIE_SATA_POS);
} }
static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy, static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy,
@ -261,14 +241,14 @@ static inline void miphy365x_set_comp(struct miphy365x_phy *miphy_phy,
{ {
u8 val, mask; u8 val, mask;
if (miphy_dev->sata_gen == SATA_GEN1) if (miphy_phy->sata_gen == SATA_GEN1)
writeb_relaxed(COMP_2MHZ_RAT_GEN1, writeb_relaxed(COMP_2MHZ_RAT_GEN1,
miphy_phy->base + COMP_CTRL2_REG); miphy_phy->base + COMP_CTRL2_REG);
else else
writeb_relaxed(COMP_2MHZ_RAT, writeb_relaxed(COMP_2MHZ_RAT,
miphy_phy->base + COMP_CTRL2_REG); miphy_phy->base + COMP_CTRL2_REG);
if (miphy_dev->sata_gen != SATA_GEN3) { if (miphy_phy->sata_gen != SATA_GEN3) {
writeb_relaxed(COMSR_COMP_REF, writeb_relaxed(COMSR_COMP_REF,
miphy_phy->base + COMP_CTRL3_REG); miphy_phy->base + COMP_CTRL3_REG);
/* /*
@ -312,7 +292,7 @@ static inline void miphy365x_set_ssc(struct miphy365x_phy *miphy_phy,
miphy_phy->base + PLL_SSC_PER_LSB_REG); miphy_phy->base + PLL_SSC_PER_LSB_REG);
/* SSC Settings complete */ /* SSC Settings complete */
if (miphy_dev->sata_gen == SATA_GEN1) { if (miphy_phy->sata_gen == SATA_GEN1) {
val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL; val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG); writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
} else { } else {
@ -334,7 +314,7 @@ static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy,
val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO; val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO;
writeb_relaxed(val, miphy_phy->base + RESET_REG); writeb_relaxed(val, miphy_phy->base + RESET_REG);
if (miphy_dev->sata_tx_pol_inv) if (miphy_phy->sata_tx_pol_inv)
writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG); writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG);
/* /*
@ -344,7 +324,7 @@ static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy,
*/ */
writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG); writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG);
writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG); writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG);
val = rx_tx_spd[miphy_dev->sata_gen]; val = rx_tx_spd[miphy_phy->sata_gen];
writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG); writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG);
/* Wait for HFC_READY = 0 */ /* Wait for HFC_READY = 0 */
@ -355,7 +335,7 @@ static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy,
/* Compensation Recalibration */ /* Compensation Recalibration */
miphy365x_set_comp(miphy_phy, miphy_dev); miphy365x_set_comp(miphy_phy, miphy_dev);
switch (miphy_dev->sata_gen) { switch (miphy_phy->sata_gen) {
case SATA_GEN3: case SATA_GEN3:
/* /*
* TX Swing target 550-600mv peak to peak diff * TX Swing target 550-600mv peak to peak diff
@ -423,7 +403,7 @@ static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy,
writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG); writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG);
writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG); writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG);
writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG); writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
val = miphy_dev->sata_tx_pol_inv ? val = miphy_phy->sata_tx_pol_inv ?
(TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN; (TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN;
writeb_relaxed(val, miphy_phy->base + CTRL_REG); writeb_relaxed(val, miphy_phy->base + CTRL_REG);
@ -459,40 +439,95 @@ static int miphy365x_init(struct phy *phy)
return ret; return ret;
} }
int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy,
int index)
{
struct device_node *phynode = miphy_phy->phy->dev.of_node;
const char *name;
const __be32 *taddr;
int type = miphy_phy->type;
int ret;
ret = of_property_read_string_index(phynode, "reg-names", index, &name);
if (ret) {
dev_err(dev, "no reg-names property not found\n");
return ret;
}
if (!strncmp(name, "syscfg", 6)) {
taddr = of_get_address(phynode, index, NULL, NULL);
if (!taddr) {
dev_err(dev, "failed to fetch syscfg address\n");
return -EINVAL;
}
miphy_phy->ctrlreg = of_translate_address(phynode, taddr);
if (miphy_phy->ctrlreg == OF_BAD_ADDR) {
dev_err(dev, "failed to translate syscfg address\n");
return -EINVAL;
}
return 0;
}
if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) ||
(!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE)))
return 0;
miphy_phy->base = of_iomap(phynode, index);
if (!miphy_phy->base) {
dev_err(dev, "Failed to map %s\n", phynode->full_name);
return -EINVAL;
}
return 0;
}
static struct phy *miphy365x_xlate(struct device *dev, static struct phy *miphy365x_xlate(struct device *dev,
struct of_phandle_args *args) struct of_phandle_args *args)
{ {
struct miphy365x_dev *state = dev_get_drvdata(dev); struct miphy365x_dev *miphy_dev = dev_get_drvdata(dev);
u8 port, type; struct miphy365x_phy *miphy_phy = NULL;
struct device_node *phynode = args->np;
int ret, index;
if (args->count != 2) { if (!of_device_is_available(phynode)) {
dev_warn(dev, "Requested PHY is disabled\n");
return ERR_PTR(-ENODEV);
}
if (args->args_count != 1) {
dev_err(dev, "Invalid number of cells in 'phy' property\n"); dev_err(dev, "Invalid number of cells in 'phy' property\n");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
if (args->args[0] & 0xFFFFFF00 || args->args[1] & 0xFFFFFF00) { for (index = 0; index < of_get_child_count(dev->of_node); index++)
dev_err(dev, "Unsupported flags set in 'phy' property\n"); if (phynode == miphy_dev->phys[index]->phy->dev.of_node) {
miphy_phy = miphy_dev->phys[index];
break;
}
if (!miphy_phy) {
dev_err(dev, "Failed to find appropriate phy\n");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
port = args->args[0]; miphy_phy->type = args->args[0];
type = args->args[1];
if (WARN_ON(port >= ARRAY_SIZE(ports))) if (!(miphy_phy->type == MIPHY_TYPE_SATA ||
return ERR_PTR(-EINVAL); miphy_phy->type == MIPHY_TYPE_PCIE)) {
dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type);
if (type == MIPHY_TYPE_SATA)
state->phys[port].base = state->phys[port].sata;
else if (type == MIPHY_TYPE_PCIE)
state->phys[port].base = state->phys[port].pcie;
else {
WARN(1, "Invalid type specified in DT");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
state->phys[port].type = type; /* Each port handles SATA and PCIE - third entry is always sysconf. */
for (index = 0; index < 3; index++) {
ret = miphy365x_get_addr(dev, miphy_phy, index);
if (ret < 0)
return ERR_PTR(ret);
}
return state->phys[port].phy; return miphy_phy->phy;
} }
static struct phy_ops miphy365x_ops = { static struct phy_ops miphy365x_ops = {
@ -500,95 +535,80 @@ static struct phy_ops miphy365x_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static int miphy365x_get_base_addr(struct platform_device *pdev, static int miphy365x_of_probe(struct device_node *phynode,
struct miphy365x_phy *phy, u8 port) struct miphy365x_phy *miphy_phy)
{ {
struct resource *res; of_property_read_u32(phynode, "st,sata-gen", &miphy_phy->sata_gen);
char type[6]; if (!miphy_phy->sata_gen)
miphy_phy->sata_gen = SATA_GEN1;
sprintf(type, "sata%d", port); miphy_phy->pcie_tx_pol_inv =
of_property_read_bool(phynode, "st,pcie-tx-pol-inv");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, type); miphy_phy->sata_tx_pol_inv =
phy->sata = devm_ioremap_resource(&pdev->dev, res)); of_property_read_bool(phynode, "st,sata-tx-pol-inv");
if (!phy->sata)
return -ENOMEM;
sprintf(type, "pcie%d", port);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, type);
phy->pcie = devm_ioremap_resource(&pdev->dev, res));
if (!phy->pcie)
return -ENOMEM;
return 0;
}
static int miphy365x_of_probe(struct device_node *np,
struct miphy365x_dev *phy_dev)
{
phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
if (IS_ERR(phy_dev->regmap)) {
dev_err(phy_dev->dev, "No syscfg phandle specified\n");
return PTR_ERR(phy_dev->regmap);
}
of_property_read_u32(np, "st,sata-gen", &phy_dev->sata_gen);
if (!phy_dev->sata_gen)
phy_dev->sata_gen = SATA_GEN1;
phy_dev->pcie_tx_pol_inv =
of_property_read_bool(np, "st,pcie-tx-pol-inv");
phy_dev->sata_tx_pol_inv =
of_property_read_bool(np, "st,sata-tx-pol-inv");
return 0; return 0;
} }
static int miphy365x_probe(struct platform_device *pdev) static int miphy365x_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *child, *np = pdev->dev.of_node;
struct miphy365x_dev *phy_dev; struct miphy365x_dev *miphy_dev;
struct device *dev = &pdev->dev;
struct phy_provider *provider; struct phy_provider *provider;
u8 port; struct phy *phy;
int chancount, port = 0;
int ret; int ret;
phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL);
if (!phy_dev) if (!miphy_dev)
return -ENOMEM; return -ENOMEM;
ret = miphy365x_of_probe(np, phy_dev); chancount = of_get_child_count(np);
if (ret) miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount,
return ret; GFP_KERNEL);
if (!miphy_dev->phys)
return -ENOMEM;
phy_dev->dev = dev; miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
if (IS_ERR(miphy_dev->regmap)) {
dev_err(miphy_dev->dev, "No syscfg phandle specified\n");
return PTR_ERR(miphy_dev->regmap);
}
dev_set_drvdata(dev, phy_dev); miphy_dev->dev = &pdev->dev;
mutex_init(&phy_dev->miphy_mutex); dev_set_drvdata(&pdev->dev, miphy_dev);
for (port = 0; port < ARRAY_SIZE(ports); port++) { mutex_init(&miphy_dev->miphy_mutex);
struct phy *phy;
phy = devm_phy_create(dev, &miphy365x_ops, NULL); for_each_child_of_node(np, child) {
struct miphy365x_phy *miphy_phy;
miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy),
GFP_KERNEL);
if (!miphy_phy)
return -ENOMEM;
miphy_dev->phys[port] = miphy_phy;
phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL);
if (IS_ERR(phy)) { if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY on port %d\n", port); dev_err(&pdev->dev, "failed to create PHY\n");
return PTR_ERR(phy); return PTR_ERR(phy);
} }
phy_dev->phys[port].phy = phy; miphy_dev->phys[port]->phy = phy;
phy_dev->phys[port].port = port;
ret = miphy365x_get_base_addr(pdev, ret = miphy365x_of_probe(child, miphy_phy);
&phy_dev->phys[port], port);
if (ret) if (ret)
return ret; return ret;
phy_set_drvdata(phy, &phy_dev->phys[port]); phy_set_drvdata(phy, miphy_dev->phys[port]);
port++;
} }
provider = devm_of_phy_provider_register(dev, miphy365x_xlate); provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate);
if (IS_ERR(provider)) if (IS_ERR(provider))
return PTR_ERR(provider); return PTR_ERR(provider);