On Thu, Sep 12, 2019 at 10:23:31, Dilip KotaThanks Gustavo for the confirmation, i will add it in the next patch version
<eswara.kota@xxxxxxxxxxxxxxx> wrote:
Quoting Andrew Murray:Hi, I just return from parental leave, therefore I still trying to get
Quoting Gustavo Pimentel:
On 9/12/2019 4:25 PM, Andrew Murray wrote:
[...]Gustavo Pimentel,
I am ok to add if maintainer agrees with it.OK, so there is a difference between initial training and then resizingintel_pcie_max_link_width_setup() is doing the lane resizing which isI will check this and get back to you.Indeed, but a user may also set num-lanes in the device tree. I'm wonderingintel_pcie_max_link_width_setup() function will be called by sysfs attribute pcie_width_store() to change on the fly.+static void intel_pcie_max_link_width_setup(struct intel_pcie_port *lpp)Is this identical functionality to the writing of PCIE_PORT_LINK_CONTROL
+{
+ u32 mask, val;
+
+ /* HW auto bandwidth negotiation must be enabled */
+ pcie_rc_cfg_wr_mask(lpp, PCIE_LCTLSTS_HW_AW_DIS, 0, PCIE_LCTLSTS);
+
+ mask = PCIE_DIRECT_LINK_WIDTH_CHANGE | PCIE_TARGET_LINK_WIDTH;
+ val = PCIE_DIRECT_LINK_WIDTH_CHANGE | lpp->lanes;
+ pcie_rc_cfg_wr_mask(lpp, mask, val, PCIE_MULTI_LANE_CTRL);
in dw_pcie_setup?
I ask because if the user sets num-lanes in the DT, will it have the
desired effect?
if, when set in device-tree, it will have the desired effect. Because I don't
see anything similar to PCIE_LCTLSTS_HW_AW_DIS in dw_pcie_setup which is what
your function does here.
I guess I'm trying to avoid the suitation where num-lanes doesn't have the
desired effect and the only way to set the num-lanes is throught the sysfs
control.
different from the link up/establishment happening during probe. Also
PCIE_LCTLSTS_HW_AW_DIS default value is 0 so not setting during the probe or
dw_pcie_setup.
intel_pcie_max_link_width_setup() is programming as per the designware PCIe
controller databook instructions for lane resizing.
Below lines are from Designware PCIe databook for lane resizing.
ÂProgram the TARGET_LINK_WIDTH[5:0] field of the MULTI_LANE_CONTROL_OFF
register.
ÂProgram the DIRECT_LINK_WIDTH_CHANGE2 field of the MULTI_LANE_CONTROL_OFF
register.
It is assumed that the PCIE_CAP_HW_AUTO_WIDTH_DISABLE field in the
LINK_CONTROL_LINK_STATUS_REG register is 0.
of the link. Given that this is not Intel specific, should this function
exist within the designware driver for the benefit of others?
Could you please let us know your opinion on this.
the pace in mailing list discussion.
However your suggestion looks good, I agree that can go into DesignWare
driver to be available to all.
Just a small request, please do in general:
s/designware/DesignWare
Regards,
Gustavo
[...]
[...]+}
+
+static void intel_pcie_port_logic_setup(struct intel_pcie_port *lpp)
+{
+ u32 val, mask, fts;
+
+ switch (lpp->max_speed) {
+ case PCIE_LINK_SPEED_GEN1:
+ case PCIE_LINK_SPEED_GEN2:
+ fts = PCIE_AFR_GEN12_FTS_NUM_DFT;
+ break;
Ok, i will update it in the next patch version.I'd prefer the helper, the added benefit of this is additional error checking.It happened in our internal review.I couldn't find an earlier version of this series that used of_pci_get_max_link_speed,I just remember, earlier we were using of_pci_get_max_link_speed() itself.Thanks for pointing it. of_pci_get_max_link_speed() can be used here. I will+Is it possible to use of_pci_get_max_link_speed here instead?
+ if (device_property_read_u32(dev, "max-link-speed", &lpp->link_gen))
+ lpp->link_gen = 0; /* Fallback to auto */
update it in the next patch revision.
As per reviewer comments changed to device_property_read_u32() to maintain
symmetry in parsing device tree properties from device node.
Let me know your view.
have I missed it somewhere?
What's your suggestion please, either to go with symmetry in parsing
"device_property_read_u32()" or with pci helper function
"of_pci_get_max_link_speed"?
It also means users can be confident that max-link-speed will behave in the
same way as other host controllers that use this field.
Regards,
Dilip
Thanks,
Andrew Murray
Sure. thank you.I don't have a problem with this, though others may have differing views.OkOK.It is PERST#, signals hardware reset to the End point .Is this not a host bridge controller?Agree. I will move it out of get_resources().+Given that this is called from a function '..._get_resources' I don't think
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "app");
+ if (!res)
+ return -ENXIO;
+
+ lpp->app_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(lpp->app_base))
+ return PTR_ERR(lpp->app_base);
+
+ ret = intel_pcie_ep_rst_init(lpp);
+ if (ret)
+ return ret;
we should be resetting anything here.
End point device. I will update it.+EP?
+ lpp->phy = devm_phy_get(dev, "pciephy");
+ if (IS_ERR(lpp->phy)) {
+ ret = PTR_ERR(lpp->phy);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "couldn't get pcie-phy: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void intel_pcie_deinit_phy(struct intel_pcie_port *lpp)
+{
+ phy_exit(lpp->phy);
+}
+
+static int intel_pcie_wait_l2(struct intel_pcie_port *lpp)
+{
+ u32 value;
+ int ret;
+
+ if (lpp->max_speed < PCIE_LINK_SPEED_GEN3)
+ return 0;
+
+ /* Send PME_TURN_OFF message */
+ pcie_app_wr_mask(lpp, PCIE_APP_MSG_XMT_PM_TURNOFF,
+ PCIE_APP_MSG_XMT_PM_TURNOFF, PCIE_APP_MSG_CR);
+
+ /* Read PMC status and wait for falling into L2 link state */
+ ret = readl_poll_timeout(lpp->app_base + PCIE_APP_PMC, value,
+ (value & PCIE_APP_PMC_IN_L2), 20,
+ jiffies_to_usecs(5 * HZ));
+ if (ret)
+ dev_err(lpp->pci.dev, "PCIe link enter L2 timeout!\n");
+
+ return ret;
+}
+
+static void intel_pcie_turn_off(struct intel_pcie_port *lpp)
+{
+ if (dw_pcie_link_up(&lpp->pci))
+ intel_pcie_wait_l2(lpp);
+
+ /* Put EP in reset state */
ÂÂÂÂÂÂÂ /* Put EP in reset state */
ÂÂÂÂÂÂÂ intel_pcie_device_rst_assert(lpp);
Sure.Please ensure that all supporting macros, functions and defines go with thatSure, i will submit these entity in separate patch.+ intel_pcie_device_rst_assert(lpp);You mentioned that a use-case for changing width/speed on the fly was to
+ pcie_rc_cfg_wr_mask(lpp, PCI_COMMAND_MEMORY, 0, PCI_COMMAND);
+}
+
+static int intel_pcie_host_setup(struct intel_pcie_port *lpp)
+{
+ int ret;
+
+ intel_pcie_core_rst_assert(lpp);
+ intel_pcie_device_rst_assert(lpp);
+
+ ret = phy_init(lpp->phy);
+ if (ret)
+ return ret;
+
+ intel_pcie_core_rst_deassert(lpp);
+
+ ret = clk_prepare_enable(lpp->core_clk);
+ if (ret) {
+ dev_err(lpp->pci.dev, "Core clock enable failed: %d\n", ret);
+ goto clk_err;
+ }
+
+ intel_pcie_rc_setup(lpp);
+ ret = intel_pcie_app_logic_setup(lpp);
+ if (ret)
+ goto app_init_err;
+
+ ret = intel_pcie_setup_irq(lpp);
+ if (!ret)
+ return ret;
+
+ intel_pcie_turn_off(lpp);
+app_init_err:
+ clk_disable_unprepare(lpp->core_clk);
+clk_err:
+ intel_pcie_core_rst_assert(lpp);
+ intel_pcie_deinit_phy(lpp);
+ return ret;
+}
+
+static ssize_t
+pcie_link_status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ u32 reg, width, gen;
+ struct intel_pcie_port *lpp;
+
+ lpp = dev_get_drvdata(dev);
+
+ reg = pcie_rc_cfg_rd(lpp, PCIE_LCTLSTS);
+ width = FIELD_GET(PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH, reg);
+ gen = FIELD_GET(PCIE_LCTLSTS_LINK_SPEED, reg);
+ if (gen > lpp->max_speed)
+ return -EINVAL;
+
+ return sprintf(buf, "Port %2u Width x%u Speed %s GT/s\n", lpp->id,
+ width, pcie_link_gen_to_str(gen));
+}
+static DEVICE_ATTR_RO(pcie_link_status);
+
+static ssize_t pcie_speed_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct intel_pcie_port *lpp;
+ unsigned long val;
+ int ret;
+
+ lpp = dev_get_drvdata(dev);
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (val > lpp->max_speed)
+ return -EINVAL;
+
+ lpp->link_gen = val;
+ intel_pcie_max_speed_setup(lpp);
+ intel_pcie_speed_change_disable(lpp);
+ intel_pcie_speed_change_enable(lpp);
+
+ return len;
+}
+static DEVICE_ATTR_WO(pcie_speed);
+
+/*
+ * Link width change on the fly is not always successful.
+ * It also depends on the partner.
+ */
+static ssize_t pcie_width_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct intel_pcie_port *lpp;
+ unsigned long val;
+
+ lpp = dev_get_drvdata(dev);
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ if (val > lpp->max_width)
+ return -EINVAL;
+
+ lpp->lanes = val;
+ intel_pcie_max_link_width_setup(lpp);
+
+ return len;
+}
+static DEVICE_ATTR_WO(pcie_width);
control power consumption (and this also helps debugging issues). As I
understand there is no current support for this in the kernel - yet it is
something that would provide value.
I haven't looked in much detail, however as I understand the PCI spec
allows an upstream partner to change the link speed and retrain. (I'm not
sure about link width). Given that we already have
[current,max]_link_[speed,width] is sysfs for each PCI device, it would
seem natural to extend this to allow for writing a max width or speed.
So ideally this type of thing would be moved to the core or at least in
the dwc driver. This way the benefits can be applied to more devices on
larger PCI fabrics - Though perhaps others here will have a different view
and I'm keen to hear them.
I'm keen to limit the differences between the DWC controller drivers and
unify common code - thus it would be helpful to have a justification as to
why this is only relevant for this controller.
For user-space only control, it is possible to achieve what you have here
with userspace utilities (something like setpci) (assuming the standard
looking registers you currently access are exposed in the normal config
space way - though PCIe capabilities).
My suggestion would be to drop these changes and later add something that
can benefit more devices. In any case if these changes stay within this
driver then it would be helpful to move them to a separate patch.
other patch as well please.
Ok sure.OK, please also see how other drivers use the COMPILE_TEST Kconfig option.Ok.+Insert new line here.
+static struct attribute *pcie_cfg_attrs[] = {
+ &dev_attr_pcie_link_status.attr,
+ &dev_attr_pcie_speed.attr,
+ &dev_attr_pcie_width.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(pcie_cfg);
+
+static int intel_pcie_sysfs_init(struct intel_pcie_port *lpp)
+{
+ return devm_device_add_groups(lpp->pci.dev, pcie_cfg_groups);
+}
+
+static void __intel_pcie_remove(struct intel_pcie_port *lpp)
+{
+ pcie_rc_cfg_wr_mask(lpp, PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER,
+ 0, PCI_COMMAND);
+ intel_pcie_core_irq_disable(lpp);
+ intel_pcie_turn_off(lpp);
+ clk_disable_unprepare(lpp->core_clk);
+ intel_pcie_core_rst_assert(lpp);
+ intel_pcie_deinit_phy(lpp);
+}
+
+static int intel_pcie_remove(struct platform_device *pdev)
+{
+ struct intel_pcie_port *lpp = platform_get_drvdata(pdev);
+ struct pcie_port *pp = &lpp->pci.pp;
+
+ dw_pcie_host_deinit(pp);
+ __intel_pcie_remove(lpp);
+
+ return 0;
+}
+
+static int __maybe_unused intel_pcie_suspend_noirq(struct device *dev)
+{
+ struct intel_pcie_port *lpp = dev_get_drvdata(dev);
+ int ret;
+
+ intel_pcie_core_irq_disable(lpp);
+ ret = intel_pcie_wait_l2(lpp);
+ if (ret)
+ return ret;
+
+ intel_pcie_deinit_phy(lpp);
+ clk_disable_unprepare(lpp->core_clk);
+ return ret;
+}
+
+static int __maybe_unused intel_pcie_resume_noirq(struct device *dev)
+{
+ struct intel_pcie_port *lpp = dev_get_drvdata(dev);
+
+ return intel_pcie_host_setup(lpp);
+}
+
+static int intel_pcie_rc_init(struct pcie_port *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct intel_pcie_port *lpp = dev_get_drvdata(pci->dev);
+ int ret;
+
+ /* RC/host initialization */
+ ret = intel_pcie_host_setup(lpp);
+ if (ret)
+ return ret;
Agree. i will mark the dependency in Kconfig.+ ret = intel_pcie_sysfs_init(lpp);What about other processors? (Noting that this driver doesn't depend on
+ if (ret)
+ __intel_pcie_remove(lpp);
+ return ret;
+}
+
+int intel_pcie_msi_init(struct pcie_port *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+
+ dev_dbg(pci->dev, "PCIe MSI/MSIx is handled by MSI in x86 processor\n");
any specific ARCH in the KConfig).
I'd suggest that the dev_dbg just becomes a code comment.
This PCIe driver is for the Intel Gateway SoCs. So how about naming it is asI'm keen to ensure that it is consistently named. I've seen other commentsOk+ return 0;Add a new line after the closing brace.
+}
+
+u64 intel_pcie_cpu_addr(struct dw_pcie *pcie, u64 cpu_addr)
+{
+ return cpu_addr + BUS_IATU_OFFS;
+}
+
+static const struct dw_pcie_ops intel_pcie_ops = {
+ .cpu_addr_fixup = intel_pcie_cpu_addr,
+};
+
+static const struct dw_pcie_host_ops intel_pcie_dw_ops = {
+ .host_init = intel_pcie_rc_init,
+ .msi_host_init = intel_pcie_msi_init,
+};
+
+static const struct intel_pcie_soc pcie_data = {
+ .pcie_ver = 0x520A,
+ .pcie_atu_offset = 0xC0000,
+ .num_viewport = 3,
+};
+
+static int intel_pcie_probe(struct platform_device *pdev)
+{
+ const struct intel_pcie_soc *data;
+ struct device *dev = &pdev->dev;
+ struct intel_pcie_port *lpp;
+ struct pcie_port *pp;
+ struct dw_pcie *pci;
+ int ret;
+
+ lpp = devm_kzalloc(dev, sizeof(*lpp), GFP_KERNEL);
+ if (!lpp)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, lpp);
+ pci = &lpp->pci;
+ pci->dev = dev;
+ pp = &pci->pp;
+
+ ret = intel_pcie_get_resources(pdev);
+ if (ret)
+ return ret;
+
+ data = device_get_match_data(dev);
+ pci->ops = &intel_pcie_ops;
+ pci->version = data->pcie_ver;
+ pci->atu_base = pci->dbi_base + data->pcie_atu_offset;
+ pp->ops = &intel_pcie_dw_ops;
+
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(dev, "cannot initialize host\n");
+ return ret;
+ }
lgm is the name of intel SoC. I will name it to pcie-intel-axi to be+ /* Intel PCIe doesn't configure IO region, so configureIs there a reason why the we use intel-lgm-pcie here and pcie-intel-axi
+ * viewport to not to access IO region during register
+ * read write operations.
+ */
+ pci->num_viewport = data->num_viewport;
+ dev_info(dev,
+ "Intel AXI PCIe Root Complex Port %d Init Done\n", lpp->id);
+ return ret;
+}
+
+static const struct dev_pm_ops intel_pcie_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(intel_pcie_suspend_noirq,
+ intel_pcie_resume_noirq)
+};
+
+static const struct of_device_id of_intel_pcie_match[] = {
+ { .compatible = "intel,lgm-pcie", .data = &pcie_data },
+ {}
+};
+
+static struct platform_driver intel_pcie_driver = {
+ .probe = intel_pcie_probe,
+ .remove = intel_pcie_remove,
+ .driver = {
+ .name = "intel-lgm-pcie",
elsewhere? What does lgm mean?
generic.
regarding what the name should be - I don't have a strong opinion though
I do think that *-axi may be too generic.
"pcie-intel-gw"; pcie-intel-gw.c and Kconfig as PCIE_INTEL_GW.
Thanks,
Andrew Murray
Regards,
Dilip
Ok, i will check and get back to you on this.Thanks,
Regards,
Andrew Murray
Dilip
Thanks,
Andrew Murray
Thanks,
Andrew Murray
+ .of_match_table = of_intel_pcie_match,
+ .pm = &intel_pcie_pm_ops,
+ },
+};
+builtin_platform_driver(intel_pcie_driver);
--
2.11.0