* [PATCH v6 0/2] add the Amlogic Meson PCIe controller driver
@ 2018-11-22 8:53 Hanjie Lin
2018-11-22 8:53 ` [PATCH v6 1/2] dt-bindings: PCI: meson: add DT bindings for Amlogic Meson PCIe controller Hanjie Lin
2018-11-22 8:53 ` [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver Hanjie Lin
0 siblings, 2 replies; 9+ messages in thread
From: Hanjie Lin @ 2018-11-22 8:53 UTC (permalink / raw)
To: linux-arm-kernel
The Amlogic Meson PCIe host controller is based on the Synopsys DesignWare
PCI core. This patchset add the driver and dt-bindings of the controller.
Changes since v5: [4]
- update MAINTAINER file in alphabetical order
- remove meaningless comment
- use ERR_PTR function instead of (void *) cast
- use is_power_of_2(size) instead of size & (size - 1)
- add comment for PCI_CLASS_REVISION register operation
Changes since v4: [3]
- fix kbuild test robot and compile warnings
Changes since v3: [2]
- modify subject format
- update Kconfig
- update MAINTAINER file
- add comment and error handle for meson_pcie_get_mem_shared()
- drop useless initialization code
- add comment for meson_size_to_payload()
- optimize meson_pcie_establish_link() return code
- optimize meson_pcie_enable_interrupts() redundant function
- drop device_attch related code
- drop dw_pcie_ops read_dbi and write_dbi function
- add error handle for meson_add_pcie_port() when probe
Changes since v2: [1]
- abandon phy driver, move reset to the controller
- use devm_add_action_or_reset() to use clock res
- format correcting
Changes since v1: [0]
- use gpio lib instead open code
- move 'apb' and 'port' reset from phy driver
- format correcting
[0] : https://lkml.kernel.org/r/1534227522-186798-1-git-send-email-hanjie.lin at amlogic.com
[1] : https://lkml.kernel.org/r/1535096165-45827-1-git-send-email-hanjie.lin at amlogic.com
[2] : https://lkml.kernel.org/r/1537509820-52040-1-git-send-email-hanjie.lin at amlogic.com
[3] : https://lkml.kernel.org/r/1538999834-156423-3-git-send-email-hanjie.lin at amlogic.com
[4] : https://lkml.kernel.org/r/1539049990-30810-1-git-send-email-hanjie.lin at amlogic.com
Yue Wang (2):
dt-bindings: PCI: meson: add DT bindings for Amlogic Meson PCIe
controller
PCI: amlogic: Add the Amlogic Meson PCIe controller driver
.../devicetree/bindings/pci/amlogic,meson-pcie.txt | 70 +++
MAINTAINERS | 7 +
drivers/pci/controller/dwc/Kconfig | 10 +
drivers/pci/controller/dwc/Makefile | 1 +
drivers/pci/controller/dwc/pci-meson.c | 597 +++++++++++++++++++++
5 files changed, 685 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/amlogic,meson-pcie.txt
create mode 100644 drivers/pci/controller/dwc/pci-meson.c
--
2.7.4
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v6 1/2] dt-bindings: PCI: meson: add DT bindings for Amlogic Meson PCIe controller 2018-11-22 8:53 [PATCH v6 0/2] add the Amlogic Meson PCIe controller driver Hanjie Lin @ 2018-11-22 8:53 ` Hanjie Lin 2018-11-22 8:53 ` [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver Hanjie Lin 1 sibling, 0 replies; 9+ messages in thread From: Hanjie Lin @ 2018-11-22 8:53 UTC (permalink / raw) To: linux-arm-kernel From: Yue Wang <yue.wang@amlogic.com> The Amlogic Meson PCIe host controller is based on the Synopsys DesignWare PCI core. This patch adds documentation for the DT bindings in Meson PCIe controller. Signed-off-by: Yue Wang <yue.wang@amlogic.com> Signed-off-by: Hanjie Lin <hanjie.lin@amlogic.com> Reviewed-by: Rob Herring <robh@kernel.org> --- .../devicetree/bindings/pci/amlogic,meson-pcie.txt | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/amlogic,meson-pcie.txt diff --git a/Documentation/devicetree/bindings/pci/amlogic,meson-pcie.txt b/Documentation/devicetree/bindings/pci/amlogic,meson-pcie.txt new file mode 100644 index 0000000..12b18f8 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/amlogic,meson-pcie.txt @@ -0,0 +1,70 @@ +Amlogic Meson AXG DWC PCIE SoC controller + +Amlogic Meson PCIe host controller is based on the Synopsys DesignWare PCI core. +It shares common functions with the PCIe DesignWare core driver and +inherits common properties defined in +Documentation/devicetree/bindings/pci/designware-pci.txt. + +Additional properties are described here: + +Required properties: +- compatible: + should contain "amlogic,axg-pcie" to identify the core. +- reg: + should contain the configuration address space. +- reg-names: Must be + - "elbi" External local bus interface registers + - "cfg" Meson specific registers + - "phy" Meson PCIE PHY registers + - "config" PCIe configuration space +- reset-gpios: The GPIO to generate PCIe PERST# assert and deassert signal. +- clocks: Must contain an entry for each entry in clock-names. +- clock-names: Must include the following entries: + - "pclk" PCIe GEN 100M PLL clock + - "port" PCIe_x(A or B) RC clock gate + - "general" PCIe Phy clock + - "mipi" PCIe_x(A or B) 100M ref clock gate +- resets: phandle to the reset lines. +- reset-names: must contain "phy" "port" and "apb" + - "phy" Share PHY reset + - "port" Port A or B reset + - "apb" Share APB reset +- device_type: + should be "pci". As specified in designware-pcie.txt + + +Example configuration: + + pcie: pcie at f9800000 { + compatible = "amlogic,axg-pcie", "snps,dw-pcie"; + reg = <0x0 0xf9800000 0x0 0x400000 + 0x0 0xff646000 0x0 0x2000 + 0x0 0xff644000 0x0 0x2000 + 0x0 0xf9f00000 0x0 0x100000>; + reg-names = "elbi", "cfg", "phy", "config"; + reset-gpios = <&gpio GPIOX_19 GPIO_ACTIVE_HIGH>; + interrupts = <GIC_SPI 177 IRQ_TYPE_EDGE_RISING>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 179 IRQ_TYPE_EDGE_RISING>; + bus-range = <0x0 0xff>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x82000000 0 0 0x0 0xf9c00000 0 0x00300000>; + + clocks = <&clkc CLKID_USB + &clkc CLKID_MIPI_ENABLE + &clkc CLKID_PCIE_A + &clkc CLKID_PCIE_CML_EN0>; + clock-names = "general", + "mipi", + "pclk", + "port"; + resets = <&reset RESET_PCIE_PHY>, + <&reset RESET_PCIE_A>, + <&reset RESET_PCIE_APB>; + reset-names = "phy", + "port", + "apb"; + }; -- 2.7.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver 2018-11-22 8:53 [PATCH v6 0/2] add the Amlogic Meson PCIe controller driver Hanjie Lin 2018-11-22 8:53 ` [PATCH v6 1/2] dt-bindings: PCI: meson: add DT bindings for Amlogic Meson PCIe controller Hanjie Lin @ 2018-11-22 8:53 ` Hanjie Lin 2018-11-29 9:03 ` Dan Carpenter 2018-12-03 16:41 ` Lorenzo Pieralisi 1 sibling, 2 replies; 9+ messages in thread From: Hanjie Lin @ 2018-11-22 8:53 UTC (permalink / raw) To: linux-arm-kernel From: Yue Wang <yue.wang@amlogic.com> The Amlogic Meson PCIe host controller is based on the Synopsys DesignWare PCI core. This patch adds the driver support for Meson PCIe controller. Signed-off-by: Yue Wang <yue.wang@amlogic.com> Signed-off-by: Hanjie Lin <hanjie.lin@amlogic.com> --- MAINTAINERS | 7 + drivers/pci/controller/dwc/Kconfig | 10 + drivers/pci/controller/dwc/Makefile | 1 + drivers/pci/controller/dwc/pci-meson.c | 597 +++++++++++++++++++++++++++++++++ 4 files changed, 615 insertions(+) create mode 100644 drivers/pci/controller/dwc/pci-meson.c diff --git a/MAINTAINERS b/MAINTAINERS index 77b1174..4fb9098 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11530,6 +11530,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git/ S: Supported F: drivers/pci/controller/ +PCIE DRIVER FOR AMLOGIC MESON +M: Yue Wang <yue.wang@Amlogic.com> +L: linux-pci at vger.kernel.org +L: linux-amlogic at lists.infradead.org +S: Maintained +F: drivers/pci/controller/dwc/pci-meson.c + PCIE DRIVER FOR AXIS ARTPEC M: Jesper Nilsson <jesper.nilsson@axis.com> L: linux-arm-kernel at axis.com diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 91b0194..7800322 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -193,4 +193,14 @@ config PCIE_HISI_STB help Say Y here if you want PCIe controller support on HiSilicon STB SoCs +config PCI_MESON + bool "MESON PCIe controller" + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say Y here if you want to enable PCI controller support on Amlogic + SoCs. The PCI controller on Amlogic is based on DesignWare hardware + and therefore the driver re-uses the DesignWare core functions to + implement the driver. + endmenu diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index fcf91ea..e05a015 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o +obj-$(CONFIG_PCI_MESON) += pci-meson.o # The following drivers are for devices that use the generic ACPI # pci_root.c driver but don't support standard ECAM config access. diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c new file mode 100644 index 0000000..ef30a5a --- /dev/null +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Amlogic MESON SoCs + * + * Copyright (c) 2018 Amlogic, inc. + * Author: Yue Wang <yue.wang@amlogic.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/resource.h> +#include <linux/types.h> +#include <linux/reset.h> + +#include "pcie-designware.h" + +#define to_meson_pcie(x) dev_get_drvdata((x)->dev) + +/* External local bus interface registers */ +#define PLR_OFFSET 0x700 +#define PCIE_PORT_LINK_CTRL_OFF (PLR_OFFSET + 0x10) +#define FAST_LINK_MODE BIT(7) +#define LINK_CAPABLE_MASK GENMASK(21, 16) +#define LINK_CAPABLE_X1 BIT(16) + +#define PCIE_GEN2_CTRL_OFF (PLR_OFFSET + 0x10c) +#define NUM_OF_LANES_MASK GENMASK(12, 8) +#define NUM_OF_LANES_X1 BIT(8) +#define DIRECT_SPEED_CHANGE BIT(17) + +#define TYPE1_HDR_OFFSET 0x0 +#define PCIE_STATUS_COMMAND (TYPE1_HDR_OFFSET + 0x04) +#define PCI_IO_EN BIT(0) +#define PCI_MEM_SPACE_EN BIT(1) +#define PCI_BUS_MASTER_EN BIT(2) + +#define PCIE_BASE_ADDR0 (TYPE1_HDR_OFFSET + 0x10) +#define PCIE_BASE_ADDR1 (TYPE1_HDR_OFFSET + 0x14) + +#define PCIE_CAP_OFFSET 0x70 +#define PCIE_DEV_CTRL_DEV_STUS (PCIE_CAP_OFFSET + 0x08) +#define PCIE_CAP_MAX_PAYLOAD_MASK GENMASK(7, 5) +#define PCIE_CAP_MAX_PAYLOAD_SIZE(x) ((x) << 5) +#define PCIE_CAP_MAX_READ_REQ_MASK GENMASK(14, 12) +#define PCIE_CAP_MAX_READ_REQ_SIZE(x) ((x) << 12) + +#define PCI_CLASS_REVISION_MASK GENMASK(7, 0) + +/* PCIe specific config registers */ +#define PCIE_CFG0 0x0 +#define APP_LTSSM_ENABLE BIT(7) + +#define PCIE_CFG_STATUS12 0x30 +#define IS_SMLH_LINK_UP(x) ((x) & (1 << 6)) +#define IS_RDLH_LINK_UP(x) ((x) & (1 << 16)) +#define IS_LTSSM_UP(x) ((((x) >> 10) & 0x1f) == 0x11) + +#define PCIE_CFG_STATUS17 0x44 +#define PM_CURRENT_STATE(x) (((x) >> 7) & 0x1) + +#define WAIT_LINKUP_TIMEOUT 2000 +#define PORT_CLK_RATE 100000000UL +#define MAX_PAYLOAD_SIZE 256 +#define MAX_READ_REQ_SIZE 256 +#define MESON_PCIE_PHY_POWERUP 0x1c +#define PCIE_RESET_DELAY 500 +#define PCIE_SHARED_RESET 1 +#define PCIE_NORMAL_RESET 0 + +enum pcie_data_rate { + PCIE_GEN1, + PCIE_GEN2, + PCIE_GEN3, + PCIE_GEN4 +}; + +struct meson_pcie_mem_res { + void __iomem *elbi_base; + void __iomem *cfg_base; + void __iomem *phy_base; +}; + +struct meson_pcie_clk_res { + struct clk *clk; + struct clk *mipi_gate; + struct clk *port_clk; + struct clk *general_clk; +}; + +struct meson_pcie_rc_reset { + struct reset_control *phy; + struct reset_control *port; + struct reset_control *apb; +}; + +struct meson_pcie { + struct dw_pcie pci; + struct meson_pcie_mem_res mem_res; + struct meson_pcie_clk_res clk_res; + struct meson_pcie_rc_reset mrst; + struct gpio_desc *reset_gpio; + + enum of_gpio_flags gpio_flag; + int pcie_num; + u32 port_num; +}; + +static struct reset_control *meson_pcie_get_reset(struct meson_pcie *mp, + const char *id, + u32 reset_type) +{ + struct device *dev = mp->pci.dev; + struct reset_control *reset; + + if (reset_type == PCIE_SHARED_RESET) + reset = devm_reset_control_get_shared(dev, id); + else + reset = devm_reset_control_get(dev, id); + + return reset; +} + +static int meson_pcie_get_resets(struct meson_pcie *mp) +{ + struct meson_pcie_rc_reset *mrst = &mp->mrst; + + mrst->phy = meson_pcie_get_reset(mp, "phy", PCIE_SHARED_RESET); + if (IS_ERR(mrst->phy)) + return PTR_ERR(mrst->phy); + reset_control_deassert(mrst->phy); + + mrst->port = meson_pcie_get_reset(mp, "port", PCIE_NORMAL_RESET); + if (IS_ERR(mrst->port)) + return PTR_ERR(mrst->port); + reset_control_deassert(mrst->port); + + mrst->apb = meson_pcie_get_reset(mp, "apb", PCIE_SHARED_RESET); + if (IS_ERR(mrst->apb)) + return PTR_ERR(mrst->apb); + reset_control_deassert(mrst->apb); + + return 0; +} + +static void __iomem *meson_pcie_get_mem(struct platform_device *pdev, + struct meson_pcie *mp, + const char *id) +{ + struct device *dev = mp->pci.dev; + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, id); + + return devm_ioremap_resource(dev, res); +} + +static void __iomem *meson_pcie_get_mem_shared(struct platform_device *pdev, + struct meson_pcie *mp, + const char *id) +{ + struct device *dev = mp->pci.dev; + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, id); + if (!res) { + dev_err(dev, "No REG resource %s\n", id); + return ERR_PTR(ENXIO); + } + + return devm_ioremap(dev, res->start, resource_size(res)); +} + +static int meson_pcie_get_mems(struct platform_device *pdev, + struct meson_pcie *mp) +{ + mp->mem_res.elbi_base = meson_pcie_get_mem(pdev, mp, "elbi"); + if (IS_ERR(mp->mem_res.elbi_base)) + return PTR_ERR(mp->mem_res.elbi_base); + + mp->mem_res.cfg_base = meson_pcie_get_mem(pdev, mp, "cfg"); + if (IS_ERR(mp->mem_res.cfg_base)) + return PTR_ERR(mp->mem_res.cfg_base); + + /* Meson SoC has two PCI controllers use same phy register*/ + mp->mem_res.phy_base = meson_pcie_get_mem_shared(pdev, mp, "phy"); + if (IS_ERR(mp->mem_res.phy_base)) + return PTR_ERR(mp->mem_res.phy_base); + + return 0; +} + +static void meson_pcie_power_on(struct meson_pcie *mp) +{ + writel(MESON_PCIE_PHY_POWERUP, mp->mem_res.phy_base); +} + +static void meson_pcie_reset(struct meson_pcie *mp) +{ + struct meson_pcie_rc_reset *mrst = &mp->mrst; + + reset_control_assert(mrst->phy); + udelay(PCIE_RESET_DELAY); + reset_control_deassert(mrst->phy); + udelay(PCIE_RESET_DELAY); + + reset_control_assert(mrst->port); + reset_control_assert(mrst->apb); + udelay(PCIE_RESET_DELAY); + reset_control_deassert(mrst->port); + reset_control_deassert(mrst->apb); + udelay(PCIE_RESET_DELAY); +} + +static inline struct clk *meson_pcie_probe_clock(struct device *dev, + const char *id, u64 rate) +{ + struct clk *clk; + int ret; + + clk = devm_clk_get(dev, id); + if (IS_ERR(clk)) + return clk; + + if (rate) { + ret = clk_set_rate(clk, rate); + if (ret) { + dev_err(dev, "set clk rate failed, ret = %d\n", ret); + return ERR_PTR(ret); + } + } + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "couldn't enable clk\n"); + return ERR_PTR(ret); + } + + devm_add_action_or_reset(dev, + (void (*) (void *))clk_disable_unprepare, + clk); + + return clk; +} + +static int meson_pcie_probe_clocks(struct meson_pcie *mp) +{ + struct device *dev = mp->pci.dev; + struct meson_pcie_clk_res *res = &mp->clk_res; + + res->port_clk = meson_pcie_probe_clock(dev, "port", PORT_CLK_RATE); + if (IS_ERR(res->port_clk)) + return PTR_ERR(res->port_clk); + + res->mipi_gate = meson_pcie_probe_clock(dev, "pcie_mipi_en", 0); + if (IS_ERR(res->mipi_gate)) + return PTR_ERR(res->mipi_gate); + + res->general_clk = meson_pcie_probe_clock(dev, "pcie_general", 0); + if (IS_ERR(res->general_clk)) + return PTR_ERR(res->general_clk); + + res->clk = meson_pcie_probe_clock(dev, "pcie", 0); + if (IS_ERR(res->clk)) + return PTR_ERR(res->clk); + + return 0; +} + +static inline void meson_elb_writel(struct meson_pcie *mp, u32 val, u32 reg) +{ + writel(val, mp->mem_res.elbi_base + reg); +} + +static inline u32 meson_elb_readl(struct meson_pcie *mp, u32 reg) +{ + return readl(mp->mem_res.elbi_base + reg); +} + +static inline u32 meson_cfg_readl(struct meson_pcie *mp, u32 reg) +{ + return readl(mp->mem_res.cfg_base + reg); +} + +static inline void meson_cfg_writel(struct meson_pcie *mp, u32 val, u32 reg) +{ + writel(val, mp->mem_res.cfg_base + reg); +} + +static void meson_pcie_assert_reset(struct meson_pcie *mp) +{ + gpiod_set_value_cansleep(mp->reset_gpio, 0); + udelay(500); + gpiod_set_value_cansleep(mp->reset_gpio, 1); +} + +static void meson_pcie_init_dw(struct meson_pcie *mp) +{ + u32 val; + + val = meson_cfg_readl(mp, PCIE_CFG0); + val |= APP_LTSSM_ENABLE; + meson_cfg_writel(mp, val, PCIE_CFG0); + + val = meson_elb_readl(mp, PCIE_PORT_LINK_CTRL_OFF); + val &= ~LINK_CAPABLE_MASK; + meson_elb_writel(mp, val, PCIE_PORT_LINK_CTRL_OFF); + + val = meson_elb_readl(mp, PCIE_PORT_LINK_CTRL_OFF); + val |= LINK_CAPABLE_X1 | FAST_LINK_MODE; + meson_elb_writel(mp, val, PCIE_PORT_LINK_CTRL_OFF); + + val = meson_elb_readl(mp, PCIE_GEN2_CTRL_OFF); + val &= ~NUM_OF_LANES_MASK; + meson_elb_writel(mp, val, PCIE_GEN2_CTRL_OFF); + + val = meson_elb_readl(mp, PCIE_GEN2_CTRL_OFF); + val |= NUM_OF_LANES_X1 | DIRECT_SPEED_CHANGE; + meson_elb_writel(mp, val, PCIE_GEN2_CTRL_OFF); + + meson_elb_writel(mp, 0x0, PCIE_BASE_ADDR0); + meson_elb_writel(mp, 0x0, PCIE_BASE_ADDR1); +} + +static int meson_size_to_payload(struct meson_pcie *mp, int size) +{ + struct device *dev = mp->pci.dev; + + /* + * dwc supports 2^(val+7) payload size, which val is 0~5 default to 1. + * So if input size is not 2^order alignment or less than 2^7 or bigger + * than 2^12, just set to default size 2^(1+7). + */ + if (!is_power_of_2(size) || size < 128 || size > 4096) { + dev_warn(dev, "playload size %d, set to default 256\n", size); + return 1; + } + + return fls(size) - 8; +} + +static void meson_set_max_payload(struct meson_pcie *mp, int size) +{ + u32 val = 0; + int max_payload_size = meson_size_to_payload(mp, size); + + val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); + val &= ~PCIE_CAP_MAX_PAYLOAD_MASK; + meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); + + val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); + val |= PCIE_CAP_MAX_PAYLOAD_SIZE(max_payload_size); + meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); +} + +static void meson_set_max_rd_req_size(struct meson_pcie *mp, int size) +{ + u32 val; + int max_rd_req_size = meson_size_to_payload(mp, size); + + val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); + val &= ~PCIE_CAP_MAX_READ_REQ_MASK; + meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); + + val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); + val |= PCIE_CAP_MAX_READ_REQ_SIZE(max_rd_req_size); + meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); +} + +static inline void meson_enable_memory_space(struct meson_pcie *mp) +{ + /* Set the RC Bus Master, Memory Space and I/O Space enables */ + meson_elb_writel(mp, PCI_IO_EN | PCI_MEM_SPACE_EN | PCI_BUS_MASTER_EN, + PCIE_STATUS_COMMAND); +} + +static int meson_pcie_establish_link(struct meson_pcie *mp) +{ + struct dw_pcie *pci = &mp->pci; + struct pcie_port *pp = &pci->pp; + + meson_pcie_init_dw(mp); + meson_set_max_payload(mp, MAX_PAYLOAD_SIZE); + meson_set_max_rd_req_size(mp, MAX_READ_REQ_SIZE); + + dw_pcie_setup_rc(pp); + meson_enable_memory_space(mp); + + meson_pcie_assert_reset(mp); + + return dw_pcie_wait_for_link(pci); +} + +static void meson_pcie_enable_interrupts(struct meson_pcie *mp) +{ + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(&mp->pci.pp); +} + +static int meson_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + /* + * there is a bug of MESON AXG pcie controller that software can not + * programe PCI_CLASS_DEVICE register, so we must return a fake right + * value to ensure driver could probe successfully. + */ + if (where == PCI_CLASS_REVISION) { + *val = readl(pci->dbi_base + PCI_CLASS_REVISION); + /* keep revision id */ + *val &= PCI_CLASS_REVISION_MASK; + *val |= PCI_CLASS_BRIDGE_PCI << 16; + return PCIBIOS_SUCCESSFUL; + } + + return dw_pcie_read(pci->dbi_base + where, size, val); +} + +static int meson_pcie_wr_own_conf(struct pcie_port *pp, int where, + int size, u32 val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + return dw_pcie_write(pci->dbi_base + where, size, val); +} + +static int meson_pcie_link_up(struct dw_pcie *pci) +{ + struct meson_pcie *mp = to_meson_pcie(pci); + struct device *dev = pci->dev; + u32 smlh_up = 0; + u32 ltssm_up = 0; + u32 rdlh_up = 0; + u32 speed_okay = 0; + u32 cnt = 0; + u32 state12, state17; + + while (smlh_up == 0 || rdlh_up == 0 || ltssm_up == 0 || + speed_okay == 0) { + udelay(20); + + state12 = meson_cfg_readl(mp, PCIE_CFG_STATUS12); + state17 = meson_cfg_readl(mp, PCIE_CFG_STATUS17); + smlh_up = IS_SMLH_LINK_UP(state12); + rdlh_up = IS_RDLH_LINK_UP(state12); + ltssm_up = IS_LTSSM_UP(state12); + + if (PM_CURRENT_STATE(state17) < PCIE_GEN3) + speed_okay = 1; + + if (smlh_up) + dev_dbg(dev, "smlh_link_up is on\n"); + if (rdlh_up) + dev_dbg(dev, "rdlh_link_up is on\n"); + if (ltssm_up) + dev_dbg(dev, "ltssm_up is on\n"); + if (speed_okay) + dev_dbg(dev, "speed_okay\n"); + + cnt++; + + if (cnt >= WAIT_LINKUP_TIMEOUT) { + dev_err(dev, "Error: Wait linkup timeout.\n"); + return 0; + } + } + + return 1; +} + +static int meson_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct meson_pcie *mp = to_meson_pcie(pci); + int ret; + + ret = meson_pcie_establish_link(mp); + if (ret) + return ret; + + meson_pcie_enable_interrupts(mp); + + return 0; +} + +static const struct dw_pcie_host_ops meson_pcie_host_ops = { + .rd_own_conf = meson_pcie_rd_own_conf, + .wr_own_conf = meson_pcie_wr_own_conf, + .host_init = meson_pcie_host_init, +}; + +static int meson_add_pcie_port(struct meson_pcie *mp, + struct platform_device *pdev) +{ + struct dw_pcie *pci = &mp->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq(pdev, 0); + if (pp->msi_irq < 0) { + dev_err(dev, "failed to get msi irq\n"); + return pp->msi_irq; + } + } + + pp->ops = &meson_pcie_host_ops; + pci->dbi_base = mp->mem_res.elbi_base; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = meson_pcie_link_up, +}; + +static int meson_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie *pci; + struct meson_pcie *mp; + int ret; + + mp = devm_kzalloc(dev, sizeof(*mp), GFP_KERNEL); + if (!mp) + return -ENOMEM; + + pci = &mp->pci; + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + mp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(mp->reset_gpio)) { + dev_err(dev, "Get reset gpio failed\n"); + return PTR_ERR(mp->reset_gpio); + } + + ret = meson_pcie_get_resets(mp); + if (ret) { + dev_err(dev, "Get reset resource failed, %d\n", ret); + return ret; + } + + ret = meson_pcie_get_mems(pdev, mp); + if (ret) { + dev_err(dev, "Get memory resource failed, %d\n", ret); + return ret; + } + + meson_pcie_power_on(mp); + meson_pcie_reset(mp); + + ret = meson_pcie_probe_clocks(mp); + if (ret) { + dev_err(dev, "Init clock resources failed, %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, mp); + + ret = meson_add_pcie_port(mp, pdev); + if (ret < 0) { + dev_err(dev, "Add PCIE port failed, %d\n", ret); + return ret; + } + + return 0; +} + +static const struct of_device_id meson_pcie_of_match[] = { + { + .compatible = "amlogic,axg-pcie", + }, + {}, +}; + +static struct platform_driver meson_pcie_driver = { + .probe = meson_pcie_probe, + .driver = { + .name = "meson-pcie", + .of_match_table = meson_pcie_of_match, + }, +}; + +builtin_platform_driver(meson_pcie_driver); -- 2.7.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver 2018-11-22 8:53 ` [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver Hanjie Lin @ 2018-11-29 9:03 ` Dan Carpenter 2018-12-03 16:41 ` Lorenzo Pieralisi 1 sibling, 0 replies; 9+ messages in thread From: Dan Carpenter @ 2018-11-29 9:03 UTC (permalink / raw) To: linux-arm-kernel Hi Yue, url: https://github.com/0day-ci/linux/commits/Hanjie-Lin/dt-bindings-PCI-meson-add-DT-bindings-for-Amlogic-Meson-PCIe-controller/20181122-225955 base: https://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next smatch warnings: drivers/pci/controller/dwc/pci-meson.c:171 meson_pcie_get_mem_shared() error: passing non negative 6 to ERR_PTR # https://github.com/0day-ci/linux/commit/c882cdc75e49b6de65cd3d95ebf688272af6b5f9 git remote add linux-review https://github.com/0day-ci/linux git remote update linux-review git checkout c882cdc75e49b6de65cd3d95ebf688272af6b5f9 vim +171 drivers/pci/controller/dwc/pci-meson.c c882cdc75 Yue Wang 2018-11-22 160 c882cdc75 Yue Wang 2018-11-22 161 static void __iomem *meson_pcie_get_mem_shared(struct platform_device *pdev, c882cdc75 Yue Wang 2018-11-22 162 struct meson_pcie *mp, c882cdc75 Yue Wang 2018-11-22 163 const char *id) c882cdc75 Yue Wang 2018-11-22 164 { c882cdc75 Yue Wang 2018-11-22 165 struct device *dev = mp->pci.dev; c882cdc75 Yue Wang 2018-11-22 166 struct resource *res; c882cdc75 Yue Wang 2018-11-22 167 c882cdc75 Yue Wang 2018-11-22 168 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, id); c882cdc75 Yue Wang 2018-11-22 169 if (!res) { c882cdc75 Yue Wang 2018-11-22 170 dev_err(dev, "No REG resource %s\n", id); c882cdc75 Yue Wang 2018-11-22 @171 return ERR_PTR(ENXIO); ^^^^^ -ENXIO c882cdc75 Yue Wang 2018-11-22 172 } c882cdc75 Yue Wang 2018-11-22 173 c882cdc75 Yue Wang 2018-11-22 174 return devm_ioremap(dev, res->start, resource_size(res)); c882cdc75 Yue Wang 2018-11-22 175 } c882cdc75 Yue Wang 2018-11-22 176 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver 2018-11-22 8:53 ` [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver Hanjie Lin 2018-11-29 9:03 ` Dan Carpenter @ 2018-12-03 16:41 ` Lorenzo Pieralisi 2018-12-03 22:57 ` Bjorn Helgaas 1 sibling, 1 reply; 9+ messages in thread From: Lorenzo Pieralisi @ 2018-12-03 16:41 UTC (permalink / raw) To: Hanjie Lin, Bjorn Helgaas Cc: Yixun Lan, Rob Herring, Jianxin Pan, Kevin Hilman, Shawn Lin, Philippe Ombredanne, linux-pci, linux-kernel, Yue Wang, Qiufang Dai, Jian Hu, Liang Yang, Cyrille Pitchen, Gustavo Pimentel, Carlo Caione, linux-amlogic, linux-arm-kernel, Jerome Brunet On Thu, Nov 22, 2018 at 04:53:54PM +0800, Hanjie Lin wrote: [...] > +static int meson_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, > + u32 *val) > +{ > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > + > + /* > + * there is a bug of MESON AXG pcie controller that software can not > + * programe PCI_CLASS_DEVICE register, so we must return a fake right > + * value to ensure driver could probe successfully. > + */ > + if (where == PCI_CLASS_REVISION) { > + *val = readl(pci->dbi_base + PCI_CLASS_REVISION); > + /* keep revision id */ > + *val &= PCI_CLASS_REVISION_MASK; > + *val |= PCI_CLASS_BRIDGE_PCI << 16; > + return PCIBIOS_SUCCESSFUL; > + } As I said before, this looks broken. If this code (or other drivers with the same broken assumptions, eg dwc/pcie-qcom.c) carries out a, say, byte sized config access of eg PCI_CLASS_DEVICE you will get junk out of it according to your comment above. I would like to pick Bjorn's brain on this to see what we can really do to fix this (and other) drivers. Thanks, Lorenzo > + return dw_pcie_read(pci->dbi_base + where, size, val); > +} > + > +static int meson_pcie_wr_own_conf(struct pcie_port *pp, int where, > + int size, u32 val) > +{ > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > + > + return dw_pcie_write(pci->dbi_base + where, size, val); > +} > + > +static int meson_pcie_link_up(struct dw_pcie *pci) > +{ > + struct meson_pcie *mp = to_meson_pcie(pci); > + struct device *dev = pci->dev; > + u32 smlh_up = 0; > + u32 ltssm_up = 0; > + u32 rdlh_up = 0; > + u32 speed_okay = 0; > + u32 cnt = 0; > + u32 state12, state17; > + > + while (smlh_up == 0 || rdlh_up == 0 || ltssm_up == 0 || > + speed_okay == 0) { > + udelay(20); > + > + state12 = meson_cfg_readl(mp, PCIE_CFG_STATUS12); > + state17 = meson_cfg_readl(mp, PCIE_CFG_STATUS17); > + smlh_up = IS_SMLH_LINK_UP(state12); > + rdlh_up = IS_RDLH_LINK_UP(state12); > + ltssm_up = IS_LTSSM_UP(state12); > + > + if (PM_CURRENT_STATE(state17) < PCIE_GEN3) > + speed_okay = 1; > + > + if (smlh_up) > + dev_dbg(dev, "smlh_link_up is on\n"); > + if (rdlh_up) > + dev_dbg(dev, "rdlh_link_up is on\n"); > + if (ltssm_up) > + dev_dbg(dev, "ltssm_up is on\n"); > + if (speed_okay) > + dev_dbg(dev, "speed_okay\n"); > + > + cnt++; > + > + if (cnt >= WAIT_LINKUP_TIMEOUT) { > + dev_err(dev, "Error: Wait linkup timeout.\n"); > + return 0; > + } > + } > + > + return 1; > +} > + > +static int meson_pcie_host_init(struct pcie_port *pp) > +{ > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > + struct meson_pcie *mp = to_meson_pcie(pci); > + int ret; > + > + ret = meson_pcie_establish_link(mp); > + if (ret) > + return ret; > + > + meson_pcie_enable_interrupts(mp); > + > + return 0; > +} > + > +static const struct dw_pcie_host_ops meson_pcie_host_ops = { > + .rd_own_conf = meson_pcie_rd_own_conf, > + .wr_own_conf = meson_pcie_wr_own_conf, > + .host_init = meson_pcie_host_init, > +}; > + > +static int meson_add_pcie_port(struct meson_pcie *mp, > + struct platform_device *pdev) > +{ > + struct dw_pcie *pci = &mp->pci; > + struct pcie_port *pp = &pci->pp; > + struct device *dev = &pdev->dev; > + int ret; > + > + if (IS_ENABLED(CONFIG_PCI_MSI)) { > + pp->msi_irq = platform_get_irq(pdev, 0); > + if (pp->msi_irq < 0) { > + dev_err(dev, "failed to get msi irq\n"); > + return pp->msi_irq; > + } > + } > + > + pp->ops = &meson_pcie_host_ops; > + pci->dbi_base = mp->mem_res.elbi_base; > + > + ret = dw_pcie_host_init(pp); > + if (ret) { > + dev_err(dev, "failed to initialize host\n"); > + return ret; > + } > + > + return 0; > +} > + > +static const struct dw_pcie_ops dw_pcie_ops = { > + .link_up = meson_pcie_link_up, > +}; > + > +static int meson_pcie_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct dw_pcie *pci; > + struct meson_pcie *mp; > + int ret; > + > + mp = devm_kzalloc(dev, sizeof(*mp), GFP_KERNEL); > + if (!mp) > + return -ENOMEM; > + > + pci = &mp->pci; > + pci->dev = dev; > + pci->ops = &dw_pcie_ops; > + > + mp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); > + if (IS_ERR(mp->reset_gpio)) { > + dev_err(dev, "Get reset gpio failed\n"); > + return PTR_ERR(mp->reset_gpio); > + } > + > + ret = meson_pcie_get_resets(mp); > + if (ret) { > + dev_err(dev, "Get reset resource failed, %d\n", ret); > + return ret; > + } > + > + ret = meson_pcie_get_mems(pdev, mp); > + if (ret) { > + dev_err(dev, "Get memory resource failed, %d\n", ret); > + return ret; > + } > + > + meson_pcie_power_on(mp); > + meson_pcie_reset(mp); > + > + ret = meson_pcie_probe_clocks(mp); > + if (ret) { > + dev_err(dev, "Init clock resources failed, %d\n", ret); > + return ret; > + } > + > + platform_set_drvdata(pdev, mp); > + > + ret = meson_add_pcie_port(mp, pdev); > + if (ret < 0) { > + dev_err(dev, "Add PCIE port failed, %d\n", ret); > + return ret; > + } > + > + return 0; > +} > + > +static const struct of_device_id meson_pcie_of_match[] = { > + { > + .compatible = "amlogic,axg-pcie", > + }, > + {}, > +}; > + > +static struct platform_driver meson_pcie_driver = { > + .probe = meson_pcie_probe, > + .driver = { > + .name = "meson-pcie", > + .of_match_table = meson_pcie_of_match, > + }, > +}; > + > +builtin_platform_driver(meson_pcie_driver); > -- > 2.7.4 > _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver 2018-12-03 16:41 ` Lorenzo Pieralisi @ 2018-12-03 22:57 ` Bjorn Helgaas 2018-12-04 10:40 ` Hanjie Lin 0 siblings, 1 reply; 9+ messages in thread From: Bjorn Helgaas @ 2018-12-03 22:57 UTC (permalink / raw) To: Lorenzo Pieralisi Cc: Rob Herring, Hanjie Lin, Jianxin Pan, linux-pci, Kevin Hilman, Shawn Lin, Yixun Lan, linux-kernel, Yue Wang, Qiufang Dai, Jian Hu, Philippe Ombredanne, Carlo Caione, Cyrille Pitchen, linux-amlogic, Gustavo Pimentel, Liang Yang, linux-arm-kernel, Jerome Brunet On Mon, Dec 03, 2018 at 04:41:50PM +0000, Lorenzo Pieralisi wrote: > On Thu, Nov 22, 2018 at 04:53:54PM +0800, Hanjie Lin wrote: > > [...] > > > +static int meson_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, > > + u32 *val) > > +{ > > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > > + > > + /* > > + * there is a bug of MESON AXG pcie controller that software can not > > + * programe PCI_CLASS_DEVICE register, so we must return a fake right > > + * value to ensure driver could probe successfully. > > + */ > > + if (where == PCI_CLASS_REVISION) { > > + *val = readl(pci->dbi_base + PCI_CLASS_REVISION); > > + /* keep revision id */ > > + *val &= PCI_CLASS_REVISION_MASK; > > + *val |= PCI_CLASS_BRIDGE_PCI << 16; > > + return PCIBIOS_SUCCESSFUL; > > + } > > As I said before, this looks broken. If this code (or other drivers with > the same broken assumptions, eg dwc/pcie-qcom.c) carries out a, say, > byte sized config access of eg PCI_CLASS_DEVICE you will get junk out of > it according to your comment above. > > I would like to pick Bjorn's brain on this to see what we can really do > to fix this (and other) drivers. - Check to see whether you're reading anything in the 32-bit dword at offset 0x08. - Do the 32-bit readl(). - Insert the correct Sub-Class and Base Class code (you also throw away the Programming Interface; not sure why that is) - If you're reading something smaller than 32 bits, mask & shift as needed. pci_bridge_emul_conf_read() does something similar that you might be able to copy. Out of curiosity, what code depends on PCI_CLASS_BRIDGE_PCI? There are several places in the kernel that currently depend on it, but I think several of them *should* be checking dev->hdr_type to identify a type 1 header instead. Bjorn _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver 2018-12-03 22:57 ` Bjorn Helgaas @ 2018-12-04 10:40 ` Hanjie Lin 2018-12-04 12:00 ` Lorenzo Pieralisi 0 siblings, 1 reply; 9+ messages in thread From: Hanjie Lin @ 2018-12-04 10:40 UTC (permalink / raw) To: Bjorn Helgaas, Lorenzo Pieralisi Cc: Rob Herring, Jianxin Pan, linux-pci, Shawn Lin, linux-kernel, Yue Wang, Qiufang Dai, Jian Hu, Kevin Hilman, Philippe Ombredanne, Carlo Caione, Cyrille Pitchen, linux-amlogic, Gustavo Pimentel, Liang Yang, linux-arm-kernel, Jerome Brunet On 2018/12/4 6:57, Bjorn Helgaas wrote: > On Mon, Dec 03, 2018 at 04:41:50PM +0000, Lorenzo Pieralisi wrote: >> On Thu, Nov 22, 2018 at 04:53:54PM +0800, Hanjie Lin wrote: >> >> [...] >> >>> +static int meson_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, >>> + u32 *val) >>> +{ >>> + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); >>> + >>> + /* >>> + * there is a bug of MESON AXG pcie controller that software can not >>> + * programe PCI_CLASS_DEVICE register, so we must return a fake right >>> + * value to ensure driver could probe successfully. >>> + */ >>> + if (where == PCI_CLASS_REVISION) { >>> + *val = readl(pci->dbi_base + PCI_CLASS_REVISION); >>> + /* keep revision id */ >>> + *val &= PCI_CLASS_REVISION_MASK; >>> + *val |= PCI_CLASS_BRIDGE_PCI << 16; >>> + return PCIBIOS_SUCCESSFUL; >>> + } >> >> As I said before, this looks broken. If this code (or other drivers with >> the same broken assumptions, eg dwc/pcie-qcom.c) carries out a, say, >> byte sized config access of eg PCI_CLASS_DEVICE you will get junk out of >> it according to your comment above. >> >> I would like to pick Bjorn's brain on this to see what we can really do >> to fix this (and other) drivers. > > - Check to see whether you're reading anything in the 32-bit dword at > offset 0x08. > > - Do the 32-bit readl(). > > - Insert the correct Sub-Class and Base Class code (you also throw > away the Programming Interface; not sure why that is) > > - If you're reading something smaller than 32 bits, mask & shift as > needed. pci_bridge_emul_conf_read() does something similar that > you might be able to copy. > > Out of curiosity, what code depends on PCI_CLASS_BRIDGE_PCI? There > are several places in the kernel that currently depend on it, but I > think several of them *should* be checking dev->hdr_type to identify a > type 1 header instead. > > Bjorn > > . > Yes, it would be broken in particular scenes(eg: read 1 or 2 bytes from 0xa/PCI_CLASS_DEVICE) that I didn't considered. As your suggestion, I consider some code below may help this issue: 1, First call dw_pcie_read() help to read 1/2/4 bytes from register, request all other *size* bytes will return error and dw_pcie_read() will also check register alignment. 2, If dw_pcie_read() return success and *where* is 0x8/PCI_CLASS_DEVICE or 0xa/PCI_CLASS_REVISION, we may need to correct class code. As PCI_CLASS_REVISION is two-bytes register, so only when read 4 bytes from 0x8/PCI_CLASS_DEVICE or read 2 bytes from 0xa/PCI_CLASS_REVISION we should correct the class code. ps: read 1 byte from 0xa/PCI_CLASS_REVISION or 0xb will get incorrect value. static int meson_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); int ret; ret = dw_pcie_read(pci->dbi_base + where, size, val); if (ret != PCIBIOS_SUCCESSFUL) return ret; /* * there is a bug of MESON AXG pcie controller that software can not * programe PCI_CLASS_DEVICE register, so we must return a fake right * value to ensure driver could probe successfully. */ if (where == PCI_CLASS_REVISION && size == 4) *val = (PCI_CLASS_BRIDGE_PCI << 16) | (*val & 0xffff); else if (where == PCI_CLASS_DEVICE && size == 2) *val = PCI_CLASS_BRIDGE_PCI; return PCIBIOS_SUCCESSFUL; } 3, We must ensure class is PCI_CLASS_BRIDGE_PCI except right hdr_type, or pci_setup_device() will get failed: ... class = pci_class(dev); dev->revision = class & 0xff; dev->class = class >> 8; /* upper 3 bytes */ .... switch (dev->hdr_type) { /* header type */ ... case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ if (class != PCI_CLASS_BRIDGE_PCI) /* class must be PCI_CLASS_BRIDGE_PCI */ goto bad; thanks. hanjie _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver 2018-12-04 10:40 ` Hanjie Lin @ 2018-12-04 12:00 ` Lorenzo Pieralisi 2018-12-05 9:55 ` Hanjie Lin 0 siblings, 1 reply; 9+ messages in thread From: Lorenzo Pieralisi @ 2018-12-04 12:00 UTC (permalink / raw) To: Hanjie Lin Cc: Rob Herring, Jianxin Pan, Kevin Hilman, Shawn Lin, linux-pci, linux-kernel, Yue Wang, Qiufang Dai, Bjorn Helgaas, Philippe Ombredanne, Carlo Caione, Cyrille Pitchen, linux-amlogic, Gustavo Pimentel, Liang Yang, Jerome Brunet, linux-arm-kernel, Jian Hu On Tue, Dec 04, 2018 at 06:40:55PM +0800, Hanjie Lin wrote: > > > On 2018/12/4 6:57, Bjorn Helgaas wrote: > > On Mon, Dec 03, 2018 at 04:41:50PM +0000, Lorenzo Pieralisi wrote: > >> On Thu, Nov 22, 2018 at 04:53:54PM +0800, Hanjie Lin wrote: > >> > >> [...] > >> > >>> +static int meson_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, > >>> + u32 *val) > >>> +{ > >>> + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > >>> + > >>> + /* > >>> + * there is a bug of MESON AXG pcie controller that software can not > >>> + * programe PCI_CLASS_DEVICE register, so we must return a fake right > >>> + * value to ensure driver could probe successfully. > >>> + */ > >>> + if (where == PCI_CLASS_REVISION) { > >>> + *val = readl(pci->dbi_base + PCI_CLASS_REVISION); > >>> + /* keep revision id */ > >>> + *val &= PCI_CLASS_REVISION_MASK; > >>> + *val |= PCI_CLASS_BRIDGE_PCI << 16; > >>> + return PCIBIOS_SUCCESSFUL; > >>> + } > >> > >> As I said before, this looks broken. If this code (or other drivers with > >> the same broken assumptions, eg dwc/pcie-qcom.c) carries out a, say, > >> byte sized config access of eg PCI_CLASS_DEVICE you will get junk out of > >> it according to your comment above. > >> > >> I would like to pick Bjorn's brain on this to see what we can really do > >> to fix this (and other) drivers. > > > > - Check to see whether you're reading anything in the 32-bit dword at > > offset 0x08. > > > > - Do the 32-bit readl(). > > > > - Insert the correct Sub-Class and Base Class code (you also throw > > away the Programming Interface; not sure why that is) > > > > - If you're reading something smaller than 32 bits, mask & shift as > > needed. pci_bridge_emul_conf_read() does something similar that > > you might be able to copy. > > > > Out of curiosity, what code depends on PCI_CLASS_BRIDGE_PCI? There > > are several places in the kernel that currently depend on it, but I > > think several of them *should* be checking dev->hdr_type to identify a > > type 1 header instead. > > > > Bjorn > > > > . > > > > Yes, it would be broken in particular scenes(eg: read 1 or 2 bytes from 0xa/PCI_CLASS_DEVICE) > that I didn't considered. > > As your suggestion, I consider some code below may help this issue: > 1, First call dw_pcie_read() help to read 1/2/4 bytes from register, > request all other *size* bytes will return error and dw_pcie_read() > will also check register alignment. > > 2, If dw_pcie_read() return success and *where* is 0x8/PCI_CLASS_DEVICE or 0xa/PCI_CLASS_REVISION, > we may need to correct class code. > As PCI_CLASS_REVISION is two-bytes register, so only when read 4 bytes from 0x8/PCI_CLASS_DEVICE > or read 2 bytes from 0xa/PCI_CLASS_REVISION we should correct the class code. > > ps: read 1 byte from 0xa/PCI_CLASS_REVISION or 0xb will get incorrect value. You can fix this too. > static int meson_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, > u32 *val) > { > struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > int ret; > > ret = dw_pcie_read(pci->dbi_base + where, size, val); > if (ret != PCIBIOS_SUCCESSFUL) > return ret; > > /* > * there is a bug of MESON AXG pcie controller that software can not > * programe PCI_CLASS_DEVICE register, so we must return a fake right "There is a bug in the MESON AXG pcie controller whereby software cannot programme the PCI_CLASS_DEVICE register, so we must fabricate the return value in the config accessors." > * value to ensure driver could probe successfully. > */ > if (where == PCI_CLASS_REVISION && size == 4) > *val = (PCI_CLASS_BRIDGE_PCI << 16) | (*val & 0xffff); > else if (where == PCI_CLASS_DEVICE && size == 2) > *val = PCI_CLASS_BRIDGE_PCI; You can further filter it with (where & 0x1) == PCI_CLASS_DEVICE and handle the size accordingly, so that even a byte access would work, for completeness. Lorenzo > return PCIBIOS_SUCCESSFUL; > } > > 3, We must ensure class is PCI_CLASS_BRIDGE_PCI except right hdr_type, > or pci_setup_device() will get failed: > > ... > class = pci_class(dev); > dev->revision = class & 0xff; > dev->class = class >> 8; /* upper 3 bytes */ > .... > switch (dev->hdr_type) { /* header type */ > ... > case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ > if (class != PCI_CLASS_BRIDGE_PCI) /* class must be PCI_CLASS_BRIDGE_PCI */ > goto bad; > > > thanks. > > hanjie _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver 2018-12-04 12:00 ` Lorenzo Pieralisi @ 2018-12-05 9:55 ` Hanjie Lin 0 siblings, 0 replies; 9+ messages in thread From: Hanjie Lin @ 2018-12-05 9:55 UTC (permalink / raw) To: Lorenzo Pieralisi Cc: Rob Herring, Jianxin Pan, Kevin Hilman, Shawn Lin, linux-pci, linux-kernel, Yue Wang, Qiufang Dai, Bjorn Helgaas, Philippe Ombredanne, Carlo Caione, Cyrille Pitchen, linux-amlogic, Gustavo Pimentel, Liang Yang, Jerome Brunet, linux-arm-kernel, Jian Hu On 2018/12/4 20:00, Lorenzo Pieralisi wrote: > On Tue, Dec 04, 2018 at 06:40:55PM +0800, Hanjie Lin wrote: >> >> >> On 2018/12/4 6:57, Bjorn Helgaas wrote: >>> On Mon, Dec 03, 2018 at 04:41:50PM +0000, Lorenzo Pieralisi wrote: >>>> On Thu, Nov 22, 2018 at 04:53:54PM +0800, Hanjie Lin wrote: >>>> >>>> [...] >>>> >>>>> +static int meson_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, >>>>> + u32 *val) >>>>> +{ >>>>> + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); >>>>> + >>>>> + /* >>>>> + * there is a bug of MESON AXG pcie controller that software can not >>>>> + * programe PCI_CLASS_DEVICE register, so we must return a fake right >>>>> + * value to ensure driver could probe successfully. >>>>> + */ >>>>> + if (where == PCI_CLASS_REVISION) { >>>>> + *val = readl(pci->dbi_base + PCI_CLASS_REVISION); >>>>> + /* keep revision id */ >>>>> + *val &= PCI_CLASS_REVISION_MASK; >>>>> + *val |= PCI_CLASS_BRIDGE_PCI << 16; >>>>> + return PCIBIOS_SUCCESSFUL; >>>>> + } >>>> >>>> As I said before, this looks broken. If this code (or other drivers with >>>> the same broken assumptions, eg dwc/pcie-qcom.c) carries out a, say, >>>> byte sized config access of eg PCI_CLASS_DEVICE you will get junk out of >>>> it according to your comment above. >>>> >>>> I would like to pick Bjorn's brain on this to see what we can really do >>>> to fix this (and other) drivers. >>> >>> - Check to see whether you're reading anything in the 32-bit dword at >>> offset 0x08. >>> >>> - Do the 32-bit readl(). >>> >>> - Insert the correct Sub-Class and Base Class code (you also throw >>> away the Programming Interface; not sure why that is) >>> >>> - If you're reading something smaller than 32 bits, mask & shift as >>> needed. pci_bridge_emul_conf_read() does something similar that >>> you might be able to copy. >>> >>> Out of curiosity, what code depends on PCI_CLASS_BRIDGE_PCI? There >>> are several places in the kernel that currently depend on it, but I >>> think several of them *should* be checking dev->hdr_type to identify a >>> type 1 header instead. >>> >>> Bjorn >>> >>> . >>> >> >> Yes, it would be broken in particular scenes(eg: read 1 or 2 bytes from 0xa/PCI_CLASS_DEVICE) >> that I didn't considered. >> >> As your suggestion, I consider some code below may help this issue: >> 1, First call dw_pcie_read() help to read 1/2/4 bytes from register, >> request all other *size* bytes will return error and dw_pcie_read() >> will also check register alignment. >> >> 2, If dw_pcie_read() return success and *where* is 0x8/PCI_CLASS_DEVICE or 0xa/PCI_CLASS_REVISION, >> we may need to correct class code. >> As PCI_CLASS_REVISION is two-bytes register, so only when read 4 bytes from 0x8/PCI_CLASS_DEVICE >> or read 2 bytes from 0xa/PCI_CLASS_REVISION we should correct the class code. >> >> ps: read 1 byte from 0xa/PCI_CLASS_REVISION or 0xb will get incorrect value. > > You can fix this too. > >> static int meson_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, >> u32 *val) >> { >> struct dw_pcie *pci = to_dw_pcie_from_pp(pp); >> int ret; >> >> ret = dw_pcie_read(pci->dbi_base + where, size, val); >> if (ret != PCIBIOS_SUCCESSFUL) >> return ret; >> >> /* >> * there is a bug of MESON AXG pcie controller that software can not >> * programe PCI_CLASS_DEVICE register, so we must return a fake right > > "There is a bug in the MESON AXG pcie controller whereby software cannot > programme the PCI_CLASS_DEVICE register, so we must fabricate the return > value in the config accessors." > Ok, it's more clear :) >> * value to ensure driver could probe successfully. >> */ >> if (where == PCI_CLASS_REVISION && size == 4) >> *val = (PCI_CLASS_BRIDGE_PCI << 16) | (*val & 0xffff); >> else if (where == PCI_CLASS_DEVICE && size == 2) >> *val = PCI_CLASS_BRIDGE_PCI; > > You can further filter it with (where & 0x1) == PCI_CLASS_DEVICE > and handle the size accordingly, so that even a byte access would > work, for completeness. > > Lorenzo > Of course, I will add process to handle one-byte access from 0xa/0xb register in next-version. thanks. hanjie >> return PCIBIOS_SUCCESSFUL; >> } >> >> 3, We must ensure class is PCI_CLASS_BRIDGE_PCI except right hdr_type, >> or pci_setup_device() will get failed: >> >> ... >> class = pci_class(dev); >> dev->revision = class & 0xff; >> dev->class = class >> 8; /* upper 3 bytes */ >> .... >> switch (dev->hdr_type) { /* header type */ >> ... >> case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ >> if (class != PCI_CLASS_BRIDGE_PCI) /* class must be PCI_CLASS_BRIDGE_PCI */ >> goto bad; >> >> >> thanks. >> >> hanjie > > . > _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2018-12-05 9:55 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2018-11-22 8:53 [PATCH v6 0/2] add the Amlogic Meson PCIe controller driver Hanjie Lin 2018-11-22 8:53 ` [PATCH v6 1/2] dt-bindings: PCI: meson: add DT bindings for Amlogic Meson PCIe controller Hanjie Lin 2018-11-22 8:53 ` [PATCH v6 2/2] PCI: amlogic: Add the Amlogic Meson PCIe controller driver Hanjie Lin 2018-11-29 9:03 ` Dan Carpenter 2018-12-03 16:41 ` Lorenzo Pieralisi 2018-12-03 22:57 ` Bjorn Helgaas 2018-12-04 10:40 ` Hanjie Lin 2018-12-04 12:00 ` Lorenzo Pieralisi 2018-12-05 9:55 ` Hanjie Lin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).