From e7aa4c0db7eb3e81163682f570a9aecbb9fa8aed Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 4 Mar 2019 22:09:36 +0100 Subject: [PATCH] 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 --- target/linux/oxnas/config-4.14 | 2 + .../oxnas/files/drivers/pci/host/pcie-oxnas.c | 81 ++-------- .../oxnas/files/drivers/phy/phy-oxnas-pcie.c | 150 ++++++++++++++++++ .../patches-4.14/320-oxnas-phy-pcie.patch | 44 +++++ .../oxnas/patches-4.14/340-oxnas-pcie.patch | 24 +-- .../oxnas/patches-4.14/500-oxnas-sata.patch | 2 +- 6 files changed, 226 insertions(+), 77 deletions(-) create mode 100644 target/linux/oxnas/files/drivers/phy/phy-oxnas-pcie.c create mode 100644 target/linux/oxnas/patches-4.14/320-oxnas-phy-pcie.patch diff --git a/target/linux/oxnas/config-4.14 b/target/linux/oxnas/config-4.14 index 9932f47e0d..5ff868ad87 100644 --- a/target/linux/oxnas/config-4.14 +++ b/target/linux/oxnas/config-4.14 @@ -278,6 +278,8 @@ CONFIG_PERF_EVENTS=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 CONFIG_PHYLIB=y +CONFIG_GENERIC_PHY=y +CONFIG_PHY_OXNAS=y CONFIG_PINCTRL=y CONFIG_PINCTRL_OXNAS=y # CONFIG_PINCTRL_SINGLE is not set 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 #include #include +#include +#include #include #include #include @@ -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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); diff --git a/target/linux/oxnas/patches-4.14/320-oxnas-phy-pcie.patch b/target/linux/oxnas/patches-4.14/320-oxnas-phy-pcie.patch new file mode 100644 index 0000000000..7dc9470424 --- /dev/null +++ b/target/linux/oxnas/patches-4.14/320-oxnas-phy-pcie.patch @@ -0,0 +1,44 @@ +--- a/arch/arm/boot/dts/ox820.dtsi ++++ b/arch/arm/boot/dts/ox820.dtsi +@@ -246,6 +246,15 @@ + }; + }; + ++ pcie_phy: pcie-phy@a00000 { ++ compatible = "oxsemi,ox820-pcie-phy"; ++ reg = <0xa00000 0x10>; ++ #phy-cells = <0>; ++ resets = <&reset RESET_PCIEPHY>; ++ reset-names = "phy"; ++ status = "disabled"; ++ }; ++ + sys: sys-ctrl@e00000 { + compatible = "oxsemi,ox820-sys-ctrl", "syscon", "simple-mfd"; + reg = <0xe00000 0x200000>; +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -26,6 +26,13 @@ config PHY_LPC18XX_USB_OTG + This driver is need for USB0 support on LPC18xx/43xx and takes + care of enabling and clock setup. + ++config PHY_OXNAS ++ tristate "Oxford Semi. OX820 PCI-E PHY support" ++ depends on HAS_IOMEM && OF && (ARM || COMPILE_TEST) ++ select GENERIC_PHY ++ help ++ This option enables support for OXNAS OX820 SoC PCIE PHY. ++ + config PHY_PISTACHIO_USB + tristate "IMG Pistachio USB2.0 PHY driver" + depends on MACH_PISTACHIO +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -5,6 +5,7 @@ + + obj-$(CONFIG_GENERIC_PHY) += phy-core.o + obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o ++obj-$(CONFIG_PHY_OXNAS) += phy-oxnas-pcie.o + obj-$(CONFIG_PHY_XGENE) += phy-xgene.o + obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o + obj-$(CONFIG_ARCH_SUNXI) += allwinner/ diff --git a/target/linux/oxnas/patches-4.14/340-oxnas-pcie.patch b/target/linux/oxnas/patches-4.14/340-oxnas-pcie.patch index 4681888da0..16f47d9056 100644 --- a/target/linux/oxnas/patches-4.14/340-oxnas-pcie.patch +++ b/target/linux/oxnas/patches-4.14/340-oxnas-pcie.patch @@ -22,7 +22,7 @@ --- a/arch/arm/boot/dts/ox820.dtsi +++ b/arch/arm/boot/dts/ox820.dtsi -@@ -307,6 +307,83 @@ +@@ -316,6 +316,89 @@ reg = <0x1000 0x1000>, <0x100 0x500>; }; @@ -41,8 +41,11 @@ + + bus-range = <0x00 0x7f>; + -+ /* cfg inbound translator phy*/ -+ reg = <0x47C00000 0x1000>, <0x47D00000 0x100>, <0x44A00000 0x10>; ++ /* cfg inbound translator */ ++ reg = <0x0 0x1000>, <0x100000 0x100>; ++ ++ phys = <&pcie_phy>; ++ phy-names = "pcie-phy"; + + #interrupt-cells = <1>; + /* wild card mask, match all bus address & interrupt specifier */ @@ -56,8 +59,8 @@ + gpios = <&gpio1 12 0>; + clocks = <&stdclk CLK_820_PCIEA>, <&pllb>; + clock-names = "pcie", "busclk"; -+ resets = <&reset RESET_PCIEA>, <&reset RESET_PCIEPHY>; -+ reset-names = "pcie", "phy"; ++ resets = <&reset RESET_PCIEA>; ++ reset-names = "pcie"; + + plxtech,pcie-hcsl-bit = <2>; + plxtech,pcie-ctrl-offset = <0x120>; @@ -79,8 +82,11 @@ + + bus-range = <0x80 0xff>; + -+ /* cfg inbound translator phy*/ -+ reg = <0x47E00000 0x1000>, <0x47F00000 0x100>, <0x44A00000 0x10>; ++ /* cfg inbound translator */ ++ reg = <0x0 0x1000>, <0x100000 0x100>; ++ ++ phys = <&pcie_phy>; ++ phy-names = "pcie-phy"; + + #interrupt-cells = <1>; + /* wild card mask, match all bus address & interrupt specifier */ @@ -94,8 +100,8 @@ + /* gpios = <&gpio1 12 0>; */ + clocks = <&stdclk CLK_820_PCIEB>, <&pllb>; + clock-names = "pcie", "busclk"; -+ resets = <&reset RESET_PCIEB>, <&reset RESET_PCIEPHY>; -+ reset-names = "pcie", "phy"; ++ resets = <&reset RESET_PCIEB>; ++ reset-names = "pcie"; + + plxtech,pcie-hcsl-bit = <3>; + plxtech,pcie-ctrl-offset = <0x124>; diff --git a/target/linux/oxnas/patches-4.14/500-oxnas-sata.patch b/target/linux/oxnas/patches-4.14/500-oxnas-sata.patch index f79b100a5e..7aa0df973f 100644 --- a/target/linux/oxnas/patches-4.14/500-oxnas-sata.patch +++ b/target/linux/oxnas/patches-4.14/500-oxnas-sata.patch @@ -26,7 +26,7 @@ obj-$(CONFIG_PATA_ALI) += pata_ali.o --- a/arch/arm/boot/dts/ox820.dtsi +++ b/arch/arm/boot/dts/ox820.dtsi -@@ -385,5 +385,20 @@ +@@ -400,5 +400,20 @@ }; }; -- 2.30.2