Drivers/PCIe: support pcie function for hikey970

Add PCIe code in kernel to support PCIe for hikey970

Signed-off-by: Yao Chen <chenyao11@huawei.com>
This commit is contained in:
c00354815
2018-02-12 16:21:10 +08:00
committed by Guodong Xu
parent e04a0e9faa
commit 698e0b3159
9 changed files with 1462 additions and 285 deletions
+72 -26
View File
@@ -9,7 +9,8 @@ Additional properties are described here:
Required properties
- compatible:
"hisilicon,kirin960-pcie" for PCIe of Kirin960 SoC
"hisilicon,hikey960" for PCIe of Kirin960 SoC or
"hisilicon,hikey970" for PCIe of Kirin970 SoC
- reg: Should contain rc_dbi, apb, phy, config registers location and length.
- reg-names: Must include the following entries:
"dbi": controller configuration registers;
@@ -23,28 +24,73 @@ Optional properties:
Example based on kirin960:
pcie@f4000000 {
compatible = "hisilicon,kirin-pcie";
reg = <0x0 0xf4000000 0x0 0x1000>, <0x0 0xff3fe000 0x0 0x1000>,
<0x0 0xf3f20000 0x0 0x40000>, <0x0 0xF4000000 0 0x2000>;
reg-names = "dbi","apb","phy", "config";
bus-range = <0x0 0x1>;
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
ranges = <0x02000000 0x0 0x00000000 0x0 0xf5000000 0x0 0x2000000>;
num-lanes = <1>;
#interrupt-cells = <1>;
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <0x0 0 0 1 &gic 0 0 0 282 4>,
<0x0 0 0 2 &gic 0 0 0 283 4>,
<0x0 0 0 3 &gic 0 0 0 284 4>,
<0x0 0 0 4 &gic 0 0 0 285 4>;
clocks = <&crg_ctrl HI3660_PCIEPHY_REF>,
<&crg_ctrl HI3660_CLK_GATE_PCIEAUX>,
<&crg_ctrl HI3660_PCLK_GATE_PCIE_PHY>,
<&crg_ctrl HI3660_PCLK_GATE_PCIE_SYS>,
<&crg_ctrl HI3660_ACLK_GATE_PCIE>;
clock-names = "pcie_phy_ref", "pcie_aux",
"pcie_apb_phy", "pcie_apb_sys", "pcie_aclk";
reset-gpios = <&gpio11 1 0 >;
};
compatible = "hisilicon,hikey960";
reg = <0x0 0xf4000000 0x0 0x1000>,
<0x0 0xff3fe000 0x0 0x1000>,
<0x0 0xf3f20000 0x0 0x40000>,
<0x0 0xf5000000 0x0 0x2000>;
reg-names = "dbi", "apb", "phy", "config";
bus-range = <0x0 0x1>;
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
ranges = <0x02000000 0x0 0x00000000
0x0 0xf6000000
0x0 0x02000000>;
num-lanes = <1>;
#interrupt-cells = <1>;
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <0x0 0 0 1
&gic GIC_SPI 282 IRQ_TYPE_LEVEL_HIGH>,
<0x0 0 0 2
&gic GIC_SPI 283 IRQ_TYPE_LEVEL_HIGH>,
<0x0 0 0 3
&gic GIC_SPI 284 IRQ_TYPE_LEVEL_HIGH>,
<0x0 0 0 4
&gic GIC_SPI 285 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&crg_ctrl HI3660_PCIEPHY_REF>,
<&crg_ctrl HI3660_CLK_GATE_PCIEAUX>,
<&crg_ctrl HI3660_PCLK_GATE_PCIE_PHY>,
<&crg_ctrl HI3660_PCLK_GATE_PCIE_SYS>,
<&crg_ctrl HI3660_ACLK_GATE_PCIE>;
clock-names = "pcie_phy_ref", "pcie_aux",
"pcie_apb_phy", "pcie_apb_sys",
"pcie_aclk";
reset-gpios = <&gpio11 1 0 >;
};
Example based on kirin970:
pcie@f4000000 {
compatible = "hisilicon,hikey970";
reg = <0x0 0xf4000000 0x0 0x1000000>,
<0x0 0xfc180000 0x0 0x1000>,
<0x0 0xfc040000 0x0 0x40000>,
<0x0 0xf5000000 0x0 0x2000>;
reg-names = "dbi", "apb", "phy", "config";
bus-range = <0x0 0x1>;
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
ranges = <0x02000000 0x0 0x00000000
0x0 0xf6000000
0x0 0x02000000>;
num-lanes = <1>;
#interrupt-cells = <1>;
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <0x0 0 0 1
&gic GIC_SPI 282 IRQ_TYPE_LEVEL_HIGH>,
<0x0 0 0 2
&gic GIC_SPI 283 IRQ_TYPE_LEVEL_HIGH>,
<0x0 0 0 3
&gic GIC_SPI 284 IRQ_TYPE_LEVEL_HIGH>,
<0x0 0 0 4
&gic GIC_SPI 285 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&crg_ctrl KIRIN970_CLK_GATE_PCIEAUX>,
<&crg_ctrl KIRIN970_PCLK_GATE_PCIE_PHY>,
<&crg_ctrl KIRIN970_PCLK_GATE_PCIE_SYS>,
<&crg_ctrl KIRIN970_ACLK_GATE_PCIE>;
clock-names = "pcie_aux", "pcie_apb_phy",
"pcie_apb_sys", "pcie_aclk";
reset-gpios = <&gpio7 0 0 >;
};
+5 -3
View File
@@ -1076,10 +1076,10 @@
};
pcie@f4000000 {
compatible = "hisilicon,kirin-pcie";
compatible = "hisilicon,hikey960";
reg = <0x0 0xf4000000 0x0 0x1000>,
<0x0 0xff3fe000 0x0 0x1000>,
<0x0 0xf3f20000 0x0 0x40000>,
<0x0 0xf3f00000 0x0 0x40000>,
<0x0 0xf5000000 0x0 0x2000>;
reg-names = "dbi", "apb", "phy", "config";
bus-range = <0x0 0x1>;
@@ -1091,6 +1091,8 @@
0x0 0x02000000>;
num-lanes = <1>;
#interrupt-cells = <1>;
interrupts = <0 283 4>;
interrups-names = "msi";
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <0x0 0 0 1
&gic GIC_SPI 282 IRQ_TYPE_LEVEL_HIGH>,
@@ -1108,7 +1110,7 @@
clock-names = "pcie_phy_ref", "pcie_aux",
"pcie_apb_phy", "pcie_apb_sys",
"pcie_aclk";
reset-gpio = <&gpio11 1 0 >;
reset-gpios = <&gpio11 1 0 >;
};
/* SD */
+1 -1
View File
@@ -77,7 +77,7 @@ CONFIG_PCI_LAYERSCAPE=y
CONFIG_PCI_HISI=y
CONFIG_PCIE_QCOM=y
CONFIG_PCIE_ARMADA_8K=y
# CONFIG_PCIE_KIRIN is not set
CONFIG_PCIE_KIRIN=y
CONFIG_ARM64_VA_BITS_48=y
CONFIG_SCHED_MC=y
CONFIG_PREEMPT=y
Regular → Executable
+2 -2
View File
@@ -305,9 +305,9 @@ config PCIE_KIRIN
depends on OF && ARM64
depends on ARCH_HISI
select PCIE_DW
bool "Hisilicon Kirin960 PCIe controller"
bool "Hisilicon Kirin PCIe controller"
help
Say Y here if you want PCIe controller support on Hisilicon
Kirin960 SoC
Kirin SoC
endmenu
Regular → Executable
+1 -1
View File
@@ -33,4 +33,4 @@ obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
obj-$(CONFIG_VMD) += vmd.o
obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o
obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o pcie-kirin960.o pcie-kirin970.o
Regular → Executable
+213 -230
View File
@@ -15,217 +15,6 @@
struct kirin_pcie *g_kirin_pcie;
static int kirin_pcie_link_up(struct pcie_port *pp);
static inline void kirin_elb_writel(struct kirin_pcie *pcie, u32 val, u32 reg)
{
writel(val, pcie->apb_base + reg);
}
static inline u32 kirin_elb_readl(struct kirin_pcie *pcie, u32 reg)
{
return readl(pcie->apb_base + reg);
}
/*Registers in PCIePHY*/
static inline void kirin_phy_writel(struct kirin_pcie *pcie, u32 val, u32 reg)
{
writel(val, pcie->phy_base + reg);
}
static inline u32 kirin_phy_readl(struct kirin_pcie *pcie, u32 reg)
{
return readl(pcie->phy_base + reg);
}
static int32_t kirin_pcie_get_clk(struct kirin_pcie *pcie,
struct platform_device *pdev)
{
pcie->phy_ref_clk = devm_clk_get(&pdev->dev, "pcie_phy_ref");
if (IS_ERR(pcie->phy_ref_clk))
return PTR_ERR(pcie->phy_ref_clk);
pcie->pcie_aux_clk = devm_clk_get(&pdev->dev, "pcie_aux");
if (IS_ERR(pcie->pcie_aux_clk))
return PTR_ERR(pcie->pcie_aux_clk);
pcie->apb_phy_clk = devm_clk_get(&pdev->dev, "pcie_apb_phy");
if (IS_ERR(pcie->apb_phy_clk))
return PTR_ERR(pcie->apb_phy_clk);
pcie->apb_sys_clk = devm_clk_get(&pdev->dev, "pcie_apb_sys");
if (IS_ERR(pcie->apb_sys_clk))
return PTR_ERR(pcie->apb_sys_clk);
pcie->pcie_aclk = devm_clk_get(&pdev->dev, "pcie_aclk");
if (IS_ERR(pcie->pcie_aclk))
return PTR_ERR(pcie->pcie_aclk);
return 0;
}
static int32_t kirin_pcie_get_resource(struct pcie_port *pp,
struct platform_device *pdev)
{
struct resource *apb;
struct resource *phy;
struct resource *dbi;
struct kirin_pcie *pcie = to_kirin_pcie(pp);
apb = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apb");
pcie->apb_base = devm_ioremap_resource(&pdev->dev, apb);
if (IS_ERR(pcie->apb_base))
return PTR_ERR(pcie->apb_base);
phy = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy);
if (IS_ERR(pcie->phy_base))
return PTR_ERR(pcie->phy_base);
dbi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi);
if (IS_ERR(pp->dbi_base))
return PTR_ERR(pp->dbi_base);
pcie->crgctrl =
syscon_regmap_lookup_by_compatible("hisilicon,hi3660-crgctrl");
if (IS_ERR(pcie->crgctrl))
return PTR_ERR(pcie->crgctrl);
pcie->sysctrl =
syscon_regmap_lookup_by_compatible("hisilicon,hi3660-sctrl");
if (IS_ERR(pcie->sysctrl))
return PTR_ERR(pcie->sysctrl);
return 0;
}
static int kirin_pcie_phy_init(struct kirin_pcie *pcie)
{
u32 reg_val;
u32 pipe_clk_stable = 0x1 << 19;
u32 time = 10;
reg_val = kirin_phy_readl(pcie, 0x4);
reg_val &= ~(0x1 << 8);
kirin_phy_writel(pcie, reg_val, 0x4);
reg_val = kirin_phy_readl(pcie, 0x0);
reg_val &= ~(0x1 << 22);
kirin_phy_writel(pcie, reg_val, 0x0);
udelay(10);
reg_val = kirin_phy_readl(pcie, 0x4);
reg_val &= ~(0x1 << 16);
kirin_phy_writel(pcie, reg_val, 0x4);
reg_val = kirin_phy_readl(pcie, 0x400);
while (reg_val & pipe_clk_stable) {
udelay(100);
if (time == 0) {
dev_err(pcie->pp.dev, "PIPE clk is not stable\n");
return -EINVAL;
}
time--;
reg_val = kirin_phy_readl(pcie, 0x400);
}
return 0;
}
static void kirin_pcie_oe_enable(struct kirin_pcie *pcie)
{
u32 val;
regmap_read(pcie->sysctrl, 0x1a4, &val);
val |= 0xF0F400;
val &= ~(0x3 << 28);
regmap_write(pcie->sysctrl, 0x1a4, val);
}
static int kirin_pcie_clk_ctrl(struct kirin_pcie *pcie, bool enable)
{
int ret = 0;
if (!enable)
goto close_clk;
ret = clk_set_rate(pcie->phy_ref_clk, REF_CLK_FREQ);
if (ret)
return ret;
ret = clk_prepare_enable(pcie->phy_ref_clk);
if (ret)
return ret;
ret = clk_prepare_enable(pcie->apb_sys_clk);
if (ret)
goto apb_sys_fail;
ret = clk_prepare_enable(pcie->apb_phy_clk);
if (ret)
goto apb_phy_fail;
ret = clk_prepare_enable(pcie->pcie_aclk);
if (ret)
goto aclk_fail;
ret = clk_prepare_enable(pcie->pcie_aux_clk);
if (ret)
goto aux_clk_fail;
return 0;
close_clk:
clk_disable_unprepare(pcie->pcie_aux_clk);
aux_clk_fail:
clk_disable_unprepare(pcie->pcie_aclk);
aclk_fail:
clk_disable_unprepare(pcie->apb_phy_clk);
apb_phy_fail:
clk_disable_unprepare(pcie->apb_sys_clk);
apb_sys_fail:
clk_disable_unprepare(pcie->phy_ref_clk);
return ret;
}
static int kirin_pcie_power_on(struct kirin_pcie *pcie)
{
int ret;
/*Power supply for Host*/
regmap_write(pcie->sysctrl, 0x60, 0x10);
udelay(100);
kirin_pcie_oe_enable(pcie);
ret = kirin_pcie_clk_ctrl(pcie, true);
if (ret)
return ret;
/*deasset PCIeCtrl&PCIePHY*/
regmap_write(pcie->sysctrl, 0x44, 0x30);
regmap_write(pcie->crgctrl, 0x88, 0x8c000000);
regmap_write(pcie->sysctrl, 0x190, 0x184000);
ret = kirin_pcie_phy_init(pcie);
if (ret)
goto close_clk;
/*perst assert*/
mdelay(20);
if (!gpio_request(pcie->gpio_id_reset, "pcie_perst")) {
ret = gpio_direction_output(pcie->gpio_id_reset, 1);
if (ret)
goto close_clk;
mdelay(10);
return 0;
}
close_clk:
kirin_pcie_clk_ctrl(pcie, false);
return -1;
}
static void kirin_pcie_sideband_dbi_w_mode(struct pcie_port *pp, bool on)
{
u32 val;
@@ -314,7 +103,7 @@ static void kirin_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val)
static int kirin_pcie_link_up(struct pcie_port *pp)
{
struct kirin_pcie *pcie = to_kirin_pcie(pp);
u32 val = kirin_elb_readl(pcie, PCIE_ELBI_RDLH_LINKUP);
u32 val = kirin_elb_readl(pcie, SOC_PCIECTRL_STATE0_ADDR);
if ((val & PCIE_LINKUP_ENABLE) == PCIE_LINKUP_ENABLE)
return 1;
@@ -335,7 +124,7 @@ static int kirin_pcie_establish_link(struct pcie_port *pp)
/* assert LTSSM enable */
kirin_elb_writel(pcie, PCIE_LTSSM_ENABLE_BIT,
PCIE_APP_LTSSM_ENABLE);
SOC_PCIECTRL_CTRL7_ADDR);
/* check if the link is up or not */
while (!kirin_pcie_link_up(pp)) {
@@ -350,9 +139,33 @@ static int kirin_pcie_establish_link(struct pcie_port *pp)
return 0;
}
static irqreturn_t kirin_pcie_msi_irq_handler(int irq, void *arg)
{
struct pcie_port *pp = arg;
return dw_handle_msi_irq(pp);
}
static void kirin_pcie_msi_init(struct pcie_port *pp)
{
dw_pcie_msi_init(pp);
}
static void kirin_pcie_enable_interrupts(struct pcie_port *pp)
{
if (IS_ENABLED(CONFIG_PCI_MSI))
kirin_pcie_msi_init(pp);
}
static void kirin_pcie_host_init(struct pcie_port *pp)
{
kirin_pcie_establish_link(pp);
if (kirin_pcie_establish_link(pp))
return;
kirin_pcie_enable_interrupts(pp);
return;
}
static struct pcie_host_ops kirin_pcie_host_ops = {
@@ -369,6 +182,22 @@ static int __init kirin_add_pcie_port(struct pcie_port *pp,
{
int ret;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
pp->msi_irq = platform_get_irq(pdev, 0);
if (!pp->msi_irq) {
dev_err(&pdev->dev, "failed to get msi irq\n");
return -ENODEV;
}
ret = devm_request_irq(&pdev->dev, pp->msi_irq,
kirin_pcie_msi_irq_handler,
IRQF_SHARED | IRQF_TRIGGER_RISING,
"kirin_pcie_msi", pp);
if (ret) {
dev_err(&pdev->dev, "failed to request msi irq\n");
return ret;
}
}
pp->root_bus_nr = -1;
pp->ops = &kirin_pcie_host_ops;
@@ -377,11 +206,78 @@ static int __init kirin_add_pcie_port(struct pcie_port *pp,
return ret;
}
int kirin_pcie_save_rc_cfg(struct kirin_pcie *pcie)
{
int ret;
u32 val = 0;
int aer_pos;
struct pcie_port *pp;
pp = &(pcie->pp);
kirin_pcie_rd_own_conf(pp, PORT_MSI_CTRL_ADDR, 4, &val);
pcie->msi_controller_config[0] = val;
kirin_pcie_rd_own_conf(pp, PORT_MSI_CTRL_UPPER_ADDR, 4, &val);
pcie->msi_controller_config[1] = val;
kirin_pcie_rd_own_conf(pp, PORT_MSI_CTRL_INT0_ENABLE, 4, &val);
pcie->msi_controller_config[2] = val;
aer_pos = pci_find_ext_capability(pcie->rc_dev, PCI_EXT_CAP_ID_ERR);
if (!aer_pos ) {
dev_err(pcie->pp.dev, "Failed to get RC PCI_EXT_CAP_ID_ERR\n");
return -EINVAL;
}
pci_read_config_dword(pcie->rc_dev, aer_pos + PCI_ERR_ROOT_COMMAND,
&pcie->aer_config);
ret = pci_save_state(pcie->rc_dev);
if (ret) {
dev_err(pcie->pp.dev, "Failed to save state of RC\n");
return -EINVAL;
}
pcie->rc_saved_state = pci_store_saved_state(pcie->rc_dev);
return 0;
}
int kirin_pcie_restore_rc_cfg(struct kirin_pcie *pcie)
{
struct pcie_port *pp;
int aer_pos;
pp = &(pcie->pp);
kirin_pcie_wr_own_conf(pp, PORT_MSI_CTRL_ADDR,
4, pcie->msi_controller_config[0]);
kirin_pcie_wr_own_conf(pp, PORT_MSI_CTRL_UPPER_ADDR,
4, pcie->msi_controller_config[1]);
kirin_pcie_wr_own_conf(pp, PORT_MSI_CTRL_INT0_ENABLE,
4, pcie->msi_controller_config[2]);
aer_pos = pci_find_ext_capability(pcie->rc_dev, PCI_EXT_CAP_ID_ERR);
if (!aer_pos ) {
dev_err(pcie->pp.dev, "Failed to get RC PCI_EXT_CAP_ID_ERR\n");
return -EINVAL;
}
pci_write_config_dword(pcie->rc_dev, aer_pos + PCI_ERR_ROOT_COMMAND,
pcie->aer_config);
pci_load_saved_state(pcie->rc_dev, pcie->rc_saved_state);
pci_restore_state(pcie->rc_dev);
return 0;
}
static int kirin_pcie_probe(struct platform_device *pdev)
{
struct kirin_pcie *pcie;
struct pcie_port *pp;
int ret;
u32 dev_id;
u32 vendor_id;
int (*pcie_probe)(struct kirin_pcie *);
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "NULL node\n");
@@ -397,22 +293,23 @@ static int kirin_pcie_probe(struct platform_device *pdev)
g_kirin_pcie = pcie;
pp->dev = &pdev->dev;
ret = kirin_pcie_get_clk(pcie, pdev);
if (ret != 0)
return -ENODEV;
pcie->pcie_ops= of_device_get_match_data(pp->dev);
if (!pcie->pcie_ops) {
dev_err(&pdev->dev, "failed to get pcie_ops\n");
return -EINVAL;
}
ret = kirin_pcie_get_resource(pp, pdev);
if (ret != 0)
return -ENODEV;
pcie_probe = pcie->pcie_ops->pcie_probe;
if (!pcie_probe) {
dev_err(&pdev->dev, "failed to get pcie_probe function\n");
return -EINVAL;
}
pcie->gpio_id_reset = of_get_named_gpio(pdev->dev.of_node,
"reset-gpio", 0);
if (pcie->gpio_id_reset < 0)
return -ENODEV;
ret = kirin_pcie_power_on(pcie);
if (ret)
return ret;
ret = pcie_probe(pcie);
if (ret) {
dev_err(&pdev->dev, "pcie_probe fail\n");
return -EINVAL;
}
ret = kirin_add_pcie_port(pp, pdev);
if (ret)
@@ -420,12 +317,97 @@ static int kirin_pcie_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pcie);
dev_dbg(&pdev->dev, "probe Done\n");
kirin_pcie_rd_own_conf(pp, PCI_VENDOR_ID, 2, &vendor_id);
kirin_pcie_rd_own_conf(pp, PCI_DEVICE_ID, 2, &dev_id);
pcie->rc_dev = pci_get_device(vendor_id, dev_id, pcie->rc_dev);
if (!pcie->rc_dev) {
dev_err(&pdev->dev, "Failed to get RC device\n");
return -EINVAL;
}
ret = kirin_pcie_save_rc_cfg(pcie);
return 0;
}
#ifdef CONFIG_PM
static int kirin_pcie_resume_noirq(struct device *dev)
{
int ret;
struct kirin_pcie *pcie;
int (*pcie_resume_noirq)(struct device *);
pcie = dev_get_drvdata(dev);
if (!pcie) {
dev_err(dev, "Failed to get drvdata\n");
return -EINVAL;
}
pcie_resume_noirq = pcie->pcie_ops->pcie_resume_noirq;
if (!pcie_resume_noirq) {
dev_err(dev, "failed to get pcie_resume_noirq function\n");
return 0;
}
ret = pcie_resume_noirq(dev);
if (ret)
return ret;
ret = kirin_pcie_establish_link(&(pcie->pp));
if (ret)
return ret;
return 0;
}
static int kirin_pcie_suspend_noirq(struct device *dev)
{
int ret;
struct kirin_pcie *pcie;
int (*pcie_suspend_noirq)(struct device *);
pcie = dev_get_drvdata(dev);
if (!pcie) {
dev_err(dev, "Failed to get drvdata\n");
return -EINVAL;
}
pcie_suspend_noirq = pcie->pcie_ops->pcie_suspend_noirq;
if (!pcie_suspend_noirq) {
dev_err(dev, "failed to get pcie_suspend_noirq function\n");
return 0;
}
ret = pcie_suspend_noirq(dev);
if (ret)
return ret;
return 0;
}
#else
#define kirin_pcie_suspend_noirq NULL
#define kirin_pcie_resume_noirq NULL
#endif
static const struct dev_pm_ops kirin_pcie_dev_pm_ops = {
.suspend_noirq = kirin_pcie_suspend_noirq,
.resume_noirq = kirin_pcie_resume_noirq,
};
static const struct of_device_id kirin_pcie_match[] = {
{ .compatible = "hisilicon,kirin-pcie" },
{
.compatible = "hisilicon,hikey960",
.data = &kirin960_pcie_ops
},
{
.compatible = "hisilicon,hikey970",
.data = &kirin970_pcie_ops
},
{},
};
MODULE_DEVICE_TABLE(of, kirin_pcie_match);
@@ -436,6 +418,7 @@ struct platform_driver kirin_pcie_driver = {
.name = "Kirin-pcie",
.owner = THIS_MODULE,
.of_match_table = kirin_pcie_match,
.pm =&kirin_pcie_dev_pm_ops,
},
};
Regular → Executable
+91 -22
View File
@@ -33,39 +33,108 @@
#include <linux/types.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/pci_regs.h>
#include <linux/regulator/consumer.h>
#include "pcie-designware.h"
#define to_kirin_pcie(x) container_of(x, struct kirin_pcie, pp)
#define REF_CLK_FREQ 100000000
#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)
/* 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_APP_LTSSM_ENABLE 0x01c
#define PCIE_ELBI_RDLH_LINKUP 0x400
#define PCIE_LINKUP_ENABLE (0x8020)
#define PCIE_LTSSM_ENABLE_BIT (0x1 << 11)
/* 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 PCIE_LINKUP_ENABLE (0x8020)
#define PCIE_ELBI_SLV_DBI_ENABLE (0x1 << 21)
#define PCIE_LTSSM_ENABLE_BIT (0x1 << 11)
#define PCIEPHY_RESET_BIT (0x1 << 17)
#define PCIEPHY_PIPE_LINE0_RESET_BIT (0x1 << 19)
#define PORT_MSI_CTRL_ADDR 0x820
#define PORT_MSI_CTRL_UPPER_ADDR 0x824
#define PORT_MSI_CTRL_INT0_ENABLE 0x828
struct kirin_pcie {
void __iomem *apb_base;
void __iomem *phy_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;
int gpio_id_reset;
struct pcie_port pp;
void __iomem *apb_base;
void __iomem *phy_base;
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 *pcie_aclk;
struct clk *pcie_aux_clk;
int gpio_id_reset[4];
int gpio_id_clkreq[3];
u32 eye_param[5];
u32 aer_config;
u32 msi_controller_config[3];
struct pcie_port pp;
struct pci_dev *rc_dev;
struct pci_saved_state *rc_saved_state;
const struct kirin_pcie_ops *pcie_ops;
struct regulator *ldo33;
};
struct kirin_pcie_ops {
int (*pcie_probe)(struct kirin_pcie *pcie);
int (*pcie_suspend_noirq)(struct device *dev);
int (*pcie_resume_noirq)(struct device *dev);
void (*kirin_phy_writel)(struct kirin_pcie *pcie, u32 val, u32 reg);
u32 (*kirin_phy_readl)(struct kirin_pcie *pcie, u32 reg);
};
static inline void kirin_elb_writel(struct kirin_pcie *pcie, u32 val, u32 reg)
{
writel(val, pcie->apb_base + reg);
}
static inline u32 kirin_elb_readl(struct kirin_pcie *pcie, u32 reg)
{
return readl(pcie->apb_base + reg);
}
static inline void kirin_phy_writel(struct kirin_pcie *pcie, u32 val, u32 reg)
{
pcie->pcie_ops->kirin_phy_writel(pcie, val, reg);
}
static inline u32 kirin_phy_readl(struct kirin_pcie *pcie, u32 reg)
{
return pcie->pcie_ops->kirin_phy_readl(pcie, reg);
}
static inline void kirin_natural_phy_writel(struct kirin_pcie * pcie, u32 val, u32 reg)
{
writel(val, pcie->phy_base + reg * 4);
}
static inline u32 kirin_natural_phy_readl(struct kirin_pcie *pcie, u32 reg)
{
return readl(pcie->phy_base + reg * 4);
}
int kirin_pcie_save_rc_cfg(struct kirin_pcie *pcie);
int kirin_pcie_restore_rc_cfg(struct kirin_pcie *pcie);
extern const struct kirin_pcie_ops kirin960_pcie_ops;
extern const struct kirin_pcie_ops kirin970_pcie_ops;
#endif
+249
View File
@@ -0,0 +1,249 @@
/*
* PCIe host controller driver for Kirin960 Phone SoCs
*
* Copyright (C) 2015 Hilisicon Electronics Co., Ltd.
* http://www.huawei.com
*
* Author: Xiaowei Song <songxiaowei@huawei.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "pcie-kirin.h"
static inline void kirin960_phy_writel(struct kirin_pcie *pcie, u32 val, u32 reg)
{
writel(val, pcie->phy_base + 0x20000 + reg);
}
static inline u32 kirin960_phy_readl(struct kirin_pcie *pcie, u32 reg)
{
return readl(pcie->phy_base + 0x20000 + reg);
}
static int32_t kirin960_pcie_get_clk(struct kirin_pcie *pcie,
struct platform_device *pdev)
{
pcie->phy_ref_clk = devm_clk_get(&pdev->dev, "pcie_phy_ref");
if (IS_ERR(pcie->phy_ref_clk))
return PTR_ERR(pcie->phy_ref_clk);
pcie->pcie_aux_clk = devm_clk_get(&pdev->dev, "pcie_aux");
if (IS_ERR(pcie->pcie_aux_clk))
return PTR_ERR(pcie->pcie_aux_clk);
pcie->apb_phy_clk = devm_clk_get(&pdev->dev, "pcie_apb_phy");
if (IS_ERR(pcie->apb_phy_clk))
return PTR_ERR(pcie->apb_phy_clk);
pcie->apb_sys_clk = devm_clk_get(&pdev->dev, "pcie_apb_sys");
if (IS_ERR(pcie->apb_sys_clk))
return PTR_ERR(pcie->apb_sys_clk);
pcie->pcie_aclk = devm_clk_get(&pdev->dev, "pcie_aclk");
if (IS_ERR(pcie->pcie_aclk))
return PTR_ERR(pcie->pcie_aclk);
return 0;
}
static int32_t kirin960_pcie_get_resource(struct pcie_port *pp,
struct platform_device *pdev)
{
struct resource *apb;
struct resource *phy;
struct resource *dbi;
struct kirin_pcie *pcie = to_kirin_pcie(pp);
apb = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apb");
pcie->apb_base = devm_ioremap_resource(&pdev->dev, apb);
if (IS_ERR(pcie->apb_base))
return PTR_ERR(pcie->apb_base);
phy = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy);
if (IS_ERR(pcie->phy_base))
return PTR_ERR(pcie->phy_base);
dbi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi);
if (IS_ERR(pp->dbi_base))
return PTR_ERR(pp->dbi_base);
pcie->crgctrl =
syscon_regmap_lookup_by_compatible("hisilicon,hi3660-crgctrl");
if (IS_ERR(pcie->crgctrl))
return PTR_ERR(pcie->crgctrl);
pcie->sysctrl =
syscon_regmap_lookup_by_compatible("hisilicon,hi3660-sctrl");
if (IS_ERR(pcie->sysctrl))
return PTR_ERR(pcie->sysctrl);
return 0;
}
static int kirin960_pcie_phy_init(struct kirin_pcie *pcie)
{
u32 reg_val;
u32 pipe_clk_stable = 0x1 << 19;
u32 time = 10;
reg_val = kirin_phy_readl(pcie, 0x4);
reg_val &= ~(0x1 << 8);
kirin_phy_writel(pcie, reg_val, 0x4);
reg_val = kirin_phy_readl(pcie, 0x0);
reg_val &= ~(0x1 << 22);
kirin_phy_writel(pcie, reg_val, 0x0);
udelay(10);
reg_val = kirin_phy_readl(pcie, 0x4);
reg_val &= ~(0x1 << 16);
kirin_phy_writel(pcie, reg_val, 0x4);
reg_val = kirin_phy_readl(pcie, 0x400);
while (reg_val & pipe_clk_stable) {
udelay(100);
if (time == 0) {
dev_err(pcie->pp.dev, "PIPE clk is not stable\n");
return -EINVAL;
}
time--;
reg_val = kirin_phy_readl(pcie, 0x400);
}
return 0;
}
static void kirin960_pcie_oe_enable(struct kirin_pcie *pcie)
{
u32 val;
regmap_read(pcie->sysctrl, 0x1a4, &val);
val |= 0xF0F400;
val &= ~(0x3 << 28);
regmap_write(pcie->sysctrl, 0x1a4, val);
}
static int kirin960_pcie_clk_ctrl(struct kirin_pcie *pcie, bool enable)
{
int ret = 0;
if (!enable)
goto close_clk;
ret = clk_set_rate(pcie->phy_ref_clk, REF_CLK_FREQ);
if (ret)
return ret;
ret = clk_prepare_enable(pcie->phy_ref_clk);
if (ret)
return ret;
ret = clk_prepare_enable(pcie->apb_sys_clk);
if (ret)
goto apb_sys_fail;
ret = clk_prepare_enable(pcie->apb_phy_clk);
if (ret)
goto apb_phy_fail;
ret = clk_prepare_enable(pcie->pcie_aclk);
if (ret)
goto aclk_fail;
ret = clk_prepare_enable(pcie->pcie_aux_clk);
if (ret)
goto aux_clk_fail;
return 0;
close_clk:
clk_disable_unprepare(pcie->pcie_aux_clk);
aux_clk_fail:
clk_disable_unprepare(pcie->pcie_aclk);
aclk_fail:
clk_disable_unprepare(pcie->apb_phy_clk);
apb_phy_fail:
clk_disable_unprepare(pcie->apb_sys_clk);
apb_sys_fail:
clk_disable_unprepare(pcie->phy_ref_clk);
return ret;
}
static int kirin960_pcie_power_on(struct kirin_pcie *pcie)
{
int ret;
/* Power supply for Host */
regmap_write(pcie->sysctrl, 0x60, 0x10);
udelay(100);
kirin960_pcie_oe_enable(pcie);
ret = kirin960_pcie_clk_ctrl(pcie, true);
if (ret)
return ret;
/* deasset PCIeCtrl&PCIePHY */
regmap_write(pcie->sysctrl, 0x44, 0x30);
regmap_write(pcie->crgctrl, 0x88, 0x8c000000);
regmap_write(pcie->sysctrl, 0x190, 0x184000);
ret = kirin960_pcie_phy_init(pcie);
if (ret)
goto close_clk;
/* perst assert */
mdelay(20);
if (!gpio_request(pcie->gpio_id_reset[0], "pcie_perst")) {
ret = gpio_direction_output(pcie->gpio_id_reset[0], 1);
if (ret)
goto close_clk;
mdelay(10);
return 0;
}
close_clk:
kirin960_pcie_clk_ctrl(pcie, false);
return -1;
}
static int kirin960_pcie_probe(struct kirin_pcie *pcie)
{
struct platform_device *pdev;
struct pcie_port *pp;
int ret;
pp = &pcie->pp;
pdev = to_platform_device(pp->dev);
ret = kirin960_pcie_get_clk(pcie, pdev);
if (ret != 0)
return -ENODEV;
ret = kirin960_pcie_get_resource(pp, pdev);
if (ret != 0)
return -ENODEV;
pcie->gpio_id_reset[0] = of_get_named_gpio(pdev->dev.of_node,
"reset-gpios", 0);
if (pcie->gpio_id_reset[0] < 0)
return -ENODEV;
ret = kirin960_pcie_power_on(pcie);
if (ret)
return ret;
return 0;
}
const struct kirin_pcie_ops kirin960_pcie_ops = {
.pcie_probe = kirin960_pcie_probe,
.kirin_phy_writel = kirin960_phy_writel,
.kirin_phy_readl = kirin960_phy_readl,
};
EXPORT_SYMBOL_GPL(kirin960_pcie_ops);
+828
View File
@@ -0,0 +1,828 @@
/*
* PCIe host controller driver for Kirin970 Phone SoCs
*
* Copyright (C) 2017 Hilisicon Electronics Co., Ltd.
* http://www.huawei.com
*
* Author: Yao Chen <chenyao11@huawei.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "pcie-kirin.h"
#define AXI_CLK_FREQ 207500000
#define EYEPARAM_NOCFG 0xFFFFFFFF
#define RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 0x3001
#define SUP_DIG_LVL_OVRD_IN 0xf
#define LANEN_DIG_ASIC_TX_OVRD_IN_1 0x1002
#define LANEN_DIG_ASIC_TX_OVRD_IN_2 0x1003
/* kirin970 pciephy register */
#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 ie,oe cfg */
#define IO_IE_EN_HARD_BYPASS (0x1 << 27)
#define IO_OE_EN_HARD_BYPASS (0x1 << 11)
#define IO_HARD_CTRL_DEBOUNCE_BYPASS (0x1 << 10)
#define IO_OE_GT_MODE (0x2 << 7)
#define DEBOUNCE_WAITCFG_IN (0xf << 20)
#define DEBOUNCE_WAITCFG_OUT (0xf << 13)
/* 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
static inline void kirin970_phy_writel(struct kirin_pcie *pcie, u32 val, u32 reg)
{
writel(val, pcie->phy_base + 0x40000 + reg);
}
static inline u32 kirin970_phy_readl(struct kirin_pcie *pcie, u32 reg)
{
return readl(pcie->phy_base + 0x40000 + reg);
}
static int32_t kirin970_pcie_pinctrl_init(struct kirin_pcie *pcie,
struct platform_device *pdev)
{
int ret;
struct pinctrl *p;
struct pinctrl_state *pinctrl_def;
p = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(p))
return PTR_ERR(p);
pinctrl_def = pinctrl_lookup_state(p, PINCTRL_STATE_DEFAULT);
if (IS_ERR(pinctrl_def)) {
dev_err(&pdev->dev, "Failed to get defult pinctrl state\n");
return PTR_ERR(pinctrl_def);
}
ret = pinctrl_select_state(p, pinctrl_def);
if (ret) {
dev_err(&pdev->dev, "Failed to select defult pinctrl state\n");
return ret;
}
return 0;
}
static int32_t kirin970_pcie_get_clk(struct kirin_pcie *pcie,
struct platform_device *pdev)
{
pcie->pcie_aux_clk = devm_clk_get(&pdev->dev, "pcie_aux");
if (IS_ERR(pcie->pcie_aux_clk))
return PTR_ERR(pcie->pcie_aux_clk);
pcie->apb_phy_clk = devm_clk_get(&pdev->dev, "pcie_apb_phy");
if (IS_ERR(pcie->apb_phy_clk))
return PTR_ERR(pcie->apb_phy_clk);
pcie->apb_sys_clk = devm_clk_get(&pdev->dev, "pcie_apb_sys");
if (IS_ERR(pcie->apb_sys_clk))
return PTR_ERR(pcie->apb_sys_clk);
pcie->pcie_aclk = devm_clk_get(&pdev->dev, "pcie_aclk");
if (IS_ERR(pcie->pcie_aclk))
return PTR_ERR(pcie->pcie_aclk);
return 0;
}
static int32_t kirin970_pcie_get_resource(struct pcie_port *pp,
struct platform_device *pdev)
{
struct resource *apb;
struct resource *phy;
struct resource *dbi;
struct kirin_pcie *pcie = to_kirin_pcie(pp);
apb = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apb");
pcie->apb_base = devm_ioremap_resource(&pdev->dev, apb);
if (IS_ERR(pcie->apb_base))
return PTR_ERR(pcie->apb_base);
phy = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy);
if (IS_ERR(pcie->phy_base))
return PTR_ERR(pcie->phy_base);
dbi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi);
if (IS_ERR(pp->dbi_base))
return PTR_ERR(pp->dbi_base);
pcie->crgctrl =
syscon_regmap_lookup_by_compatible("hisilicon,kirin970-crgctrl");
if (IS_ERR(pcie->crgctrl))
return PTR_ERR(pcie->crgctrl);
pcie->sysctrl =
syscon_regmap_lookup_by_compatible("hisilicon,kirin970-sctrl");
if (IS_ERR(pcie->sysctrl))
return PTR_ERR(pcie->sysctrl);
pcie->pmctrl =
syscon_regmap_lookup_by_compatible("hisilicon,kirin970-pmctrl");
if (IS_ERR(pcie->pmctrl))
return PTR_ERR(pcie->pmctrl);
return 0;
}
void kirin_pcie_get_eyeparam(struct kirin_pcie *pcie, struct platform_device *pdev)
{
int i;
struct device_node *np;
np = pdev->dev.of_node;
if (of_property_read_u32_array(np, "eye_param", pcie->eye_param, 5)) {
for (i = 0; i < 5; i++)
pcie->eye_param[i] = EYEPARAM_NOCFG;
}
dev_err(&pdev->dev, "eye_param_vboost = [0x%x]\n", pcie->eye_param[0]);
dev_err(&pdev->dev, "eye_param_iboost = [0x%x]\n", pcie->eye_param[1]);
dev_err(&pdev->dev, "eye_param_pre = [0x%x]\n", pcie->eye_param[2]);
dev_err(&pdev->dev, "eye_param_post = [0x%x]\n", pcie->eye_param[3]);
dev_err(&pdev->dev, "eye_param_main = [0x%x]\n", pcie->eye_param[4]);
}
static void set_phy_eye_param(struct kirin_pcie *pcie)
{
u32 val;
val = kirin_natural_phy_readl(pcie, RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
if (pcie->eye_param[1] != EYEPARAM_NOCFG) {
val &= (~0xf00);
val |= (pcie->eye_param[1] << 8) | (0x1 << 12);
}
kirin_natural_phy_writel(pcie, val, RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1);
val = kirin_natural_phy_readl(pcie, LANEN_DIG_ASIC_TX_OVRD_IN_2);
val &= (~0x1FBF);
if (pcie->eye_param[2] != EYEPARAM_NOCFG)
val |= (pcie->eye_param[2]<< 0) | (0x1 << 6);
if (pcie->eye_param[3] != EYEPARAM_NOCFG)
val |= (pcie->eye_param[3] << 7) | (0x1 << 13);
kirin_natural_phy_writel(pcie, val, LANEN_DIG_ASIC_TX_OVRD_IN_2);
val = kirin_natural_phy_readl(pcie, SUP_DIG_LVL_OVRD_IN);
if (pcie->eye_param[0] != EYEPARAM_NOCFG) {
val &= (~0x1C0);
val |= (pcie->eye_param[0] << 6) | (0x1 << 9);
}
kirin_natural_phy_writel(pcie, val, SUP_DIG_LVL_OVRD_IN);
val = kirin_natural_phy_readl(pcie, LANEN_DIG_ASIC_TX_OVRD_IN_1);
if (pcie->eye_param[4] != EYEPARAM_NOCFG) {
val &= (~0x7E00);
val |= (pcie->eye_param[4] << 9) | (0x1 << 15);
}
kirin_natural_phy_writel(pcie, val, LANEN_DIG_ASIC_TX_OVRD_IN_1);
}
static int kirin970_pcie_pclk_ctrl(struct kirin_pcie *pcie, bool enable)
{
int ret = 0;
if (!enable)
goto close_clk;
ret = clk_prepare_enable(pcie->apb_phy_clk);
if (ret)
return ret;
ret = clk_prepare_enable(pcie->apb_sys_clk);
if (ret)
goto apb_sys_fail;
return 0;
close_clk:
clk_disable_unprepare(pcie->apb_sys_clk);
apb_sys_fail:
clk_disable_unprepare(pcie->phy_ref_clk);
return ret;
}
static int kirin970_pcie_clk_ctrl(struct clk *clk, int clk_on)
{
int ret = 0;
if (clk_on) {
ret = clk_prepare_enable(clk);
if (ret)
return ret;
} else {
clk_disable_unprepare(clk);
}
return ret;
}
static void kirin970_pcie_natural_cfg(struct kirin_pcie *pcie)
{
u32 val;
/* change 2p mem_ctrl */
kirin_elb_writel(pcie, 0x02605550, SOC_PCIECTRL_CTRL20_ADDR);
/* pull up sys_aux_pwr_det */
val = kirin_elb_readl(pcie, SOC_PCIECTRL_CTRL7_ADDR);
val |= (0x1 << 10);
kirin_elb_writel(pcie, val, SOC_PCIECTRL_CTRL7_ADDR);
/* output, pull down */
val = kirin_elb_readl(pcie, SOC_PCIECTRL_CTRL12_ADDR);
val &= ~(0x3 << 2);
val |= (0x1 << 1);
val &= ~(0x1 << 0);
kirin_elb_writel(pcie, val, SOC_PCIECTRL_CTRL12_ADDR);
/* Handle phy_reset and lane0_reset to HW */
val = kirin_phy_readl(pcie, SOC_PCIEPHY_CTRL1_ADDR);
val |= PCIEPHY_RESET_BIT;
val &= ~PCIEPHY_PIPE_LINE0_RESET_BIT;
kirin_phy_writel(pcie, val, SOC_PCIEPHY_CTRL1_ADDR);
/* fix chip bug: TxDetectRx fail */
val = kirin_phy_readl(pcie, SOC_PCIEPHY_CTRL38_ADDR);
val |= (0x1 << 2);
kirin_phy_writel(pcie, val, SOC_PCIEPHY_CTRL38_ADDR);
}
static void kirin970_pcie_pll_init(struct kirin_pcie *pcie)
{
u32 val;
/* choose FNPLL */
val = kirin_phy_readl(pcie, SOC_PCIEPHY_MMC1PLL_CTRL1);
val |= (0x1 << 27);
kirin_phy_writel(pcie, val, SOC_PCIEPHY_MMC1PLL_CTRL1);
val = kirin_phy_readl(pcie, SOC_PCIEPHY_MMC1PLL_CTRL16);
val &= 0xF000FFFF;
/* fnpll fbdiv = 0xD0 */
val |= (0xd0 << 16);
kirin_phy_writel(pcie, val, SOC_PCIEPHY_MMC1PLL_CTRL16);
val = kirin_phy_readl(pcie, SOC_PCIEPHY_MMC1PLL_CTRL17);
val &= 0xFF000000;
/* fnpll fracdiv = 0x555555 */
val |= (0x555555 << 0);
kirin_phy_writel(pcie, val, SOC_PCIEPHY_MMC1PLL_CTRL17);
val = kirin_phy_readl(pcie, SOC_PCIEPHY_MMC1PLL_CTRL20);
val &= 0xF5FF88FF;
/* fnpll dll_en = 0x1 */
val |= (0x1 << 27);
/* fnpll postdiv1 = 0x5 */
val |= (0x5 << 8);
/* fnpll postdiv2 = 0x4 */
val |= (0x4 << 12);
/* fnpll pll_mode = 0x0 */
val &= ~(0x1 << 25);
kirin_phy_writel(pcie, val, SOC_PCIEPHY_MMC1PLL_CTRL20);
kirin_phy_writel(pcie, 0x20, SOC_PCIEPHY_MMC1PLL_CTRL21);
}
static int kirin970_pcie_pll_ctrl(struct kirin_pcie *pcie, bool enable)
{
u32 val;
int time = 200;
if (enable) {
/* pd = 0 */
val = kirin_phy_readl(pcie, SOC_PCIEPHY_MMC1PLL_CTRL16);
val &= ~(0x1 << 0);
kirin_phy_writel(pcie, val, SOC_PCIEPHY_MMC1PLL_CTRL16);
val = kirin_phy_readl(pcie, SOC_PCIEPHY_MMC1PLL_STAT0);
/* choose FNPLL */
while (!(val & 0x10)) {
if (!time) {
dev_err(pcie->pp.dev, "wait for pll_lock timeout\n");
return -1;
}
time --;
udelay(1);
val = kirin_phy_readl(pcie, SOC_PCIEPHY_MMC1PLL_STAT0);
}
/* pciepll_bp = 0 */
val = kirin_phy_readl(pcie, SOC_PCIEPHY_MMC1PLL_CTRL20);
val &= ~(0x1 << 16);
kirin_phy_writel(pcie, val, SOC_PCIEPHY_MMC1PLL_CTRL20);
} else {
/* pd = 1 */
val = kirin_phy_readl(pcie, SOC_PCIEPHY_MMC1PLL_CTRL16);
val |= (0x1 << 0);
kirin_phy_writel(pcie, val, SOC_PCIEPHY_MMC1PLL_CTRL16);
/* pciepll_bp = 1 */
val = kirin_phy_readl(pcie, SOC_PCIEPHY_MMC1PLL_CTRL20);
val |= (0x1 << 16);
kirin_phy_writel(pcie, val, SOC_PCIEPHY_MMC1PLL_CTRL20);
}
return 0;
}
static void kirin970_pcie_hp_debounce_gt(struct kirin_pcie *pcie, bool open)
{
if (open)
/* gt_clk_pcie_hp/gt_clk_pcie_debounce open */
regmap_write(pcie->crgctrl, CRGPERIPH_PEREN12, 0x9000);
else
/* gt_clk_pcie_hp/gt_clk_pcie_debounce close */
regmap_write(pcie->crgctrl, CRGPERIPH_PERDIS12, 0x9000);
}
static void kirin970_pcie_phyref_gt(struct kirin_pcie *pcie, bool open)
{
unsigned int val;
regmap_read(pcie->crgctrl, CRGPERIPH_PCIECTRL0, &val);
if (open)
val &= ~(0x1 << 1); //enable hard gt mode
else
val |= (0x1 << 1); //disable hard gt mode
regmap_write(pcie->crgctrl, CRGPERIPH_PCIECTRL0, val);
/* disable soft gt mode */
regmap_write(pcie->crgctrl, CRGPERIPH_PERDIS12, 0x4000);
}
static void kirin970_pcie_oe_ctrl(struct kirin_pcie *pcie, bool en_flag)
{
unsigned int val;
regmap_read(pcie->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(pcie->crgctrl, CRGPERIPH_PCIECTRL0, val);
}
static void kirin970_pcie_ioref_gt(struct kirin_pcie *pcie, bool open)
{
unsigned int val;
if (open) {
kirin_elb_writel(pcie, 0x20000070, SOC_PCIECTRL_CTRL21_ADDR);
kirin970_pcie_oe_ctrl(pcie, true);
/* en hard gt mode */
regmap_read(pcie->crgctrl, CRGPERIPH_PCIECTRL0, &val);
val &= ~(0x1 << 0);
regmap_write(pcie->crgctrl, CRGPERIPH_PCIECTRL0, val);
/* disable soft gt mode */
regmap_write(pcie->crgctrl, CRGPERIPH_PERDIS12, 0x2000);
} else {
/* disable hard gt mode */
regmap_read(pcie->crgctrl, CRGPERIPH_PCIECTRL0, &val);
val |= (0x1 << 0);
regmap_write(pcie->crgctrl, CRGPERIPH_PCIECTRL0, val);
/* disable soft gt mode */
regmap_write(pcie->crgctrl, CRGPERIPH_PERDIS12, 0x2000);
kirin970_pcie_oe_ctrl(pcie, false);
}
}
static int kirin970_pcie_allclk_ctrl(struct kirin_pcie *pcie, bool clk_on)
{
u32 val;
int ret = 0;
if (!clk_on)
goto ALL_CLOSE;
/* choose 100MHz clk src: Bit[8]==1 pad, Bit[8]==0 pll */
val = kirin_phy_readl(pcie, SOC_PCIEPHY_CTRL1_ADDR);
val &= ~(0x1 << 8);
kirin_phy_writel(pcie, val, SOC_PCIEPHY_CTRL1_ADDR);
kirin970_pcie_pll_init(pcie);
ret = kirin970_pcie_pll_ctrl(pcie, true);
if (ret) {
dev_err(pcie->pp.dev, "Failed to enable pll\n");
return -1;
}
kirin970_pcie_hp_debounce_gt(pcie, true);
kirin970_pcie_phyref_gt(pcie, true);
kirin970_pcie_ioref_gt(pcie, true);
ret = clk_set_rate(pcie->pcie_aclk, AXI_CLK_FREQ);
if (ret) {
dev_err(pcie->pp.dev, "Failed to set rate\n");
goto GT_CLOSE;
}
ret = kirin970_pcie_clk_ctrl(pcie->pcie_aclk, true);
if (ret) {
dev_err(pcie->pp.dev, "Failed to enable pcie_aclk\n");
goto GT_CLOSE;
}
ret = kirin970_pcie_clk_ctrl(pcie->pcie_aux_clk, true);
if (ret) {
dev_err(pcie->pp.dev, "Failed to enable pcie_aux_clk\n");
goto AUX_CLK_FAIL;
}
return 0;
ALL_CLOSE:
kirin970_pcie_clk_ctrl(pcie->pcie_aux_clk, false);
AUX_CLK_FAIL:
kirin970_pcie_clk_ctrl(pcie->pcie_aclk, false);
GT_CLOSE:
kirin970_pcie_ioref_gt(pcie, false);
kirin970_pcie_phyref_gt(pcie, false);
kirin970_pcie_hp_debounce_gt(pcie, false);
kirin970_pcie_pll_ctrl(pcie, false);
return ret;
}
static bool is_pipe_clk_stable(struct kirin_pcie *pcie)
{
u32 val;
u32 time = 100;
u32 pipe_clk_stable = 0x1 << 19;
val = kirin_phy_readl(pcie, SOC_PCIEPHY_STATE0_ADDR);
while (val & pipe_clk_stable) {
mdelay(1);
if (time == 0) {
dev_err(pcie->pp.dev, "PIPE clk is not stable\n");
return false;
}
time--;
val = kirin_phy_readl(pcie, SOC_PCIEPHY_STATE0_ADDR);
}
return true;
}
static int kirin970_pcie_noc_power(struct kirin_pcie *pcie, bool enable)
{
u32 time = 100;
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(pcie->pmctrl, NOC_POWER_IDLEREQ_1, val);
time = 100;
regmap_read(pcie->pmctrl, NOC_POWER_IDLE_1, &val);
while((val & NOC_PW_SET_BIT) != rst) {
udelay(10);
if (!time) {
dev_err(pcie->pp.dev, "Failed to reverse noc power-status\n");
return -1;
}
time--;
regmap_read(pcie->pmctrl, NOC_POWER_IDLE_1, &val);
}
return 0;
}
static int kirin970_pcie_perst_cfg(struct kirin_pcie *pcie, int pull_up)
{
int ret;
if (pull_up)
usleep_range(21000, 23000);
ret = gpio_direction_output(pcie->gpio_id_reset[0], pull_up);
if (ret)
dev_err(pcie->pp.dev, "Failed to pulse switch perst signal\n");
ret = gpio_direction_output(pcie->gpio_id_reset[1], pull_up);
if (ret)
dev_err(pcie->pp.dev, "Failed to pulse eth perst signal\n");
ret = gpio_direction_output(pcie->gpio_id_reset[2], pull_up);
if (ret)
dev_err(pcie->pp.dev, "Failed to pulse m.2 perst signal\n");
ret = gpio_direction_output(pcie->gpio_id_reset[3], pull_up);
if (ret)
dev_err(pcie->pp.dev, "Failed to pulse mini1 perst signal\n");
usleep_range(10000, 11000);
return ret;
}
static int kirin970_pcie_clkreq_cfg(struct kirin_pcie *pcie, int pull_up)
{
int ret;
ret = gpio_direction_output(pcie->gpio_id_clkreq[0], pull_up);
if (ret)
dev_err(pcie->pp.dev, "Failed to pulse eth clkreq signal\n");
ret = gpio_direction_output(pcie->gpio_id_clkreq[1], pull_up);
if (ret)
dev_err(pcie->pp.dev, "Failed to pulse m.2 clkreq signal\n");
ret = gpio_direction_output(pcie->gpio_id_clkreq[2], pull_up);
if (ret)
dev_err(pcie->pp.dev, "Failed to pulse mini1 clkreq signal\n");
return ret;
}
int kirin970_pcie_turn_on(struct kirin_pcie *pcie)
{
int ret;
u32 val;
ret = kirin970_pcie_clkreq_cfg(pcie, false);
if (ret)
return ret;
/* pull downphy ISO */
regmap_write(pcie->sysctrl, 0x44, 0x20);
/* enable PCIe sys&phy pclk */
ret = kirin970_pcie_pclk_ctrl(pcie, true);
if (ret)
return ret;
/* deasset PCIeCtrl&PCIePHY */
regmap_write(pcie->crgctrl, 0x88, 0x8c000000);
kirin970_pcie_natural_cfg(pcie);
if (kirin970_pcie_allclk_ctrl(pcie, true))
goto PCLK_CLOSE;
/* pull down phy_test_powerdown signal */
val = kirin_phy_readl(pcie, SOC_PCIEPHY_CTRL0_ADDR);
val &= ~(0x1 << 22);
kirin_phy_writel(pcie, val, SOC_PCIEPHY_CTRL0_ADDR);
/* deassert controller perst_n */
val = kirin_elb_readl(pcie, SOC_PCIECTRL_CTRL12_ADDR);
val |= (0x1 << 2);
kirin_elb_writel(pcie, val, SOC_PCIECTRL_CTRL12_ADDR);
udelay(10);
kirin970_pcie_perst_cfg(pcie, true);
if (!is_pipe_clk_stable(pcie))
goto ALLCLK_CLOSE;
set_phy_eye_param(pcie);
if (kirin970_pcie_noc_power(pcie, false))
goto ALLCLK_CLOSE;
return 0;
ALLCLK_CLOSE:
kirin970_pcie_allclk_ctrl(pcie, false);
PCLK_CLOSE:
kirin970_pcie_pclk_ctrl(pcie, false);
return -1;
}
int kirin970_pcie_turn_off(struct kirin_pcie *pcie)
{
u32 val;
int ret;
ret = kirin970_pcie_noc_power(pcie, true);
if (ret)
return ret;
kirin970_pcie_perst_cfg(pcie, false);
val = kirin_elb_readl(pcie, SOC_PCIECTRL_CTRL12_ADDR);
val &= ~(0x1 << 2);
kirin_elb_writel(pcie, val, SOC_PCIECTRL_CTRL12_ADDR);
kirin970_pcie_allclk_ctrl(pcie, false);
regmap_write(pcie->crgctrl, 0x84, 0x8c000000);
kirin970_pcie_pclk_ctrl(pcie, false);
regmap_write(pcie->sysctrl, 0x40, 0x20);
ret = kirin970_pcie_clkreq_cfg(pcie, true);
if (ret)
return ret;
return 0;
}
int kirin970_pcie_power_on(struct kirin_pcie *pcie, bool on)
{
if (on)
return kirin970_pcie_turn_on(pcie);
else
return kirin970_pcie_turn_off(pcie);
}
static int kirin970_pcie_probe(struct kirin_pcie *pcie)
{
struct platform_device *pdev;
struct pcie_port *pp;
int ret;
pp = &pcie->pp;
pdev = to_platform_device(pp->dev);
ret = kirin970_pcie_get_clk(pcie, pdev);
if (ret != 0)
return -ENODEV;
ret = kirin970_pcie_get_resource(pp, pdev);
if (ret != 0)
return -ENODEV;
kirin_pcie_get_eyeparam(pcie, pdev);
pcie->gpio_id_reset[0] = of_get_named_gpio(pdev->dev.of_node,
"switch,reset-gpios", 0);
pcie->gpio_id_reset[1] = of_get_named_gpio(pdev->dev.of_node,
"eth,reset-gpios", 0);
pcie->gpio_id_reset[2] = of_get_named_gpio(pdev->dev.of_node,
"m_2,reset-gpios", 0);
pcie->gpio_id_reset[3] = of_get_named_gpio(pdev->dev.of_node,
"mini1,reset-gpios", 0);
if (pcie->gpio_id_reset[0] < 0)
return -ENODEV;
if (pcie->gpio_id_reset[1] < 0)
return -ENODEV;
if (pcie->gpio_id_reset[2] < 0)
return -ENODEV;
if (pcie->gpio_id_reset[3] < 0)
return -ENODEV;
if (gpio_request((unsigned int)pcie->gpio_id_reset[0], "pcie_switch_reset"))
return -EINVAL;
if (gpio_request((unsigned int)pcie->gpio_id_reset[1], "pcie_eth_reset"))
return -EINVAL;
if (gpio_request((unsigned int)pcie->gpio_id_reset[2], "pcie_m_2_reset"))
return -EINVAL;
if (gpio_request((unsigned int)pcie->gpio_id_reset[3], "pcie_mini1_reset"))
return -EINVAL;
pcie->ldo33 = devm_regulator_get(pp->dev, "ldo33");
if(IS_ERR_OR_NULL(pcie->ldo33))
return PTR_ERR(pcie->ldo33);
ret = regulator_enable(pcie->ldo33);
if (ret) {
dev_err(pp->dev, "Failed to enable ldo33\n");
return ret;
}
ret = kirin970_pcie_pinctrl_init(pcie, pdev);
if (ret != 0)
return -ENODEV;
pcie->gpio_id_clkreq[0] = of_get_named_gpio(pdev->dev.of_node,
"eth,clkreq-gpios", 0);
pcie->gpio_id_clkreq[1] = of_get_named_gpio(pdev->dev.of_node,
"m_2,clkreq-gpios", 0);
pcie->gpio_id_clkreq[2] = of_get_named_gpio(pdev->dev.of_node,
"mini1,clkreq-gpios", 0);
if ((pcie->gpio_id_clkreq[0] < 0) || (pcie->gpio_id_clkreq[1] < 0)
|| (pcie->gpio_id_clkreq[2] < 0))
return -ENODEV;
if (gpio_request((unsigned int)pcie->gpio_id_clkreq[0], "pcie_eth_clkreq"))
return -EINVAL;
if (gpio_request((unsigned int)pcie->gpio_id_clkreq[1], "pcie_m_2_clkreq"))
return -EINVAL;
if (gpio_request((unsigned int)pcie->gpio_id_clkreq[2], "pcie_mini1_clkreq"))
return -EINVAL;
ret = kirin970_pcie_power_on(pcie, true);
if (ret)
return ret;
return 0;
}
static int kirin970_pcie_resume_noirq(struct device *dev)
{
struct pci_dev *rc_dev;
struct pcie_port *pp;
struct kirin_pcie *pcie;
pcie = dev_get_drvdata(dev);
if (!pcie) {
dev_err(dev, "Failed to get drvdata\n");
return -EINVAL;
}
pp = &pcie->pp;
rc_dev = pcie->rc_dev;
if (kirin970_pcie_power_on(pcie, true)) {
dev_err(dev, "Failed to power on\n");
return -EINVAL;
}
if (rc_dev)
kirin_pcie_restore_rc_cfg(pcie);
return 0;
}
static int kirin970_pcie_suspend_noirq(struct device *dev)
{
struct kirin_pcie *pcie;
struct pci_dev *rc_dev;
struct pcie_port *pp;
pcie = dev_get_drvdata(dev);
if (!pcie) {
dev_err(dev, "Failed to get drvdata\n");
return -EINVAL;
}
rc_dev = pcie->rc_dev;
pp = &pcie->pp;
if (kirin970_pcie_power_on(pcie, false)) {
dev_err(dev, "Failed to power off\n");
return -EINVAL;
}
return 0;
}
const struct kirin_pcie_ops kirin970_pcie_ops = {
.pcie_probe = kirin970_pcie_probe,
.kirin_phy_writel = kirin970_phy_writel,
.kirin_phy_readl = kirin970_phy_readl,
.pcie_suspend_noirq = kirin970_pcie_suspend_noirq,
.pcie_resume_noirq = kirin970_pcie_resume_noirq,
};
EXPORT_SYMBOL_GPL(kirin970_pcie_ops);