785 lines
18 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for Kirin Phone SoCs
*
* Copyright (C) 2017 HiSilicon Electronics Co., Ltd.
* https://www.huawei.com
*
* Author: Xiaowei Song <songxiaowei@huawei.com>
*/
#include <linux/clk.h>
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_pci.h>
#include <linux/phy/phy.h>
#include <linux/pci.h>
#include <linux/pci_regs.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/resource.h>
#include <linux/types.h>
#include "pcie-designware.h"
#define to_kirin_pcie(x) dev_get_drvdata((x)->dev)
/* PCIe ELBI registers */
#define SOC_PCIECTRL_CTRL0_ADDR 0x000
#define SOC_PCIECTRL_CTRL1_ADDR 0x004
#define PCIE_ELBI_SLV_DBI_ENABLE (0x1 << 21)
/* info located in APB */
#define PCIE_APP_LTSSM_ENABLE 0x01c
#define PCIE_APB_PHY_STATUS0 0x400
#define PCIE_LINKUP_ENABLE (0x8020)
#define PCIE_LTSSM_ENABLE_BIT (0x1 << 11)
/* info located in sysctrl */
#define SCTRL_PCIE_CMOS_OFFSET 0x60
#define SCTRL_PCIE_CMOS_BIT 0x10
#define SCTRL_PCIE_ISO_OFFSET 0x44
#define SCTRL_PCIE_ISO_BIT 0x30
#define SCTRL_PCIE_HPCLK_OFFSET 0x190
#define SCTRL_PCIE_HPCLK_BIT 0x184000
#define SCTRL_PCIE_OE_OFFSET 0x14a
#define PCIE_DEBOUNCE_PARAM 0xF0F400
#define PCIE_OE_BYPASS (0x3 << 28)
/*
* Max number of connected PCI slots at an external PCI bridge
*
* This is used on HiKey 970, which has a PEX 8606 bridge with 4 connected
* lanes (lane 0 upstream, and the other three lanes, one connected to an
* in-board Ethernet adapter and the other two connected to M.2 and mini
* PCI slots.
*
* Each slot has a different clock source and uses a separate PERST# pin.
*/
#define MAX_PCI_SLOTS 3
enum pcie_kirin_phy_type {
PCIE_KIRIN_INTERNAL_PHY,
PCIE_KIRIN_EXTERNAL_PHY
};
struct kirin_pcie {
enum pcie_kirin_phy_type type;
struct dw_pcie *pci;
struct regmap *apb;
struct phy *phy;
void *phy_priv; /* only for PCIE_KIRIN_INTERNAL_PHY */
/* DWC PERST# */
struct gpio_desc *id_dwc_perst_gpio;
/* Per-slot PERST# */
int num_slots;
struct gpio_desc *id_reset_gpio[MAX_PCI_SLOTS];
const char *reset_names[MAX_PCI_SLOTS];
/* Per-slot clkreq */
int n_gpio_clkreq;
struct gpio_desc *id_clkreq_gpio[MAX_PCI_SLOTS];
const char *clkreq_names[MAX_PCI_SLOTS];
};
/*
* Kirin 960 PHY. Can't be split into a PHY driver without changing the
* DT schema.
*/
#define REF_CLK_FREQ 100000000
/* PHY info located in APB */
#define PCIE_APB_PHY_CTRL0 0x0
#define PCIE_APB_PHY_CTRL1 0x4
#define PCIE_APB_PHY_STATUS0 0x400
#define PIPE_CLK_STABLE BIT(19)
#define PHY_REF_PAD_BIT BIT(8)
#define PHY_PWR_DOWN_BIT BIT(22)
#define PHY_RST_ACK_BIT BIT(16)
/* peri_crg ctrl */
#define CRGCTRL_PCIE_ASSERT_OFFSET 0x88
#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000
/* Time for delay */
#define REF_2_PERST_MIN 21000
#define REF_2_PERST_MAX 25000
#define PERST_2_ACCESS_MIN 10000
#define PERST_2_ACCESS_MAX 12000
#define PIPE_CLK_WAIT_MIN 550
#define PIPE_CLK_WAIT_MAX 600
#define TIME_CMOS_MIN 100
#define TIME_CMOS_MAX 105
#define TIME_PHY_PD_MIN 10
#define TIME_PHY_PD_MAX 11
struct hi3660_pcie_phy {
struct device *dev;
void __iomem *base;
struct regmap *crgctrl;
struct regmap *sysctrl;
struct clk *apb_sys_clk;
struct clk *apb_phy_clk;
struct clk *phy_ref_clk;
struct clk *aclk;
struct clk *aux_clk;
};
/* Registers in PCIePHY */
static inline void kirin_apb_phy_writel(struct hi3660_pcie_phy *hi3660_pcie_phy,
u32 val, u32 reg)
{
writel(val, hi3660_pcie_phy->base + reg);
}
static inline u32 kirin_apb_phy_readl(struct hi3660_pcie_phy *hi3660_pcie_phy,
u32 reg)
{
return readl(hi3660_pcie_phy->base + reg);
}
static int hi3660_pcie_phy_get_clk(struct hi3660_pcie_phy *phy)
{
struct device *dev = phy->dev;
phy->phy_ref_clk = devm_clk_get(dev, "pcie_phy_ref");
if (IS_ERR(phy->phy_ref_clk))
return PTR_ERR(phy->phy_ref_clk);
phy->aux_clk = devm_clk_get(dev, "pcie_aux");
if (IS_ERR(phy->aux_clk))
return PTR_ERR(phy->aux_clk);
phy->apb_phy_clk = devm_clk_get(dev, "pcie_apb_phy");
if (IS_ERR(phy->apb_phy_clk))
return PTR_ERR(phy->apb_phy_clk);
phy->apb_sys_clk = devm_clk_get(dev, "pcie_apb_sys");
if (IS_ERR(phy->apb_sys_clk))
return PTR_ERR(phy->apb_sys_clk);
phy->aclk = devm_clk_get(dev, "pcie_aclk");
if (IS_ERR(phy->aclk))
return PTR_ERR(phy->aclk);
return 0;
}
static int hi3660_pcie_phy_get_resource(struct hi3660_pcie_phy *phy)
{
struct device *dev = phy->dev;
struct platform_device *pdev;
/* registers */
pdev = container_of(dev, struct platform_device, dev);
phy->base = devm_platform_ioremap_resource_byname(pdev, "phy");
if (IS_ERR(phy->base))
return PTR_ERR(phy->base);
phy->crgctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3660-crgctrl");
if (IS_ERR(phy->crgctrl))
return PTR_ERR(phy->crgctrl);
phy->sysctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3660-sctrl");
if (IS_ERR(phy->sysctrl))
return PTR_ERR(phy->sysctrl);
return 0;
}
static int hi3660_pcie_phy_start(struct hi3660_pcie_phy *phy)
{
struct device *dev = phy->dev;
u32 reg_val;
reg_val = kirin_apb_phy_readl(phy, PCIE_APB_PHY_CTRL1);
reg_val &= ~PHY_REF_PAD_BIT;
kirin_apb_phy_writel(phy, reg_val, PCIE_APB_PHY_CTRL1);
reg_val = kirin_apb_phy_readl(phy, PCIE_APB_PHY_CTRL0);
reg_val &= ~PHY_PWR_DOWN_BIT;
kirin_apb_phy_writel(phy, reg_val, PCIE_APB_PHY_CTRL0);
usleep_range(TIME_PHY_PD_MIN, TIME_PHY_PD_MAX);
reg_val = kirin_apb_phy_readl(phy, PCIE_APB_PHY_CTRL1);
reg_val &= ~PHY_RST_ACK_BIT;
kirin_apb_phy_writel(phy, reg_val, PCIE_APB_PHY_CTRL1);
usleep_range(PIPE_CLK_WAIT_MIN, PIPE_CLK_WAIT_MAX);
reg_val = kirin_apb_phy_readl(phy, PCIE_APB_PHY_STATUS0);
if (reg_val & PIPE_CLK_STABLE) {
dev_err(dev, "PIPE clk is not stable\n");
return -EINVAL;
}
return 0;
}
static void hi3660_pcie_phy_oe_enable(struct hi3660_pcie_phy *phy)
{
u32 val;
regmap_read(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, &val);
val |= PCIE_DEBOUNCE_PARAM;
val &= ~PCIE_OE_BYPASS;
regmap_write(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, val);
}
static int hi3660_pcie_phy_clk_ctrl(struct hi3660_pcie_phy *phy, bool enable)
{
int ret = 0;
if (!enable)
goto close_clk;
ret = clk_set_rate(phy->phy_ref_clk, REF_CLK_FREQ);
if (ret)
return ret;
ret = clk_prepare_enable(phy->phy_ref_clk);
if (ret)
return ret;
ret = clk_prepare_enable(phy->apb_sys_clk);
if (ret)
goto apb_sys_fail;
ret = clk_prepare_enable(phy->apb_phy_clk);
if (ret)
goto apb_phy_fail;
ret = clk_prepare_enable(phy->aclk);
if (ret)
goto aclk_fail;
ret = clk_prepare_enable(phy->aux_clk);
if (ret)
goto aux_clk_fail;
return 0;
close_clk:
clk_disable_unprepare(phy->aux_clk);
aux_clk_fail:
clk_disable_unprepare(phy->aclk);
aclk_fail:
clk_disable_unprepare(phy->apb_phy_clk);
apb_phy_fail:
clk_disable_unprepare(phy->apb_sys_clk);
apb_sys_fail:
clk_disable_unprepare(phy->phy_ref_clk);
return ret;
}
static int hi3660_pcie_phy_power_on(struct kirin_pcie *pcie)
{
struct hi3660_pcie_phy *phy = pcie->phy_priv;
int ret;
/* Power supply for Host */
regmap_write(phy->sysctrl,
SCTRL_PCIE_CMOS_OFFSET, SCTRL_PCIE_CMOS_BIT);
usleep_range(TIME_CMOS_MIN, TIME_CMOS_MAX);
hi3660_pcie_phy_oe_enable(phy);
ret = hi3660_pcie_phy_clk_ctrl(phy, true);
if (ret)
return ret;
/* ISO disable, PCIeCtrl, PHY assert and clk gate clear */
regmap_write(phy->sysctrl,
SCTRL_PCIE_ISO_OFFSET, SCTRL_PCIE_ISO_BIT);
regmap_write(phy->crgctrl,
CRGCTRL_PCIE_ASSERT_OFFSET, CRGCTRL_PCIE_ASSERT_BIT);
regmap_write(phy->sysctrl,
SCTRL_PCIE_HPCLK_OFFSET, SCTRL_PCIE_HPCLK_BIT);
ret = hi3660_pcie_phy_start(phy);
if (ret)
goto disable_clks;
return 0;
disable_clks:
hi3660_pcie_phy_clk_ctrl(phy, false);
return ret;
}
static int hi3660_pcie_phy_init(struct platform_device *pdev,
struct kirin_pcie *pcie)
{
struct device *dev = &pdev->dev;
struct hi3660_pcie_phy *phy;
int ret;
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
return -ENOMEM;
pcie->phy_priv = phy;
phy->dev = dev;
ret = hi3660_pcie_phy_get_clk(phy);
if (ret)
return ret;
return hi3660_pcie_phy_get_resource(phy);
}
static int hi3660_pcie_phy_power_off(struct kirin_pcie *pcie)
{
struct hi3660_pcie_phy *phy = pcie->phy_priv;
/* Drop power supply for Host */
regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, 0x00);
hi3660_pcie_phy_clk_ctrl(phy, false);
return 0;
}
/*
* The non-PHY part starts here
*/
static const struct regmap_config pcie_kirin_regmap_conf = {
.name = "kirin_pcie_apb",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static int kirin_pcie_get_gpio_enable(struct kirin_pcie *pcie,
struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int ret, i;
/* This is an optional property */
ret = gpiod_count(dev, "hisilicon,clken");
if (ret < 0)
return 0;
if (ret > MAX_PCI_SLOTS) {
dev_err(dev, "Too many GPIO clock requests!\n");
return -EINVAL;
}
pcie->n_gpio_clkreq = ret;
for (i = 0; i < pcie->n_gpio_clkreq; i++) {
pcie->id_clkreq_gpio[i] = devm_gpiod_get_index(dev,
"hisilicon,clken", i,
GPIOD_OUT_LOW);
if (IS_ERR(pcie->id_clkreq_gpio[i]))
return dev_err_probe(dev, PTR_ERR(pcie->id_clkreq_gpio[i]),
"unable to get a valid clken gpio\n");
pcie->clkreq_names[i] = devm_kasprintf(dev, GFP_KERNEL,
"pcie_clkreq_%d", i);
if (!pcie->clkreq_names[i])
return -ENOMEM;
gpiod_set_consumer_name(pcie->id_clkreq_gpio[i],
pcie->clkreq_names[i]);
}
return 0;
}
static int kirin_pcie_parse_port(struct kirin_pcie *pcie,
struct platform_device *pdev,
struct device_node *node)
{
struct device *dev = &pdev->dev;
int ret, slot, i;
for_each_available_child_of_node_scoped(node, parent) {
for_each_available_child_of_node_scoped(parent, child) {
i = pcie->num_slots;
pcie->id_reset_gpio[i] = devm_fwnode_gpiod_get_index(dev,
of_fwnode_handle(child),
"reset", 0, GPIOD_OUT_LOW,
NULL);
if (IS_ERR(pcie->id_reset_gpio[i])) {
if (PTR_ERR(pcie->id_reset_gpio[i]) == -ENOENT)
continue;
return dev_err_probe(dev, PTR_ERR(pcie->id_reset_gpio[i]),
"unable to get a valid reset gpio\n");
}
pcie->num_slots++;
if (pcie->num_slots > MAX_PCI_SLOTS) {
dev_err(dev, "Too many PCI slots!\n");
return -EINVAL;
}
ret = of_pci_get_devfn(child);
if (ret < 0) {
dev_err(dev, "failed to parse devfn: %d\n", ret);
return ret;
}
slot = PCI_SLOT(ret);
pcie->reset_names[i] = devm_kasprintf(dev, GFP_KERNEL,
"pcie_perst_%d",
slot);
if (!pcie->reset_names[i])
return -ENOMEM;
gpiod_set_consumer_name(pcie->id_reset_gpio[i],
pcie->reset_names[i]);
}
}
return 0;
}
static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie,
struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *child, *node = dev->of_node;
void __iomem *apb_base;
int ret;
apb_base = devm_platform_ioremap_resource_byname(pdev, "apb");
if (IS_ERR(apb_base))
return PTR_ERR(apb_base);
kirin_pcie->apb = devm_regmap_init_mmio(dev, apb_base,
&pcie_kirin_regmap_conf);
if (IS_ERR(kirin_pcie->apb))
return PTR_ERR(kirin_pcie->apb);
/* pcie internal PERST# gpio */
kirin_pcie->id_dwc_perst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(kirin_pcie->id_dwc_perst_gpio))
return dev_err_probe(dev, PTR_ERR(kirin_pcie->id_dwc_perst_gpio),
"unable to get a valid gpio pin\n");
gpiod_set_consumer_name(kirin_pcie->id_dwc_perst_gpio, "pcie_perst_bridge");
ret = kirin_pcie_get_gpio_enable(kirin_pcie, pdev);
if (ret)
return ret;
/* Parse OF children */
for_each_available_child_of_node(node, child) {
ret = kirin_pcie_parse_port(kirin_pcie, pdev, child);
if (ret)
goto put_node;
}
return 0;
put_node:
of_node_put(child);
return ret;
}
static void kirin_pcie_sideband_dbi_w_mode(struct kirin_pcie *kirin_pcie,
bool on)
{
u32 val;
regmap_read(kirin_pcie->apb, SOC_PCIECTRL_CTRL0_ADDR, &val);
if (on)
val = val | PCIE_ELBI_SLV_DBI_ENABLE;
else
val = val & ~PCIE_ELBI_SLV_DBI_ENABLE;
regmap_write(kirin_pcie->apb, SOC_PCIECTRL_CTRL0_ADDR, val);
}
static void kirin_pcie_sideband_dbi_r_mode(struct kirin_pcie *kirin_pcie,
bool on)
{
u32 val;
regmap_read(kirin_pcie->apb, SOC_PCIECTRL_CTRL1_ADDR, &val);
if (on)
val = val | PCIE_ELBI_SLV_DBI_ENABLE;
else
val = val & ~PCIE_ELBI_SLV_DBI_ENABLE;
regmap_write(kirin_pcie->apb, SOC_PCIECTRL_CTRL1_ADDR, val);
}
static int kirin_pcie_rd_own_conf(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(bus->sysdata);
if (PCI_SLOT(devfn))
return PCIBIOS_DEVICE_NOT_FOUND;
*val = dw_pcie_read_dbi(pci, where, size);
return PCIBIOS_SUCCESSFUL;
}
static int kirin_pcie_wr_own_conf(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(bus->sysdata);
if (PCI_SLOT(devfn))
return PCIBIOS_DEVICE_NOT_FOUND;
dw_pcie_write_dbi(pci, where, size, val);
return PCIBIOS_SUCCESSFUL;
}
static int kirin_pcie_add_bus(struct pci_bus *bus)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(bus->sysdata);
struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci);
int i, ret;
if (!kirin_pcie->num_slots)
return 0;
/* Send PERST# to each slot */
for (i = 0; i < kirin_pcie->num_slots; i++) {
ret = gpiod_direction_output_raw(kirin_pcie->id_reset_gpio[i], 1);
if (ret) {
dev_err(pci->dev, "PERST# %s error: %d\n",
kirin_pcie->reset_names[i], ret);
}
}
usleep_range(PERST_2_ACCESS_MIN, PERST_2_ACCESS_MAX);
return 0;
}
static struct pci_ops kirin_pci_ops = {
.read = kirin_pcie_rd_own_conf,
.write = kirin_pcie_wr_own_conf,
.add_bus = kirin_pcie_add_bus,
};
static u32 kirin_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base,
u32 reg, size_t size)
{
struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci);
u32 ret;
kirin_pcie_sideband_dbi_r_mode(kirin_pcie, true);
dw_pcie_read(base + reg, size, &ret);
kirin_pcie_sideband_dbi_r_mode(kirin_pcie, false);
return ret;
}
static void kirin_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base,
u32 reg, size_t size, u32 val)
{
struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci);
kirin_pcie_sideband_dbi_w_mode(kirin_pcie, true);
dw_pcie_write(base + reg, size, val);
kirin_pcie_sideband_dbi_w_mode(kirin_pcie, false);
}
static int kirin_pcie_link_up(struct dw_pcie *pci)
{
struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci);
u32 val;
regmap_read(kirin_pcie->apb, PCIE_APB_PHY_STATUS0, &val);
if ((val & PCIE_LINKUP_ENABLE) == PCIE_LINKUP_ENABLE)
return 1;
return 0;
}
PCI: dwc: Move link handling into common code All the DWC drivers do link setup and checks at roughly the same time. Let's use the existing .start_link() hook (currently only used in EP mode) and move the link handling to the core code. The behavior for a link down was inconsistent as some drivers would fail probe in that case while others succeed. Let's standardize this to succeed as there are usecases where devices (and the link) appear later even without hotplug. For example, a reconfigured FPGA device. Link: https://lore.kernel.org/r/20201105211159.1814485-11-robh@kernel.org Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Rob Herring <robh@kernel.org> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Acked-by: Jingoo Han <jingoohan1@gmail.com> Cc: Kishon Vijay Abraham I <kishon@ti.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Kukjin Kim <kgene@kernel.org> Cc: Krzysztof Kozlowski <krzk@kernel.org> Cc: Richard Zhu <hongxing.zhu@nxp.com> Cc: Lucas Stach <l.stach@pengutronix.de> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Murali Karicheri <m-karicheri2@ti.com> Cc: Yue Wang <yue.wang@Amlogic.com> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com> Cc: Jesper Nilsson <jesper.nilsson@axis.com> Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com> Cc: Xiaowei Song <songxiaowei@hisilicon.com> Cc: Binghui Wang <wangbinghui@hisilicon.com> Cc: Andy Gross <agross@kernel.org> Cc: Bjorn Andersson <bjorn.andersson@linaro.org> Cc: Stanimir Varbanov <svarbanov@mm-sol.com> Cc: Pratyush Anand <pratyush.anand@gmail.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: linux-omap@vger.kernel.org Cc: linux-samsung-soc@vger.kernel.org Cc: linux-amlogic@lists.infradead.org Cc: linux-arm-kernel@axis.com Cc: linux-arm-msm@vger.kernel.org Cc: linux-tegra@vger.kernel.org
2020-11-05 15:11:53 -06:00
static int kirin_pcie_start_link(struct dw_pcie *pci)
{
struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci);
/* assert LTSSM enable */
regmap_write(kirin_pcie->apb, PCIE_APP_LTSSM_ENABLE,
PCIE_LTSSM_ENABLE_BIT);
return 0;
}
static int kirin_pcie_host_init(struct dw_pcie_rp *pp)
{
pp->bridge->ops = &kirin_pci_ops;
return 0;
}
static const struct dw_pcie_ops kirin_dw_pcie_ops = {
.read_dbi = kirin_pcie_read_dbi,
.write_dbi = kirin_pcie_write_dbi,
.link_up = kirin_pcie_link_up,
PCI: dwc: Move link handling into common code All the DWC drivers do link setup and checks at roughly the same time. Let's use the existing .start_link() hook (currently only used in EP mode) and move the link handling to the core code. The behavior for a link down was inconsistent as some drivers would fail probe in that case while others succeed. Let's standardize this to succeed as there are usecases where devices (and the link) appear later even without hotplug. For example, a reconfigured FPGA device. Link: https://lore.kernel.org/r/20201105211159.1814485-11-robh@kernel.org Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Rob Herring <robh@kernel.org> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Acked-by: Jingoo Han <jingoohan1@gmail.com> Cc: Kishon Vijay Abraham I <kishon@ti.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Kukjin Kim <kgene@kernel.org> Cc: Krzysztof Kozlowski <krzk@kernel.org> Cc: Richard Zhu <hongxing.zhu@nxp.com> Cc: Lucas Stach <l.stach@pengutronix.de> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Murali Karicheri <m-karicheri2@ti.com> Cc: Yue Wang <yue.wang@Amlogic.com> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com> Cc: Jesper Nilsson <jesper.nilsson@axis.com> Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com> Cc: Xiaowei Song <songxiaowei@hisilicon.com> Cc: Binghui Wang <wangbinghui@hisilicon.com> Cc: Andy Gross <agross@kernel.org> Cc: Bjorn Andersson <bjorn.andersson@linaro.org> Cc: Stanimir Varbanov <svarbanov@mm-sol.com> Cc: Pratyush Anand <pratyush.anand@gmail.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: linux-omap@vger.kernel.org Cc: linux-samsung-soc@vger.kernel.org Cc: linux-amlogic@lists.infradead.org Cc: linux-arm-kernel@axis.com Cc: linux-arm-msm@vger.kernel.org Cc: linux-tegra@vger.kernel.org
2020-11-05 15:11:53 -06:00
.start_link = kirin_pcie_start_link,
};
static const struct dw_pcie_host_ops kirin_pcie_host_ops = {
.init = kirin_pcie_host_init,
};
static int kirin_pcie_power_off(struct kirin_pcie *kirin_pcie)
{
int i;
if (kirin_pcie->type == PCIE_KIRIN_INTERNAL_PHY)
return hi3660_pcie_phy_power_off(kirin_pcie);
for (i = 0; i < kirin_pcie->n_gpio_clkreq; i++)
gpiod_direction_output_raw(kirin_pcie->id_clkreq_gpio[i], 1);
phy_power_off(kirin_pcie->phy);
phy_exit(kirin_pcie->phy);
return 0;
}
static int kirin_pcie_power_on(struct platform_device *pdev,
struct kirin_pcie *kirin_pcie)
{
struct device *dev = &pdev->dev;
int ret;
if (kirin_pcie->type == PCIE_KIRIN_INTERNAL_PHY) {
ret = hi3660_pcie_phy_init(pdev, kirin_pcie);
if (ret)
return ret;
ret = hi3660_pcie_phy_power_on(kirin_pcie);
if (ret)
return ret;
} else {
kirin_pcie->phy = devm_of_phy_get(dev, dev->of_node, NULL);
if (IS_ERR(kirin_pcie->phy))
return PTR_ERR(kirin_pcie->phy);
ret = phy_init(kirin_pcie->phy);
if (ret)
goto err;
ret = phy_power_on(kirin_pcie->phy);
if (ret)
goto err;
}
/* perst assert Endpoint */
usleep_range(REF_2_PERST_MIN, REF_2_PERST_MAX);
ret = gpiod_direction_output_raw(kirin_pcie->id_dwc_perst_gpio, 1);
if (ret)
goto err;
usleep_range(PERST_2_ACCESS_MIN, PERST_2_ACCESS_MAX);
return 0;
err:
kirin_pcie_power_off(kirin_pcie);
return ret;
}
static void kirin_pcie_remove(struct platform_device *pdev)
{
struct kirin_pcie *kirin_pcie = platform_get_drvdata(pdev);
dw_pcie_host_deinit(&kirin_pcie->pci->pp);
kirin_pcie_power_off(kirin_pcie);
}
struct kirin_pcie_data {
enum pcie_kirin_phy_type phy_type;
};
static const struct kirin_pcie_data kirin_960_data = {
.phy_type = PCIE_KIRIN_INTERNAL_PHY,
};
static const struct kirin_pcie_data kirin_970_data = {
.phy_type = PCIE_KIRIN_EXTERNAL_PHY,
};
static const struct of_device_id kirin_pcie_match[] = {
{ .compatible = "hisilicon,kirin960-pcie", .data = &kirin_960_data },
{ .compatible = "hisilicon,kirin970-pcie", .data = &kirin_970_data },
{},
};
static int kirin_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct kirin_pcie_data *data;
struct kirin_pcie *kirin_pcie;
struct dw_pcie *pci;
int ret;
if (!dev->of_node) {
dev_err(dev, "NULL node\n");
return -EINVAL;
}
data = of_device_get_match_data(dev);
if (!data) {
dev_err(dev, "OF data missing\n");
return -EINVAL;
}
kirin_pcie = devm_kzalloc(dev, sizeof(struct kirin_pcie), GFP_KERNEL);
if (!kirin_pcie)
return -ENOMEM;
pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
if (!pci)
return -ENOMEM;
pci->dev = dev;
pci->ops = &kirin_dw_pcie_ops;
PCI: dwc: Remove unnecessary wrappers around dw_pcie_host_init() Many calls to dw_pcie_host_init() are in a wrapper function with nothing else now. Let's remove the pointless extra layer. Link: https://lore.kernel.org/r/20201105211159.1814485-14-robh@kernel.org Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Rob Herring <robh@kernel.org> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Richard Zhu <hongxing.zhu@nxp.com> Cc: Lucas Stach <l.stach@pengutronix.de> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Murali Karicheri <m-karicheri2@ti.com> Cc: Minghuan Lian <minghuan.Lian@nxp.com> Cc: Mingkai Hu <mingkai.hu@nxp.com> Cc: Roy Zang <roy.zang@nxp.com> Cc: Yue Wang <yue.wang@Amlogic.com> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Jonathan Chocron <jonnyc@amazon.com> Cc: Jesper Nilsson <jesper.nilsson@axis.com> Cc: Xiaowei Song <songxiaowei@hisilicon.com> Cc: Binghui Wang <wangbinghui@hisilicon.com> Cc: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-amlogic@lists.infradead.org Cc: linux-arm-kernel@axis.com
2020-11-05 15:11:56 -06:00
pci->pp.ops = &kirin_pcie_host_ops;
kirin_pcie->pci = pci;
kirin_pcie->type = data->phy_type;
ret = kirin_pcie_get_resource(kirin_pcie, pdev);
if (ret)
return ret;
platform_set_drvdata(pdev, kirin_pcie);
ret = kirin_pcie_power_on(pdev, kirin_pcie);
if (ret)
return ret;
PCI: dwc: Remove unnecessary wrappers around dw_pcie_host_init() Many calls to dw_pcie_host_init() are in a wrapper function with nothing else now. Let's remove the pointless extra layer. Link: https://lore.kernel.org/r/20201105211159.1814485-14-robh@kernel.org Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Rob Herring <robh@kernel.org> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Richard Zhu <hongxing.zhu@nxp.com> Cc: Lucas Stach <l.stach@pengutronix.de> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Murali Karicheri <m-karicheri2@ti.com> Cc: Minghuan Lian <minghuan.Lian@nxp.com> Cc: Mingkai Hu <mingkai.hu@nxp.com> Cc: Roy Zang <roy.zang@nxp.com> Cc: Yue Wang <yue.wang@Amlogic.com> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Jonathan Chocron <jonnyc@amazon.com> Cc: Jesper Nilsson <jesper.nilsson@axis.com> Cc: Xiaowei Song <songxiaowei@hisilicon.com> Cc: Binghui Wang <wangbinghui@hisilicon.com> Cc: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-amlogic@lists.infradead.org Cc: linux-arm-kernel@axis.com
2020-11-05 15:11:56 -06:00
return dw_pcie_host_init(&pci->pp);
}
static struct platform_driver kirin_pcie_driver = {
.probe = kirin_pcie_probe,
.remove_new = kirin_pcie_remove,
.driver = {
.name = "kirin-pcie",
.of_match_table = kirin_pcie_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(kirin_pcie_driver);
MODULE_DEVICE_TABLE(of, kirin_pcie_match);
MODULE_DESCRIPTION("PCIe host controller driver for Kirin Phone SoCs");
MODULE_AUTHOR("Xiaowei Song <songxiaowei@huawei.com>");
MODULE_LICENSE("GPL v2");