2021-08-12 08:05:19

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: [PATCH v11 00/11] Add support for Hikey 970 PCIe

The DT schema used by this series got merged at:

https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git/log/?h=dt/next

Version 11 was modified to reflect this patch:

https://lore.kernel.org/lkml/655e21422a14620ae2d55335eb72bcaa66f5384d.1628754620.git.mchehab+huawei@kernel.org/T/#u

Which contains a fix to the DT schema meant to make it produce the right sysfs
of_node devnodes.

The series should apply cleanly on the top of v5.14-rc1.

patch1 contains a PHY for Kirin 970 PCIe.

The remaining patches add support for Kirin 970 at the pcie-kirin driver, and
add the needed logic to compile it as module and to allow to dynamically
remove the driver in runtime.

Tested on HiKey970:

# lspci -D -PP
0000:00:00.0 PCI bridge: Huawei Technologies Co., Ltd. Device 3670 (rev 01)
0000:00:00.0/01:00.0 PCI bridge: PLX Technology, Inc. PEX 8606 6 Lane, 6 Port PCI Express Gen 2 (5.0 GT/s) Switch (rev ba)
0000:00:00.0/01:00.0/02:01.0 PCI bridge: PLX Technology, Inc. PEX 8606 6 Lane, 6 Port PCI Express Gen 2 (5.0 GT/s) Switch (rev ba)
0000:00:00.0/01:00.0/02:04.0 PCI bridge: PLX Technology, Inc. PEX 8606 6 Lane, 6 Port PCI Express Gen 2 (5.0 GT/s) Switch (rev ba)
0000:00:00.0/01:00.0/02:05.0 PCI bridge: PLX Technology, Inc. PEX 8606 6 Lane, 6 Port PCI Express Gen 2 (5.0 GT/s) Switch (rev ba)
0000:00:00.0/01:00.0/02:07.0 PCI bridge: PLX Technology, Inc. PEX 8606 6 Lane, 6 Port PCI Express Gen 2 (5.0 GT/s) Switch (rev ba)
0000:00:00.0/01:00.0/02:09.0 PCI bridge: PLX Technology, Inc. PEX 8606 6 Lane, 6 Port PCI Express Gen 2 (5.0 GT/s) Switch (rev ba)
0000:00:00.0/01:00.0/02:01.0/03:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd Device a809
0000:00:00.0/01:00.0/02:07.0/06:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (rev 07)

Tested on HiKey960:

# lspci -D
0000:00:00.0 PCI bridge: Huawei Technologies Co., Ltd. Device 3660 (rev 01)

---

v11:
- patch 5 changed to use the right PCIe topology
- all other patches are identical to v10.

v10:
- patch 1: dropped magic numbers from PHY driver
- patch 5: allow pcie child nodes without reset-gpios
- all other patches are identical to v9.

v9:
- Did some cleanups at patches 1 and 5

Mauro Carvalho Chehab (11):
phy: HiSilicon: Add driver for Kirin 970 PCIe PHY
PCI: kirin: Reorganize the PHY logic inside the driver
PCI: kirin: Add support for a PHY layer
PCI: kirin: Use regmap for APB registers
PCI: kirin: Add support for bridge slot DT schema
PCI: kirin: Add Kirin 970 compatible
PCI: kirin: Add MODULE_* macros
PCI: kirin: Allow building it as a module
PCI: kirin: Add power_off support for Kirin 960 PHY
PCI: kirin: fix poweroff sequence
PCI: kirin: Allow removing the driver

drivers/pci/controller/dwc/Kconfig | 2 +-
drivers/pci/controller/dwc/pcie-kirin.c | 644 ++++++++++++++----
drivers/phy/hisilicon/Kconfig | 10 +
drivers/phy/hisilicon/Makefile | 1 +
drivers/phy/hisilicon/phy-hi3670-pcie.c | 857 ++++++++++++++++++++++++
5 files changed, 1366 insertions(+), 148 deletions(-)
create mode 100644 drivers/phy/hisilicon/phy-hi3670-pcie.c

--
2.31.1



2021-08-12 08:05:19

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: [PATCH v11 07/11] PCI: kirin: Add MODULE_* macros

This driver misses the MODULE_* macros. Add them.

Signed-off-by: Mauro Carvalho Chehab <[email protected]>
---
drivers/pci/controller/dwc/pcie-kirin.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c
index 4f4c86b92353..d5c29a266756 100644
--- a/drivers/pci/controller/dwc/pcie-kirin.c
+++ b/drivers/pci/controller/dwc/pcie-kirin.c
@@ -804,3 +804,8 @@ static struct platform_driver kirin_pcie_driver = {
},
};
builtin_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 <[email protected]>");
+MODULE_LICENSE("GPL v2");
--
2.31.1

2021-08-12 08:06:01

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: [PATCH v11 09/11] PCI: kirin: Add power_off support for Kirin 960 PHY

In order to prepare for module unload, add a power_off method
for HiKey 960.

Signed-off-by: Mauro Carvalho Chehab <[email protected]>
---
drivers/pci/controller/dwc/pcie-kirin.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c
index d5c29a266756..b17a194cf78d 100644
--- a/drivers/pci/controller/dwc/pcie-kirin.c
+++ b/drivers/pci/controller/dwc/pcie-kirin.c
@@ -343,6 +343,18 @@ static int hi3660_pcie_phy_init(struct platform_device *pdev,
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
*/
@@ -560,7 +572,6 @@ static int kirin_pcie_add_bus(struct pci_bus *bus)
return 0;
}

-
static struct pci_ops kirin_pci_ops = {
.read = kirin_pcie_rd_own_conf,
.write = kirin_pcie_wr_own_conf,
@@ -714,8 +725,12 @@ static int kirin_pcie_power_on(struct platform_device *pdev,

return 0;
err:
- if (kirin_pcie->type != PCIE_KIRIN_INTERNAL_PHY)
+ if (kirin_pcie->type == PCIE_KIRIN_INTERNAL_PHY) {
+ hi3660_pcie_phy_power_off(kirin_pcie);
+ } else {
+ phy_power_off(kirin_pcie->phy);
phy_exit(kirin_pcie->phy);
+ }

return ret;
}
@@ -725,7 +740,7 @@ static int __exit kirin_pcie_remove(struct platform_device *pdev)
struct kirin_pcie *kirin_pcie = platform_get_drvdata(pdev);

if (kirin_pcie->type == PCIE_KIRIN_INTERNAL_PHY)
- return 0;
+ return hi3660_pcie_phy_power_off(kirin_pcie);

phy_power_off(kirin_pcie->phy);
phy_exit(kirin_pcie->phy);
--
2.31.1

2021-08-12 08:06:20

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: [PATCH v11 05/11] PCI: kirin: Add support for bridge slot DT schema

On HiKey970, there's a PEX 8606 PCI bridge on its PHY with
6 lanes. Only 4 lanes are connected:

lane 0 - connected to Kirin 970;
lane 4 - M.2 slot;
lane 5 - mini PCIe slot;
lane 6 - in-board Ethernet controller.

Each lane has its own PERST# gpio pin, and needs a clock
request.

Add support to parse a DT schema containing the above data.

Signed-off-by: Mauro Carvalho Chehab <[email protected]>
---
drivers/pci/controller/dwc/pcie-kirin.c | 264 +++++++++++++++++++++---
1 file changed, 232 insertions(+), 32 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c
index 0ea92a521e1c..5c97e91adbb0 100644
--- a/drivers/pci/controller/dwc/pcie-kirin.c
+++ b/drivers/pci/controller/dwc/pcie-kirin.c
@@ -52,6 +52,19 @@
#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 has
+ * 4 connected lanes (lane 0 upstream, and the other tree 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
@@ -64,6 +77,19 @@ struct kirin_pcie {
struct regmap *apb;
struct phy *phy;
void *phy_priv; /* only for PCIE_KIRIN_INTERNAL_PHY */
+
+ /* DWC PERST# */
+ int gpio_id_dwc_perst;
+
+ /* Per-slot PERST# */
+ int num_slots;
+ int gpio_id_reset[MAX_PCI_SLOTS];
+ const char *reset_names[MAX_PCI_SLOTS];
+
+ /* Per-slot clkreq */
+ int n_gpio_clkreq;
+ int gpio_id_clkreq[MAX_PCI_SLOTS];
+ const char *clkreq_names[MAX_PCI_SLOTS];
};

/*
@@ -87,7 +113,7 @@ struct kirin_pcie {
#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000

/* Time for delay */
-#define REF_2_PERST_MIN 20000
+#define REF_2_PERST_MIN 21000
#define REF_2_PERST_MAX 25000
#define PERST_2_ACCESS_MIN 10000
#define PERST_2_ACCESS_MAX 12000
@@ -108,7 +134,6 @@ struct hi3660_pcie_phy {
struct clk *phy_ref_clk;
struct clk *aclk;
struct clk *aux_clk;
- int gpio_id_reset;
};

/* Registers in PCIePHY */
@@ -171,16 +196,6 @@ static int hi3660_pcie_phy_get_resource(struct hi3660_pcie_phy *phy)
if (IS_ERR(phy->sysctrl))
return PTR_ERR(phy->sysctrl);

- /* gpios */
- phy->gpio_id_reset = of_get_named_gpio(dev->of_node,
- "reset-gpios", 0);
- if (phy->gpio_id_reset == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (!gpio_is_valid(phy->gpio_id_reset)) {
- dev_err(phy->dev, "unable to get a valid gpio pin\n");
- return -ENODEV;
- }
-
return 0;
}

@@ -297,15 +312,7 @@ static int hi3660_pcie_phy_power_on(struct kirin_pcie *pcie)
if (ret)
goto disable_clks;

- /* perst assert Endpoint */
- if (!gpio_request(phy->gpio_id_reset, "pcie_perst")) {
- usleep_range(REF_2_PERST_MIN, REF_2_PERST_MAX);
- ret = gpio_direction_output(phy->gpio_id_reset, 1);
- if (ret)
- goto disable_clks;
- usleep_range(PERST_2_ACCESS_MIN, PERST_2_ACCESS_MAX);
- return 0;
- }
+ return 0;

disable_clks:
hi3660_pcie_phy_clk_ctrl(phy, false);
@@ -347,11 +354,98 @@ static const struct regmap_config pcie_kirin_regmap_conf = {
.reg_stride = 4,
};

+static int kirin_pcie_get_gpio_enable(struct kirin_pcie *pcie,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ char name[32];
+ int ret, i;
+
+ /* This is an optional property */
+ ret = of_gpio_named_count(np, "hisilicon,clken-gpios");
+ 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->gpio_id_clkreq[i] = of_get_named_gpio(dev->of_node,
+ "hisilicon,clken-gpios", i);
+ if (pcie->gpio_id_clkreq[i] < 0)
+ return pcie->gpio_id_clkreq[i];
+
+ sprintf(name, "pcie_clkreq_%d", i);
+ pcie->clkreq_names[i] = devm_kstrdup_const(dev, name,
+ GFP_KERNEL);
+ if (!pcie->clkreq_names[i])
+ return -ENOMEM;
+ }
+
+ 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;
+ struct device_node *parent, *child;
+ int ret, slot, i;
+ char name[32];
+
+ for_each_available_child_of_node(node, parent) {
+ for_each_available_child_of_node(parent, child) {
+ i = pcie->num_slots;
+
+ pcie->gpio_id_reset[i] = of_get_named_gpio(child,
+ "reset-gpios", 0);
+ if (pcie->gpio_id_reset[i] < 0)
+ continue;
+
+ 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);
+ goto put_node;
+ }
+
+ slot = PCI_SLOT(ret);
+
+ sprintf(name, "pcie_perst_%d", slot);
+ pcie->reset_names[i] = devm_kstrdup_const(dev, name,
+ GFP_KERNEL);
+ if (!pcie->reset_names[i]) {
+ ret = -ENOMEM;
+ goto put_node;
+ }
+ }
+ }
+
+ return 0;
+
+put_node:
+ of_node_put(child);
+ return ret;
+}
+
static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie,
struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node, *child;
void __iomem *apb_base;
+ int ret;

apb_base = devm_platform_ioremap_resource_byname(pdev, "apb");
if (IS_ERR(apb_base))
@@ -362,7 +456,32 @@ static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie,
if (IS_ERR(kirin_pcie->apb))
return PTR_ERR(kirin_pcie->apb);

+ /* pcie internal PERST# gpio */
+ kirin_pcie->gpio_id_dwc_perst = of_get_named_gpio(dev->of_node,
+ "reset-gpios", 0);
+ if (kirin_pcie->gpio_id_dwc_perst == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (!gpio_is_valid(kirin_pcie->gpio_id_dwc_perst)) {
+ dev_err(dev, "unable to get a valid gpio pin\n");
+ return -ENODEV;
+ }
+
+ 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,
@@ -419,9 +538,33 @@ static int kirin_pcie_wr_own_conf(struct pci_bus *bus, unsigned int devfn,
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 = gpio_direction_output(kirin_pcie->gpio_id_reset[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,
@@ -477,6 +620,44 @@ static int kirin_pcie_host_init(struct pcie_port *pp)
return 0;
}

+static int kirin_pcie_gpio_request(struct kirin_pcie *kirin_pcie,
+ struct device *dev)
+{
+ int ret, i;
+
+ for (i = 0; i < kirin_pcie->num_slots; i++) {
+ if (!gpio_is_valid(kirin_pcie->gpio_id_reset[i])) {
+ dev_err(dev, "unable to get a valid %s gpio\n",
+ kirin_pcie->reset_names[i]);
+ return -ENODEV;
+ }
+
+ ret = devm_gpio_request(dev, kirin_pcie->gpio_id_reset[i],
+ kirin_pcie->reset_names[i]);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < kirin_pcie->n_gpio_clkreq; i++) {
+ if (!gpio_is_valid(kirin_pcie->gpio_id_clkreq[i])) {
+ dev_err(dev, "unable to get a valid %s gpio\n",
+ kirin_pcie->clkreq_names[i]);
+ return -ENODEV;
+ }
+
+ ret = devm_gpio_request(dev, kirin_pcie->gpio_id_clkreq[i],
+ kirin_pcie->clkreq_names[i]);
+ if (ret)
+ return ret;
+
+ ret = gpio_direction_output(kirin_pcie->gpio_id_clkreq[i], 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static const struct dw_pcie_ops kirin_dw_pcie_ops = {
.read_dbi = kirin_pcie_read_dbi,
.write_dbi = kirin_pcie_write_dbi,
@@ -499,24 +680,43 @@ static int kirin_pcie_power_on(struct platform_device *pdev,
if (ret)
return ret;

- return hi3660_pcie_phy_power_on(kirin_pcie);
+ 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 = kirin_pcie_gpio_request(kirin_pcie, dev);
+ if (ret)
+ return ret;
+
+ ret = phy_init(kirin_pcie->phy);
+ if (ret)
+ goto err;
+
+ ret = phy_power_on(kirin_pcie->phy);
+ if (ret)
+ goto err;
}

- kirin_pcie->phy = devm_of_phy_get(dev, dev->of_node, NULL);
- if (IS_ERR(kirin_pcie->phy))
- return PTR_ERR(kirin_pcie->phy);
+ /* perst assert Endpoint */
+ usleep_range(REF_2_PERST_MIN, REF_2_PERST_MAX);

- ret = phy_init(kirin_pcie->phy);
- if (ret)
- goto err;
+ if (!gpio_request(kirin_pcie->gpio_id_dwc_perst, "pcie_perst_bridge")) {
+ ret = gpio_direction_output(kirin_pcie->gpio_id_dwc_perst, 1);
+ if (ret)
+ goto err;
+ }

- ret = phy_power_on(kirin_pcie->phy);
- if (ret)
- goto err;
+ usleep_range(PERST_2_ACCESS_MIN, PERST_2_ACCESS_MAX);

return 0;
err:
- phy_exit(kirin_pcie->phy);
+ if (kirin_pcie->type != PCIE_KIRIN_INTERNAL_PHY)
+ phy_exit(kirin_pcie->phy);
+
return ret;
}

--
2.31.1

2021-08-12 08:06:31

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: [PATCH v11 11/11] PCI: kirin: Allow removing the driver

Now that everything is in place at the poweroff sequence,
this driver can use module_platform_driver(), which allows
it to be removed.

Signed-off-by: Mauro Carvalho Chehab <[email protected]>
---
drivers/pci/controller/dwc/pcie-kirin.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c
index ffc63d12f8ed..efab2af093b6 100644
--- a/drivers/pci/controller/dwc/pcie-kirin.c
+++ b/drivers/pci/controller/dwc/pcie-kirin.c
@@ -828,7 +828,7 @@ static struct platform_driver kirin_pcie_driver = {
.suppress_bind_attrs = true,
},
};
-builtin_platform_driver(kirin_pcie_driver);
+module_platform_driver(kirin_pcie_driver);

MODULE_DEVICE_TABLE(of, kirin_pcie_match);
MODULE_DESCRIPTION("PCIe host controller driver for Kirin Phone SoCs");
--
2.31.1

2021-08-12 08:06:47

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: [PATCH v11 02/11] PCI: kirin: Reorganize the PHY logic inside the driver

The pcie-kirin PCIe driver contains internally a PHY interface for
Kirin 960.

As the next patches will add support for using an external PHY
driver, reorganize the driver in a way that the PHY part
will be self-contained.

This could be moved to a separate PHY driver, but a change
like that would mean a non-backward-compatible DT schema
change.

Signed-off-by: Mauro Carvalho Chehab <[email protected]>
---
drivers/pci/controller/dwc/pcie-kirin.c | 298 ++++++++++++++----------
1 file changed, 173 insertions(+), 125 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c
index 026fd1e42a55..b4063a3434df 100644
--- a/drivers/pci/controller/dwc/pcie-kirin.c
+++ b/drivers/pci/controller/dwc/pcie-kirin.c
@@ -28,26 +28,16 @@

#define to_kirin_pcie(x) dev_get_drvdata((x)->dev)

-#define REF_CLK_FREQ 100000000
-
/* PCIe ELBI registers */
#define SOC_PCIECTRL_CTRL0_ADDR 0x000
#define SOC_PCIECTRL_CTRL1_ADDR 0x004
-#define SOC_PCIEPHY_CTRL2_ADDR 0x008
-#define SOC_PCIEPHY_CTRL3_ADDR 0x00c
#define PCIE_ELBI_SLV_DBI_ENABLE (0x1 << 21)

/* info located in APB */
#define PCIE_APP_LTSSM_ENABLE 0x01c
-#define PCIE_APB_PHY_CTRL0 0x0
-#define PCIE_APB_PHY_CTRL1 0x4
#define PCIE_APB_PHY_STATUS0 0x400
#define PCIE_LINKUP_ENABLE (0x8020)
#define PCIE_LTSSM_ENABLE_BIT (0x1 << 11)
-#define PIPE_CLK_STABLE (0x1 << 19)
-#define PHY_REF_PAD_BIT (0x1 << 8)
-#define PHY_PWR_DOWN_BIT (0x1 << 22)
-#define PHY_RST_ACK_BIT (0x1 << 16)

/* info located in sysctrl */
#define SCTRL_PCIE_CMOS_OFFSET 0x60
@@ -60,6 +50,29 @@
#define PCIE_DEBOUNCE_PARAM 0xF0F400
#define PCIE_OE_BYPASS (0x3 << 28)

+struct kirin_pcie {
+ struct dw_pcie *pci;
+ struct phy *phy;
+ void __iomem *apb_base;
+ void *phy_priv; /* Needed for Kirin 960 PHY */
+};
+
+/*
+ * 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
@@ -69,8 +82,6 @@
#define REF_2_PERST_MAX 25000
#define PERST_2_ACCESS_MIN 10000
#define PERST_2_ACCESS_MAX 12000
-#define LINK_WAIT_MIN 900
-#define LINK_WAIT_MAX 1000
#define PIPE_CLK_WAIT_MIN 550
#define PIPE_CLK_WAIT_MAX 600
#define TIME_CMOS_MIN 100
@@ -78,118 +89,112 @@
#define TIME_PHY_PD_MIN 10
#define TIME_PHY_PD_MAX 11

-struct kirin_pcie {
- struct dw_pcie *pci;
- void __iomem *apb_base;
- void __iomem *phy_base;
+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 *pcie_aclk;
- struct clk *pcie_aux_clk;
+ struct clk *aclk;
+ struct clk *aux_clk;
int gpio_id_reset;
};

-/* Registers in PCIeCTRL */
-static inline void kirin_apb_ctrl_writel(struct kirin_pcie *kirin_pcie,
- u32 val, u32 reg)
-{
- writel(val, kirin_pcie->apb_base + reg);
-}
-
-static inline u32 kirin_apb_ctrl_readl(struct kirin_pcie *kirin_pcie, u32 reg)
-{
- return readl(kirin_pcie->apb_base + reg);
-}
-
/* Registers in PCIePHY */
-static inline void kirin_apb_phy_writel(struct kirin_pcie *kirin_pcie,
+static inline void kirin_apb_phy_writel(struct hi3660_pcie_phy *hi3660_pcie_phy,
u32 val, u32 reg)
{
- writel(val, kirin_pcie->phy_base + reg);
+ writel(val, hi3660_pcie_phy->base + reg);
}

-static inline u32 kirin_apb_phy_readl(struct kirin_pcie *kirin_pcie, u32 reg)
+static inline u32 kirin_apb_phy_readl(struct hi3660_pcie_phy *hi3660_pcie_phy,
+ u32 reg)
{
- return readl(kirin_pcie->phy_base + reg);
+ return readl(hi3660_pcie_phy->base + reg);
}

-static long kirin_pcie_get_clk(struct kirin_pcie *kirin_pcie,
- struct platform_device *pdev)
+static int hi3660_pcie_phy_get_clk(struct hi3660_pcie_phy *phy)
{
- struct device *dev = &pdev->dev;
+ struct device *dev = phy->dev;

- kirin_pcie->phy_ref_clk = devm_clk_get(dev, "pcie_phy_ref");
- if (IS_ERR(kirin_pcie->phy_ref_clk))
- return PTR_ERR(kirin_pcie->phy_ref_clk);
+ 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);

- kirin_pcie->pcie_aux_clk = devm_clk_get(dev, "pcie_aux");
- if (IS_ERR(kirin_pcie->pcie_aux_clk))
- return PTR_ERR(kirin_pcie->pcie_aux_clk);
+ phy->aux_clk = devm_clk_get(dev, "pcie_aux");
+ if (IS_ERR(phy->aux_clk))
+ return PTR_ERR(phy->aux_clk);

- kirin_pcie->apb_phy_clk = devm_clk_get(dev, "pcie_apb_phy");
- if (IS_ERR(kirin_pcie->apb_phy_clk))
- return PTR_ERR(kirin_pcie->apb_phy_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);

- kirin_pcie->apb_sys_clk = devm_clk_get(dev, "pcie_apb_sys");
- if (IS_ERR(kirin_pcie->apb_sys_clk))
- return PTR_ERR(kirin_pcie->apb_sys_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);

- kirin_pcie->pcie_aclk = devm_clk_get(dev, "pcie_aclk");
- if (IS_ERR(kirin_pcie->pcie_aclk))
- return PTR_ERR(kirin_pcie->pcie_aclk);
+ phy->aclk = devm_clk_get(dev, "pcie_aclk");
+ if (IS_ERR(phy->aclk))
+ return PTR_ERR(phy->aclk);

return 0;
}

-static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie,
- struct platform_device *pdev)
+static int hi3660_pcie_phy_get_resource(struct hi3660_pcie_phy *phy)
{
- kirin_pcie->apb_base =
- devm_platform_ioremap_resource_byname(pdev, "apb");
- if (IS_ERR(kirin_pcie->apb_base))
- return PTR_ERR(kirin_pcie->apb_base);
-
- kirin_pcie->phy_base =
- devm_platform_ioremap_resource_byname(pdev, "phy");
- if (IS_ERR(kirin_pcie->phy_base))
- return PTR_ERR(kirin_pcie->phy_base);
-
- kirin_pcie->crgctrl =
- syscon_regmap_lookup_by_compatible("hisilicon,hi3660-crgctrl");
- if (IS_ERR(kirin_pcie->crgctrl))
- return PTR_ERR(kirin_pcie->crgctrl);
-
- kirin_pcie->sysctrl =
- syscon_regmap_lookup_by_compatible("hisilicon,hi3660-sctrl");
- if (IS_ERR(kirin_pcie->sysctrl))
- return PTR_ERR(kirin_pcie->sysctrl);
+ 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);
+
+ /* gpios */
+ phy->gpio_id_reset = of_get_named_gpio(dev->of_node,
+ "reset-gpios", 0);
+ if (phy->gpio_id_reset == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (!gpio_is_valid(phy->gpio_id_reset)) {
+ dev_err(phy->dev, "unable to get a valid gpio pin\n");
+ return -ENODEV;
+ }

return 0;
}

-static int kirin_pcie_phy_init(struct kirin_pcie *kirin_pcie)
+static int hi3660_pcie_phy_start(struct hi3660_pcie_phy *phy)
{
- struct device *dev = kirin_pcie->pci->dev;
+ struct device *dev = phy->dev;
u32 reg_val;

- reg_val = kirin_apb_phy_readl(kirin_pcie, PCIE_APB_PHY_CTRL1);
+ reg_val = kirin_apb_phy_readl(phy, PCIE_APB_PHY_CTRL1);
reg_val &= ~PHY_REF_PAD_BIT;
- kirin_apb_phy_writel(kirin_pcie, reg_val, PCIE_APB_PHY_CTRL1);
+ kirin_apb_phy_writel(phy, reg_val, PCIE_APB_PHY_CTRL1);

- reg_val = kirin_apb_phy_readl(kirin_pcie, PCIE_APB_PHY_CTRL0);
+ reg_val = kirin_apb_phy_readl(phy, PCIE_APB_PHY_CTRL0);
reg_val &= ~PHY_PWR_DOWN_BIT;
- kirin_apb_phy_writel(kirin_pcie, reg_val, PCIE_APB_PHY_CTRL0);
+ 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(kirin_pcie, PCIE_APB_PHY_CTRL1);
+ reg_val = kirin_apb_phy_readl(phy, PCIE_APB_PHY_CTRL1);
reg_val &= ~PHY_RST_ACK_BIT;
- kirin_apb_phy_writel(kirin_pcie, reg_val, PCIE_APB_PHY_CTRL1);
+ 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(kirin_pcie, PCIE_APB_PHY_STATUS0);
+ 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;
@@ -198,105 +203,157 @@ static int kirin_pcie_phy_init(struct kirin_pcie *kirin_pcie)
return 0;
}

-static void kirin_pcie_oe_enable(struct kirin_pcie *kirin_pcie)
+static void hi3660_pcie_phy_oe_enable(struct hi3660_pcie_phy *phy)
{
u32 val;

- regmap_read(kirin_pcie->sysctrl, SCTRL_PCIE_OE_OFFSET, &val);
+ regmap_read(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, &val);
val |= PCIE_DEBOUNCE_PARAM;
val &= ~PCIE_OE_BYPASS;
- regmap_write(kirin_pcie->sysctrl, SCTRL_PCIE_OE_OFFSET, val);
+ regmap_write(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, val);
}

-static int kirin_pcie_clk_ctrl(struct kirin_pcie *kirin_pcie, bool enable)
+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(kirin_pcie->phy_ref_clk, REF_CLK_FREQ);
+ ret = clk_set_rate(phy->phy_ref_clk, REF_CLK_FREQ);
if (ret)
return ret;

- ret = clk_prepare_enable(kirin_pcie->phy_ref_clk);
+ ret = clk_prepare_enable(phy->phy_ref_clk);
if (ret)
return ret;

- ret = clk_prepare_enable(kirin_pcie->apb_sys_clk);
+ ret = clk_prepare_enable(phy->apb_sys_clk);
if (ret)
goto apb_sys_fail;

- ret = clk_prepare_enable(kirin_pcie->apb_phy_clk);
+ ret = clk_prepare_enable(phy->apb_phy_clk);
if (ret)
goto apb_phy_fail;

- ret = clk_prepare_enable(kirin_pcie->pcie_aclk);
+ ret = clk_prepare_enable(phy->aclk);
if (ret)
goto aclk_fail;

- ret = clk_prepare_enable(kirin_pcie->pcie_aux_clk);
+ ret = clk_prepare_enable(phy->aux_clk);
if (ret)
goto aux_clk_fail;

return 0;

close_clk:
- clk_disable_unprepare(kirin_pcie->pcie_aux_clk);
+ clk_disable_unprepare(phy->aux_clk);
aux_clk_fail:
- clk_disable_unprepare(kirin_pcie->pcie_aclk);
+ clk_disable_unprepare(phy->aclk);
aclk_fail:
- clk_disable_unprepare(kirin_pcie->apb_phy_clk);
+ clk_disable_unprepare(phy->apb_phy_clk);
apb_phy_fail:
- clk_disable_unprepare(kirin_pcie->apb_sys_clk);
+ clk_disable_unprepare(phy->apb_sys_clk);
apb_sys_fail:
- clk_disable_unprepare(kirin_pcie->phy_ref_clk);
+ clk_disable_unprepare(phy->phy_ref_clk);

return ret;
}

-static int kirin_pcie_power_on(struct kirin_pcie *kirin_pcie)
+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(kirin_pcie->sysctrl,
+ regmap_write(phy->sysctrl,
SCTRL_PCIE_CMOS_OFFSET, SCTRL_PCIE_CMOS_BIT);
usleep_range(TIME_CMOS_MIN, TIME_CMOS_MAX);
- kirin_pcie_oe_enable(kirin_pcie);

- ret = kirin_pcie_clk_ctrl(kirin_pcie, true);
+ 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(kirin_pcie->sysctrl,
+ regmap_write(phy->sysctrl,
SCTRL_PCIE_ISO_OFFSET, SCTRL_PCIE_ISO_BIT);
- regmap_write(kirin_pcie->crgctrl,
+ regmap_write(phy->crgctrl,
CRGCTRL_PCIE_ASSERT_OFFSET, CRGCTRL_PCIE_ASSERT_BIT);
- regmap_write(kirin_pcie->sysctrl,
+ regmap_write(phy->sysctrl,
SCTRL_PCIE_HPCLK_OFFSET, SCTRL_PCIE_HPCLK_BIT);

- ret = kirin_pcie_phy_init(kirin_pcie);
+ ret = hi3660_pcie_phy_start(phy);
if (ret)
- goto close_clk;
+ goto disable_clks;

/* perst assert Endpoint */
- if (!gpio_request(kirin_pcie->gpio_id_reset, "pcie_perst")) {
+ if (!gpio_request(phy->gpio_id_reset, "pcie_perst")) {
usleep_range(REF_2_PERST_MIN, REF_2_PERST_MAX);
- ret = gpio_direction_output(kirin_pcie->gpio_id_reset, 1);
+ ret = gpio_direction_output(phy->gpio_id_reset, 1);
if (ret)
- goto close_clk;
+ goto disable_clks;
usleep_range(PERST_2_ACCESS_MIN, PERST_2_ACCESS_MAX);
-
return 0;
}

-close_clk:
- kirin_pcie_clk_ctrl(kirin_pcie, false);
+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;
+
+ /* registers */
+ pdev = container_of(dev, struct platform_device, dev);
+
+ ret = hi3660_pcie_phy_get_clk(phy);
+ if (ret)
+ return ret;
+
+ return hi3660_pcie_phy_get_resource(phy);
+}
+
+/*
+ * The non-PHY part starts here
+ */
+
+/* Registers in PCIeCTRL */
+static inline void kirin_apb_ctrl_writel(struct kirin_pcie *kirin_pcie,
+ u32 val, u32 reg)
+{
+ writel(val, kirin_pcie->apb_base + reg);
+}
+
+static inline u32 kirin_apb_ctrl_readl(struct kirin_pcie *kirin_pcie, u32 reg)
+{
+ return readl(kirin_pcie->apb_base + reg);
+}
+
+static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie,
+ struct platform_device *pdev)
+{
+ kirin_pcie->apb_base =
+ devm_platform_ioremap_resource_byname(pdev, "apb");
+ if (IS_ERR(kirin_pcie->apb_base))
+ return PTR_ERR(kirin_pcie->apb_base);
+
+ return 0;
+}
+
static void kirin_pcie_sideband_dbi_w_mode(struct kirin_pcie *kirin_pcie,
bool on)
{
@@ -444,7 +501,7 @@ static int kirin_pcie_probe(struct platform_device *pdev)
pci->pp.ops = &kirin_pcie_host_ops;
kirin_pcie->pci = pci;

- ret = kirin_pcie_get_clk(kirin_pcie, pdev);
+ ret = hi3660_pcie_phy_init(pdev, kirin_pcie);
if (ret)
return ret;

@@ -452,16 +509,7 @@ static int kirin_pcie_probe(struct platform_device *pdev)
if (ret)
return ret;

- kirin_pcie->gpio_id_reset = of_get_named_gpio(dev->of_node,
- "reset-gpios", 0);
- if (kirin_pcie->gpio_id_reset == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (!gpio_is_valid(kirin_pcie->gpio_id_reset)) {
- dev_err(dev, "unable to get a valid gpio pin\n");
- return -ENODEV;
- }
-
- ret = kirin_pcie_power_on(kirin_pcie);
+ ret = hi3660_pcie_phy_power_on(kirin_pcie);
if (ret)
return ret;

@@ -479,8 +527,8 @@ static struct platform_driver kirin_pcie_driver = {
.probe = kirin_pcie_probe,
.driver = {
.name = "kirin-pcie",
- .of_match_table = kirin_pcie_match,
- .suppress_bind_attrs = true,
+ .of_match_table = kirin_pcie_match,
+ .suppress_bind_attrs = true,
},
};
builtin_platform_driver(kirin_pcie_driver);
--
2.31.1

2021-08-12 08:06:50

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: [PATCH v11 01/11] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

The Kirin 970 PHY is somewhat similar to the Kirin 960, but it
does a lot more. Add the needed bits for PCIe to start working on
HiKey 970 boards.

Co-developed-by: Manivannan Sadhasivam <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
Signed-off-by: Mauro Carvalho Chehab <[email protected]>
---
drivers/phy/hisilicon/Kconfig | 10 +
drivers/phy/hisilicon/Makefile | 1 +
drivers/phy/hisilicon/phy-hi3670-pcie.c | 857 ++++++++++++++++++++++++
3 files changed, 868 insertions(+)
create mode 100644 drivers/phy/hisilicon/phy-hi3670-pcie.c

diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig
index 4d008cfc279c..d3b92c288554 100644
--- a/drivers/phy/hisilicon/Kconfig
+++ b/drivers/phy/hisilicon/Kconfig
@@ -33,6 +33,16 @@ config PHY_HI3670_USB

To compile this driver as a module, choose M here.

+config PHY_HI3670_PCIE
+ tristate "hi3670 PCIe PHY support"
+ depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+ select GENERIC_PHY
+ select MFD_SYSCON
+ help
+ Enable this to support the HiSilicon hi3670 PCIe PHY.
+
+ To compile this driver as a module, choose M here.
+
config PHY_HISTB_COMBPHY
tristate "HiSilicon STB SoCs COMBPHY support"
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile
index 51729868145b..4029d3813b1e 100644
--- a/drivers/phy/hisilicon/Makefile
+++ b/drivers/phy/hisilicon/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
obj-$(CONFIG_PHY_HI3660_USB) += phy-hi3660-usb3.o
obj-$(CONFIG_PHY_HI3670_USB) += phy-hi3670-usb3.o
+obj-$(CONFIG_PHY_HI3670_PCIE) += phy-hi3670-pcie.o
obj-$(CONFIG_PHY_HISTB_COMBPHY) += phy-histb-combphy.o
obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
diff --git a/drivers/phy/hisilicon/phy-hi3670-pcie.c b/drivers/phy/hisilicon/phy-hi3670-pcie.c
new file mode 100644
index 000000000000..2d2c35ed7652
--- /dev/null
+++ b/drivers/phy/hisilicon/phy-hi3670-pcie.c
@@ -0,0 +1,857 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe phy driver for Kirin 970
+ *
+ * Copyright (C) 2017 HiSilicon Electronics Co., Ltd.
+ * https://www.huawei.com
+ * Copyright (C) 2021 Huawei Technologies Co., Ltd.
+ * https://www.huawei.com
+ *
+ * Authors:
+ * Mauro Carvalho Chehab <[email protected]>
+ * Manivannan Sadhasivam <[email protected]>
+ *
+ * Based on:
+ * https://lore.kernel.org/lkml/4c9d6581478aa966698758c0420933f5defab4dd.1612335031.git.mchehab+huawei@kernel.org/
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define AXI_CLK_FREQ 207500000
+#define REF_CLK_FREQ 100000000
+
+/* PCIe CTRL registers */
+#define SOC_PCIECTRL_CTRL0_ADDR 0x000
+#define SOC_PCIECTRL_CTRL1_ADDR 0x004
+#define SOC_PCIECTRL_CTRL7_ADDR 0x01c
+#define SOC_PCIECTRL_CTRL12_ADDR 0x030
+#define SOC_PCIECTRL_CTRL20_ADDR 0x050
+#define SOC_PCIECTRL_CTRL21_ADDR 0x054
+#define SOC_PCIECTRL_STATE0_ADDR 0x400
+
+#define PCIE_OUTPUT_PULL_BITS GENMASK(3, 0)
+#define SOC_PCIECTRL_CTRL20_2P_MEM_CTRL 0x02605550
+#define SOC_PCIECTRL_CTRL21_DEFAULT 0x20000070
+#define PCIE_PULL_UP_SYS_AUX_PWR_DET BIT(10)
+#define PCIE_OUTPUT_PULL_DOWN BIT(1)
+
+/* PCIe PHY registers */
+#define SOC_PCIEPHY_CTRL0_ADDR 0x000
+#define SOC_PCIEPHY_CTRL1_ADDR 0x004
+#define SOC_PCIEPHY_CTRL2_ADDR 0x008
+#define SOC_PCIEPHY_CTRL3_ADDR 0x00c
+#define SOC_PCIEPHY_CTRL38_ADDR 0x0098
+#define SOC_PCIEPHY_STATE0_ADDR 0x400
+
+#define PORT_MSI_CTRL_ADDR 0x820
+#define PORT_MSI_CTRL_UPPER_ADDR 0x824
+#define PORT_MSI_CTRL_INT0_ENABLE 0x828
+
+#define RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 0xc004
+#define SUP_DIG_LVL_OVRD_IN 0x003c
+#define LANEN_DIG_ASIC_TX_OVRD_IN_1 0x4008
+#define LANEN_DIG_ASIC_TX_OVRD_IN_2 0x400c
+
+#define PCIE_LINKUP_ENABLE 0x8020
+#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21)
+#define PCIE_LTSSM_ENABLE_BIT BIT(11)
+#define PCIEPHY_RESET_BIT BIT(17)
+#define PCIEPHY_PIPE_LINE0_RESET_BIT BIT(19)
+#define PCIE_TXDETECT_RX_FAIL BIT(2)
+#define PCIE_CLK_SOURCE BIT(8)
+#define PCIE_IS_CLOCK_STABLE BIT(19)
+#define PCIE_PULL_DOWN_PHY_TEST_POWERDOWN BIT(22)
+#define PCIE_DEASSERT_CONTROLLER_PERST BIT(2)
+
+#define EYEPARAM_NOCFG 0xffffffff
+#define EYE_PARM0_MASK GENMASK(8, 6)
+#define EYE_PARM1_MASK GENMASK(11, 8)
+#define EYE_PARM2_MASK GENMASK(5, 0)
+#define EYE_PARM3_MASK GENMASK(12, 7)
+#define EYE_PARM4_MASK GENMASK(14, 9)
+#define EYE_PARM0_EN BIT(9)
+#define EYE_PARM1_EN BIT(12)
+#define EYE_PARM2_EN BIT(6)
+#define EYE_PARM3_EN BIT(13)
+#define EYE_PARM4_EN BIT(15)
+
+/* hi3670 pciephy register */
+#define APB_PHY_START_ADDR 0x40000
+#define SOC_PCIEPHY_MMC1PLL_CTRL1 0xc04
+#define SOC_PCIEPHY_MMC1PLL_CTRL16 0xC40
+#define SOC_PCIEPHY_MMC1PLL_CTRL17 0xC44
+#define SOC_PCIEPHY_MMC1PLL_CTRL20 0xC50
+#define SOC_PCIEPHY_MMC1PLL_CTRL21 0xC54
+#define SOC_PCIEPHY_MMC1PLL_STAT0 0xE00
+
+#define CRGPERIPH_PEREN12 0x470
+#define CRGPERIPH_PERDIS12 0x474
+#define CRGPERIPH_PCIECTRL0 0x800
+
+#define PCIE_FNPLL_FBDIV_MASK GENMASK(27, 16)
+#define PCIE_FNPLL_FRACDIV_MASK GENMASK(23, 0)
+#define PCIE_FNPLL_POSTDIV1_MASK GENMASK(10, 8)
+#define PCIE_FNPLL_POSTDIV2_MASK GENMASK(14, 12)
+#define PCIE_FNPLL_PLL_MODE_MASK BIT(25)
+
+#define PCIE_FNPLL_DLL_EN BIT(27)
+#define PCIE_FNPLL_FBDIV 0xd0
+#define PCIE_FNPLL_FRACDIV 0x555555
+#define PCIE_FNPLL_POSTDIV1 0x5
+#define PCIE_FNPLL_POSTDIV2 0x4
+#define PCIE_FNPLL_PLL_MODE 0x0
+
+#define PCIE_PHY_MMC1PLL 0x20
+#define PCIE_PHY_CHOOSE_FNPLL BIT(27)
+#define PCIE_PHY_MMC1PLL_DISABLE BIT(0)
+#define PCIE_PHY_PCIEPL_BP BIT(16)
+
+/* define ie,oe cfg */
+#define IO_OE_HARD_GT_MODE BIT(1)
+#define IO_IE_EN_HARD_BYPASS BIT(27)
+#define IO_OE_EN_HARD_BYPASS BIT(11)
+#define IO_HARD_CTRL_DEBOUNCE_BYPASS BIT(10)
+#define IO_OE_GT_MODE BIT(8)
+#define DEBOUNCE_WAITCFG_IN GENMASK(23, 20)
+#define DEBOUNCE_WAITCFG_OUT GENMASK(16, 13)
+
+#define IO_HP_DEBOUNCE_GT (BIT(12) | BIT(15))
+#define IO_PHYREF_SOFT_GT_MODE BIT(14)
+#define IO_REF_SOFT_GT_MODE BIT(13)
+#define IO_REF_HARD_GT_MODE BIT(0)
+
+/* noc power domain */
+#define NOC_POWER_IDLEREQ_1 0x38c
+#define NOC_POWER_IDLE_1 0x394
+#define NOC_PW_MASK 0x10000
+#define NOC_PW_SET_BIT 0x1
+
+#define NUM_EYEPARAM 5
+
+/* 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 GENMASK(29, 28)
+
+/* peri_crg ctrl */
+#define CRGCTRL_PCIE_ASSERT_OFFSET 0x88
+#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000
+
+#define FNPLL_HAS_LOCKED BIT(4)
+
+/* Time for delay */
+#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
+
+#define PIPE_CLK_STABLE_TIME 100
+#define PLL_CTRL_WAIT_TIME 200
+#define NOC_POWER_TIME 100
+
+struct hi3670_pcie_phy {
+ struct device *dev;
+ void __iomem *base;
+ struct regmap *apb;
+ struct regmap *crgctrl;
+ struct regmap *sysctrl;
+ struct regmap *pmctrl;
+ struct clk *apb_sys_clk;
+ struct clk *apb_phy_clk;
+ struct clk *phy_ref_clk;
+ struct clk *aclk;
+ struct clk *aux_clk;
+ u32 eye_param[NUM_EYEPARAM];
+};
+
+/* Registers in PCIePHY */
+static inline void hi3670_apb_phy_writel(struct hi3670_pcie_phy *phy, u32 val,
+ u32 reg)
+{
+ writel(val, phy->base + APB_PHY_START_ADDR + reg);
+}
+
+static inline u32 hi3670_apb_phy_readl(struct hi3670_pcie_phy *phy, u32 reg)
+{
+ return readl(phy->base + APB_PHY_START_ADDR + reg);
+}
+
+static inline void kirin_apb_natural_phy_writel(struct hi3670_pcie_phy *phy,
+ u32 val, u32 reg)
+{
+ writel(val, phy->base + reg);
+}
+
+static inline u32 kirin_apb_natural_phy_readl(struct hi3670_pcie_phy *phy,
+ u32 reg)
+{
+ return readl(phy->base + reg);
+}
+
+static void hi3670_pcie_phy_oe_enable(struct hi3670_pcie_phy *phy, bool enable)
+{
+ u32 val;
+
+ regmap_read(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, &val);
+ val |= PCIE_DEBOUNCE_PARAM;
+ if (enable)
+ val &= ~PCIE_OE_BYPASS;
+ else
+ val |= PCIE_OE_BYPASS;
+ regmap_write(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, val);
+}
+
+static void hi3670_pcie_get_eyeparam(struct hi3670_pcie_phy *phy)
+{
+ struct device *dev = phy->dev;
+ struct device_node *np;
+ int ret, i;
+
+ np = dev->of_node;
+
+ ret = of_property_read_u32_array(np, "hisilicon,eye-diagram-param",
+ phy->eye_param, NUM_EYEPARAM);
+ if (!ret)
+ return;
+
+ /* There's no optional eye_param property. Set array to default */
+ for (i = 0; i < NUM_EYEPARAM; i++)
+ phy->eye_param[i] = EYEPARAM_NOCFG;
+}
+
+static void hi3670_pcie_set_eyeparam(struct hi3670_pcie_phy *phy)
+{
+ u32 val;
+
+ val = kirin_apb_natural_phy_readl(phy,
+ RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
+
+ if (phy->eye_param[1] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM1_MASK;
+ val |= FIELD_PREP(EYE_PARM1_MASK, phy->eye_param[1]);
+ val |= EYE_PARM1_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val,
+ RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
+
+ val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_2);
+ val &= ~(EYE_PARM2_MASK | EYE_PARM3_MASK);
+ if (phy->eye_param[2] != EYEPARAM_NOCFG) {
+ val |= FIELD_PREP(EYE_PARM2_MASK, phy->eye_param[2]);
+ val |= EYE_PARM2_EN;
+ }
+
+ if (phy->eye_param[3] != EYEPARAM_NOCFG) {
+ val |= FIELD_PREP(EYE_PARM3_MASK, phy->eye_param[3]);
+ val |= EYE_PARM3_EN;
+ }
+
+ kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_2);
+
+ val = kirin_apb_natural_phy_readl(phy, SUP_DIG_LVL_OVRD_IN);
+ if (phy->eye_param[0] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM0_MASK;
+ val |= FIELD_PREP(EYE_PARM0_MASK, phy->eye_param[0]);
+ val |= EYE_PARM0_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val, SUP_DIG_LVL_OVRD_IN);
+
+ val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_1);
+ if (phy->eye_param[4] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM4_MASK;
+ val |= FIELD_PREP(EYE_PARM4_MASK, phy->eye_param[4]);
+ val |= EYE_PARM4_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_1);
+}
+
+static void hi3670_pcie_natural_cfg(struct hi3670_pcie_phy *phy)
+{
+ u32 val;
+
+ /* change 2p mem_ctrl */
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL20_ADDR,
+ SOC_PCIECTRL_CTRL20_2P_MEM_CTRL);
+
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, &val);
+ val |= PCIE_PULL_UP_SYS_AUX_PWR_DET;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, val);
+
+ /* output, pull down */
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val);
+ val &= ~PCIE_OUTPUT_PULL_BITS;
+ val |= PCIE_OUTPUT_PULL_DOWN;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val);
+
+ /* Handle phy_reset and lane0_reset to HW */
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_CTRL1_ADDR);
+ val |= PCIEPHY_RESET_BIT;
+ val &= ~PCIEPHY_PIPE_LINE0_RESET_BIT;
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_CTRL1_ADDR);
+
+ /* fix chip bug: TxDetectRx fail */
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_CTRL38_ADDR);
+ val |= PCIE_TXDETECT_RX_FAIL;
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_CTRL38_ADDR);
+}
+
+static void hi3670_pcie_pll_init(struct hi3670_pcie_phy *phy)
+{
+ u32 val;
+
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_CTRL1);
+ val |= PCIE_PHY_CHOOSE_FNPLL;
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_MMC1PLL_CTRL1);
+
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_CTRL16);
+ val &= ~PCIE_FNPLL_FBDIV_MASK;
+ val |= FIELD_PREP(PCIE_FNPLL_FBDIV_MASK, PCIE_FNPLL_FBDIV);
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_CTRL17);
+ val &= PCIE_FNPLL_FRACDIV_MASK;
+ val |= FIELD_PREP(PCIE_FNPLL_FRACDIV_MASK, PCIE_FNPLL_FRACDIV);
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_MMC1PLL_CTRL17);
+
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_CTRL20);
+ val &= ~(PCIE_FNPLL_POSTDIV1_MASK | PCIE_FNPLL_POSTDIV2_MASK |
+ PCIE_FNPLL_PLL_MODE_MASK | PCIE_FNPLL_DLL_EN);
+ val |= PCIE_FNPLL_DLL_EN;
+ val |= FIELD_PREP(PCIE_FNPLL_POSTDIV1_MASK, PCIE_FNPLL_POSTDIV1);
+ val |= FIELD_PREP(PCIE_FNPLL_POSTDIV2_MASK, PCIE_FNPLL_POSTDIV2);
+ val |= FIELD_PREP(PCIE_FNPLL_PLL_MODE_MASK, PCIE_FNPLL_PLL_MODE);
+
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_MMC1PLL_CTRL20);
+
+ hi3670_apb_phy_writel(phy, PCIE_PHY_MMC1PLL,
+ SOC_PCIEPHY_MMC1PLL_CTRL21);
+}
+
+static int hi3670_pcie_pll_ctrl(struct hi3670_pcie_phy *phy, bool enable)
+{
+ struct device *dev = phy->dev;
+ u32 val;
+ int time = PLL_CTRL_WAIT_TIME;
+
+ if (enable) {
+ /* pd = 0 */
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_CTRL16);
+ val &= ~PCIE_PHY_MMC1PLL_DISABLE;
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0);
+
+ /* choose FNPLL */
+ while (!(val & FNPLL_HAS_LOCKED)) {
+ if (!time) {
+ dev_err(dev, "wait for pll_lock timeout\n");
+ return -EINVAL;
+ }
+ time--;
+ udelay(1);
+ val = hi3670_apb_phy_readl(phy,
+ SOC_PCIEPHY_MMC1PLL_STAT0);
+ }
+
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_CTRL20);
+ val &= ~PCIE_PHY_PCIEPL_BP;
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_MMC1PLL_CTRL20);
+
+ } else {
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_CTRL16);
+ val |= PCIE_PHY_MMC1PLL_DISABLE;
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_CTRL20);
+ val |= PCIE_PHY_PCIEPL_BP;
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_MMC1PLL_CTRL20);
+ }
+
+ return 0;
+}
+
+static void hi3670_pcie_hp_debounce_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ if (open)
+ /* gt_clk_pcie_hp/gt_clk_pcie_debounce open */
+ regmap_write(phy->crgctrl, CRGPERIPH_PEREN12,
+ IO_HP_DEBOUNCE_GT);
+ else
+ /* gt_clk_pcie_hp/gt_clk_pcie_debounce close */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_HP_DEBOUNCE_GT);
+}
+
+static void hi3670_pcie_phyref_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ unsigned int val;
+
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+
+ if (open)
+ val &= ~IO_OE_HARD_GT_MODE; // enable hard gt mode
+ else
+ val |= IO_OE_HARD_GT_MODE; // disable hard gt mode
+
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, IO_PHYREF_SOFT_GT_MODE);
+}
+
+static void hi3670_pcie_oe_ctrl(struct hi3670_pcie_phy *phy, bool en_flag)
+{
+ unsigned int val;
+
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+
+ /* set ie cfg */
+ val |= IO_IE_EN_HARD_BYPASS;
+
+ /* set oe cfg */
+ val &= ~IO_HARD_CTRL_DEBOUNCE_BYPASS;
+
+ /* set phy_debounce in&out time */
+ val |= (DEBOUNCE_WAITCFG_IN | DEBOUNCE_WAITCFG_OUT);
+
+ /* select oe_gt_mode */
+ val |= IO_OE_GT_MODE;
+
+ if (en_flag)
+ val &= ~IO_OE_EN_HARD_BYPASS;
+ else
+ val |= IO_OE_EN_HARD_BYPASS;
+
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+}
+
+static void hi3670_pcie_ioref_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ unsigned int val;
+
+ if (open) {
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL21_ADDR,
+ SOC_PCIECTRL_CTRL21_DEFAULT);
+
+ hi3670_pcie_oe_ctrl(phy, true);
+
+ /* en hard gt mode */
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+ val &= ~IO_REF_HARD_GT_MODE;
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_REF_SOFT_GT_MODE);
+
+ } else {
+ /* disable hard gt mode */
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+ val |= IO_REF_HARD_GT_MODE;
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_REF_SOFT_GT_MODE);
+
+ hi3670_pcie_oe_ctrl(phy, false);
+ }
+}
+
+static int hi3670_pcie_allclk_ctrl(struct hi3670_pcie_phy *phy, bool clk_on)
+{
+ struct device *dev = phy->dev;
+ u32 val;
+ int ret = 0;
+
+ if (!clk_on)
+ goto close_clocks;
+
+ /* choose 100MHz clk src: Bit[8]==1 pad, Bit[8]==0 pll */
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_CTRL1_ADDR);
+ val &= ~PCIE_CLK_SOURCE;
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_CTRL1_ADDR);
+
+ hi3670_pcie_pll_init(phy);
+
+ ret = hi3670_pcie_pll_ctrl(phy, true);
+ if (ret) {
+ dev_err(dev, "Failed to enable pll\n");
+ return -EINVAL;
+ }
+ hi3670_pcie_hp_debounce_gt(phy, true);
+ hi3670_pcie_phyref_gt(phy, true);
+ hi3670_pcie_ioref_gt(phy, true);
+
+ ret = clk_set_rate(phy->aclk, AXI_CLK_FREQ);
+ if (ret) {
+ dev_err(dev, "Failed to set rate\n");
+ goto close_clocks;
+ }
+
+ return 0;
+
+close_clocks:
+ hi3670_pcie_ioref_gt(phy, false);
+ hi3670_pcie_phyref_gt(phy, false);
+ hi3670_pcie_hp_debounce_gt(phy, false);
+
+ hi3670_pcie_pll_ctrl(phy, false);
+
+ return ret;
+}
+
+static bool is_pipe_clk_stable(struct hi3670_pcie_phy *phy)
+{
+ struct device *dev = phy->dev;
+ u32 val;
+ u32 time = PIPE_CLK_STABLE_TIME;
+ u32 pipe_clk_stable = PCIE_IS_CLOCK_STABLE;
+
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR);
+ while (val & pipe_clk_stable) {
+ mdelay(1);
+ if (!time) {
+ dev_err(dev, "PIPE clk is not stable\n");
+ return false;
+ }
+ time--;
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR);
+ }
+
+ return true;
+}
+
+static int hi3670_pcie_noc_power(struct hi3670_pcie_phy *phy, bool enable)
+{
+ struct device *dev = phy->dev;
+ u32 time = NOC_POWER_TIME;
+ unsigned int val = NOC_PW_MASK;
+ int rst;
+
+ if (enable)
+ val = NOC_PW_MASK | NOC_PW_SET_BIT;
+ else
+ val = NOC_PW_MASK;
+ rst = enable ? 1 : 0;
+
+ regmap_write(phy->pmctrl, NOC_POWER_IDLEREQ_1, val);
+
+ time = NOC_POWER_TIME;
+ regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val);
+ while ((val & NOC_PW_SET_BIT) != rst) {
+ udelay(10);
+ if (!time) {
+ dev_err(dev, "Failed to reverse noc power-status\n");
+ return -EINVAL;
+ }
+ time--;
+ regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val);
+ }
+
+ return 0;
+}
+
+static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy)
+{
+ struct device_node *pcie_port;
+ struct device *dev = phy->dev;
+ struct device *pcie_dev;
+
+ pcie_port = of_get_child_by_name(dev->parent->of_node, "pcie");
+ if (!pcie_port) {
+ dev_err(dev, "no pcie node found in %s\n",
+ dev->parent->of_node->full_name);
+ return -ENODEV;
+ }
+
+ pcie_dev = bus_find_device_by_of_node(&platform_bus_type, pcie_port);
+ if (!pcie_dev) {
+ dev_err(dev, "Didn't find pcie device\n");
+ return -ENODEV;
+ }
+
+ /*
+ * We might just use NULL instead of the APB name, as the
+ * pcie-kirin currently registers directly just one regmap (although
+ * the DWC driver register other regmaps).
+ *
+ * Yet, it sounds safer to warrant that it will be accessing the
+ * right regmap. So, let's use the named version.
+ */
+ phy->apb = dev_get_regmap(pcie_dev, "kirin_pcie_apb");
+ if (!phy->apb) {
+ dev_err(dev, "Failed to get APB regmap\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int kirin_pcie_clk_ctrl(struct hi3670_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 hi3670_pcie_phy_init(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+ int ret;
+
+ /*
+ * The code under hi3670_pcie_get_resources_from_pcie() need to
+ * access the reset-gpios and the APB registers, both from the
+ * pcie-kirin driver.
+ *
+ * The APB is obtained via the pcie driver's regmap
+ * Such kind of resource can only be obtained during the PCIe
+ * power_on sequence, as the code inside pcie-kirin needs to
+ * be already probed, as it needs to register the APB regmap.
+ */
+
+ ret = hi3670_pcie_get_resources_from_pcie(phy);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int hi3670_pcie_phy_power_on(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+ int val, 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);
+
+ hi3670_pcie_phy_oe_enable(phy, true);
+
+ ret = kirin_pcie_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);
+
+ hi3670_pcie_natural_cfg(phy);
+
+ ret = hi3670_pcie_allclk_ctrl(phy, true);
+ if (ret)
+ goto disable_clks;
+
+ /* pull down phy_test_powerdown signal */
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_CTRL0_ADDR);
+ val &= ~PCIE_PULL_DOWN_PHY_TEST_POWERDOWN;
+ hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_CTRL0_ADDR);
+
+ /* deassert controller perst_n */
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val);
+ val |= PCIE_DEASSERT_CONTROLLER_PERST;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val);
+ udelay(10);
+
+ ret = is_pipe_clk_stable(phy);
+ if (!ret)
+ goto disable_clks;
+
+ hi3670_pcie_set_eyeparam(phy);
+
+ ret = hi3670_pcie_noc_power(phy, false);
+ if (ret)
+ goto disable_clks;
+
+ return 0;
+
+disable_clks:
+ kirin_pcie_clk_ctrl(phy, false);
+ return ret;
+}
+
+static int hi3670_pcie_phy_power_off(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+
+ hi3670_pcie_phy_oe_enable(phy, false);
+
+ hi3670_pcie_allclk_ctrl(phy, false);
+
+ /* Drop power supply for Host */
+ regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, 0);
+
+ /* FIXME: calling it causes an Asynchronous SError interrupt */
+// kirin_pcie_clk_ctrl(phy, false);
+
+ return 0;
+}
+
+static const struct phy_ops hi3670_phy_ops = {
+ .init = hi3670_pcie_phy_init,
+ .power_on = hi3670_pcie_phy_power_on,
+ .power_off = hi3670_pcie_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int hi3670_pcie_phy_get_resources(struct hi3670_pcie_phy *phy,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ /* syscon */
+ phy->crgctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-crgctrl");
+ if (IS_ERR(phy->crgctrl))
+ return PTR_ERR(phy->crgctrl);
+
+ phy->sysctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-sctrl");
+ if (IS_ERR(phy->sysctrl))
+ return PTR_ERR(phy->sysctrl);
+
+ phy->pmctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-pmctrl");
+ if (IS_ERR(phy->sysctrl))
+ return PTR_ERR(phy->sysctrl);
+
+ /* clocks */
+ phy->phy_ref_clk = devm_clk_get(dev, "phy_ref");
+ if (IS_ERR(phy->phy_ref_clk))
+ return PTR_ERR(phy->phy_ref_clk);
+
+ phy->aux_clk = devm_clk_get(dev, "aux");
+ if (IS_ERR(phy->aux_clk))
+ return PTR_ERR(phy->aux_clk);
+
+ phy->apb_phy_clk = devm_clk_get(dev, "apb_phy");
+ if (IS_ERR(phy->apb_phy_clk))
+ return PTR_ERR(phy->apb_phy_clk);
+
+ phy->apb_sys_clk = devm_clk_get(dev, "apb_sys");
+ if (IS_ERR(phy->apb_sys_clk))
+ return PTR_ERR(phy->apb_sys_clk);
+
+ phy->aclk = devm_clk_get(dev, "aclk");
+ if (IS_ERR(phy->aclk))
+ return PTR_ERR(phy->aclk);
+
+ /* registers */
+ phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(phy->base))
+ return PTR_ERR(phy->base);
+
+ hi3670_pcie_get_eyeparam(phy);
+
+ return 0;
+}
+
+static int hi3670_pcie_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct hi3670_pcie_phy *phy;
+ struct phy *generic_phy;
+ int ret;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->dev = dev;
+
+ ret = hi3670_pcie_phy_get_resources(phy, pdev);
+ if (ret)
+ return ret;
+
+ generic_phy = devm_phy_create(dev, dev->of_node, &hi3670_phy_ops);
+ if (IS_ERR(generic_phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(generic_phy);
+ }
+
+ phy_set_drvdata(generic_phy, phy);
+ 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 hi3670_pcie_phy_match[] = {
+ {
+ .compatible = "hisilicon,hi970-pcie-phy",
+ },
+ {},
+};
+
+static struct platform_driver hi3670_pcie_phy_driver = {
+ .probe = hi3670_pcie_phy_probe,
+ .driver = {
+ .of_match_table = hi3670_pcie_phy_match,
+ .name = "hi3670_pcie_phy",
+ .suppress_bind_attrs = true,
+ }
+};
+builtin_platform_driver(hi3670_pcie_phy_driver);
+
+MODULE_DEVICE_TABLE(of, hi3670_pcie_phy_match);
+MODULE_DESCRIPTION("PCIe phy driver for Kirin 970");
+MODULE_AUTHOR("Mauro Carvalho Chehab <[email protected]>");
+MODULE_AUTHOR("Manivannan Sadhasivam <[email protected]>");
+MODULE_LICENSE("GPL v2");
--
2.31.1

2021-08-12 08:08:17

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: [PATCH v11 04/11] PCI: kirin: Use regmap for APB registers

The PHY layer need to access APB registers too, for Kirin 970.
So, place them into a named regmap.

Signed-off-by: Mauro Carvalho Chehab <[email protected]>
---
drivers/pci/controller/dwc/pcie-kirin.c | 49 +++++++++++++------------
1 file changed, 26 insertions(+), 23 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c
index 31514a5d4bb4..0ea92a521e1c 100644
--- a/drivers/pci/controller/dwc/pcie-kirin.c
+++ b/drivers/pci/controller/dwc/pcie-kirin.c
@@ -61,8 +61,8 @@ struct kirin_pcie {
enum pcie_kirin_phy_type type;

struct dw_pcie *pci;
+ struct regmap *apb;
struct phy *phy;
- void __iomem *apb_base;
void *phy_priv; /* only for PCIE_KIRIN_INTERNAL_PHY */
};

@@ -340,25 +340,27 @@ static int hi3660_pcie_phy_init(struct platform_device *pdev,
* The non-PHY part starts here
*/

-/* Registers in PCIeCTRL */
-static inline void kirin_apb_ctrl_writel(struct kirin_pcie *kirin_pcie,
- u32 val, u32 reg)
-{
- writel(val, kirin_pcie->apb_base + reg);
-}
-
-static inline u32 kirin_apb_ctrl_readl(struct kirin_pcie *kirin_pcie, u32 reg)
-{
- return readl(kirin_pcie->apb_base + reg);
-}
+static const struct regmap_config pcie_kirin_regmap_conf = {
+ .name = "kirin_pcie_apb",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};

static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie,
struct platform_device *pdev)
{
- kirin_pcie->apb_base =
- devm_platform_ioremap_resource_byname(pdev, "apb");
- if (IS_ERR(kirin_pcie->apb_base))
- return PTR_ERR(kirin_pcie->apb_base);
+ struct device *dev = &pdev->dev;
+ void __iomem *apb_base;
+
+ 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);

return 0;
}
@@ -368,13 +370,13 @@ static void kirin_pcie_sideband_dbi_w_mode(struct kirin_pcie *kirin_pcie,
{
u32 val;

- val = kirin_apb_ctrl_readl(kirin_pcie, SOC_PCIECTRL_CTRL0_ADDR);
+ 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;

- kirin_apb_ctrl_writel(kirin_pcie, val, SOC_PCIECTRL_CTRL0_ADDR);
+ regmap_write(kirin_pcie->apb, SOC_PCIECTRL_CTRL0_ADDR, val);
}

static void kirin_pcie_sideband_dbi_r_mode(struct kirin_pcie *kirin_pcie,
@@ -382,13 +384,13 @@ static void kirin_pcie_sideband_dbi_r_mode(struct kirin_pcie *kirin_pcie,
{
u32 val;

- val = kirin_apb_ctrl_readl(kirin_pcie, SOC_PCIECTRL_CTRL1_ADDR);
+ 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;

- kirin_apb_ctrl_writel(kirin_pcie, val, SOC_PCIECTRL_CTRL1_ADDR);
+ regmap_write(kirin_pcie->apb, SOC_PCIECTRL_CTRL1_ADDR, val);
}

static int kirin_pcie_rd_own_conf(struct pci_bus *bus, unsigned int devfn,
@@ -448,8 +450,9 @@ static void kirin_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base,
static int kirin_pcie_link_up(struct dw_pcie *pci)
{
struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci);
- u32 val = kirin_apb_ctrl_readl(kirin_pcie, PCIE_APB_PHY_STATUS0);
+ u32 val;

+ regmap_read(kirin_pcie->apb, PCIE_APB_PHY_STATUS0, &val);
if ((val & PCIE_LINKUP_ENABLE) == PCIE_LINKUP_ENABLE)
return 1;

@@ -461,8 +464,8 @@ static int kirin_pcie_start_link(struct dw_pcie *pci)
struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci);

/* assert LTSSM enable */
- kirin_apb_ctrl_writel(kirin_pcie, PCIE_LTSSM_ENABLE_BIT,
- PCIE_APP_LTSSM_ENABLE);
+ regmap_write(kirin_pcie->apb, PCIE_APP_LTSSM_ENABLE,
+ PCIE_LTSSM_ENABLE_BIT);

return 0;
}
--
2.31.1

2021-08-17 10:43:36

by Vinod Koul

[permalink] [raw]
Subject: Re: [PATCH v11 01/11] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

On 12-08-21, 10:02, Mauro Carvalho Chehab wrote:

> +static void hi3670_pcie_set_eyeparam(struct hi3670_pcie_phy *phy)
> +{
> + u32 val;
> +
> + val = kirin_apb_natural_phy_readl(phy,
> + RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);

Maybe use one line for this (hint: we can go beyong 80 now)

> +static void hi3670_pcie_natural_cfg(struct hi3670_pcie_phy *phy)
> +{
> + u32 val;
> +
> + /* change 2p mem_ctrl */
> + regmap_write(phy->apb, SOC_PCIECTRL_CTRL20_ADDR,
> + SOC_PCIECTRL_CTRL20_2P_MEM_CTRL);
> +
> + regmap_read(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, &val);
> + val |= PCIE_PULL_UP_SYS_AUX_PWR_DET;
> + regmap_write(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, val);
> +
> + /* output, pull down */
> + regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val);
> + val &= ~PCIE_OUTPUT_PULL_BITS;
> + val |= PCIE_OUTPUT_PULL_DOWN;
> + regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val);
> +
> + /* Handle phy_reset and lane0_reset to HW */
> + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_CTRL1_ADDR);
> + val |= PCIEPHY_RESET_BIT;
> + val &= ~PCIEPHY_PIPE_LINE0_RESET_BIT;
> + hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_CTRL1_ADDR);
> +
> + /* fix chip bug: TxDetectRx fail */
> + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_CTRL38_ADDR);
> + val |= PCIE_TXDETECT_RX_FAIL;
> + hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_CTRL38_ADDR);

maybe add a hi3670_apb_phy_updatel() so that above would become:

hi3670_apb_phy_updatel(phy, val, mask);

> +static int hi3670_pcie_pll_ctrl(struct hi3670_pcie_phy *phy, bool enable)
> +{
> + struct device *dev = phy->dev;
> + u32 val;
> + int time = PLL_CTRL_WAIT_TIME;
> +
> + if (enable) {
> + /* pd = 0 */
> + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_CTRL16);
> + val &= ~PCIE_PHY_MMC1PLL_DISABLE;
> + hi3670_apb_phy_writel(phy, val, SOC_PCIEPHY_MMC1PLL_CTRL16);
> +
> + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0);
> +
> + /* choose FNPLL */
> + while (!(val & FNPLL_HAS_LOCKED)) {
> + if (!time) {
> + dev_err(dev, "wait for pll_lock timeout\n");
> + return -EINVAL;
> + }
> + time--;
> + udelay(1);
> + val = hi3670_apb_phy_readl(phy,
> + SOC_PCIEPHY_MMC1PLL_STAT0);

single line here too

> +static void hi3670_pcie_phyref_gt(struct hi3670_pcie_phy *phy, bool open)
> +{
> + unsigned int val;
> +
> + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
> +
> + if (open)
> + val &= ~IO_OE_HARD_GT_MODE; // enable hard gt mode
> + else
> + val |= IO_OE_HARD_GT_MODE; // disable hard gt mode

pls change the comment style here and above, we dont use c99 style!

> +static int hi3670_pcie_phy_power_off(struct phy *generic_phy)
> +{
> + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
> +
> + hi3670_pcie_phy_oe_enable(phy, false);
> +
> + hi3670_pcie_allclk_ctrl(phy, false);
> +
> + /* Drop power supply for Host */
> + regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, 0);
> +
> + /* FIXME: calling it causes an Asynchronous SError interrupt */
> +// kirin_pcie_clk_ctrl(phy, false);

when will you fix the fixme and pls remove the deadcode
--
~Vinod

2021-08-18 09:05:43

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: Re: [PATCH v11 01/11] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

Hi Vinod,

Em Tue, 17 Aug 2021 16:12:37 +0530
Vinod Koul <[email protected]> escreveu:

> > + /* FIXME: calling it causes an Asynchronous SError interrupt */
> > +// kirin_pcie_clk_ctrl(phy, false);
>
> when will you fix the fixme and pls remove the deadcode

Working with clocks on this SoC is very tricky: there are lots of clock
lines (~70) that are critical for this device to work. Such lines are
enabled via the Device's firmware, and are supposed to be always
powered. Powering off such clock lines cause a SError.

Most clocks on this device are managed by the clk-hikey3670 driver.
At the current state of clk-hi3670, the only way for HiKey 970
to even boot is to add:

clk_ignore_unused=true

as a Kernel boot parameter. That is the solution given by the downstream
official distributions for HiKey970 at 96boards.

The fix is to flag the critical clocks with CLK_IS_CRITICAL at the
clk-hi3670 driver, but finding the right clock set has been a challenge.

I spent the last couple of weeks trying to identify the critical ones,
as I'm aiming to be able to use a Kernel built with a default arm64
one of my goals is to have this device working fine with a
"make defconfig" Kernel.

So, I added this patch:

https://lore.kernel.org/lkml/2d2de5e902ced072bcfd5e5311d6b10326b9245b.1627041240.git.mchehab+huawei@kernel.org/

to my tree (which reduces the set of clocks using CLK_IGNORE_UNUSED
from 308 to 163 clocks). Than I ran script that was dropping the
flag one by one, boots the new Kernel and do a sanity check. When it
fails to boot, I manually dropped the patch, and re-run the script
to test the remaining clocks. After a couple of weeks, I reached a patch
with 78 clock lines that seemed critical, but the resulting patch was
not stable, as, depending on the day I boot the Kernel with such patch,
it crashes with SError in a couple of seconds after booting, or
cause the Ethernet firmware to not load.

I intend to keep trying to find the clock lines that can't be disabled,
but this is very time consuming, as I couldn't find any documentation
about that. So, it has to be done empirically.

-

In any case, fixing it doesn't sound a critical issue for the PHY
driver. I mean, right now, this patchset allows removing and
re-inseting the PCIe driver, which is already an improvement over the
original upstream driver, which was missing the power-off logic for
Kirin 960.

With this patchset, both power-off/power-on logic for both HiKey960
(where the PHY is inside the pcie-kirin driver) and for HiKey970,
which uses this PHY driver. On both devices, I tested an endless loop
with rmmod/modprobe for the PCIe.

Besides that, in practice, removing PCIe in runtime is something that
people usually don't do.

So, while it would be cool to balance the clock disable logic,
I don't think this is a critical issue in this particular case.

Thanks,
Mauro

2021-08-18 10:10:24

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: Re: [PATCH v11 01/11] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

Em Wed, 18 Aug 2021 11:01:23 +0200
Mauro Carvalho Chehab <[email protected]> escreveu:

> Hi Vinod,
>
> Em Tue, 17 Aug 2021 16:12:37 +0530
> Vinod Koul <[email protected]> escreveu:
>
> > > + /* FIXME: calling it causes an Asynchronous SError interrupt */
> > > +// kirin_pcie_clk_ctrl(phy, false);
> >
> > when will you fix the fixme and pls remove the deadcode
>
> Working with clocks on this SoC is very tricky: there are lots of clock
> lines (~70) that are critical for this device to work. Such lines are
> enabled via the Device's firmware, and are supposed to be always
> powered. Powering off such clock lines cause a SError.
>
> Most clocks on this device are managed by the clk-hikey3670 driver.
> At the current state of clk-hi3670, the only way for HiKey 970
> to even boot is to add:
>
> clk_ignore_unused=true
>
> as a Kernel boot parameter. That is the solution given by the downstream
> official distributions for HiKey970 at 96boards.
>
> The fix is to flag the critical clocks with CLK_IS_CRITICAL at the
> clk-hi3670 driver, but finding the right clock set has been a challenge.
>
> I spent the last couple of weeks trying to identify the critical ones,
> as I'm aiming to be able to use a Kernel built with a default arm64
> one of my goals is to have this device working fine with a
> "make defconfig" Kernel.
>
> So, I added this patch:
>
> https://lore.kernel.org/lkml/2d2de5e902ced072bcfd5e5311d6b10326b9245b.1627041240.git.mchehab+huawei@kernel.org/
>
> to my tree (which reduces the set of clocks using CLK_IGNORE_UNUSED
> from 308 to 163 clocks). Than I ran script that was dropping the
> flag one by one, boots the new Kernel and do a sanity check. When it
> fails to boot, I manually dropped the patch, and re-run the script
> to test the remaining clocks. After a couple of weeks, I reached a patch
> with 78 clock lines that seemed critical, but the resulting patch was
> not stable, as, depending on the day I boot the Kernel with such patch,
> it crashes with SError in a couple of seconds after booting, or
> cause the Ethernet firmware to not load.
>
> I intend to keep trying to find the clock lines that can't be disabled,
> but this is very time consuming, as I couldn't find any documentation
> about that. So, it has to be done empirically.
>
> -
>
> In any case, fixing it doesn't sound a critical issue for the PHY
> driver. I mean, right now, this patchset allows removing and
> re-inseting the PCIe driver, which is already an improvement over the
> original upstream driver, which was missing the power-off logic for
> Kirin 960.
>
> With this patchset, both power-off/power-on logic for both HiKey960
> (where the PHY is inside the pcie-kirin driver) and for HiKey970,
> which uses this PHY driver. On both devices, I tested an endless loop
> with rmmod/modprobe for the PCIe.
>
> Besides that, in practice, removing PCIe in runtime is something that
> people usually don't do.
>
> So, while it would be cool to balance the clock disable logic,
> I don't think this is a critical issue in this particular case.
>
> Thanks,
> Mauro

Btw, this is one of such panic errors:

[ 4.468948] hi3670_pcie_phy fc000000.pcie-phy: PIPE clk is not stable
[ 4.522530] SError Interrupt on CPU4, code 0xbf000002 -- SError
[ 4.522535] CPU: 4 PID: 223 Comm: systemd-udevd Not tainted 5.14.0-rc1+ #370
[ 4.522537] Hardware name: HiKey970 (DT)
[ 4.522539] pstate: 400000c5 (nZcv daIF -PAN -UAO -TCO BTYPE=--)
[ 4.522540] pc : el1_interrupt+0x20/0x80
[ 4.522542] lr : el1h_64_irq_handler+0x18/0x24
[ 4.522543] sp : ffff800012903610
[ 4.522543] x29: ffff800012903610 x28: ffff000108410e40 x27: ffff0001bf3e4100
[ 4.522551] x26: 0000000000000000 x25: 0000000000000000 x24: ffff0001009dec10
[ 4.522554] x23: 0000000040000005 x22: ffff800010ed1330 x21: ffff800012903790
[ 4.522556] x20: ffff8000104e42e0 x19: ffff800012903640 x18: 0000000000000000
[ 4.522559] x17: 0000000000000000 x16: 0000000000000000 x15: 0763072007450750
[ 4.522563] x14: 074907500720073a x13: ffff0001b87e0000 x12: 000000000000053a
[ 4.522565] x11: 00000000000001be x10: ffff0001bf2386c0 x9 : 00000000ffff0000
[ 4.522568] x8 : ffff0001b87e0000 x7 : ffff0001bf2386c0 x6 : 0000000000000000
[ 4.522571] x5 : ffff00010370aac0 x4 : ffff000108410e40 x3 : ffff800011f20cd8
[ 4.522573] x2 : ffff000108410e40 x1 : 00000000000000c0 x0 : ffff800012903640
[ 4.522577] Kernel panic - not syncing: Asynchronous SError Interrupt
[ 4.522578] CPU: 4 PID: 223 Comm: systemd-udevd Not tainted 5.14.0-rc1+ #370
[ 4.522579] Hardware name: HiKey970 (DT)
[ 4.522579] Call trace:
[ 4.522580] dump_backtrace+0x0/0x1e0
[ 4.522581] show_stack+0x18/0x24
[ 4.522581] dump_stack_lvl+0x68/0x84
[ 4.522582] dump_stack+0x18/0x34
[ 4.522583] panic+0x16c/0x334
[ 4.522583] nmi_panic+0x8c/0x90
[ 4.522584] arm64_serror_panic+0x78/0x84
[ 4.522585] do_serror+0x58/0x5c
[ 4.522586] el1h_64_error_handler+0x30/0x50
[ 4.522586] el1h_64_error+0x78/0x7c
[ 4.522588] el1_interrupt+0x20/0x80
[ 4.522588] el1h_64_irq_handler+0x18/0x24
[ 4.522589] el1h_64_irq+0x78/0x7c
[ 4.522590] mutex_lock_io+0xf0/0x370
[ 4.522591] clk_unprepare+0x28/0x50
[ 4.522591] kirin_pcie_clk_ctrl+0x164/0x1a0 [phy_hi3670_pcie]
[ 4.522592] hi3670_pcie_phy_power_on+0x720/0xb00 [phy_hi3670_pcie]
[ 4.522593] phy_power_on+0x78/0x130
[ 4.522594] kirin_pcie_probe+0x6a8/0x88c [pcie_kirin]
[ 4.522595] platform_probe+0x68/0xe0
[ 4.522596] really_probe+0x1b0/0x42c
[ 4.522596] __driver_probe_device+0x114/0x190
[ 4.522597] driver_probe_device+0x40/0x100
[ 4.522598] __driver_attach+0xcc/0x1e0
[ 4.522599] bus_for_each_dev+0x70/0xd0
[ 4.522600] driver_attach+0x24/0x30
[ 4.522601] bus_add_driver+0x140/0x234
[ 4.522601] driver_register+0x78/0x130
[ 4.522602] __platform_driver_register+0x28/0x34
[ 4.522603] kirin_pcie_driver_init+0x24/0x1000 [pcie_kirin]
[ 4.522604] do_one_initcall+0x50/0x1b0
[ 4.522605] do_init_module+0x5c/0x254
[ 4.522605] load_module+0x21cc/0x2820
[ 4.522606] __do_sys_finit_module+0xbc/0x130
[ 4.522607] __arm64_sys_finit_module+0x24/0x30
[ 4.522608] invoke_syscall+0x48/0x114
[ 4.522608] el0_svc_common+0xc4/0xdc
[ 4.522609] do_el0_svc+0x28/0x90
[ 4.522610] el0_svc+0x2c/0x54
[ 4.522610] el0t_64_sync_handler+0x1a4/0x1b0
[ 4.522611] el0t_64_sync+0x198/0x19c
[ 4.522633] SMP: stopping secondary CPUs
[ 4.522634] Kernel Offset: disabled
[ 4.522635] CPU features: 0x00003051,00000846
[ 4.522636] Memory Limit: none

Thanks,
Mauro

2021-08-18 10:11:33

by Vinod Koul

[permalink] [raw]
Subject: Re: [PATCH v11 01/11] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

On 18-08-21, 11:01, Mauro Carvalho Chehab wrote:
> Hi Vinod,
>
> Em Tue, 17 Aug 2021 16:12:37 +0530
> Vinod Koul <[email protected]> escreveu:
>
> > > + /* FIXME: calling it causes an Asynchronous SError interrupt */
> > > +// kirin_pcie_clk_ctrl(phy, false);
> >
> > when will you fix the fixme and pls remove the deadcode
>
> Working with clocks on this SoC is very tricky: there are lots of clock
> lines (~70) that are critical for this device to work. Such lines are
> enabled via the Device's firmware, and are supposed to be always
> powered. Powering off such clock lines cause a SError.
>
> Most clocks on this device are managed by the clk-hikey3670 driver.
> At the current state of clk-hi3670, the only way for HiKey 970
> to even boot is to add:
>
> clk_ignore_unused=true
>
> as a Kernel boot parameter. That is the solution given by the downstream
> official distributions for HiKey970 at 96boards.
>
> The fix is to flag the critical clocks with CLK_IS_CRITICAL at the
> clk-hi3670 driver, but finding the right clock set has been a challenge.
>
> I spent the last couple of weeks trying to identify the critical ones,
> as I'm aiming to be able to use a Kernel built with a default arm64
> one of my goals is to have this device working fine with a
> "make defconfig" Kernel.
>
> So, I added this patch:
>
> https://lore.kernel.org/lkml/2d2de5e902ced072bcfd5e5311d6b10326b9245b.1627041240.git.mchehab+huawei@kernel.org/
>
> to my tree (which reduces the set of clocks using CLK_IGNORE_UNUSED
> from 308 to 163 clocks). Than I ran script that was dropping the
> flag one by one, boots the new Kernel and do a sanity check. When it
> fails to boot, I manually dropped the patch, and re-run the script
> to test the remaining clocks. After a couple of weeks, I reached a patch
> with 78 clock lines that seemed critical, but the resulting patch was
> not stable, as, depending on the day I boot the Kernel with such patch,
> it crashes with SError in a couple of seconds after booting, or
> cause the Ethernet firmware to not load.
>
> I intend to keep trying to find the clock lines that can't be disabled,
> but this is very time consuming, as I couldn't find any documentation
> about that. So, it has to be done empirically.
>
> -
>
> In any case, fixing it doesn't sound a critical issue for the PHY
> driver. I mean, right now, this patchset allows removing and
> re-inseting the PCIe driver, which is already an improvement over the
> original upstream driver, which was missing the power-off logic for
> Kirin 960.
>
> With this patchset, both power-off/power-on logic for both HiKey960
> (where the PHY is inside the pcie-kirin driver) and for HiKey970,
> which uses this PHY driver. On both devices, I tested an endless loop
> with rmmod/modprobe for the PCIe.
>
> Besides that, in practice, removing PCIe in runtime is something that
> people usually don't do.
>
> So, while it would be cool to balance the clock disable logic,
> I don't think this is a critical issue in this particular case.

Okay sounds fair to me, I think fixme should be left but the c99 style
code commented out can be removed

--
~Vinod

2021-08-18 10:31:46

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: Re: [PATCH v11 01/11] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

Em Wed, 18 Aug 2021 15:40:27 +0530
Vinod Koul <[email protected]> escreveu:

> On 18-08-21, 11:01, Mauro Carvalho Chehab wrote:
> > Hi Vinod,
> >
> > Em Tue, 17 Aug 2021 16:12:37 +0530
> > Vinod Koul <[email protected]> escreveu:
> >
> > > > + /* FIXME: calling it causes an Asynchronous SError interrupt */
> > > > +// kirin_pcie_clk_ctrl(phy, false);
> > >
> > > when will you fix the fixme and pls remove the deadcode
> >
> > Working with clocks on this SoC is very tricky: there are lots of clock
> > lines (~70) that are critical for this device to work. Such lines are
> > enabled via the Device's firmware, and are supposed to be always
> > powered. Powering off such clock lines cause a SError.
> >
> > Most clocks on this device are managed by the clk-hikey3670 driver.
> > At the current state of clk-hi3670, the only way for HiKey 970
> > to even boot is to add:
> >
> > clk_ignore_unused=true
> >
> > as a Kernel boot parameter. That is the solution given by the downstream
> > official distributions for HiKey970 at 96boards.
> >
> > The fix is to flag the critical clocks with CLK_IS_CRITICAL at the
> > clk-hi3670 driver, but finding the right clock set has been a challenge.
> >
> > I spent the last couple of weeks trying to identify the critical ones,
> > as I'm aiming to be able to use a Kernel built with a default arm64
> > one of my goals is to have this device working fine with a
> > "make defconfig" Kernel.
> >
> > So, I added this patch:
> >
> > https://lore.kernel.org/lkml/2d2de5e902ced072bcfd5e5311d6b10326b9245b.1627041240.git.mchehab+huawei@kernel.org/
> >
> > to my tree (which reduces the set of clocks using CLK_IGNORE_UNUSED
> > from 308 to 163 clocks). Than I ran script that was dropping the
> > flag one by one, boots the new Kernel and do a sanity check. When it
> > fails to boot, I manually dropped the patch, and re-run the script
> > to test the remaining clocks. After a couple of weeks, I reached a patch
> > with 78 clock lines that seemed critical, but the resulting patch was
> > not stable, as, depending on the day I boot the Kernel with such patch,
> > it crashes with SError in a couple of seconds after booting, or
> > cause the Ethernet firmware to not load.
> >
> > I intend to keep trying to find the clock lines that can't be disabled,
> > but this is very time consuming, as I couldn't find any documentation
> > about that. So, it has to be done empirically.
> >
> > -
> >
> > In any case, fixing it doesn't sound a critical issue for the PHY
> > driver. I mean, right now, this patchset allows removing and
> > re-inseting the PCIe driver, which is already an improvement over the
> > original upstream driver, which was missing the power-off logic for
> > Kirin 960.
> >
> > With this patchset, both power-off/power-on logic for both HiKey960
> > (where the PHY is inside the pcie-kirin driver) and for HiKey970,
> > which uses this PHY driver. On both devices, I tested an endless loop
> > with rmmod/modprobe for the PCIe.
> >
> > Besides that, in practice, removing PCIe in runtime is something that
> > people usually don't do.
> >
> > So, while it would be cool to balance the clock disable logic,
> > I don't think this is a critical issue in this particular case.
>
> Okay sounds fair to me, I think fixme should be left but the c99 style
> code commented out can be removed

Agreed. I'll replace it with:

+ /*
+ * FIXME: The enabled clocks should be disabled here by calling
+ * kirin_pcie_clk_ctrl(phy, false);
+ * However, some clocks used at Kirin 970 should be marked as
+ * CLK_IS_CRITICAL at clk-hi3670 driver, as powering such clocks off
+ * cause an Asynchronous SError interrupt, which produces panic().
+ * While clk-hi3670 is not fixed, we cannot risk disabling clocks here.
+ */

Thanks,
Mauro

2021-08-18 10:39:38

by Vinod Koul

[permalink] [raw]
Subject: Re: [PATCH v11 01/11] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

On 18-08-21, 12:30, Mauro Carvalho Chehab wrote:
> Em Wed, 18 Aug 2021 15:40:27 +0530
> Vinod Koul <[email protected]> escreveu:

> > Okay sounds fair to me, I think fixme should be left but the c99 style
> > code commented out can be removed
>
> Agreed. I'll replace it with:
>
> + /*
> + * FIXME: The enabled clocks should be disabled here by calling
> + * kirin_pcie_clk_ctrl(phy, false);
> + * However, some clocks used at Kirin 970 should be marked as
> + * CLK_IS_CRITICAL at clk-hi3670 driver, as powering such clocks off
> + * cause an Asynchronous SError interrupt, which produces panic().
> + * While clk-hi3670 is not fixed, we cannot risk disabling clocks here.
> + */

sounds good!

--
~Vinod

2021-08-18 11:08:44

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: [PATCH v12] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

The Kirin 970 PHY is somewhat similar to the Kirin 960, but it
does a lot more. Add the needed bits for PCIe to start working on
HiKey 970 boards.

Co-developed-by: Manivannan Sadhasivam <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
Signed-off-by: Mauro Carvalho Chehab <[email protected]>
---

PS.:
As the PCIe patches aren't untouched, I'm sending just the PHY
driver patch on v12.

v12:
- removed c99-style comments;
- added hi3670_apb_phy_updatel() to update bits;
- allowed a couple of lines to be bigger than 80 columns.

drivers/phy/hisilicon/Kconfig | 10 +
drivers/phy/hisilicon/Makefile | 1 +
drivers/phy/hisilicon/phy-hi3670-pcie.c | 861 ++++++++++++++++++++++++
3 files changed, 872 insertions(+)
create mode 100644 drivers/phy/hisilicon/phy-hi3670-pcie.c

diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig
index 4d008cfc279c..d3b92c288554 100644
--- a/drivers/phy/hisilicon/Kconfig
+++ b/drivers/phy/hisilicon/Kconfig
@@ -33,6 +33,16 @@ config PHY_HI3670_USB

To compile this driver as a module, choose M here.

+config PHY_HI3670_PCIE
+ tristate "hi3670 PCIe PHY support"
+ depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+ select GENERIC_PHY
+ select MFD_SYSCON
+ help
+ Enable this to support the HiSilicon hi3670 PCIe PHY.
+
+ To compile this driver as a module, choose M here.
+
config PHY_HISTB_COMBPHY
tristate "HiSilicon STB SoCs COMBPHY support"
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile
index 51729868145b..4029d3813b1e 100644
--- a/drivers/phy/hisilicon/Makefile
+++ b/drivers/phy/hisilicon/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
obj-$(CONFIG_PHY_HI3660_USB) += phy-hi3660-usb3.o
obj-$(CONFIG_PHY_HI3670_USB) += phy-hi3670-usb3.o
+obj-$(CONFIG_PHY_HI3670_PCIE) += phy-hi3670-pcie.o
obj-$(CONFIG_PHY_HISTB_COMBPHY) += phy-histb-combphy.o
obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
diff --git a/drivers/phy/hisilicon/phy-hi3670-pcie.c b/drivers/phy/hisilicon/phy-hi3670-pcie.c
new file mode 100644
index 000000000000..f0c66e8e155d
--- /dev/null
+++ b/drivers/phy/hisilicon/phy-hi3670-pcie.c
@@ -0,0 +1,861 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe phy driver for Kirin 970
+ *
+ * Copyright (C) 2017 HiSilicon Electronics Co., Ltd.
+ * https://www.huawei.com
+ * Copyright (C) 2021 Huawei Technologies Co., Ltd.
+ * https://www.huawei.com
+ *
+ * Authors:
+ * Mauro Carvalho Chehab <[email protected]>
+ * Manivannan Sadhasivam <[email protected]>
+ *
+ * Based on:
+ * https://lore.kernel.org/lkml/4c9d6581478aa966698758c0420933f5defab4dd.1612335031.git.mchehab+huawei@kernel.org/
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define AXI_CLK_FREQ 207500000
+#define REF_CLK_FREQ 100000000
+
+/* PCIe CTRL registers */
+#define SOC_PCIECTRL_CTRL0_ADDR 0x000
+#define SOC_PCIECTRL_CTRL1_ADDR 0x004
+#define SOC_PCIECTRL_CTRL7_ADDR 0x01c
+#define SOC_PCIECTRL_CTRL12_ADDR 0x030
+#define SOC_PCIECTRL_CTRL20_ADDR 0x050
+#define SOC_PCIECTRL_CTRL21_ADDR 0x054
+#define SOC_PCIECTRL_STATE0_ADDR 0x400
+
+#define PCIE_OUTPUT_PULL_BITS GENMASK(3, 0)
+#define SOC_PCIECTRL_CTRL20_2P_MEM_CTRL 0x02605550
+#define SOC_PCIECTRL_CTRL21_DEFAULT 0x20000070
+#define PCIE_PULL_UP_SYS_AUX_PWR_DET BIT(10)
+#define PCIE_OUTPUT_PULL_DOWN BIT(1)
+
+/* PCIe PHY registers */
+#define SOC_PCIEPHY_CTRL0_ADDR 0x000
+#define SOC_PCIEPHY_CTRL1_ADDR 0x004
+#define SOC_PCIEPHY_CTRL2_ADDR 0x008
+#define SOC_PCIEPHY_CTRL3_ADDR 0x00c
+#define SOC_PCIEPHY_CTRL38_ADDR 0x0098
+#define SOC_PCIEPHY_STATE0_ADDR 0x400
+
+#define PORT_MSI_CTRL_ADDR 0x820
+#define PORT_MSI_CTRL_UPPER_ADDR 0x824
+#define PORT_MSI_CTRL_INT0_ENABLE 0x828
+
+#define RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 0xc004
+#define SUP_DIG_LVL_OVRD_IN 0x003c
+#define LANEN_DIG_ASIC_TX_OVRD_IN_1 0x4008
+#define LANEN_DIG_ASIC_TX_OVRD_IN_2 0x400c
+
+#define PCIE_LINKUP_ENABLE 0x8020
+#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21)
+#define PCIE_LTSSM_ENABLE_BIT BIT(11)
+#define PCIEPHY_RESET_BIT BIT(17)
+#define PCIEPHY_PIPE_LINE0_RESET_BIT BIT(19)
+#define PCIE_TXDETECT_RX_FAIL BIT(2)
+#define PCIE_CLK_SOURCE BIT(8)
+#define PCIE_IS_CLOCK_STABLE BIT(19)
+#define PCIE_PULL_DOWN_PHY_TEST_POWERDOWN BIT(22)
+#define PCIE_DEASSERT_CONTROLLER_PERST BIT(2)
+
+#define EYEPARAM_NOCFG 0xffffffff
+#define EYE_PARM0_MASK GENMASK(8, 6)
+#define EYE_PARM1_MASK GENMASK(11, 8)
+#define EYE_PARM2_MASK GENMASK(5, 0)
+#define EYE_PARM3_MASK GENMASK(12, 7)
+#define EYE_PARM4_MASK GENMASK(14, 9)
+#define EYE_PARM0_EN BIT(9)
+#define EYE_PARM1_EN BIT(12)
+#define EYE_PARM2_EN BIT(6)
+#define EYE_PARM3_EN BIT(13)
+#define EYE_PARM4_EN BIT(15)
+
+/* hi3670 pciephy register */
+#define APB_PHY_START_ADDR 0x40000
+#define SOC_PCIEPHY_MMC1PLL_CTRL1 0xc04
+#define SOC_PCIEPHY_MMC1PLL_CTRL16 0xC40
+#define SOC_PCIEPHY_MMC1PLL_CTRL17 0xC44
+#define SOC_PCIEPHY_MMC1PLL_CTRL20 0xC50
+#define SOC_PCIEPHY_MMC1PLL_CTRL21 0xC54
+#define SOC_PCIEPHY_MMC1PLL_STAT0 0xE00
+
+#define CRGPERIPH_PEREN12 0x470
+#define CRGPERIPH_PERDIS12 0x474
+#define CRGPERIPH_PCIECTRL0 0x800
+
+#define PCIE_FNPLL_FBDIV_MASK GENMASK(27, 16)
+#define PCIE_FNPLL_FRACDIV_MASK GENMASK(23, 0)
+#define PCIE_FNPLL_POSTDIV1_MASK GENMASK(10, 8)
+#define PCIE_FNPLL_POSTDIV2_MASK GENMASK(14, 12)
+#define PCIE_FNPLL_PLL_MODE_MASK BIT(25)
+
+#define PCIE_FNPLL_DLL_EN BIT(27)
+#define PCIE_FNPLL_FBDIV 0xd0
+#define PCIE_FNPLL_FRACDIV 0x555555
+#define PCIE_FNPLL_POSTDIV1 0x5
+#define PCIE_FNPLL_POSTDIV2 0x4
+#define PCIE_FNPLL_PLL_MODE 0x0
+
+#define PCIE_PHY_MMC1PLL 0x20
+#define PCIE_PHY_CHOOSE_FNPLL BIT(27)
+#define PCIE_PHY_MMC1PLL_DISABLE BIT(0)
+#define PCIE_PHY_PCIEPL_BP BIT(16)
+
+/* define ie,oe cfg */
+#define IO_OE_HARD_GT_MODE BIT(1)
+#define IO_IE_EN_HARD_BYPASS BIT(27)
+#define IO_OE_EN_HARD_BYPASS BIT(11)
+#define IO_HARD_CTRL_DEBOUNCE_BYPASS BIT(10)
+#define IO_OE_GT_MODE BIT(8)
+#define DEBOUNCE_WAITCFG_IN GENMASK(23, 20)
+#define DEBOUNCE_WAITCFG_OUT GENMASK(16, 13)
+
+#define IO_HP_DEBOUNCE_GT (BIT(12) | BIT(15))
+#define IO_PHYREF_SOFT_GT_MODE BIT(14)
+#define IO_REF_SOFT_GT_MODE BIT(13)
+#define IO_REF_HARD_GT_MODE BIT(0)
+
+/* noc power domain */
+#define NOC_POWER_IDLEREQ_1 0x38c
+#define NOC_POWER_IDLE_1 0x394
+#define NOC_PW_MASK 0x10000
+#define NOC_PW_SET_BIT 0x1
+
+#define NUM_EYEPARAM 5
+
+/* 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 GENMASK(29, 28)
+
+/* peri_crg ctrl */
+#define CRGCTRL_PCIE_ASSERT_OFFSET 0x88
+#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000
+
+#define FNPLL_HAS_LOCKED BIT(4)
+
+/* Time for delay */
+#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
+
+#define PIPE_CLK_STABLE_TIME 100
+#define PLL_CTRL_WAIT_TIME 200
+#define NOC_POWER_TIME 100
+
+struct hi3670_pcie_phy {
+ struct device *dev;
+ void __iomem *base;
+ struct regmap *apb;
+ struct regmap *crgctrl;
+ struct regmap *sysctrl;
+ struct regmap *pmctrl;
+ struct clk *apb_sys_clk;
+ struct clk *apb_phy_clk;
+ struct clk *phy_ref_clk;
+ struct clk *aclk;
+ struct clk *aux_clk;
+ u32 eye_param[NUM_EYEPARAM];
+};
+
+/* Registers in PCIePHY */
+static inline void hi3670_apb_phy_writel(struct hi3670_pcie_phy *phy, u32 val,
+ u32 reg)
+{
+ writel(val, phy->base + APB_PHY_START_ADDR + reg);
+}
+
+static inline u32 hi3670_apb_phy_readl(struct hi3670_pcie_phy *phy, u32 reg)
+{
+ return readl(phy->base + APB_PHY_START_ADDR + reg);
+}
+
+static inline void hi3670_apb_phy_updatel(struct hi3670_pcie_phy *phy,
+ u32 val, u32 mask, u32 reg)
+{
+ u32 regval;
+
+ regval = hi3670_apb_phy_readl(phy, reg);
+ regval &= ~mask;
+ regval |= val;
+ hi3670_apb_phy_writel(phy, regval, reg);
+}
+
+static inline void kirin_apb_natural_phy_writel(struct hi3670_pcie_phy *phy,
+ u32 val, u32 reg)
+{
+ writel(val, phy->base + reg);
+}
+
+static inline u32 kirin_apb_natural_phy_readl(struct hi3670_pcie_phy *phy,
+ u32 reg)
+{
+ return readl(phy->base + reg);
+}
+
+static void hi3670_pcie_phy_oe_enable(struct hi3670_pcie_phy *phy, bool enable)
+{
+ u32 val;
+
+ regmap_read(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, &val);
+ val |= PCIE_DEBOUNCE_PARAM;
+ if (enable)
+ val &= ~PCIE_OE_BYPASS;
+ else
+ val |= PCIE_OE_BYPASS;
+ regmap_write(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, val);
+}
+
+static void hi3670_pcie_get_eyeparam(struct hi3670_pcie_phy *phy)
+{
+ struct device *dev = phy->dev;
+ struct device_node *np;
+ int ret, i;
+
+ np = dev->of_node;
+
+ ret = of_property_read_u32_array(np, "hisilicon,eye-diagram-param",
+ phy->eye_param, NUM_EYEPARAM);
+ if (!ret)
+ return;
+
+ /* There's no optional eye_param property. Set array to default */
+ for (i = 0; i < NUM_EYEPARAM; i++)
+ phy->eye_param[i] = EYEPARAM_NOCFG;
+}
+
+static void hi3670_pcie_set_eyeparam(struct hi3670_pcie_phy *phy)
+{
+ u32 val;
+
+ val = kirin_apb_natural_phy_readl(phy, RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
+
+ if (phy->eye_param[1] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM1_MASK;
+ val |= FIELD_PREP(EYE_PARM1_MASK, phy->eye_param[1]);
+ val |= EYE_PARM1_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val,
+ RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
+
+ val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_2);
+ val &= ~(EYE_PARM2_MASK | EYE_PARM3_MASK);
+ if (phy->eye_param[2] != EYEPARAM_NOCFG) {
+ val |= FIELD_PREP(EYE_PARM2_MASK, phy->eye_param[2]);
+ val |= EYE_PARM2_EN;
+ }
+
+ if (phy->eye_param[3] != EYEPARAM_NOCFG) {
+ val |= FIELD_PREP(EYE_PARM3_MASK, phy->eye_param[3]);
+ val |= EYE_PARM3_EN;
+ }
+
+ kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_2);
+
+ val = kirin_apb_natural_phy_readl(phy, SUP_DIG_LVL_OVRD_IN);
+ if (phy->eye_param[0] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM0_MASK;
+ val |= FIELD_PREP(EYE_PARM0_MASK, phy->eye_param[0]);
+ val |= EYE_PARM0_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val, SUP_DIG_LVL_OVRD_IN);
+
+ val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_1);
+ if (phy->eye_param[4] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM4_MASK;
+ val |= FIELD_PREP(EYE_PARM4_MASK, phy->eye_param[4]);
+ val |= EYE_PARM4_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_1);
+}
+
+static void hi3670_pcie_natural_cfg(struct hi3670_pcie_phy *phy)
+{
+ u32 val;
+
+ /* change 2p mem_ctrl */
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL20_ADDR,
+ SOC_PCIECTRL_CTRL20_2P_MEM_CTRL);
+
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, &val);
+ val |= PCIE_PULL_UP_SYS_AUX_PWR_DET;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, val);
+
+ /* output, pull down */
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val);
+ val &= ~PCIE_OUTPUT_PULL_BITS;
+ val |= PCIE_OUTPUT_PULL_DOWN;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val);
+
+ /* Handle phy_reset and lane0_reset to HW */
+ hi3670_apb_phy_updatel(phy, PCIEPHY_RESET_BIT,
+ PCIEPHY_PIPE_LINE0_RESET_BIT | PCIEPHY_RESET_BIT,
+ SOC_PCIEPHY_CTRL1_ADDR);
+
+ /* fix chip bug: TxDetectRx fail */
+ hi3670_apb_phy_updatel(phy, PCIE_TXDETECT_RX_FAIL, PCIE_TXDETECT_RX_FAIL,
+ SOC_PCIEPHY_CTRL38_ADDR);
+}
+
+static void hi3670_pcie_pll_init(struct hi3670_pcie_phy *phy)
+{
+ hi3670_apb_phy_updatel(phy, PCIE_PHY_CHOOSE_FNPLL, PCIE_PHY_CHOOSE_FNPLL,
+ SOC_PCIEPHY_MMC1PLL_CTRL1);
+
+
+ hi3670_apb_phy_updatel(phy,
+ FIELD_PREP(PCIE_FNPLL_FBDIV_MASK, PCIE_FNPLL_FBDIV),
+ PCIE_FNPLL_FBDIV_MASK,
+ SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ hi3670_apb_phy_updatel(phy,
+ FIELD_PREP(PCIE_FNPLL_FRACDIV_MASK, PCIE_FNPLL_FRACDIV),
+ PCIE_FNPLL_FRACDIV_MASK, SOC_PCIEPHY_MMC1PLL_CTRL17);
+
+ hi3670_apb_phy_updatel(phy,
+ PCIE_FNPLL_DLL_EN |
+ FIELD_PREP(PCIE_FNPLL_POSTDIV1_MASK, PCIE_FNPLL_POSTDIV1) |
+ FIELD_PREP(PCIE_FNPLL_POSTDIV2_MASK, PCIE_FNPLL_POSTDIV2) |
+ FIELD_PREP(PCIE_FNPLL_PLL_MODE_MASK, PCIE_FNPLL_PLL_MODE),
+ PCIE_FNPLL_POSTDIV1_MASK |
+ PCIE_FNPLL_POSTDIV2_MASK |
+ PCIE_FNPLL_PLL_MODE_MASK | PCIE_FNPLL_DLL_EN,
+ SOC_PCIEPHY_MMC1PLL_CTRL20);
+
+ hi3670_apb_phy_writel(phy, PCIE_PHY_MMC1PLL,
+ SOC_PCIEPHY_MMC1PLL_CTRL21);
+}
+
+static int hi3670_pcie_pll_ctrl(struct hi3670_pcie_phy *phy, bool enable)
+{
+ struct device *dev = phy->dev;
+ u32 val;
+ int time = PLL_CTRL_WAIT_TIME;
+
+ if (enable) {
+ /* pd = 0 */
+ hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_MMC1PLL_DISABLE,
+ SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ /* choose FNPLL */
+ while (!(val & FNPLL_HAS_LOCKED)) {
+ if (!time) {
+ dev_err(dev, "wait for pll_lock timeout\n");
+ return -EINVAL;
+ }
+ time--;
+ udelay(1);
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0);
+ }
+
+ hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_PCIEPL_BP,
+ SOC_PCIEPHY_MMC1PLL_CTRL20);
+
+ } else {
+ hi3670_apb_phy_updatel(phy,
+ PCIE_PHY_MMC1PLL_DISABLE,
+ PCIE_PHY_MMC1PLL_DISABLE,
+ SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ hi3670_apb_phy_updatel(phy, PCIE_PHY_PCIEPL_BP,
+ PCIE_PHY_PCIEPL_BP,
+ SOC_PCIEPHY_MMC1PLL_CTRL20);
+ }
+
+ return 0;
+}
+
+static void hi3670_pcie_hp_debounce_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ if (open)
+ /* gt_clk_pcie_hp/gt_clk_pcie_debounce open */
+ regmap_write(phy->crgctrl, CRGPERIPH_PEREN12,
+ IO_HP_DEBOUNCE_GT);
+ else
+ /* gt_clk_pcie_hp/gt_clk_pcie_debounce close */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_HP_DEBOUNCE_GT);
+}
+
+static void hi3670_pcie_phyref_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ unsigned int val;
+
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+
+ if (open)
+ val &= ~IO_OE_HARD_GT_MODE; /* enable hard gt mode */
+ else
+ val |= IO_OE_HARD_GT_MODE; /* disable hard gt mode */
+
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, IO_PHYREF_SOFT_GT_MODE);
+}
+
+static void hi3670_pcie_oe_ctrl(struct hi3670_pcie_phy *phy, bool en_flag)
+{
+ unsigned int val;
+
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+
+ /* set ie cfg */
+ val |= IO_IE_EN_HARD_BYPASS;
+
+ /* set oe cfg */
+ val &= ~IO_HARD_CTRL_DEBOUNCE_BYPASS;
+
+ /* set phy_debounce in&out time */
+ val |= (DEBOUNCE_WAITCFG_IN | DEBOUNCE_WAITCFG_OUT);
+
+ /* select oe_gt_mode */
+ val |= IO_OE_GT_MODE;
+
+ if (en_flag)
+ val &= ~IO_OE_EN_HARD_BYPASS;
+ else
+ val |= IO_OE_EN_HARD_BYPASS;
+
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+}
+
+static void hi3670_pcie_ioref_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ unsigned int val;
+
+ if (open) {
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL21_ADDR,
+ SOC_PCIECTRL_CTRL21_DEFAULT);
+
+ hi3670_pcie_oe_ctrl(phy, true);
+
+ /* en hard gt mode */
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+ val &= ~IO_REF_HARD_GT_MODE;
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_REF_SOFT_GT_MODE);
+
+ } else {
+ /* disable hard gt mode */
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+ val |= IO_REF_HARD_GT_MODE;
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_REF_SOFT_GT_MODE);
+
+ hi3670_pcie_oe_ctrl(phy, false);
+ }
+}
+
+static int hi3670_pcie_allclk_ctrl(struct hi3670_pcie_phy *phy, bool clk_on)
+{
+ struct device *dev = phy->dev;
+ int ret = 0;
+
+ if (!clk_on)
+ goto close_clocks;
+
+ /* choose 100MHz clk src: Bit[8]==1 pad, Bit[8]==0 pll */
+ hi3670_apb_phy_updatel(phy, 0, PCIE_CLK_SOURCE,
+ SOC_PCIEPHY_CTRL1_ADDR);
+
+ hi3670_pcie_pll_init(phy);
+
+ ret = hi3670_pcie_pll_ctrl(phy, true);
+ if (ret) {
+ dev_err(dev, "Failed to enable pll\n");
+ return -EINVAL;
+ }
+ hi3670_pcie_hp_debounce_gt(phy, true);
+ hi3670_pcie_phyref_gt(phy, true);
+ hi3670_pcie_ioref_gt(phy, true);
+
+ ret = clk_set_rate(phy->aclk, AXI_CLK_FREQ);
+ if (ret) {
+ dev_err(dev, "Failed to set rate\n");
+ goto close_clocks;
+ }
+
+ return 0;
+
+close_clocks:
+ hi3670_pcie_ioref_gt(phy, false);
+ hi3670_pcie_phyref_gt(phy, false);
+ hi3670_pcie_hp_debounce_gt(phy, false);
+
+ hi3670_pcie_pll_ctrl(phy, false);
+
+ return ret;
+}
+
+static bool is_pipe_clk_stable(struct hi3670_pcie_phy *phy)
+{
+ struct device *dev = phy->dev;
+ u32 val;
+ u32 time = PIPE_CLK_STABLE_TIME;
+ u32 pipe_clk_stable = PCIE_IS_CLOCK_STABLE;
+
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR);
+ while (val & pipe_clk_stable) {
+ mdelay(1);
+ if (!time) {
+ dev_err(dev, "PIPE clk is not stable\n");
+ return false;
+ }
+ time--;
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR);
+ }
+
+ return true;
+}
+
+static int hi3670_pcie_noc_power(struct hi3670_pcie_phy *phy, bool enable)
+{
+ struct device *dev = phy->dev;
+ u32 time = NOC_POWER_TIME;
+ unsigned int val = NOC_PW_MASK;
+ int rst;
+
+ if (enable)
+ val = NOC_PW_MASK | NOC_PW_SET_BIT;
+ else
+ val = NOC_PW_MASK;
+ rst = enable ? 1 : 0;
+
+ regmap_write(phy->pmctrl, NOC_POWER_IDLEREQ_1, val);
+
+ time = NOC_POWER_TIME;
+ regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val);
+ while ((val & NOC_PW_SET_BIT) != rst) {
+ udelay(10);
+ if (!time) {
+ dev_err(dev, "Failed to reverse noc power-status\n");
+ return -EINVAL;
+ }
+ time--;
+ regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val);
+ }
+
+ return 0;
+}
+
+static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy)
+{
+ struct device_node *pcie_port;
+ struct device *dev = phy->dev;
+ struct device *pcie_dev;
+
+ pcie_port = of_get_child_by_name(dev->parent->of_node, "pcie");
+ if (!pcie_port) {
+ dev_err(dev, "no pcie node found in %s\n",
+ dev->parent->of_node->full_name);
+ return -ENODEV;
+ }
+
+ pcie_dev = bus_find_device_by_of_node(&platform_bus_type, pcie_port);
+ if (!pcie_dev) {
+ dev_err(dev, "Didn't find pcie device\n");
+ return -ENODEV;
+ }
+
+ /*
+ * We might just use NULL instead of the APB name, as the
+ * pcie-kirin currently registers directly just one regmap (although
+ * the DWC driver register other regmaps).
+ *
+ * Yet, it sounds safer to warrant that it will be accessing the
+ * right regmap. So, let's use the named version.
+ */
+ phy->apb = dev_get_regmap(pcie_dev, "kirin_pcie_apb");
+ if (!phy->apb) {
+ dev_err(dev, "Failed to get APB regmap\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int kirin_pcie_clk_ctrl(struct hi3670_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 hi3670_pcie_phy_init(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+ int ret;
+
+ /*
+ * The code under hi3670_pcie_get_resources_from_pcie() need to
+ * access the reset-gpios and the APB registers, both from the
+ * pcie-kirin driver.
+ *
+ * The APB is obtained via the pcie driver's regmap
+ * Such kind of resource can only be obtained during the PCIe
+ * power_on sequence, as the code inside pcie-kirin needs to
+ * be already probed, as it needs to register the APB regmap.
+ */
+
+ ret = hi3670_pcie_get_resources_from_pcie(phy);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int hi3670_pcie_phy_power_on(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+ int val, 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);
+
+ hi3670_pcie_phy_oe_enable(phy, true);
+
+ ret = kirin_pcie_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);
+
+ hi3670_pcie_natural_cfg(phy);
+
+ ret = hi3670_pcie_allclk_ctrl(phy, true);
+ if (ret)
+ goto disable_clks;
+
+ /* pull down phy_test_powerdown signal */
+ hi3670_apb_phy_updatel(phy, 0, PCIE_PULL_DOWN_PHY_TEST_POWERDOWN,
+ SOC_PCIEPHY_CTRL0_ADDR);
+
+ /* deassert controller perst_n */
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val);
+ val |= PCIE_DEASSERT_CONTROLLER_PERST;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val);
+ udelay(10);
+
+ ret = is_pipe_clk_stable(phy);
+ if (!ret)
+ goto disable_clks;
+
+ hi3670_pcie_set_eyeparam(phy);
+
+ ret = hi3670_pcie_noc_power(phy, false);
+ if (ret)
+ goto disable_clks;
+
+ return 0;
+
+disable_clks:
+ kirin_pcie_clk_ctrl(phy, false);
+ return ret;
+}
+
+static int hi3670_pcie_phy_power_off(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+
+ hi3670_pcie_phy_oe_enable(phy, false);
+
+ hi3670_pcie_allclk_ctrl(phy, false);
+
+ /* Drop power supply for Host */
+ regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, 0);
+
+ /*
+ * FIXME: The enabled clocks should be disabled here by calling
+ * kirin_pcie_clk_ctrl(phy, false);
+ * However, some clocks used at Kirin 970 should be marked as
+ * CLK_IS_CRITICAL at clk-hi3670 driver, as powering such clocks off
+ * cause an Asynchronous SError interrupt, which produces panic().
+ * While clk-hi3670 is not fixed, we cannot risk disabling clocks here.
+ */
+
+ return 0;
+}
+
+static const struct phy_ops hi3670_phy_ops = {
+ .init = hi3670_pcie_phy_init,
+ .power_on = hi3670_pcie_phy_power_on,
+ .power_off = hi3670_pcie_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int hi3670_pcie_phy_get_resources(struct hi3670_pcie_phy *phy,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ /* syscon */
+ phy->crgctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-crgctrl");
+ if (IS_ERR(phy->crgctrl))
+ return PTR_ERR(phy->crgctrl);
+
+ phy->sysctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-sctrl");
+ if (IS_ERR(phy->sysctrl))
+ return PTR_ERR(phy->sysctrl);
+
+ phy->pmctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-pmctrl");
+ if (IS_ERR(phy->sysctrl))
+ return PTR_ERR(phy->sysctrl);
+
+ /* clocks */
+ phy->phy_ref_clk = devm_clk_get(dev, "phy_ref");
+ if (IS_ERR(phy->phy_ref_clk))
+ return PTR_ERR(phy->phy_ref_clk);
+
+ phy->aux_clk = devm_clk_get(dev, "aux");
+ if (IS_ERR(phy->aux_clk))
+ return PTR_ERR(phy->aux_clk);
+
+ phy->apb_phy_clk = devm_clk_get(dev, "apb_phy");
+ if (IS_ERR(phy->apb_phy_clk))
+ return PTR_ERR(phy->apb_phy_clk);
+
+ phy->apb_sys_clk = devm_clk_get(dev, "apb_sys");
+ if (IS_ERR(phy->apb_sys_clk))
+ return PTR_ERR(phy->apb_sys_clk);
+
+ phy->aclk = devm_clk_get(dev, "aclk");
+ if (IS_ERR(phy->aclk))
+ return PTR_ERR(phy->aclk);
+
+ /* registers */
+ phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(phy->base))
+ return PTR_ERR(phy->base);
+
+ hi3670_pcie_get_eyeparam(phy);
+
+ return 0;
+}
+
+static int hi3670_pcie_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct hi3670_pcie_phy *phy;
+ struct phy *generic_phy;
+ int ret;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->dev = dev;
+
+ ret = hi3670_pcie_phy_get_resources(phy, pdev);
+ if (ret)
+ return ret;
+
+ generic_phy = devm_phy_create(dev, dev->of_node, &hi3670_phy_ops);
+ if (IS_ERR(generic_phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(generic_phy);
+ }
+
+ phy_set_drvdata(generic_phy, phy);
+ 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 hi3670_pcie_phy_match[] = {
+ {
+ .compatible = "hisilicon,hi970-pcie-phy",
+ },
+ {},
+};
+
+static struct platform_driver hi3670_pcie_phy_driver = {
+ .probe = hi3670_pcie_phy_probe,
+ .driver = {
+ .of_match_table = hi3670_pcie_phy_match,
+ .name = "hi3670_pcie_phy",
+ .suppress_bind_attrs = true,
+ }
+};
+builtin_platform_driver(hi3670_pcie_phy_driver);
+
+MODULE_DEVICE_TABLE(of, hi3670_pcie_phy_match);
+MODULE_DESCRIPTION("PCIe phy driver for Kirin 970");
+MODULE_AUTHOR("Mauro Carvalho Chehab <[email protected]>");
+MODULE_AUTHOR("Manivannan Sadhasivam <[email protected]>");
+MODULE_LICENSE("GPL v2");
--
2.31.1


2021-08-18 15:23:24

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v12] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

Hi Mauro,

I love your patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v5.14-rc6 next-20210818]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/0day-ci/linux/commits/Mauro-Carvalho-Chehab/phy-HiSilicon-Add-driver-for-Kirin-970-PCIe-PHY/20210818-190539
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 614cb2751d3150850d459bee596c397f344a7936
config: ia64-allmodconfig (attached as .config)
compiler: ia64-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/90e806228f10f8375d808f4813c6ea64aaa91a2a
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Mauro-Carvalho-Chehab/phy-HiSilicon-Add-driver-for-Kirin-970-PCIe-PHY/20210818-190539
git checkout 90e806228f10f8375d808f4813c6ea64aaa91a2a
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=ia64

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>):

drivers/phy/hisilicon/phy-hi3670-pcie.c: In function 'hi3670_pcie_set_eyeparam':
>> drivers/phy/hisilicon/phy-hi3670-pcie.c:257:24: error: implicit declaration of function 'FIELD_PREP' [-Werror=implicit-function-declaration]
257 | val |= FIELD_PREP(EYE_PARM1_MASK, phy->eye_param[1]);
| ^~~~~~~~~~
cc1: some warnings being treated as errors


vim +/FIELD_PREP +257 drivers/phy/hisilicon/phy-hi3670-pcie.c

248
249 static void hi3670_pcie_set_eyeparam(struct hi3670_pcie_phy *phy)
250 {
251 u32 val;
252
253 val = kirin_apb_natural_phy_readl(phy, RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
254
255 if (phy->eye_param[1] != EYEPARAM_NOCFG) {
256 val &= ~EYE_PARM1_MASK;
> 257 val |= FIELD_PREP(EYE_PARM1_MASK, phy->eye_param[1]);
258 val |= EYE_PARM1_EN;
259 }
260 kirin_apb_natural_phy_writel(phy, val,
261 RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
262
263 val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_2);
264 val &= ~(EYE_PARM2_MASK | EYE_PARM3_MASK);
265 if (phy->eye_param[2] != EYEPARAM_NOCFG) {
266 val |= FIELD_PREP(EYE_PARM2_MASK, phy->eye_param[2]);
267 val |= EYE_PARM2_EN;
268 }
269
270 if (phy->eye_param[3] != EYEPARAM_NOCFG) {
271 val |= FIELD_PREP(EYE_PARM3_MASK, phy->eye_param[3]);
272 val |= EYE_PARM3_EN;
273 }
274
275 kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_2);
276
277 val = kirin_apb_natural_phy_readl(phy, SUP_DIG_LVL_OVRD_IN);
278 if (phy->eye_param[0] != EYEPARAM_NOCFG) {
279 val &= ~EYE_PARM0_MASK;
280 val |= FIELD_PREP(EYE_PARM0_MASK, phy->eye_param[0]);
281 val |= EYE_PARM0_EN;
282 }
283 kirin_apb_natural_phy_writel(phy, val, SUP_DIG_LVL_OVRD_IN);
284
285 val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_1);
286 if (phy->eye_param[4] != EYEPARAM_NOCFG) {
287 val &= ~EYE_PARM4_MASK;
288 val |= FIELD_PREP(EYE_PARM4_MASK, phy->eye_param[4]);
289 val |= EYE_PARM4_EN;
290 }
291 kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_1);
292 }
293

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (3.79 kB)
.config.gz (62.97 kB)
Download all attachments

2021-08-20 13:49:36

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: [PATCH v13] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

The Kirin 970 PHY is somewhat similar to the Kirin 960, but it
does a lot more. Add the needed bits for PCIe to start working on
HiKey 970 boards.

Co-developed-by: Manivannan Sadhasivam <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
Signed-off-by: Mauro Carvalho Chehab <[email protected]>
---

PS.:
As the PCIe patches aren't untouched, I'm sending just the PHY
driver patch on v12.

v13:
- add missing include bitfield.h

v12:
- removed c99-style comments;
- added hi3670_apb_phy_updatel() to update bits;
- allowed a couple of lines to be bigger than 80 columns.


drivers/phy/hisilicon/Kconfig | 10 +
drivers/phy/hisilicon/Makefile | 1 +
drivers/phy/hisilicon/phy-hi3670-pcie.c | 862 ++++++++++++++++++++++++
3 files changed, 873 insertions(+)
create mode 100644 drivers/phy/hisilicon/phy-hi3670-pcie.c

diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig
index 4d008cfc279c..d3b92c288554 100644
--- a/drivers/phy/hisilicon/Kconfig
+++ b/drivers/phy/hisilicon/Kconfig
@@ -33,6 +33,16 @@ config PHY_HI3670_USB

To compile this driver as a module, choose M here.

+config PHY_HI3670_PCIE
+ tristate "hi3670 PCIe PHY support"
+ depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+ select GENERIC_PHY
+ select MFD_SYSCON
+ help
+ Enable this to support the HiSilicon hi3670 PCIe PHY.
+
+ To compile this driver as a module, choose M here.
+
config PHY_HISTB_COMBPHY
tristate "HiSilicon STB SoCs COMBPHY support"
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile
index 51729868145b..4029d3813b1e 100644
--- a/drivers/phy/hisilicon/Makefile
+++ b/drivers/phy/hisilicon/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
obj-$(CONFIG_PHY_HI3660_USB) += phy-hi3660-usb3.o
obj-$(CONFIG_PHY_HI3670_USB) += phy-hi3670-usb3.o
+obj-$(CONFIG_PHY_HI3670_PCIE) += phy-hi3670-pcie.o
obj-$(CONFIG_PHY_HISTB_COMBPHY) += phy-histb-combphy.o
obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
diff --git a/drivers/phy/hisilicon/phy-hi3670-pcie.c b/drivers/phy/hisilicon/phy-hi3670-pcie.c
new file mode 100644
index 000000000000..7403484f7141
--- /dev/null
+++ b/drivers/phy/hisilicon/phy-hi3670-pcie.c
@@ -0,0 +1,862 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe phy driver for Kirin 970
+ *
+ * Copyright (C) 2017 HiSilicon Electronics Co., Ltd.
+ * https://www.huawei.com
+ * Copyright (C) 2021 Huawei Technologies Co., Ltd.
+ * https://www.huawei.com
+ *
+ * Authors:
+ * Mauro Carvalho Chehab <[email protected]>
+ * Manivannan Sadhasivam <[email protected]>
+ *
+ * Based on:
+ * https://lore.kernel.org/lkml/4c9d6581478aa966698758c0420933f5defab4dd.1612335031.git.mchehab+huawei@kernel.org/
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define AXI_CLK_FREQ 207500000
+#define REF_CLK_FREQ 100000000
+
+/* PCIe CTRL registers */
+#define SOC_PCIECTRL_CTRL0_ADDR 0x000
+#define SOC_PCIECTRL_CTRL1_ADDR 0x004
+#define SOC_PCIECTRL_CTRL7_ADDR 0x01c
+#define SOC_PCIECTRL_CTRL12_ADDR 0x030
+#define SOC_PCIECTRL_CTRL20_ADDR 0x050
+#define SOC_PCIECTRL_CTRL21_ADDR 0x054
+#define SOC_PCIECTRL_STATE0_ADDR 0x400
+
+#define PCIE_OUTPUT_PULL_BITS GENMASK(3, 0)
+#define SOC_PCIECTRL_CTRL20_2P_MEM_CTRL 0x02605550
+#define SOC_PCIECTRL_CTRL21_DEFAULT 0x20000070
+#define PCIE_PULL_UP_SYS_AUX_PWR_DET BIT(10)
+#define PCIE_OUTPUT_PULL_DOWN BIT(1)
+
+/* PCIe PHY registers */
+#define SOC_PCIEPHY_CTRL0_ADDR 0x000
+#define SOC_PCIEPHY_CTRL1_ADDR 0x004
+#define SOC_PCIEPHY_CTRL2_ADDR 0x008
+#define SOC_PCIEPHY_CTRL3_ADDR 0x00c
+#define SOC_PCIEPHY_CTRL38_ADDR 0x0098
+#define SOC_PCIEPHY_STATE0_ADDR 0x400
+
+#define PORT_MSI_CTRL_ADDR 0x820
+#define PORT_MSI_CTRL_UPPER_ADDR 0x824
+#define PORT_MSI_CTRL_INT0_ENABLE 0x828
+
+#define RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 0xc004
+#define SUP_DIG_LVL_OVRD_IN 0x003c
+#define LANEN_DIG_ASIC_TX_OVRD_IN_1 0x4008
+#define LANEN_DIG_ASIC_TX_OVRD_IN_2 0x400c
+
+#define PCIE_LINKUP_ENABLE 0x8020
+#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21)
+#define PCIE_LTSSM_ENABLE_BIT BIT(11)
+#define PCIEPHY_RESET_BIT BIT(17)
+#define PCIEPHY_PIPE_LINE0_RESET_BIT BIT(19)
+#define PCIE_TXDETECT_RX_FAIL BIT(2)
+#define PCIE_CLK_SOURCE BIT(8)
+#define PCIE_IS_CLOCK_STABLE BIT(19)
+#define PCIE_PULL_DOWN_PHY_TEST_POWERDOWN BIT(22)
+#define PCIE_DEASSERT_CONTROLLER_PERST BIT(2)
+
+#define EYEPARAM_NOCFG 0xffffffff
+#define EYE_PARM0_MASK GENMASK(8, 6)
+#define EYE_PARM1_MASK GENMASK(11, 8)
+#define EYE_PARM2_MASK GENMASK(5, 0)
+#define EYE_PARM3_MASK GENMASK(12, 7)
+#define EYE_PARM4_MASK GENMASK(14, 9)
+#define EYE_PARM0_EN BIT(9)
+#define EYE_PARM1_EN BIT(12)
+#define EYE_PARM2_EN BIT(6)
+#define EYE_PARM3_EN BIT(13)
+#define EYE_PARM4_EN BIT(15)
+
+/* hi3670 pciephy register */
+#define APB_PHY_START_ADDR 0x40000
+#define SOC_PCIEPHY_MMC1PLL_CTRL1 0xc04
+#define SOC_PCIEPHY_MMC1PLL_CTRL16 0xC40
+#define SOC_PCIEPHY_MMC1PLL_CTRL17 0xC44
+#define SOC_PCIEPHY_MMC1PLL_CTRL20 0xC50
+#define SOC_PCIEPHY_MMC1PLL_CTRL21 0xC54
+#define SOC_PCIEPHY_MMC1PLL_STAT0 0xE00
+
+#define CRGPERIPH_PEREN12 0x470
+#define CRGPERIPH_PERDIS12 0x474
+#define CRGPERIPH_PCIECTRL0 0x800
+
+#define PCIE_FNPLL_FBDIV_MASK GENMASK(27, 16)
+#define PCIE_FNPLL_FRACDIV_MASK GENMASK(23, 0)
+#define PCIE_FNPLL_POSTDIV1_MASK GENMASK(10, 8)
+#define PCIE_FNPLL_POSTDIV2_MASK GENMASK(14, 12)
+#define PCIE_FNPLL_PLL_MODE_MASK BIT(25)
+
+#define PCIE_FNPLL_DLL_EN BIT(27)
+#define PCIE_FNPLL_FBDIV 0xd0
+#define PCIE_FNPLL_FRACDIV 0x555555
+#define PCIE_FNPLL_POSTDIV1 0x5
+#define PCIE_FNPLL_POSTDIV2 0x4
+#define PCIE_FNPLL_PLL_MODE 0x0
+
+#define PCIE_PHY_MMC1PLL 0x20
+#define PCIE_PHY_CHOOSE_FNPLL BIT(27)
+#define PCIE_PHY_MMC1PLL_DISABLE BIT(0)
+#define PCIE_PHY_PCIEPL_BP BIT(16)
+
+/* define ie,oe cfg */
+#define IO_OE_HARD_GT_MODE BIT(1)
+#define IO_IE_EN_HARD_BYPASS BIT(27)
+#define IO_OE_EN_HARD_BYPASS BIT(11)
+#define IO_HARD_CTRL_DEBOUNCE_BYPASS BIT(10)
+#define IO_OE_GT_MODE BIT(8)
+#define DEBOUNCE_WAITCFG_IN GENMASK(23, 20)
+#define DEBOUNCE_WAITCFG_OUT GENMASK(16, 13)
+
+#define IO_HP_DEBOUNCE_GT (BIT(12) | BIT(15))
+#define IO_PHYREF_SOFT_GT_MODE BIT(14)
+#define IO_REF_SOFT_GT_MODE BIT(13)
+#define IO_REF_HARD_GT_MODE BIT(0)
+
+/* noc power domain */
+#define NOC_POWER_IDLEREQ_1 0x38c
+#define NOC_POWER_IDLE_1 0x394
+#define NOC_PW_MASK 0x10000
+#define NOC_PW_SET_BIT 0x1
+
+#define NUM_EYEPARAM 5
+
+/* 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 GENMASK(29, 28)
+
+/* peri_crg ctrl */
+#define CRGCTRL_PCIE_ASSERT_OFFSET 0x88
+#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000
+
+#define FNPLL_HAS_LOCKED BIT(4)
+
+/* Time for delay */
+#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
+
+#define PIPE_CLK_STABLE_TIME 100
+#define PLL_CTRL_WAIT_TIME 200
+#define NOC_POWER_TIME 100
+
+struct hi3670_pcie_phy {
+ struct device *dev;
+ void __iomem *base;
+ struct regmap *apb;
+ struct regmap *crgctrl;
+ struct regmap *sysctrl;
+ struct regmap *pmctrl;
+ struct clk *apb_sys_clk;
+ struct clk *apb_phy_clk;
+ struct clk *phy_ref_clk;
+ struct clk *aclk;
+ struct clk *aux_clk;
+ u32 eye_param[NUM_EYEPARAM];
+};
+
+/* Registers in PCIePHY */
+static inline void hi3670_apb_phy_writel(struct hi3670_pcie_phy *phy, u32 val,
+ u32 reg)
+{
+ writel(val, phy->base + APB_PHY_START_ADDR + reg);
+}
+
+static inline u32 hi3670_apb_phy_readl(struct hi3670_pcie_phy *phy, u32 reg)
+{
+ return readl(phy->base + APB_PHY_START_ADDR + reg);
+}
+
+static inline void hi3670_apb_phy_updatel(struct hi3670_pcie_phy *phy,
+ u32 val, u32 mask, u32 reg)
+{
+ u32 regval;
+
+ regval = hi3670_apb_phy_readl(phy, reg);
+ regval &= ~mask;
+ regval |= val;
+ hi3670_apb_phy_writel(phy, regval, reg);
+}
+
+static inline void kirin_apb_natural_phy_writel(struct hi3670_pcie_phy *phy,
+ u32 val, u32 reg)
+{
+ writel(val, phy->base + reg);
+}
+
+static inline u32 kirin_apb_natural_phy_readl(struct hi3670_pcie_phy *phy,
+ u32 reg)
+{
+ return readl(phy->base + reg);
+}
+
+static void hi3670_pcie_phy_oe_enable(struct hi3670_pcie_phy *phy, bool enable)
+{
+ u32 val;
+
+ regmap_read(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, &val);
+ val |= PCIE_DEBOUNCE_PARAM;
+ if (enable)
+ val &= ~PCIE_OE_BYPASS;
+ else
+ val |= PCIE_OE_BYPASS;
+ regmap_write(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, val);
+}
+
+static void hi3670_pcie_get_eyeparam(struct hi3670_pcie_phy *phy)
+{
+ struct device *dev = phy->dev;
+ struct device_node *np;
+ int ret, i;
+
+ np = dev->of_node;
+
+ ret = of_property_read_u32_array(np, "hisilicon,eye-diagram-param",
+ phy->eye_param, NUM_EYEPARAM);
+ if (!ret)
+ return;
+
+ /* There's no optional eye_param property. Set array to default */
+ for (i = 0; i < NUM_EYEPARAM; i++)
+ phy->eye_param[i] = EYEPARAM_NOCFG;
+}
+
+static void hi3670_pcie_set_eyeparam(struct hi3670_pcie_phy *phy)
+{
+ u32 val;
+
+ val = kirin_apb_natural_phy_readl(phy, RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
+
+ if (phy->eye_param[1] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM1_MASK;
+ val |= FIELD_PREP(EYE_PARM1_MASK, phy->eye_param[1]);
+ val |= EYE_PARM1_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val,
+ RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
+
+ val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_2);
+ val &= ~(EYE_PARM2_MASK | EYE_PARM3_MASK);
+ if (phy->eye_param[2] != EYEPARAM_NOCFG) {
+ val |= FIELD_PREP(EYE_PARM2_MASK, phy->eye_param[2]);
+ val |= EYE_PARM2_EN;
+ }
+
+ if (phy->eye_param[3] != EYEPARAM_NOCFG) {
+ val |= FIELD_PREP(EYE_PARM3_MASK, phy->eye_param[3]);
+ val |= EYE_PARM3_EN;
+ }
+
+ kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_2);
+
+ val = kirin_apb_natural_phy_readl(phy, SUP_DIG_LVL_OVRD_IN);
+ if (phy->eye_param[0] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM0_MASK;
+ val |= FIELD_PREP(EYE_PARM0_MASK, phy->eye_param[0]);
+ val |= EYE_PARM0_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val, SUP_DIG_LVL_OVRD_IN);
+
+ val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_1);
+ if (phy->eye_param[4] != EYEPARAM_NOCFG) {
+ val &= ~EYE_PARM4_MASK;
+ val |= FIELD_PREP(EYE_PARM4_MASK, phy->eye_param[4]);
+ val |= EYE_PARM4_EN;
+ }
+ kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_1);
+}
+
+static void hi3670_pcie_natural_cfg(struct hi3670_pcie_phy *phy)
+{
+ u32 val;
+
+ /* change 2p mem_ctrl */
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL20_ADDR,
+ SOC_PCIECTRL_CTRL20_2P_MEM_CTRL);
+
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, &val);
+ val |= PCIE_PULL_UP_SYS_AUX_PWR_DET;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, val);
+
+ /* output, pull down */
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val);
+ val &= ~PCIE_OUTPUT_PULL_BITS;
+ val |= PCIE_OUTPUT_PULL_DOWN;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val);
+
+ /* Handle phy_reset and lane0_reset to HW */
+ hi3670_apb_phy_updatel(phy, PCIEPHY_RESET_BIT,
+ PCIEPHY_PIPE_LINE0_RESET_BIT | PCIEPHY_RESET_BIT,
+ SOC_PCIEPHY_CTRL1_ADDR);
+
+ /* fix chip bug: TxDetectRx fail */
+ hi3670_apb_phy_updatel(phy, PCIE_TXDETECT_RX_FAIL, PCIE_TXDETECT_RX_FAIL,
+ SOC_PCIEPHY_CTRL38_ADDR);
+}
+
+static void hi3670_pcie_pll_init(struct hi3670_pcie_phy *phy)
+{
+ hi3670_apb_phy_updatel(phy, PCIE_PHY_CHOOSE_FNPLL, PCIE_PHY_CHOOSE_FNPLL,
+ SOC_PCIEPHY_MMC1PLL_CTRL1);
+
+
+ hi3670_apb_phy_updatel(phy,
+ FIELD_PREP(PCIE_FNPLL_FBDIV_MASK, PCIE_FNPLL_FBDIV),
+ PCIE_FNPLL_FBDIV_MASK,
+ SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ hi3670_apb_phy_updatel(phy,
+ FIELD_PREP(PCIE_FNPLL_FRACDIV_MASK, PCIE_FNPLL_FRACDIV),
+ PCIE_FNPLL_FRACDIV_MASK, SOC_PCIEPHY_MMC1PLL_CTRL17);
+
+ hi3670_apb_phy_updatel(phy,
+ PCIE_FNPLL_DLL_EN |
+ FIELD_PREP(PCIE_FNPLL_POSTDIV1_MASK, PCIE_FNPLL_POSTDIV1) |
+ FIELD_PREP(PCIE_FNPLL_POSTDIV2_MASK, PCIE_FNPLL_POSTDIV2) |
+ FIELD_PREP(PCIE_FNPLL_PLL_MODE_MASK, PCIE_FNPLL_PLL_MODE),
+ PCIE_FNPLL_POSTDIV1_MASK |
+ PCIE_FNPLL_POSTDIV2_MASK |
+ PCIE_FNPLL_PLL_MODE_MASK | PCIE_FNPLL_DLL_EN,
+ SOC_PCIEPHY_MMC1PLL_CTRL20);
+
+ hi3670_apb_phy_writel(phy, PCIE_PHY_MMC1PLL,
+ SOC_PCIEPHY_MMC1PLL_CTRL21);
+}
+
+static int hi3670_pcie_pll_ctrl(struct hi3670_pcie_phy *phy, bool enable)
+{
+ struct device *dev = phy->dev;
+ u32 val;
+ int time = PLL_CTRL_WAIT_TIME;
+
+ if (enable) {
+ /* pd = 0 */
+ hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_MMC1PLL_DISABLE,
+ SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ /* choose FNPLL */
+ while (!(val & FNPLL_HAS_LOCKED)) {
+ if (!time) {
+ dev_err(dev, "wait for pll_lock timeout\n");
+ return -EINVAL;
+ }
+ time--;
+ udelay(1);
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0);
+ }
+
+ hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_PCIEPL_BP,
+ SOC_PCIEPHY_MMC1PLL_CTRL20);
+
+ } else {
+ hi3670_apb_phy_updatel(phy,
+ PCIE_PHY_MMC1PLL_DISABLE,
+ PCIE_PHY_MMC1PLL_DISABLE,
+ SOC_PCIEPHY_MMC1PLL_CTRL16);
+
+ hi3670_apb_phy_updatel(phy, PCIE_PHY_PCIEPL_BP,
+ PCIE_PHY_PCIEPL_BP,
+ SOC_PCIEPHY_MMC1PLL_CTRL20);
+ }
+
+ return 0;
+}
+
+static void hi3670_pcie_hp_debounce_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ if (open)
+ /* gt_clk_pcie_hp/gt_clk_pcie_debounce open */
+ regmap_write(phy->crgctrl, CRGPERIPH_PEREN12,
+ IO_HP_DEBOUNCE_GT);
+ else
+ /* gt_clk_pcie_hp/gt_clk_pcie_debounce close */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_HP_DEBOUNCE_GT);
+}
+
+static void hi3670_pcie_phyref_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ unsigned int val;
+
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+
+ if (open)
+ val &= ~IO_OE_HARD_GT_MODE; /* enable hard gt mode */
+ else
+ val |= IO_OE_HARD_GT_MODE; /* disable hard gt mode */
+
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, IO_PHYREF_SOFT_GT_MODE);
+}
+
+static void hi3670_pcie_oe_ctrl(struct hi3670_pcie_phy *phy, bool en_flag)
+{
+ unsigned int val;
+
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+
+ /* set ie cfg */
+ val |= IO_IE_EN_HARD_BYPASS;
+
+ /* set oe cfg */
+ val &= ~IO_HARD_CTRL_DEBOUNCE_BYPASS;
+
+ /* set phy_debounce in&out time */
+ val |= (DEBOUNCE_WAITCFG_IN | DEBOUNCE_WAITCFG_OUT);
+
+ /* select oe_gt_mode */
+ val |= IO_OE_GT_MODE;
+
+ if (en_flag)
+ val &= ~IO_OE_EN_HARD_BYPASS;
+ else
+ val |= IO_OE_EN_HARD_BYPASS;
+
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+}
+
+static void hi3670_pcie_ioref_gt(struct hi3670_pcie_phy *phy, bool open)
+{
+ unsigned int val;
+
+ if (open) {
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL21_ADDR,
+ SOC_PCIECTRL_CTRL21_DEFAULT);
+
+ hi3670_pcie_oe_ctrl(phy, true);
+
+ /* en hard gt mode */
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+ val &= ~IO_REF_HARD_GT_MODE;
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_REF_SOFT_GT_MODE);
+
+ } else {
+ /* disable hard gt mode */
+ regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
+ val |= IO_REF_HARD_GT_MODE;
+ regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
+
+ /* disable soft gt mode */
+ regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
+ IO_REF_SOFT_GT_MODE);
+
+ hi3670_pcie_oe_ctrl(phy, false);
+ }
+}
+
+static int hi3670_pcie_allclk_ctrl(struct hi3670_pcie_phy *phy, bool clk_on)
+{
+ struct device *dev = phy->dev;
+ int ret = 0;
+
+ if (!clk_on)
+ goto close_clocks;
+
+ /* choose 100MHz clk src: Bit[8]==1 pad, Bit[8]==0 pll */
+ hi3670_apb_phy_updatel(phy, 0, PCIE_CLK_SOURCE,
+ SOC_PCIEPHY_CTRL1_ADDR);
+
+ hi3670_pcie_pll_init(phy);
+
+ ret = hi3670_pcie_pll_ctrl(phy, true);
+ if (ret) {
+ dev_err(dev, "Failed to enable pll\n");
+ return -EINVAL;
+ }
+ hi3670_pcie_hp_debounce_gt(phy, true);
+ hi3670_pcie_phyref_gt(phy, true);
+ hi3670_pcie_ioref_gt(phy, true);
+
+ ret = clk_set_rate(phy->aclk, AXI_CLK_FREQ);
+ if (ret) {
+ dev_err(dev, "Failed to set rate\n");
+ goto close_clocks;
+ }
+
+ return 0;
+
+close_clocks:
+ hi3670_pcie_ioref_gt(phy, false);
+ hi3670_pcie_phyref_gt(phy, false);
+ hi3670_pcie_hp_debounce_gt(phy, false);
+
+ hi3670_pcie_pll_ctrl(phy, false);
+
+ return ret;
+}
+
+static bool is_pipe_clk_stable(struct hi3670_pcie_phy *phy)
+{
+ struct device *dev = phy->dev;
+ u32 val;
+ u32 time = PIPE_CLK_STABLE_TIME;
+ u32 pipe_clk_stable = PCIE_IS_CLOCK_STABLE;
+
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR);
+ while (val & pipe_clk_stable) {
+ mdelay(1);
+ if (!time) {
+ dev_err(dev, "PIPE clk is not stable\n");
+ return false;
+ }
+ time--;
+ val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR);
+ }
+
+ return true;
+}
+
+static int hi3670_pcie_noc_power(struct hi3670_pcie_phy *phy, bool enable)
+{
+ struct device *dev = phy->dev;
+ u32 time = NOC_POWER_TIME;
+ unsigned int val = NOC_PW_MASK;
+ int rst;
+
+ if (enable)
+ val = NOC_PW_MASK | NOC_PW_SET_BIT;
+ else
+ val = NOC_PW_MASK;
+ rst = enable ? 1 : 0;
+
+ regmap_write(phy->pmctrl, NOC_POWER_IDLEREQ_1, val);
+
+ time = NOC_POWER_TIME;
+ regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val);
+ while ((val & NOC_PW_SET_BIT) != rst) {
+ udelay(10);
+ if (!time) {
+ dev_err(dev, "Failed to reverse noc power-status\n");
+ return -EINVAL;
+ }
+ time--;
+ regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val);
+ }
+
+ return 0;
+}
+
+static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy)
+{
+ struct device_node *pcie_port;
+ struct device *dev = phy->dev;
+ struct device *pcie_dev;
+
+ pcie_port = of_get_child_by_name(dev->parent->of_node, "pcie");
+ if (!pcie_port) {
+ dev_err(dev, "no pcie node found in %s\n",
+ dev->parent->of_node->full_name);
+ return -ENODEV;
+ }
+
+ pcie_dev = bus_find_device_by_of_node(&platform_bus_type, pcie_port);
+ if (!pcie_dev) {
+ dev_err(dev, "Didn't find pcie device\n");
+ return -ENODEV;
+ }
+
+ /*
+ * We might just use NULL instead of the APB name, as the
+ * pcie-kirin currently registers directly just one regmap (although
+ * the DWC driver register other regmaps).
+ *
+ * Yet, it sounds safer to warrant that it will be accessing the
+ * right regmap. So, let's use the named version.
+ */
+ phy->apb = dev_get_regmap(pcie_dev, "kirin_pcie_apb");
+ if (!phy->apb) {
+ dev_err(dev, "Failed to get APB regmap\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int kirin_pcie_clk_ctrl(struct hi3670_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 hi3670_pcie_phy_init(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+ int ret;
+
+ /*
+ * The code under hi3670_pcie_get_resources_from_pcie() need to
+ * access the reset-gpios and the APB registers, both from the
+ * pcie-kirin driver.
+ *
+ * The APB is obtained via the pcie driver's regmap
+ * Such kind of resource can only be obtained during the PCIe
+ * power_on sequence, as the code inside pcie-kirin needs to
+ * be already probed, as it needs to register the APB regmap.
+ */
+
+ ret = hi3670_pcie_get_resources_from_pcie(phy);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int hi3670_pcie_phy_power_on(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+ int val, 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);
+
+ hi3670_pcie_phy_oe_enable(phy, true);
+
+ ret = kirin_pcie_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);
+
+ hi3670_pcie_natural_cfg(phy);
+
+ ret = hi3670_pcie_allclk_ctrl(phy, true);
+ if (ret)
+ goto disable_clks;
+
+ /* pull down phy_test_powerdown signal */
+ hi3670_apb_phy_updatel(phy, 0, PCIE_PULL_DOWN_PHY_TEST_POWERDOWN,
+ SOC_PCIEPHY_CTRL0_ADDR);
+
+ /* deassert controller perst_n */
+ regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val);
+ val |= PCIE_DEASSERT_CONTROLLER_PERST;
+ regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val);
+ udelay(10);
+
+ ret = is_pipe_clk_stable(phy);
+ if (!ret)
+ goto disable_clks;
+
+ hi3670_pcie_set_eyeparam(phy);
+
+ ret = hi3670_pcie_noc_power(phy, false);
+ if (ret)
+ goto disable_clks;
+
+ return 0;
+
+disable_clks:
+ kirin_pcie_clk_ctrl(phy, false);
+ return ret;
+}
+
+static int hi3670_pcie_phy_power_off(struct phy *generic_phy)
+{
+ struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
+
+ hi3670_pcie_phy_oe_enable(phy, false);
+
+ hi3670_pcie_allclk_ctrl(phy, false);
+
+ /* Drop power supply for Host */
+ regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, 0);
+
+ /*
+ * FIXME: The enabled clocks should be disabled here by calling
+ * kirin_pcie_clk_ctrl(phy, false);
+ * However, some clocks used at Kirin 970 should be marked as
+ * CLK_IS_CRITICAL at clk-hi3670 driver, as powering such clocks off
+ * cause an Asynchronous SError interrupt, which produces panic().
+ * While clk-hi3670 is not fixed, we cannot risk disabling clocks here.
+ */
+
+ return 0;
+}
+
+static const struct phy_ops hi3670_phy_ops = {
+ .init = hi3670_pcie_phy_init,
+ .power_on = hi3670_pcie_phy_power_on,
+ .power_off = hi3670_pcie_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int hi3670_pcie_phy_get_resources(struct hi3670_pcie_phy *phy,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ /* syscon */
+ phy->crgctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-crgctrl");
+ if (IS_ERR(phy->crgctrl))
+ return PTR_ERR(phy->crgctrl);
+
+ phy->sysctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-sctrl");
+ if (IS_ERR(phy->sysctrl))
+ return PTR_ERR(phy->sysctrl);
+
+ phy->pmctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-pmctrl");
+ if (IS_ERR(phy->sysctrl))
+ return PTR_ERR(phy->sysctrl);
+
+ /* clocks */
+ phy->phy_ref_clk = devm_clk_get(dev, "phy_ref");
+ if (IS_ERR(phy->phy_ref_clk))
+ return PTR_ERR(phy->phy_ref_clk);
+
+ phy->aux_clk = devm_clk_get(dev, "aux");
+ if (IS_ERR(phy->aux_clk))
+ return PTR_ERR(phy->aux_clk);
+
+ phy->apb_phy_clk = devm_clk_get(dev, "apb_phy");
+ if (IS_ERR(phy->apb_phy_clk))
+ return PTR_ERR(phy->apb_phy_clk);
+
+ phy->apb_sys_clk = devm_clk_get(dev, "apb_sys");
+ if (IS_ERR(phy->apb_sys_clk))
+ return PTR_ERR(phy->apb_sys_clk);
+
+ phy->aclk = devm_clk_get(dev, "aclk");
+ if (IS_ERR(phy->aclk))
+ return PTR_ERR(phy->aclk);
+
+ /* registers */
+ phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(phy->base))
+ return PTR_ERR(phy->base);
+
+ hi3670_pcie_get_eyeparam(phy);
+
+ return 0;
+}
+
+static int hi3670_pcie_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct hi3670_pcie_phy *phy;
+ struct phy *generic_phy;
+ int ret;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->dev = dev;
+
+ ret = hi3670_pcie_phy_get_resources(phy, pdev);
+ if (ret)
+ return ret;
+
+ generic_phy = devm_phy_create(dev, dev->of_node, &hi3670_phy_ops);
+ if (IS_ERR(generic_phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(generic_phy);
+ }
+
+ phy_set_drvdata(generic_phy, phy);
+ 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 hi3670_pcie_phy_match[] = {
+ {
+ .compatible = "hisilicon,hi970-pcie-phy",
+ },
+ {},
+};
+
+static struct platform_driver hi3670_pcie_phy_driver = {
+ .probe = hi3670_pcie_phy_probe,
+ .driver = {
+ .of_match_table = hi3670_pcie_phy_match,
+ .name = "hi3670_pcie_phy",
+ .suppress_bind_attrs = true,
+ }
+};
+builtin_platform_driver(hi3670_pcie_phy_driver);
+
+MODULE_DEVICE_TABLE(of, hi3670_pcie_phy_match);
+MODULE_DESCRIPTION("PCIe phy driver for Kirin 970");
+MODULE_AUTHOR("Mauro Carvalho Chehab <[email protected]>");
+MODULE_AUTHOR("Manivannan Sadhasivam <[email protected]>");
+MODULE_LICENSE("GPL v2");
--
2.31.1


2021-08-20 23:06:02

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v13] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

Hi Mauro,

I love your patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.14-rc6 next-20210820]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/0day-ci/linux/commits/Mauro-Carvalho-Chehab/phy-HiSilicon-Add-driver-for-Kirin-970-PCIe-PHY/20210820-214510
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git d992fe5318d8d7af9510b879439a3c7f283da442
config: x86_64-buildonly-randconfig-r002-20210821 (attached as .config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project d9c5613e856cf2addfbf892fc4c1ce9ef9feceaa)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/1cc067fe7e9ca4a45f061447087187d1852fb716
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Mauro-Carvalho-Chehab/phy-HiSilicon-Add-driver-for-Kirin-970-PCIe-PHY/20210820-214510
git checkout 1cc067fe7e9ca4a45f061447087187d1852fb716
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All warnings (new ones prefixed by >>):

>> drivers/phy/hisilicon/phy-hi3670-pcie.c:358:6: warning: variable 'val' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
if (enable) {
^~~~~~
drivers/phy/hisilicon/phy-hi3670-pcie.c:364:12: note: uninitialized use occurs here
while (!(val & FNPLL_HAS_LOCKED)) {
^~~
drivers/phy/hisilicon/phy-hi3670-pcie.c:358:2: note: remove the 'if' if its condition is always false
if (enable) {
^~~~~~~~~~~~~
drivers/phy/hisilicon/phy-hi3670-pcie.c:355:9: note: initialize the variable 'val' to silence this warning
u32 val;
^
= 0
1 warning generated.


vim +358 drivers/phy/hisilicon/phy-hi3670-pcie.c

351
352 static int hi3670_pcie_pll_ctrl(struct hi3670_pcie_phy *phy, bool enable)
353 {
354 struct device *dev = phy->dev;
355 u32 val;
356 int time = PLL_CTRL_WAIT_TIME;
357
> 358 if (enable) {
359 /* pd = 0 */
360 hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_MMC1PLL_DISABLE,
361 SOC_PCIEPHY_MMC1PLL_CTRL16);
362
363 /* choose FNPLL */
364 while (!(val & FNPLL_HAS_LOCKED)) {
365 if (!time) {
366 dev_err(dev, "wait for pll_lock timeout\n");
367 return -EINVAL;
368 }
369 time--;
370 udelay(1);
371 val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0);
372 }
373
374 hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_PCIEPL_BP,
375 SOC_PCIEPHY_MMC1PLL_CTRL20);
376
377 } else {
378 hi3670_apb_phy_updatel(phy,
379 PCIE_PHY_MMC1PLL_DISABLE,
380 PCIE_PHY_MMC1PLL_DISABLE,
381 SOC_PCIEPHY_MMC1PLL_CTRL16);
382
383 hi3670_apb_phy_updatel(phy, PCIE_PHY_PCIEPL_BP,
384 PCIE_PHY_PCIEPL_BP,
385 SOC_PCIEPHY_MMC1PLL_CTRL20);
386 }
387
388 return 0;
389 }
390

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (3.68 kB)
.config.gz (38.76 kB)
Download all attachments

2021-09-15 13:13:14

by Mauro Carvalho Chehab

[permalink] [raw]
Subject: Re: [PATCH v12] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY

Hi Vinod,

Em Wed, 18 Aug 2021 13:04:07 +0200
Mauro Carvalho Chehab <[email protected]> escreveu:

> The Kirin 970 PHY is somewhat similar to the Kirin 960, but it
> does a lot more. Add the needed bits for PCIe to start working on
> HiKey 970 boards.
>
> Co-developed-by: Manivannan Sadhasivam <[email protected]>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
> Signed-off-by: Mauro Carvalho Chehab <[email protected]>

Gentile ping.

Regards,
Mauro

> ---
>
> PS.:
> As the PCIe patches aren't untouched, I'm sending just the PHY
> driver patch on v12.
>
> v12:
> - removed c99-style comments;
> - added hi3670_apb_phy_updatel() to update bits;
> - allowed a couple of lines to be bigger than 80 columns.
>
> drivers/phy/hisilicon/Kconfig | 10 +
> drivers/phy/hisilicon/Makefile | 1 +
> drivers/phy/hisilicon/phy-hi3670-pcie.c | 861 ++++++++++++++++++++++++
> 3 files changed, 872 insertions(+)
> create mode 100644 drivers/phy/hisilicon/phy-hi3670-pcie.c
>
> diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig
> index 4d008cfc279c..d3b92c288554 100644
> --- a/drivers/phy/hisilicon/Kconfig
> +++ b/drivers/phy/hisilicon/Kconfig
> @@ -33,6 +33,16 @@ config PHY_HI3670_USB
>
> To compile this driver as a module, choose M here.
>
> +config PHY_HI3670_PCIE
> + tristate "hi3670 PCIe PHY support"
> + depends on (ARCH_HISI && ARM64) || COMPILE_TEST
> + select GENERIC_PHY
> + select MFD_SYSCON
> + help
> + Enable this to support the HiSilicon hi3670 PCIe PHY.
> +
> + To compile this driver as a module, choose M here.
> +
> config PHY_HISTB_COMBPHY
> tristate "HiSilicon STB SoCs COMBPHY support"
> depends on (ARCH_HISI && ARM64) || COMPILE_TEST
> diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile
> index 51729868145b..4029d3813b1e 100644
> --- a/drivers/phy/hisilicon/Makefile
> +++ b/drivers/phy/hisilicon/Makefile
> @@ -2,6 +2,7 @@
> obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
> obj-$(CONFIG_PHY_HI3660_USB) += phy-hi3660-usb3.o
> obj-$(CONFIG_PHY_HI3670_USB) += phy-hi3670-usb3.o
> +obj-$(CONFIG_PHY_HI3670_PCIE) += phy-hi3670-pcie.o
> obj-$(CONFIG_PHY_HISTB_COMBPHY) += phy-histb-combphy.o
> obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o
> obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
> diff --git a/drivers/phy/hisilicon/phy-hi3670-pcie.c b/drivers/phy/hisilicon/phy-hi3670-pcie.c
> new file mode 100644
> index 000000000000..f0c66e8e155d
> --- /dev/null
> +++ b/drivers/phy/hisilicon/phy-hi3670-pcie.c
> @@ -0,0 +1,861 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * PCIe phy driver for Kirin 970
> + *
> + * Copyright (C) 2017 HiSilicon Electronics Co., Ltd.
> + * https://www.huawei.com
> + * Copyright (C) 2021 Huawei Technologies Co., Ltd.
> + * https://www.huawei.com
> + *
> + * Authors:
> + * Mauro Carvalho Chehab <[email protected]>
> + * Manivannan Sadhasivam <[email protected]>
> + *
> + * Based on:
> + * https://lore.kernel.org/lkml/4c9d6581478aa966698758c0420933f5defab4dd.1612335031.git.mchehab+huawei@kernel.org/
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/gpio.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of_gpio.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#define AXI_CLK_FREQ 207500000
> +#define REF_CLK_FREQ 100000000
> +
> +/* PCIe CTRL registers */
> +#define SOC_PCIECTRL_CTRL0_ADDR 0x000
> +#define SOC_PCIECTRL_CTRL1_ADDR 0x004
> +#define SOC_PCIECTRL_CTRL7_ADDR 0x01c
> +#define SOC_PCIECTRL_CTRL12_ADDR 0x030
> +#define SOC_PCIECTRL_CTRL20_ADDR 0x050
> +#define SOC_PCIECTRL_CTRL21_ADDR 0x054
> +#define SOC_PCIECTRL_STATE0_ADDR 0x400
> +
> +#define PCIE_OUTPUT_PULL_BITS GENMASK(3, 0)
> +#define SOC_PCIECTRL_CTRL20_2P_MEM_CTRL 0x02605550
> +#define SOC_PCIECTRL_CTRL21_DEFAULT 0x20000070
> +#define PCIE_PULL_UP_SYS_AUX_PWR_DET BIT(10)
> +#define PCIE_OUTPUT_PULL_DOWN BIT(1)
> +
> +/* PCIe PHY registers */
> +#define SOC_PCIEPHY_CTRL0_ADDR 0x000
> +#define SOC_PCIEPHY_CTRL1_ADDR 0x004
> +#define SOC_PCIEPHY_CTRL2_ADDR 0x008
> +#define SOC_PCIEPHY_CTRL3_ADDR 0x00c
> +#define SOC_PCIEPHY_CTRL38_ADDR 0x0098
> +#define SOC_PCIEPHY_STATE0_ADDR 0x400
> +
> +#define PORT_MSI_CTRL_ADDR 0x820
> +#define PORT_MSI_CTRL_UPPER_ADDR 0x824
> +#define PORT_MSI_CTRL_INT0_ENABLE 0x828
> +
> +#define RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 0xc004
> +#define SUP_DIG_LVL_OVRD_IN 0x003c
> +#define LANEN_DIG_ASIC_TX_OVRD_IN_1 0x4008
> +#define LANEN_DIG_ASIC_TX_OVRD_IN_2 0x400c
> +
> +#define PCIE_LINKUP_ENABLE 0x8020
> +#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21)
> +#define PCIE_LTSSM_ENABLE_BIT BIT(11)
> +#define PCIEPHY_RESET_BIT BIT(17)
> +#define PCIEPHY_PIPE_LINE0_RESET_BIT BIT(19)
> +#define PCIE_TXDETECT_RX_FAIL BIT(2)
> +#define PCIE_CLK_SOURCE BIT(8)
> +#define PCIE_IS_CLOCK_STABLE BIT(19)
> +#define PCIE_PULL_DOWN_PHY_TEST_POWERDOWN BIT(22)
> +#define PCIE_DEASSERT_CONTROLLER_PERST BIT(2)
> +
> +#define EYEPARAM_NOCFG 0xffffffff
> +#define EYE_PARM0_MASK GENMASK(8, 6)
> +#define EYE_PARM1_MASK GENMASK(11, 8)
> +#define EYE_PARM2_MASK GENMASK(5, 0)
> +#define EYE_PARM3_MASK GENMASK(12, 7)
> +#define EYE_PARM4_MASK GENMASK(14, 9)
> +#define EYE_PARM0_EN BIT(9)
> +#define EYE_PARM1_EN BIT(12)
> +#define EYE_PARM2_EN BIT(6)
> +#define EYE_PARM3_EN BIT(13)
> +#define EYE_PARM4_EN BIT(15)
> +
> +/* hi3670 pciephy register */
> +#define APB_PHY_START_ADDR 0x40000
> +#define SOC_PCIEPHY_MMC1PLL_CTRL1 0xc04
> +#define SOC_PCIEPHY_MMC1PLL_CTRL16 0xC40
> +#define SOC_PCIEPHY_MMC1PLL_CTRL17 0xC44
> +#define SOC_PCIEPHY_MMC1PLL_CTRL20 0xC50
> +#define SOC_PCIEPHY_MMC1PLL_CTRL21 0xC54
> +#define SOC_PCIEPHY_MMC1PLL_STAT0 0xE00
> +
> +#define CRGPERIPH_PEREN12 0x470
> +#define CRGPERIPH_PERDIS12 0x474
> +#define CRGPERIPH_PCIECTRL0 0x800
> +
> +#define PCIE_FNPLL_FBDIV_MASK GENMASK(27, 16)
> +#define PCIE_FNPLL_FRACDIV_MASK GENMASK(23, 0)
> +#define PCIE_FNPLL_POSTDIV1_MASK GENMASK(10, 8)
> +#define PCIE_FNPLL_POSTDIV2_MASK GENMASK(14, 12)
> +#define PCIE_FNPLL_PLL_MODE_MASK BIT(25)
> +
> +#define PCIE_FNPLL_DLL_EN BIT(27)
> +#define PCIE_FNPLL_FBDIV 0xd0
> +#define PCIE_FNPLL_FRACDIV 0x555555
> +#define PCIE_FNPLL_POSTDIV1 0x5
> +#define PCIE_FNPLL_POSTDIV2 0x4
> +#define PCIE_FNPLL_PLL_MODE 0x0
> +
> +#define PCIE_PHY_MMC1PLL 0x20
> +#define PCIE_PHY_CHOOSE_FNPLL BIT(27)
> +#define PCIE_PHY_MMC1PLL_DISABLE BIT(0)
> +#define PCIE_PHY_PCIEPL_BP BIT(16)
> +
> +/* define ie,oe cfg */
> +#define IO_OE_HARD_GT_MODE BIT(1)
> +#define IO_IE_EN_HARD_BYPASS BIT(27)
> +#define IO_OE_EN_HARD_BYPASS BIT(11)
> +#define IO_HARD_CTRL_DEBOUNCE_BYPASS BIT(10)
> +#define IO_OE_GT_MODE BIT(8)
> +#define DEBOUNCE_WAITCFG_IN GENMASK(23, 20)
> +#define DEBOUNCE_WAITCFG_OUT GENMASK(16, 13)
> +
> +#define IO_HP_DEBOUNCE_GT (BIT(12) | BIT(15))
> +#define IO_PHYREF_SOFT_GT_MODE BIT(14)
> +#define IO_REF_SOFT_GT_MODE BIT(13)
> +#define IO_REF_HARD_GT_MODE BIT(0)
> +
> +/* noc power domain */
> +#define NOC_POWER_IDLEREQ_1 0x38c
> +#define NOC_POWER_IDLE_1 0x394
> +#define NOC_PW_MASK 0x10000
> +#define NOC_PW_SET_BIT 0x1
> +
> +#define NUM_EYEPARAM 5
> +
> +/* 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 GENMASK(29, 28)
> +
> +/* peri_crg ctrl */
> +#define CRGCTRL_PCIE_ASSERT_OFFSET 0x88
> +#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000
> +
> +#define FNPLL_HAS_LOCKED BIT(4)
> +
> +/* Time for delay */
> +#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
> +
> +#define PIPE_CLK_STABLE_TIME 100
> +#define PLL_CTRL_WAIT_TIME 200
> +#define NOC_POWER_TIME 100
> +
> +struct hi3670_pcie_phy {
> + struct device *dev;
> + void __iomem *base;
> + struct regmap *apb;
> + struct regmap *crgctrl;
> + struct regmap *sysctrl;
> + struct regmap *pmctrl;
> + struct clk *apb_sys_clk;
> + struct clk *apb_phy_clk;
> + struct clk *phy_ref_clk;
> + struct clk *aclk;
> + struct clk *aux_clk;
> + u32 eye_param[NUM_EYEPARAM];
> +};
> +
> +/* Registers in PCIePHY */
> +static inline void hi3670_apb_phy_writel(struct hi3670_pcie_phy *phy, u32 val,
> + u32 reg)
> +{
> + writel(val, phy->base + APB_PHY_START_ADDR + reg);
> +}
> +
> +static inline u32 hi3670_apb_phy_readl(struct hi3670_pcie_phy *phy, u32 reg)
> +{
> + return readl(phy->base + APB_PHY_START_ADDR + reg);
> +}
> +
> +static inline void hi3670_apb_phy_updatel(struct hi3670_pcie_phy *phy,
> + u32 val, u32 mask, u32 reg)
> +{
> + u32 regval;
> +
> + regval = hi3670_apb_phy_readl(phy, reg);
> + regval &= ~mask;
> + regval |= val;
> + hi3670_apb_phy_writel(phy, regval, reg);
> +}
> +
> +static inline void kirin_apb_natural_phy_writel(struct hi3670_pcie_phy *phy,
> + u32 val, u32 reg)
> +{
> + writel(val, phy->base + reg);
> +}
> +
> +static inline u32 kirin_apb_natural_phy_readl(struct hi3670_pcie_phy *phy,
> + u32 reg)
> +{
> + return readl(phy->base + reg);
> +}
> +
> +static void hi3670_pcie_phy_oe_enable(struct hi3670_pcie_phy *phy, bool enable)
> +{
> + u32 val;
> +
> + regmap_read(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, &val);
> + val |= PCIE_DEBOUNCE_PARAM;
> + if (enable)
> + val &= ~PCIE_OE_BYPASS;
> + else
> + val |= PCIE_OE_BYPASS;
> + regmap_write(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, val);
> +}
> +
> +static void hi3670_pcie_get_eyeparam(struct hi3670_pcie_phy *phy)
> +{
> + struct device *dev = phy->dev;
> + struct device_node *np;
> + int ret, i;
> +
> + np = dev->of_node;
> +
> + ret = of_property_read_u32_array(np, "hisilicon,eye-diagram-param",
> + phy->eye_param, NUM_EYEPARAM);
> + if (!ret)
> + return;
> +
> + /* There's no optional eye_param property. Set array to default */
> + for (i = 0; i < NUM_EYEPARAM; i++)
> + phy->eye_param[i] = EYEPARAM_NOCFG;
> +}
> +
> +static void hi3670_pcie_set_eyeparam(struct hi3670_pcie_phy *phy)
> +{
> + u32 val;
> +
> + val = kirin_apb_natural_phy_readl(phy, RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
> +
> + if (phy->eye_param[1] != EYEPARAM_NOCFG) {
> + val &= ~EYE_PARM1_MASK;
> + val |= FIELD_PREP(EYE_PARM1_MASK, phy->eye_param[1]);
> + val |= EYE_PARM1_EN;
> + }
> + kirin_apb_natural_phy_writel(phy, val,
> + RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
> +
> + val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_2);
> + val &= ~(EYE_PARM2_MASK | EYE_PARM3_MASK);
> + if (phy->eye_param[2] != EYEPARAM_NOCFG) {
> + val |= FIELD_PREP(EYE_PARM2_MASK, phy->eye_param[2]);
> + val |= EYE_PARM2_EN;
> + }
> +
> + if (phy->eye_param[3] != EYEPARAM_NOCFG) {
> + val |= FIELD_PREP(EYE_PARM3_MASK, phy->eye_param[3]);
> + val |= EYE_PARM3_EN;
> + }
> +
> + kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_2);
> +
> + val = kirin_apb_natural_phy_readl(phy, SUP_DIG_LVL_OVRD_IN);
> + if (phy->eye_param[0] != EYEPARAM_NOCFG) {
> + val &= ~EYE_PARM0_MASK;
> + val |= FIELD_PREP(EYE_PARM0_MASK, phy->eye_param[0]);
> + val |= EYE_PARM0_EN;
> + }
> + kirin_apb_natural_phy_writel(phy, val, SUP_DIG_LVL_OVRD_IN);
> +
> + val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_1);
> + if (phy->eye_param[4] != EYEPARAM_NOCFG) {
> + val &= ~EYE_PARM4_MASK;
> + val |= FIELD_PREP(EYE_PARM4_MASK, phy->eye_param[4]);
> + val |= EYE_PARM4_EN;
> + }
> + kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_1);
> +}
> +
> +static void hi3670_pcie_natural_cfg(struct hi3670_pcie_phy *phy)
> +{
> + u32 val;
> +
> + /* change 2p mem_ctrl */
> + regmap_write(phy->apb, SOC_PCIECTRL_CTRL20_ADDR,
> + SOC_PCIECTRL_CTRL20_2P_MEM_CTRL);
> +
> + regmap_read(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, &val);
> + val |= PCIE_PULL_UP_SYS_AUX_PWR_DET;
> + regmap_write(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, val);
> +
> + /* output, pull down */
> + regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val);
> + val &= ~PCIE_OUTPUT_PULL_BITS;
> + val |= PCIE_OUTPUT_PULL_DOWN;
> + regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val);
> +
> + /* Handle phy_reset and lane0_reset to HW */
> + hi3670_apb_phy_updatel(phy, PCIEPHY_RESET_BIT,
> + PCIEPHY_PIPE_LINE0_RESET_BIT | PCIEPHY_RESET_BIT,
> + SOC_PCIEPHY_CTRL1_ADDR);
> +
> + /* fix chip bug: TxDetectRx fail */
> + hi3670_apb_phy_updatel(phy, PCIE_TXDETECT_RX_FAIL, PCIE_TXDETECT_RX_FAIL,
> + SOC_PCIEPHY_CTRL38_ADDR);
> +}
> +
> +static void hi3670_pcie_pll_init(struct hi3670_pcie_phy *phy)
> +{
> + hi3670_apb_phy_updatel(phy, PCIE_PHY_CHOOSE_FNPLL, PCIE_PHY_CHOOSE_FNPLL,
> + SOC_PCIEPHY_MMC1PLL_CTRL1);
> +
> +
> + hi3670_apb_phy_updatel(phy,
> + FIELD_PREP(PCIE_FNPLL_FBDIV_MASK, PCIE_FNPLL_FBDIV),
> + PCIE_FNPLL_FBDIV_MASK,
> + SOC_PCIEPHY_MMC1PLL_CTRL16);
> +
> + hi3670_apb_phy_updatel(phy,
> + FIELD_PREP(PCIE_FNPLL_FRACDIV_MASK, PCIE_FNPLL_FRACDIV),
> + PCIE_FNPLL_FRACDIV_MASK, SOC_PCIEPHY_MMC1PLL_CTRL17);
> +
> + hi3670_apb_phy_updatel(phy,
> + PCIE_FNPLL_DLL_EN |
> + FIELD_PREP(PCIE_FNPLL_POSTDIV1_MASK, PCIE_FNPLL_POSTDIV1) |
> + FIELD_PREP(PCIE_FNPLL_POSTDIV2_MASK, PCIE_FNPLL_POSTDIV2) |
> + FIELD_PREP(PCIE_FNPLL_PLL_MODE_MASK, PCIE_FNPLL_PLL_MODE),
> + PCIE_FNPLL_POSTDIV1_MASK |
> + PCIE_FNPLL_POSTDIV2_MASK |
> + PCIE_FNPLL_PLL_MODE_MASK | PCIE_FNPLL_DLL_EN,
> + SOC_PCIEPHY_MMC1PLL_CTRL20);
> +
> + hi3670_apb_phy_writel(phy, PCIE_PHY_MMC1PLL,
> + SOC_PCIEPHY_MMC1PLL_CTRL21);
> +}
> +
> +static int hi3670_pcie_pll_ctrl(struct hi3670_pcie_phy *phy, bool enable)
> +{
> + struct device *dev = phy->dev;
> + u32 val;
> + int time = PLL_CTRL_WAIT_TIME;
> +
> + if (enable) {
> + /* pd = 0 */
> + hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_MMC1PLL_DISABLE,
> + SOC_PCIEPHY_MMC1PLL_CTRL16);
> +
> + /* choose FNPLL */
> + while (!(val & FNPLL_HAS_LOCKED)) {
> + if (!time) {
> + dev_err(dev, "wait for pll_lock timeout\n");
> + return -EINVAL;
> + }
> + time--;
> + udelay(1);
> + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0);
> + }
> +
> + hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_PCIEPL_BP,
> + SOC_PCIEPHY_MMC1PLL_CTRL20);
> +
> + } else {
> + hi3670_apb_phy_updatel(phy,
> + PCIE_PHY_MMC1PLL_DISABLE,
> + PCIE_PHY_MMC1PLL_DISABLE,
> + SOC_PCIEPHY_MMC1PLL_CTRL16);
> +
> + hi3670_apb_phy_updatel(phy, PCIE_PHY_PCIEPL_BP,
> + PCIE_PHY_PCIEPL_BP,
> + SOC_PCIEPHY_MMC1PLL_CTRL20);
> + }
> +
> + return 0;
> +}
> +
> +static void hi3670_pcie_hp_debounce_gt(struct hi3670_pcie_phy *phy, bool open)
> +{
> + if (open)
> + /* gt_clk_pcie_hp/gt_clk_pcie_debounce open */
> + regmap_write(phy->crgctrl, CRGPERIPH_PEREN12,
> + IO_HP_DEBOUNCE_GT);
> + else
> + /* gt_clk_pcie_hp/gt_clk_pcie_debounce close */
> + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
> + IO_HP_DEBOUNCE_GT);
> +}
> +
> +static void hi3670_pcie_phyref_gt(struct hi3670_pcie_phy *phy, bool open)
> +{
> + unsigned int val;
> +
> + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
> +
> + if (open)
> + val &= ~IO_OE_HARD_GT_MODE; /* enable hard gt mode */
> + else
> + val |= IO_OE_HARD_GT_MODE; /* disable hard gt mode */
> +
> + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
> +
> + /* disable soft gt mode */
> + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, IO_PHYREF_SOFT_GT_MODE);
> +}
> +
> +static void hi3670_pcie_oe_ctrl(struct hi3670_pcie_phy *phy, bool en_flag)
> +{
> + unsigned int val;
> +
> + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
> +
> + /* set ie cfg */
> + val |= IO_IE_EN_HARD_BYPASS;
> +
> + /* set oe cfg */
> + val &= ~IO_HARD_CTRL_DEBOUNCE_BYPASS;
> +
> + /* set phy_debounce in&out time */
> + val |= (DEBOUNCE_WAITCFG_IN | DEBOUNCE_WAITCFG_OUT);
> +
> + /* select oe_gt_mode */
> + val |= IO_OE_GT_MODE;
> +
> + if (en_flag)
> + val &= ~IO_OE_EN_HARD_BYPASS;
> + else
> + val |= IO_OE_EN_HARD_BYPASS;
> +
> + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
> +}
> +
> +static void hi3670_pcie_ioref_gt(struct hi3670_pcie_phy *phy, bool open)
> +{
> + unsigned int val;
> +
> + if (open) {
> + regmap_write(phy->apb, SOC_PCIECTRL_CTRL21_ADDR,
> + SOC_PCIECTRL_CTRL21_DEFAULT);
> +
> + hi3670_pcie_oe_ctrl(phy, true);
> +
> + /* en hard gt mode */
> + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
> + val &= ~IO_REF_HARD_GT_MODE;
> + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
> +
> + /* disable soft gt mode */
> + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
> + IO_REF_SOFT_GT_MODE);
> +
> + } else {
> + /* disable hard gt mode */
> + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val);
> + val |= IO_REF_HARD_GT_MODE;
> + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val);
> +
> + /* disable soft gt mode */
> + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12,
> + IO_REF_SOFT_GT_MODE);
> +
> + hi3670_pcie_oe_ctrl(phy, false);
> + }
> +}
> +
> +static int hi3670_pcie_allclk_ctrl(struct hi3670_pcie_phy *phy, bool clk_on)
> +{
> + struct device *dev = phy->dev;
> + int ret = 0;
> +
> + if (!clk_on)
> + goto close_clocks;
> +
> + /* choose 100MHz clk src: Bit[8]==1 pad, Bit[8]==0 pll */
> + hi3670_apb_phy_updatel(phy, 0, PCIE_CLK_SOURCE,
> + SOC_PCIEPHY_CTRL1_ADDR);
> +
> + hi3670_pcie_pll_init(phy);
> +
> + ret = hi3670_pcie_pll_ctrl(phy, true);
> + if (ret) {
> + dev_err(dev, "Failed to enable pll\n");
> + return -EINVAL;
> + }
> + hi3670_pcie_hp_debounce_gt(phy, true);
> + hi3670_pcie_phyref_gt(phy, true);
> + hi3670_pcie_ioref_gt(phy, true);
> +
> + ret = clk_set_rate(phy->aclk, AXI_CLK_FREQ);
> + if (ret) {
> + dev_err(dev, "Failed to set rate\n");
> + goto close_clocks;
> + }
> +
> + return 0;
> +
> +close_clocks:
> + hi3670_pcie_ioref_gt(phy, false);
> + hi3670_pcie_phyref_gt(phy, false);
> + hi3670_pcie_hp_debounce_gt(phy, false);
> +
> + hi3670_pcie_pll_ctrl(phy, false);
> +
> + return ret;
> +}
> +
> +static bool is_pipe_clk_stable(struct hi3670_pcie_phy *phy)
> +{
> + struct device *dev = phy->dev;
> + u32 val;
> + u32 time = PIPE_CLK_STABLE_TIME;
> + u32 pipe_clk_stable = PCIE_IS_CLOCK_STABLE;
> +
> + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR);
> + while (val & pipe_clk_stable) {
> + mdelay(1);
> + if (!time) {
> + dev_err(dev, "PIPE clk is not stable\n");
> + return false;
> + }
> + time--;
> + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR);
> + }
> +
> + return true;
> +}
> +
> +static int hi3670_pcie_noc_power(struct hi3670_pcie_phy *phy, bool enable)
> +{
> + struct device *dev = phy->dev;
> + u32 time = NOC_POWER_TIME;
> + unsigned int val = NOC_PW_MASK;
> + int rst;
> +
> + if (enable)
> + val = NOC_PW_MASK | NOC_PW_SET_BIT;
> + else
> + val = NOC_PW_MASK;
> + rst = enable ? 1 : 0;
> +
> + regmap_write(phy->pmctrl, NOC_POWER_IDLEREQ_1, val);
> +
> + time = NOC_POWER_TIME;
> + regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val);
> + while ((val & NOC_PW_SET_BIT) != rst) {
> + udelay(10);
> + if (!time) {
> + dev_err(dev, "Failed to reverse noc power-status\n");
> + return -EINVAL;
> + }
> + time--;
> + regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val);
> + }
> +
> + return 0;
> +}
> +
> +static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy)
> +{
> + struct device_node *pcie_port;
> + struct device *dev = phy->dev;
> + struct device *pcie_dev;
> +
> + pcie_port = of_get_child_by_name(dev->parent->of_node, "pcie");
> + if (!pcie_port) {
> + dev_err(dev, "no pcie node found in %s\n",
> + dev->parent->of_node->full_name);
> + return -ENODEV;
> + }
> +
> + pcie_dev = bus_find_device_by_of_node(&platform_bus_type, pcie_port);
> + if (!pcie_dev) {
> + dev_err(dev, "Didn't find pcie device\n");
> + return -ENODEV;
> + }
> +
> + /*
> + * We might just use NULL instead of the APB name, as the
> + * pcie-kirin currently registers directly just one regmap (although
> + * the DWC driver register other regmaps).
> + *
> + * Yet, it sounds safer to warrant that it will be accessing the
> + * right regmap. So, let's use the named version.
> + */
> + phy->apb = dev_get_regmap(pcie_dev, "kirin_pcie_apb");
> + if (!phy->apb) {
> + dev_err(dev, "Failed to get APB regmap\n");
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static int kirin_pcie_clk_ctrl(struct hi3670_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 hi3670_pcie_phy_init(struct phy *generic_phy)
> +{
> + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
> + int ret;
> +
> + /*
> + * The code under hi3670_pcie_get_resources_from_pcie() need to
> + * access the reset-gpios and the APB registers, both from the
> + * pcie-kirin driver.
> + *
> + * The APB is obtained via the pcie driver's regmap
> + * Such kind of resource can only be obtained during the PCIe
> + * power_on sequence, as the code inside pcie-kirin needs to
> + * be already probed, as it needs to register the APB regmap.
> + */
> +
> + ret = hi3670_pcie_get_resources_from_pcie(phy);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int hi3670_pcie_phy_power_on(struct phy *generic_phy)
> +{
> + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
> + int val, 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);
> +
> + hi3670_pcie_phy_oe_enable(phy, true);
> +
> + ret = kirin_pcie_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);
> +
> + hi3670_pcie_natural_cfg(phy);
> +
> + ret = hi3670_pcie_allclk_ctrl(phy, true);
> + if (ret)
> + goto disable_clks;
> +
> + /* pull down phy_test_powerdown signal */
> + hi3670_apb_phy_updatel(phy, 0, PCIE_PULL_DOWN_PHY_TEST_POWERDOWN,
> + SOC_PCIEPHY_CTRL0_ADDR);
> +
> + /* deassert controller perst_n */
> + regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val);
> + val |= PCIE_DEASSERT_CONTROLLER_PERST;
> + regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val);
> + udelay(10);
> +
> + ret = is_pipe_clk_stable(phy);
> + if (!ret)
> + goto disable_clks;
> +
> + hi3670_pcie_set_eyeparam(phy);
> +
> + ret = hi3670_pcie_noc_power(phy, false);
> + if (ret)
> + goto disable_clks;
> +
> + return 0;
> +
> +disable_clks:
> + kirin_pcie_clk_ctrl(phy, false);
> + return ret;
> +}
> +
> +static int hi3670_pcie_phy_power_off(struct phy *generic_phy)
> +{
> + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy);
> +
> + hi3670_pcie_phy_oe_enable(phy, false);
> +
> + hi3670_pcie_allclk_ctrl(phy, false);
> +
> + /* Drop power supply for Host */
> + regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, 0);
> +
> + /*
> + * FIXME: The enabled clocks should be disabled here by calling
> + * kirin_pcie_clk_ctrl(phy, false);
> + * However, some clocks used at Kirin 970 should be marked as
> + * CLK_IS_CRITICAL at clk-hi3670 driver, as powering such clocks off
> + * cause an Asynchronous SError interrupt, which produces panic().
> + * While clk-hi3670 is not fixed, we cannot risk disabling clocks here.
> + */
> +
> + return 0;
> +}
> +
> +static const struct phy_ops hi3670_phy_ops = {
> + .init = hi3670_pcie_phy_init,
> + .power_on = hi3670_pcie_phy_power_on,
> + .power_off = hi3670_pcie_phy_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static int hi3670_pcie_phy_get_resources(struct hi3670_pcie_phy *phy,
> + struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> +
> + /* syscon */
> + phy->crgctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-crgctrl");
> + if (IS_ERR(phy->crgctrl))
> + return PTR_ERR(phy->crgctrl);
> +
> + phy->sysctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-sctrl");
> + if (IS_ERR(phy->sysctrl))
> + return PTR_ERR(phy->sysctrl);
> +
> + phy->pmctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-pmctrl");
> + if (IS_ERR(phy->sysctrl))
> + return PTR_ERR(phy->sysctrl);
> +
> + /* clocks */
> + phy->phy_ref_clk = devm_clk_get(dev, "phy_ref");
> + if (IS_ERR(phy->phy_ref_clk))
> + return PTR_ERR(phy->phy_ref_clk);
> +
> + phy->aux_clk = devm_clk_get(dev, "aux");
> + if (IS_ERR(phy->aux_clk))
> + return PTR_ERR(phy->aux_clk);
> +
> + phy->apb_phy_clk = devm_clk_get(dev, "apb_phy");
> + if (IS_ERR(phy->apb_phy_clk))
> + return PTR_ERR(phy->apb_phy_clk);
> +
> + phy->apb_sys_clk = devm_clk_get(dev, "apb_sys");
> + if (IS_ERR(phy->apb_sys_clk))
> + return PTR_ERR(phy->apb_sys_clk);
> +
> + phy->aclk = devm_clk_get(dev, "aclk");
> + if (IS_ERR(phy->aclk))
> + return PTR_ERR(phy->aclk);
> +
> + /* registers */
> + phy->base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(phy->base))
> + return PTR_ERR(phy->base);
> +
> + hi3670_pcie_get_eyeparam(phy);
> +
> + return 0;
> +}
> +
> +static int hi3670_pcie_phy_probe(struct platform_device *pdev)
> +{
> + struct phy_provider *phy_provider;
> + struct device *dev = &pdev->dev;
> + struct hi3670_pcie_phy *phy;
> + struct phy *generic_phy;
> + int ret;
> +
> + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
> + if (!phy)
> + return -ENOMEM;
> +
> + phy->dev = dev;
> +
> + ret = hi3670_pcie_phy_get_resources(phy, pdev);
> + if (ret)
> + return ret;
> +
> + generic_phy = devm_phy_create(dev, dev->of_node, &hi3670_phy_ops);
> + if (IS_ERR(generic_phy)) {
> + dev_err(dev, "failed to create PHY\n");
> + return PTR_ERR(generic_phy);
> + }
> +
> + phy_set_drvdata(generic_phy, phy);
> + 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 hi3670_pcie_phy_match[] = {
> + {
> + .compatible = "hisilicon,hi970-pcie-phy",
> + },
> + {},
> +};
> +
> +static struct platform_driver hi3670_pcie_phy_driver = {
> + .probe = hi3670_pcie_phy_probe,
> + .driver = {
> + .of_match_table = hi3670_pcie_phy_match,
> + .name = "hi3670_pcie_phy",
> + .suppress_bind_attrs = true,
> + }
> +};
> +builtin_platform_driver(hi3670_pcie_phy_driver);
> +
> +MODULE_DEVICE_TABLE(of, hi3670_pcie_phy_match);
> +MODULE_DESCRIPTION("PCIe phy driver for Kirin 970");
> +MODULE_AUTHOR("Mauro Carvalho Chehab <[email protected]>");
> +MODULE_AUTHOR("Manivannan Sadhasivam <[email protected]>");
> +MODULE_LICENSE("GPL v2");



Thanks,
Mauro