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:
Regular → Executable
+72
-26
@@ -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 >;
|
||||
};
|
||||
|
||||
Regular → Executable
+5
-3
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
Executable
+249
@@ -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);
|
||||
Executable
+828
@@ -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);
|
||||
Reference in New Issue
Block a user