diff options
author | Daniel Golle <daniel@makrotopia.org> | 2019-03-04 22:09:36 +0100 |
---|---|---|
committer | Daniel Golle <daniel@makrotopia.org> | 2019-03-04 23:40:43 +0100 |
commit | e7aa4c0db7eb3e81163682f570a9aecbb9fa8aed (patch) | |
tree | acb39e2a04e4cbf11bbe978bef5522404477b48e /target/linux/oxnas/files | |
parent | 3bb9dcf44627ffdd313fe92c563ae454b6ff8aa6 (diff) | |
download | upstream-e7aa4c0db7eb3e81163682f570a9aecbb9fa8aed.tar.gz upstream-e7aa4c0db7eb3e81163682f570a9aecbb9fa8aed.tar.bz2 upstream-e7aa4c0db7eb3e81163682f570a9aecbb9fa8aed.zip |
oxnas: pcie: model shared resource as external pcie-phy driver
Refactor pcie-oxnas to have shared resources in syscon and new pcie-phy
driver. Hopefully this revives PCIe...
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Diffstat (limited to 'target/linux/oxnas/files')
-rw-r--r-- | target/linux/oxnas/files/drivers/pci/host/pcie-oxnas.c | 81 | ||||
-rw-r--r-- | target/linux/oxnas/files/drivers/phy/phy-oxnas-pcie.c | 150 |
2 files changed, 164 insertions, 67 deletions
diff --git a/target/linux/oxnas/files/drivers/pci/host/pcie-oxnas.c b/target/linux/oxnas/files/drivers/pci/host/pcie-oxnas.c index 76e42d13ef..68898c3beb 100644 --- a/target/linux/oxnas/files/drivers/pci/host/pcie-oxnas.c +++ b/target/linux/oxnas/files/drivers/pci/host/pcie-oxnas.c @@ -25,6 +25,8 @@ #include <linux/gpio.h> #include <linux/delay.h> #include <linux/clk.h> +#include <linux/phy.h> +#include <linux/phy/phy.h> #include <linux/regmap.h> #include <linux/reset.h> #include <linux/io.h> @@ -75,23 +77,6 @@ enum { PCIE_OBTRANS = BIT(12), }; -enum { - HCSL_BIAS_ON = BIT(0), - HCSL_PCIE_EN = BIT(1), - HCSL_PCIEA_EN = BIT(2), - HCSL_PCIEB_EN = BIT(3), -}; - -enum { - /* pcie phy reg offset */ - PHY_ADDR = 0, - PHY_DATA = 4, - /* phy data reg bits */ - READ_EN = BIT(16), - WRITE_EN = BIT(17), - CAP_DATA = BIT(18), -}; - /* core config registers */ enum { PCI_CONFIG_VERSION_DEVICEID = 0, @@ -127,9 +112,6 @@ enum { PCIE_SLAVE_BE_SHIFT = 22, }; -#define ADDR_VAL(val) ((val) & 0xFFFF) -#define DATA_VAL(val) ((val) & 0xFFFF) - #define PCIE_SLAVE_BE(val) ((val) << PCIE_SLAVE_BE_SHIFT) #define PCIE_SLAVE_BE_MASK PCIE_SLAVE_BE(0xF) @@ -146,7 +128,7 @@ struct oxnas_pcie { struct regmap *sys_ctrl; unsigned int outbound_offset; unsigned int pcie_ctrl_offset; - + struct phy *phy; int haslink; struct platform_device *pdev; struct resource io; @@ -406,52 +388,11 @@ static void oxnas_pcie_enable(struct device *dev, struct oxnas_pcie *pcie) pci_common_init_dev(dev, &hw); } -void oxnas_pcie_init_shared_hw(struct platform_device *pdev, - void __iomem *phybase, - struct regmap *sys_ctrl) -{ - struct reset_control *rstc; - int ret; - - /* generate clocks from HCSL buffers, shared parts */ - regmap_write(sys_ctrl, SYS_CTRL_HCSL_CTRL_REGOFFSET, HCSL_BIAS_ON|HCSL_PCIE_EN); - - /* Ensure PCIe PHY is properly reset */ - rstc = reset_control_get(&pdev->dev, "phy"); - if (IS_ERR(rstc)) { - ret = PTR_ERR(rstc); - } else { - ret = reset_control_reset(rstc); - reset_control_put(rstc); - } - - if (ret) { - dev_err(&pdev->dev, "phy reset failed %d\n", ret); - return; - } - - /* Enable PCIe Pre-Emphasis: What these value means? */ - writel(ADDR_VAL(0x0014), phybase + PHY_ADDR); - writel(DATA_VAL(0xce10) | CAP_DATA, phybase + PHY_DATA); - writel(DATA_VAL(0xce10) | WRITE_EN, phybase + PHY_DATA); - - writel(ADDR_VAL(0x2004), phybase + PHY_ADDR); - writel(DATA_VAL(0x82c7) | CAP_DATA, phybase + PHY_DATA); - writel(DATA_VAL(0x82c7) | WRITE_EN, phybase + PHY_DATA); -} - -static int oxnas_pcie_shared_init(struct platform_device *pdev, struct regmap *sys_ctrl) +static int oxnas_pcie_shared_init(struct platform_device *pdev, struct oxnas_pcie *pcie) { if (++pcie_shared.refcount == 1) { - /* we are the first */ - struct device_node *np = pdev->dev.of_node; - void __iomem *phy = of_iomap(np, 2); - if (!phy) { - --pcie_shared.refcount; - return -ENOMEM; - } - oxnas_pcie_init_shared_hw(pdev, phy, sys_ctrl); - iounmap(phy); + phy_init(pcie->phy); + phy_power_on(pcie->phy); return 0; } else { return 0; @@ -478,7 +419,6 @@ oxnas_pcie_map_registers(struct platform_device *pdev, u32 outbound_ctrl_offset; u32 pcie_ctrl_offset; - /* 2 is reserved for shared phy */ ret = of_address_to_resource(np, 0, ®s); if (ret) return -EINVAL; @@ -493,6 +433,12 @@ oxnas_pcie_map_registers(struct platform_device *pdev, if (!pcie->inbound) return -ENOMEM; + pcie->phy = devm_of_phy_get(&pdev->dev, np, NULL); + if (IS_ERR(pcie->phy)) { + if (PTR_ERR(pcie->phy) == -EPROBE_DEFER) + return PTR_ERR(pcie->phy); + pcie->phy = NULL; + } if (of_property_read_u32(np, "plxtech,pcie-outbound-offset", &outbound_ctrl_offset)) @@ -589,6 +535,7 @@ static void oxnas_pcie_init_hw(struct platform_device *pdev, mdelay(100); } + /* ToDo: use phy power-on port... */ regmap_update_bits(pcie->sys_ctrl, SYS_CTRL_HCSL_CTRL_REGOFFSET, BIT(pcie->hcsl_en), BIT(pcie->hcsl_en)); @@ -664,7 +611,7 @@ static int oxnas_pcie_probe(struct platform_device *pdev) goto err_free_gpio; } - ret = oxnas_pcie_shared_init(pdev, pcie->sys_ctrl); + ret = oxnas_pcie_shared_init(pdev, pcie); if (ret) goto err_free_gpio; diff --git a/target/linux/oxnas/files/drivers/phy/phy-oxnas-pcie.c b/target/linux/oxnas/files/drivers/phy/phy-oxnas-pcie.c new file mode 100644 index 0000000000..676a5f9332 --- /dev/null +++ b/target/linux/oxnas/files/drivers/phy/phy-oxnas-pcie.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Daniel Golle <daniel@makrotopia.org> + * + */ + +#include <dt-bindings/phy/phy.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/mfd/syscon.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#define ADDR_VAL(val) ((val) & 0xFFFF) +#define DATA_VAL(val) ((val) & 0xFFFF) + +#define SYS_CTRL_HCSL_CTRL_REGOFFSET 0x114 + +enum { + HCSL_BIAS_ON = BIT(0), + HCSL_PCIE_EN = BIT(1), + HCSL_PCIEA_EN = BIT(2), + HCSL_PCIEB_EN = BIT(3), +}; + +enum { + /* pcie phy reg offset */ + PHY_ADDR = 0, + PHY_DATA = 4, + /* phy data reg bits */ + READ_EN = BIT(16), + WRITE_EN = BIT(17), + CAP_DATA = BIT(18), +}; + +struct oxnas_pcie_phy { + struct device *dev; + void __iomem *membase; + const struct phy_ops *ops; + struct regmap *sys_ctrl; +}; + +static int oxnas_pcie_phy_init(struct phy *phy) +{ + struct oxnas_pcie_phy *pciephy = phy_get_drvdata(phy); + struct reset_control *rstc; + int ret; + + /* generate clocks from HCSL buffers, shared parts */ + regmap_write(pciephy->sys_ctrl, SYS_CTRL_HCSL_CTRL_REGOFFSET, HCSL_BIAS_ON|HCSL_PCIE_EN); + + /* Ensure PCIe PHY is properly reset */ + rstc = reset_control_get(pciephy->dev, "phy"); + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + } else { + ret = reset_control_reset(rstc); + reset_control_put(rstc); + } + + if (ret) { + dev_err(pciephy->dev, "phy reset failed %d\n", ret); + return ret; + } + + return 0; +} + +static int oxnas_pcie_phy_power_on(struct phy *phy) +{ + struct oxnas_pcie_phy *pciephy = phy_get_drvdata(phy); + + /* Enable PCIe Pre-Emphasis: What these value means? */ + writel(ADDR_VAL(0x0014), pciephy->membase + PHY_ADDR); + writel(DATA_VAL(0xce10) | CAP_DATA, pciephy->membase + PHY_DATA); + writel(DATA_VAL(0xce10) | WRITE_EN, pciephy->membase + PHY_DATA); + + writel(ADDR_VAL(0x2004), pciephy->membase + PHY_ADDR); + writel(DATA_VAL(0x82c7) | CAP_DATA, pciephy->membase + PHY_DATA); + writel(DATA_VAL(0x82c7) | WRITE_EN, pciephy->membase + PHY_DATA); + + return 0; +} + +static const struct phy_ops ops = { + .init = oxnas_pcie_phy_init, + .power_on = oxnas_pcie_phy_power_on, + .owner = THIS_MODULE, +}; + +static int oxnas_pcie_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct oxnas_pcie_phy *pciephy; + struct regmap *sys_ctrl; + void __iomem *membase; + + membase = of_iomap(np, 0); + if (IS_ERR(membase)) + return PTR_ERR(membase); + + sys_ctrl = syscon_regmap_lookup_by_compatible("oxsemi,ox820-sys-ctrl"); + if (IS_ERR(sys_ctrl)) { + dev_err(dev, "Cannot find OX820 SYSCRTL\n"); + return PTR_ERR(sys_ctrl); + } + + pciephy = devm_kzalloc(dev, sizeof(*pciephy), GFP_KERNEL); + if (!pciephy) + return -ENOMEM; + + pciephy->sys_ctrl = sys_ctrl; + pciephy->membase = membase; + pciephy->dev = dev; + pciephy->ops = &ops; + + generic_phy = devm_phy_create(dev, dev->of_node, pciephy->ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, pciephy); + 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 oxnas_pcie_phy_id_table[] = { + { .compatible = "oxsemi,ox820-pcie-phy" }, + { }, +}; + +static struct platform_driver oxnas_pcie_phy_driver = { + .probe = oxnas_pcie_phy_probe, + .driver = { + .name = "ox820-pcie-phy", + .of_match_table = oxnas_pcie_phy_id_table, + }, +}; + +builtin_platform_driver(oxnas_pcie_phy_driver); |