From 652edc0233649231ae244942c249af1dd878f0a4 Mon Sep 17 00:00:00 2001 From: Zan Peng Date: Fri, 2 Feb 2018 12:42:02 +0800 Subject: [PATCH] pcie: add pcie driver support for G12A PD#156734: pcie: add pcie driver support for G12A Change-Id: I497e17ac212448934416c8af73206cd88766e406 Signed-off-by: Zan Peng --- MAINTAINERS | 1 + drivers/amlogic/pci/Makefile | 1 + drivers/amlogic/pci/pcie-amlogic-v2.c | 1121 +++++++++++++++++++++++++++++++++ 3 files changed, 1123 insertions(+) create mode 100644 drivers/amlogic/pci/pcie-amlogic-v2.c diff --git a/MAINTAINERS b/MAINTAINERS index 0e6bdc3..a6a469d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13942,6 +13942,7 @@ F: arch/arm64/boot/dts/amlogic/axg_s420.dts AMLOGIC PCIE DRIVER SUPPORT M: Yue Wang F: drivers/amlogic/pci/pcie-amlogic.c +F: drivers/amlogic/pci/pcie-amlogic-v2.c F: drivers/amlogic/pci/pcie-amlogic.h F: drivers/amlogic/pci/Kconfig F: drivers/amlogic/pci/Makefile diff --git a/drivers/amlogic/pci/Makefile b/drivers/amlogic/pci/Makefile index 22f19e6..dcaa0b6 100644 --- a/drivers/amlogic/pci/Makefile +++ b/drivers/amlogic/pci/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_AMLOGIC_PCIE) += pcie-amlogic.o +obj-$(CONFIG_AMLOGIC_PCIE) += pcie-amlogic-v2.o diff --git a/drivers/amlogic/pci/pcie-amlogic-v2.c b/drivers/amlogic/pci/pcie-amlogic-v2.c new file mode 100644 index 0000000..4ba2bb6 --- /dev/null +++ b/drivers/amlogic/pci/pcie-amlogic-v2.c @@ -0,0 +1,1121 @@ +/* + * drivers/amlogic/pci/pcie-amlogic-v2.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../drivers/pci/host/pcie-designware.h" +#include "pcie-amlogic.h" + +#include +#include +#include "../clk/clkc.h" + + +struct amlogic_pcie { + struct pcie_port pp; + struct pcie_phy *phy; + void __iomem *elbi_base; /* DT 0th resource */ + void __iomem *cfg_base; /* DT 2nd resource */ + int reset_gpio; + struct clk *clk; + struct clk *phy_clk; + struct clk *bus_clk; + int pcie_num; + int gpio_type; + u32 port_num; + u32 pm_enable; + u32 device_attch; + u32 rst_mod; +}; + +#define to_amlogic_pcie(x) container_of(x, struct amlogic_pcie, pp) +struct pcie_phy_aml_regs pcie_aml_regs_v2; +struct pcie_phy *g_pcie_phy_v2; + +static void amlogic_elb_writel(struct amlogic_pcie *amlogic_pcie, u32 val, + u32 reg) +{ + writel(val, amlogic_pcie->elbi_base + reg); +} + +static u32 amlogic_elb_readl(struct amlogic_pcie *amlogic_pcie, u32 reg) +{ + return readl(amlogic_pcie->elbi_base + reg); +} + +static void amlogic_cfg_writel(struct amlogic_pcie *amlogic_pcie, u32 val, + u32 reg) +{ + writel(val, amlogic_pcie->cfg_base + reg); +} + +static u32 amlogic_cfg_readl(struct amlogic_pcie *amlogic_pcie, u32 reg) +{ + return readl(amlogic_pcie->cfg_base + reg); +} + +static void cr_bus_addr(unsigned int addr) +{ + union phy_r4 phy_r4 = {.d32 = 0}; + union phy_r5 phy_r5 = {.d32 = 0}; + unsigned long timeout_jiffies; + + phy_r4.b.phy_cr_data_in = addr; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + + phy_r4.b.phy_cr_cap_addr = 0; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + phy_r4.b.phy_cr_cap_addr = 1; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]); + } while (phy_r5.b.phy_cr_ack == 0 && + time_is_after_jiffies(timeout_jiffies)); + + phy_r4.b.phy_cr_cap_addr = 0; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]); + } while (phy_r5.b.phy_cr_ack == 1 && + time_is_after_jiffies(timeout_jiffies)); +} + +static int cr_bus_read(unsigned int addr) +{ + int data; + union phy_r4 phy_r4 = {.d32 = 0}; + union phy_r5 phy_r5 = {.d32 = 0}; + unsigned long timeout_jiffies; + + cr_bus_addr(addr); + + phy_r4.b.phy_cr_read = 0; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + phy_r4.b.phy_cr_read = 1; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]); + } while (phy_r5.b.phy_cr_ack == 0 && + time_is_after_jiffies(timeout_jiffies)); + + data = phy_r5.b.phy_cr_data_out; + + phy_r4.b.phy_cr_read = 0; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]); + } while (phy_r5.b.phy_cr_ack == 1 && + time_is_after_jiffies(timeout_jiffies)); + + return data; +} + +static void cr_bus_write(unsigned int addr, unsigned int data) +{ + union phy_r4 phy_r4 = {.d32 = 0}; + union phy_r5 phy_r5 = {.d32 = 0}; + unsigned long timeout_jiffies; + + cr_bus_addr(addr); + + phy_r4.b.phy_cr_data_in = data; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + + phy_r4.b.phy_cr_cap_data = 0; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + phy_r4.b.phy_cr_cap_data = 1; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]); + } while (phy_r5.b.phy_cr_ack == 0 && + time_is_after_jiffies(timeout_jiffies)); + + phy_r4.b.phy_cr_cap_data = 0; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]); + } while (phy_r5.b.phy_cr_ack == 1 && + time_is_after_jiffies(timeout_jiffies)); + + phy_r4.b.phy_cr_write = 0; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + phy_r4.b.phy_cr_write = 1; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]); + } while (phy_r5.b.phy_cr_ack == 0 && + time_is_after_jiffies(timeout_jiffies)); + + phy_r4.b.phy_cr_write = 0; + writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]); + } while (phy_r5.b.phy_cr_ack == 1 && + time_is_after_jiffies(timeout_jiffies)); +} + +static void amlogic_phy_cr_writel(u32 val, u32 reg) +{ + cr_bus_write(reg, val); +} + +static u32 amlogic_phy_cr_readl(u32 reg) +{ + return cr_bus_read(reg); +} + +static ssize_t show_pcie_cr_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 status = 0; + + return sprintf(buf, "%d\n", status); +} + + +static ssize_t store_pcie_cr_read(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 reg; + u32 val; + + if (kstrtouint(buf, 0, ®) != 0) + return -EINVAL; + + val = amlogic_phy_cr_readl(reg); + dev_info(dev, "reg 0x%x value is 0x%x\n", reg, val); + + return count; +} +DEVICE_ATTR(phyread_v2, 0644, show_pcie_cr_read, store_pcie_cr_read); + +static ssize_t show_pcie_cr_write(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 status = 0; + + return sprintf(buf, "%d\n", status); +} + +static ssize_t store_pcie_cr_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int reg, val; + int ret = 0; + char *pstr; + char *pend; + char *strbuf; + + if (count > 40) + return -EINVAL; + strbuf = kzalloc(count + 2, GFP_KERNEL); + if (!strbuf) + return -ENOMEM; + + strncpy(strbuf, buf, count); + pend = strbuf + count; + pstr = strchr(strbuf, ' '); + if (!pstr) { + ret = -EINVAL; + goto cr_end; + } + *pstr = 0; + ret = kstrtouint(strbuf, 0, ®); + pstr++; + if (pstr >= pend) { + ret = -EINVAL; + goto cr_end; + } + ret = kstrtouint(pstr, 0, &val); + if (ret) { + ret = -EINVAL; + goto cr_end; + } + amlogic_phy_cr_writel(val, reg); + ret = count; +cr_end: + kfree(strbuf); + return ret; +} +DEVICE_ATTR(phywrite_v2, 0644, show_pcie_cr_write, store_pcie_cr_write); + +static void amlogic_pcie_assert_reset(struct amlogic_pcie *amlogic_pcie) +{ + struct pcie_port *pp = &amlogic_pcie->pp; + struct device *dev = pp->dev; + + if (amlogic_pcie->gpio_type == 0) { + dev_info(amlogic_pcie->pp.dev, + "gpio multiplex, don't reset!\n"); + } else if (amlogic_pcie->gpio_type == 1) { + dev_info(amlogic_pcie->pp.dev, "pad gpio\n"); + if (amlogic_pcie->reset_gpio >= 0) + devm_gpio_request(dev, + amlogic_pcie->reset_gpio, "RESET"); + + if (gpio_is_valid(amlogic_pcie->reset_gpio)) { + dev_info(amlogic_pcie->pp.dev, + "GPIO pad: amlogic_pcie_assert_reset\n"); + gpio_direction_output( + amlogic_pcie->reset_gpio, 0); + usleep_range(5000, 6000); + gpio_direction_input( + amlogic_pcie->reset_gpio); + } + } else { + dev_info(amlogic_pcie->pp.dev, "normal gpio\n"); + if (amlogic_pcie->reset_gpio >= 0) + devm_gpio_request(dev, + amlogic_pcie->reset_gpio, "RESET"); + + if (gpio_is_valid(amlogic_pcie->reset_gpio)) { + dev_info(amlogic_pcie->pp.dev, + "GPIO normal: amlogic_pcie_assert_reset\n"); + gpio_set_value_cansleep( + amlogic_pcie->reset_gpio, 0); + usleep_range(5000, 6000); + gpio_set_value_cansleep( + amlogic_pcie->reset_gpio, 1); + } + } + +} + +static void amlogic_set_max_payload(struct amlogic_pcie *amlogic_pcie, int size) +{ + int max_payload_size = 1; + u32 val = 0; + + switch (size) { + case 128: + max_payload_size = 0; + break; + case 256: + max_payload_size = 1; + break; + case 512: + max_payload_size = 2; + break; + case 1024: + max_payload_size = 3; + break; + case 2048: + max_payload_size = 4; + break; + case 4096: + max_payload_size = 5; + break; + default: + max_payload_size = 1; + break; + } + + val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS); + val &= (~(0x7<<5)); + amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS); + val |= (max_payload_size<<5); + amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS); +} + +static void amlogic_set_max_rd_req_size + (struct amlogic_pcie *amlogic_pcie, int size) +{ + int max_rd_req_size = 1; + u32 val = 0; + + switch (size) { + case 128: + max_rd_req_size = 0; + break; + case 256: + max_rd_req_size = 1; + break; + case 512: + max_rd_req_size = 2; + break; + case 1024: + max_rd_req_size = 3; + break; + case 2048: + max_rd_req_size = 4; + break; + case 4096: + max_rd_req_size = 5; + break; + default: + max_rd_req_size = 1; + break; + } + + val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS); + val &= (~(0x7<<12)); + amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS); + val |= (max_rd_req_size<<12); + amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS); +} + +static void amlogic_pcie_init_dw(struct amlogic_pcie *amlogic_pcie) +{ + u32 val = 0; + + val = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG0); + val &= (~APP_LTSSM_ENABLE); + amlogic_cfg_writel(amlogic_pcie, val, PCIE_CFG0); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_PORT_LINK_CTRL_OFF); + val |= (1<<7); + amlogic_elb_writel(amlogic_pcie, val, PCIE_PORT_LINK_CTRL_OFF); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_PORT_LINK_CTRL_OFF); + val &= (~(0x3f<<16)); + amlogic_elb_writel(amlogic_pcie, val, PCIE_PORT_LINK_CTRL_OFF); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_PORT_LINK_CTRL_OFF); + val |= (0x1<<16); + amlogic_elb_writel(amlogic_pcie, val, PCIE_PORT_LINK_CTRL_OFF); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_GEN2_CTRL_OFF); + val &= (~(0x1f<<8)); + amlogic_elb_writel(amlogic_pcie, val, PCIE_GEN2_CTRL_OFF); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_GEN2_CTRL_OFF); + val |= (0x1<<8); + amlogic_elb_writel(amlogic_pcie, val, PCIE_GEN2_CTRL_OFF); + + val = amlogic_elb_readl(amlogic_pcie, PCIE_GEN2_CTRL_OFF); + val |= (1<<17); + amlogic_elb_writel(amlogic_pcie, val, PCIE_GEN2_CTRL_OFF); + + amlogic_elb_writel(amlogic_pcie, 0x0, PCIE_BASE_ADDR0); + amlogic_elb_writel(amlogic_pcie, 0x0, PCIE_BASE_ADDR1); +} + +static void amlogic_enable_memory_space(struct amlogic_pcie *amlogic_pcie) +{ + u32 val = 0; + + dev_info(amlogic_pcie->pp.dev, "Set the RC Bus Master, Memory Space and I/O Space enables.\n"); + val = amlogic_elb_readl(amlogic_pcie, 0x04); + val = 7; + amlogic_elb_writel(amlogic_pcie, val, 0x04); +} + + +static int amlogic_pcie_establish_link(struct amlogic_pcie *amlogic_pcie) +{ + struct pcie_port *pp = &amlogic_pcie->pp; + u32 val; + + amlogic_pcie_init_dw(amlogic_pcie); + amlogic_set_max_payload(amlogic_pcie, 256); + amlogic_set_max_rd_req_size(amlogic_pcie, 256); + + dw_pcie_setup_rc(pp); + amlogic_enable_memory_space(amlogic_pcie); + + amlogic_pcie_assert_reset(amlogic_pcie); + + val = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG0); + val |= APP_LTSSM_ENABLE; + amlogic_cfg_writel(amlogic_pcie, val, PCIE_CFG0); + + /* check if the link is up or not */ + if (!dw_pcie_wait_for_link(pp)) + return 0; + + return -ETIMEDOUT; +} + +static irqreturn_t amlogic_pcie_irq_handler(int irq, void *arg) +{ + return IRQ_HANDLED; +} + +static irqreturn_t amlogic_pcie_msi_irq_handler(int irq, void *arg) +{ + struct amlogic_pcie *amlogic_pcie = arg; + struct pcie_port *pp = &amlogic_pcie->pp; + + return dw_handle_msi_irq(pp); +} + +static void amlogic_pcie_msi_init(struct amlogic_pcie *amlogic_pcie) +{ + struct pcie_port *pp = &amlogic_pcie->pp; + + dw_pcie_msi_init(pp); +} + +static void amlogic_pcie_enable_interrupts(struct amlogic_pcie *amlogic_pcie) +{ + if (IS_ENABLED(CONFIG_PCI_MSI)) + amlogic_pcie_msi_init(amlogic_pcie); +} + +static u32 amlogic_pcie_readl_rc(struct pcie_port *pp, u32 reg) +{ + u32 val; + + val = readl(pp->dbi_base + reg); + return val; +} + +static void amlogic_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val) +{ + writel(val, pp->dbi_base + reg); +} + +static int amlogic_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) +{ + int ret; + struct amlogic_pcie *amlogic_pcie = to_amlogic_pcie(pp); + + if (amlogic_pcie->device_attch == 0) + return 0; + + /* the device class is not reported correctly from the register */ + if (where == PCI_CLASS_REVISION) { + dev_info(pp->dev, "the device class is not reported correctly from the register\n"); + *val = readl(pp->dbi_base + PCI_CLASS_REVISION); + *val &= 0xff; /* keep revision id */ + *val |= PCI_CLASS_BRIDGE_PCI << 16; + return PCIBIOS_SUCCESSFUL; + } + + ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val); + return ret; +} + +static int amlogic_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) +{ + int ret; + struct amlogic_pcie *amlogic_pcie = to_amlogic_pcie(pp); + + if (amlogic_pcie->device_attch == 0) + return 0; + + ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val); + return ret; +} + +static int amlogic_pcie_link_up(struct pcie_port *pp) +{ + u32 smlh_up = 0; + u32 rdlh_up = 0; + u32 ltssm_up = 0; + u32 speed_okay = 0; + u32 current_data_rate; + int cnt = 0; + struct amlogic_pcie *amlogic_pcie = to_amlogic_pcie(pp); + + while (smlh_up == 0 || rdlh_up == 0 + || ltssm_up == 0 || speed_okay == 0) { + udelay(20); + smlh_up = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG_STATUS12); + smlh_up = (smlh_up >> 6) & 0x1; + + rdlh_up = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG_STATUS12); + rdlh_up = (rdlh_up >> 16) & 0x1; + ltssm_up = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG_STATUS12); + ltssm_up = ((ltssm_up >> 10) & 0x1f) == 0x11 ? 1 : 0; + current_data_rate = amlogic_cfg_readl( + amlogic_pcie, PCIE_CFG_STATUS17); + current_data_rate = (current_data_rate >> 7) & 0x1; + + if ((current_data_rate == PCIE_GEN2) || + (current_data_rate == PCIE_GEN1)) + speed_okay = 1; + + if (smlh_up) + dev_dbg(pp->dev, "smlh_link_up is on\n"); + if (rdlh_up) + dev_dbg(pp->dev, "rdlh_link_up is on\n"); + if (ltssm_up) + dev_dbg(pp->dev, "ltssm_up is on\n"); + if (speed_okay) + dev_dbg(pp->dev, "speed_okay\n"); + + cnt++; + + if (cnt >= WAIT_LINKUP_TIMEOUT) { + dev_err(pp->dev, "Error: Wait linkup timeout.\n"); + return 0; + } + + udelay(20); + } + + if (current_data_rate == PCIE_GEN2) + dev_info(pp->dev, "PCIE SPEED IS GEN2\n"); + + return 1; +} + +static void amlogic_pcie_host_init(struct pcie_port *pp) +{ + struct amlogic_pcie *amlogic_pcie = to_amlogic_pcie(pp); + int ret; + + ret = amlogic_pcie_establish_link(amlogic_pcie); + if (ret) + if (amlogic_pcie->phy->device_attch == 0) + return; + + amlogic_pcie->phy->device_attch = 1; + if (!ret) + amlogic_pcie->device_attch = 1; + + amlogic_pcie_enable_interrupts(amlogic_pcie); +} + +static struct pcie_host_ops amlogic_pcie_host_ops = { + .readl_rc = amlogic_pcie_readl_rc, + .writel_rc = amlogic_pcie_writel_rc, + .rd_own_conf = amlogic_pcie_rd_own_conf, + .wr_own_conf = amlogic_pcie_wr_own_conf, + .link_up = amlogic_pcie_link_up, + .host_init = amlogic_pcie_host_init, +}; + +static int __init amlogic_add_pcie_port(struct amlogic_pcie *amlogic_pcie, + struct platform_device *pdev) +{ + struct pcie_port *pp = &amlogic_pcie->pp; + struct device *dev = pp->dev; + int ret; + + pp->irq = platform_get_irq(pdev, 1); + if (!pp->irq) { + dev_err(dev, "failed to get irq\n"); + return -ENODEV; + } + ret = devm_request_irq(dev, pp->irq, amlogic_pcie_irq_handler, + IRQF_SHARED, "amlogic-pcie", amlogic_pcie); + if (ret) { + dev_err(dev, "failed to request irq\n"); + return ret; + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq(pdev, 0); + if (!pp->msi_irq) { + dev_err(dev, "failed to get msi irq\n"); + return -ENODEV; + } + + ret = devm_request_irq(dev, pp->msi_irq, + amlogic_pcie_msi_irq_handler, + IRQF_SHARED | IRQF_NO_THREAD, + "amlogic-pcie", amlogic_pcie); + if (ret) { + dev_err(dev, "failed to request msi irq\n"); + return ret; + } + } + + pp->root_bus_nr = -1; + pp->ops = &amlogic_pcie_host_ops; + pp->dbi_base = amlogic_pcie->elbi_base; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + if (amlogic_pcie->device_attch == 0) { + dev_err(pp->dev, "link timeout, disable PCIE PLL\n"); + clk_disable_unprepare(amlogic_pcie->bus_clk); + clk_disable_unprepare(amlogic_pcie->clk); + dev_err(pp->dev, "power down pcie phy\n"); + writel(0x1d, pcie_aml_regs_v2.pcie_phy_r[0]); + amlogic_pcie->phy->power_state = 0; + } + + return 0; +} + +static int __init amlogic_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct amlogic_pcie *amlogic_pcie; + struct pcie_port *pp; + struct device_node *np = dev->of_node; + struct pcie_phy *phy; + struct resource *elbi_base; + struct resource *phy_base; + struct resource *cfg_base; + struct resource *reset_base; + int ret; + int pcie_num = 0; + int num_lanes = 0; + int gpio_type = 0; + unsigned long rate; +#define PCIE_PLL_RATE 100000000 + int j = 0; + u32 val = 0; + static u32 port_num; + u32 pm_enable = 1; + int pcie_apb_rst_bit = 0; + int pcie_phy_rst_bit = 0; + int pcie_ctrl_a_rst_bit = 0; + + dev_info(&pdev->dev, "amlogic_pcie_probe!\n"); + + amlogic_pcie = devm_kzalloc(dev, sizeof(*amlogic_pcie), GFP_KERNEL); + if (!amlogic_pcie) + return -ENOMEM; + + amlogic_pcie->device_attch = 0; + amlogic_pcie->rst_mod = 1; + pp = &amlogic_pcie->pp; + pp->dev = dev; + port_num++; + amlogic_pcie->port_num = port_num; + if (amlogic_pcie->port_num == 1) { + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + port_num--; + return -ENOMEM; + } + g_pcie_phy_v2 = phy; + } + + amlogic_pcie->phy = g_pcie_phy_v2; + + ret = of_property_read_u32(np, "pcie-apb-rst-bit", &pcie_apb_rst_bit); + if (ret) + amlogic_pcie->rst_mod = 0; + + ret = of_property_read_u32(np, "pcie-phy-rst-bit", &pcie_phy_rst_bit); + if (ret) + amlogic_pcie->rst_mod = 0; + + ret = of_property_read_u32(np, "pcie-ctrl-a-rst-bit", + &pcie_ctrl_a_rst_bit); + if (ret) + amlogic_pcie->rst_mod = 0; + + ret = of_property_read_u32(np, "pcie-num", &pcie_num); + if (ret) + amlogic_pcie->pcie_num = 0; + + amlogic_pcie->pcie_num = pcie_num; + + ret = of_property_read_u32(np, "pm-enable", &pm_enable); + if (ret) + amlogic_pcie->pm_enable = 1; + else + amlogic_pcie->pm_enable = pm_enable; + + ret = of_property_read_u32(np, "num-lanes", &num_lanes); + if (ret) + pp->lanes = 0; + pp->lanes = num_lanes; + + if (!amlogic_pcie->phy->phy_base) { + phy_base = platform_get_resource_byname( + pdev, IORESOURCE_MEM, "phy"); + amlogic_pcie->phy->phy_base = + devm_ioremap_resource(dev, phy_base); + if (IS_ERR(amlogic_pcie->phy->phy_base)) { + ret = PTR_ERR(amlogic_pcie->phy->phy_base); + port_num--; + return ret; + } + } + + if (!amlogic_pcie->phy->power_state) { + for (j = 0; j < 7; j++) + pcie_aml_regs_v2.pcie_phy_r[j] = (void __iomem *) + ((unsigned long)amlogic_pcie->phy->phy_base + + 4*j); + writel(0x1c, pcie_aml_regs_v2.pcie_phy_r[0]); + amlogic_pcie->phy->power_state = 1; + } + + ret = of_property_read_u32(np, "gpio-type", &gpio_type); + amlogic_pcie->gpio_type = gpio_type; + + amlogic_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + + if (!amlogic_pcie->phy->reset_base) { + reset_base = platform_get_resource_byname( + pdev, IORESOURCE_MEM, "reset"); + amlogic_pcie->phy->reset_base = devm_ioremap_resource( + dev, reset_base); + if (IS_ERR(amlogic_pcie->phy->reset_base)) { + ret = PTR_ERR(amlogic_pcie->phy->reset_base); + goto fail_pcie; + } + } + + /* RESET0[1,2,6,7] = 0*/ + if (!amlogic_pcie->phy->reset_state) { + if (amlogic_pcie->rst_mod == 0) { + val = readl(amlogic_pcie->phy->reset_base); + val &= ~((0x3<<6) | (0x3<<1)); + writel(val, amlogic_pcie->phy->reset_base); + } else { + val = readl(amlogic_pcie->phy->reset_base); + val &= ~((0x1<phy->reset_base); + } + } + amlogic_pcie->phy_clk = devm_clk_get(dev, "pcie_phy"); + if (IS_ERR(amlogic_pcie->phy_clk)) { + dev_err(dev, "Failed to get pcie pcie_phy clock\n"); + ret = PTR_ERR(amlogic_pcie->phy_clk); + goto fail_pcie; + } + + ret = clk_prepare_enable(amlogic_pcie->phy_clk); + if (ret) + goto fail_pcie_phy; + amlogic_pcie->bus_clk = devm_clk_get(dev, "pcie_refpll"); + if (IS_ERR(amlogic_pcie->bus_clk)) { + dev_err(dev, "Failed to get pcie pcie_refpll clock\n"); + ret = PTR_ERR(amlogic_pcie->bus_clk); + goto fail_pcie; + } + + ret = clk_prepare_enable(amlogic_pcie->bus_clk); + if (ret) + goto fail_pcie; + + if (!amlogic_pcie->phy->reset_state) { + rate = clk_get_rate(amlogic_pcie->bus_clk); + if (rate != PCIE_PLL_RATE) { + ret = -ENODEV; + goto fail_pcie; + } + } + + /*RESET0[6,7] = 1*/ + if (!amlogic_pcie->phy->reset_state) { + if (amlogic_pcie->rst_mod == 0) { + val = readl(amlogic_pcie->phy->reset_base); + val |= (0x3<<6); + writel(val, amlogic_pcie->phy->reset_base); + } else { + val = readl(amlogic_pcie->phy->reset_base); + val |= ((0x1<phy->reset_base); + } + mdelay(1); + } + amlogic_pcie->clk = devm_clk_get(dev, "pcie"); + if (IS_ERR(amlogic_pcie->clk)) { + dev_err(dev, "Failed to get pcie rc clock\n"); + ret = PTR_ERR(amlogic_pcie->clk); + goto fail_bus_clk; + } + + ret = clk_prepare_enable(amlogic_pcie->clk); + if (ret) + goto fail_bus_clk; + + /*RESET0[1,2] = 1*/ + if (amlogic_pcie->pcie_num == 1) { + if (amlogic_pcie->rst_mod == 0) { + val = readl(amlogic_pcie->phy->reset_base); + val |= (0x1<<1); + writel(val, amlogic_pcie->phy->reset_base); + } else { + val = readl(amlogic_pcie->phy->reset_base); + val |= (0x1<phy->reset_base); + } + mdelay(1); + } else { + if (amlogic_pcie->rst_mod == 0) { + val = readl(amlogic_pcie->phy->reset_base); + val |= (0x1<<2); + writel(val, amlogic_pcie->phy->reset_base); + } else { + val = readl(amlogic_pcie->phy->reset_base); + val |= (0x1<phy->reset_base); + } + mdelay(1); + } + + amlogic_pcie->phy->reset_state = 1; + + elbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); + amlogic_pcie->elbi_base = devm_ioremap_resource(dev, elbi_base); + if (IS_ERR(amlogic_pcie->elbi_base)) { + ret = PTR_ERR(amlogic_pcie->elbi_base); + goto fail_clk; + } + + cfg_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + amlogic_pcie->cfg_base = devm_ioremap_resource(dev, cfg_base); + if (IS_ERR(amlogic_pcie->cfg_base)) { + ret = PTR_ERR(amlogic_pcie->cfg_base); + goto fail_clk; + } + + ret = amlogic_add_pcie_port(amlogic_pcie, pdev); + if (ret < 0) + goto fail_clk; + + platform_set_drvdata(pdev, amlogic_pcie); + device_create_file(&pdev->dev, &dev_attr_phyread_v2); + device_create_file(&pdev->dev, &dev_attr_phywrite_v2); + return 0; + +fail_clk: + clk_disable_unprepare(amlogic_pcie->clk); +fail_bus_clk: + clk_disable_unprepare(amlogic_pcie->bus_clk); +fail_pcie: + clk_disable_unprepare(amlogic_pcie->phy_clk); + port_num--; +fail_pcie_phy: + return ret; +} + +static int __exit amlogic_pcie_remove(struct platform_device *pdev) +{ + struct amlogic_pcie *amlogic_pcie = platform_get_drvdata(pdev); + + if (amlogic_pcie->phy->power_state == 0) { + dev_info(&pdev->dev, "PCIE phy power off, no remove\n"); + return 0; + } + + device_remove_file(&pdev->dev, &dev_attr_phywrite_v2); + device_remove_file(&pdev->dev, &dev_attr_phyread_v2); + + clk_disable_unprepare(amlogic_pcie->clk); + clk_disable_unprepare(amlogic_pcie->bus_clk); + clk_disable_unprepare(amlogic_pcie->phy_clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int amlogic_pcie_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct amlogic_pcie *amlogic_pcie = platform_get_drvdata(pdev); + struct pcie_port *pp = &amlogic_pcie->pp; + u32 val; + + if (!amlogic_pcie->pm_enable) { + dev_info(dev, "don't suspend amlogic pcie\n"); + return 0; + } + + if (amlogic_pcie->device_attch == 0) { + dev_info(dev, "controller power off, no suspend\n"); + return 0; + } + + dev_info(dev, "amlogic_pcie_suspend\n"); + + /* clear MSE */ + val = dw_pcie_readl_rc(pp, PCI_COMMAND); + val &= ~PCI_COMMAND_MEMORY; + dw_pcie_writel_rc(pp, PCI_COMMAND, val); + + return 0; +} + +static int amlogic_pcie_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct amlogic_pcie *amlogic_pcie = platform_get_drvdata(pdev); + struct pcie_port *pp = &amlogic_pcie->pp; + u32 val; + + if (!amlogic_pcie->pm_enable) { + dev_info(dev, "don't resume amlogic pcie\n"); + return 0; + } + + if (amlogic_pcie->device_attch == 0) { + dev_info(dev, "controller power off, no resume\n"); + return 0; + } + + dev_info(dev, "amlogic_pcie_resume\n"); + + /* set MSE */ + val = dw_pcie_readl_rc(pp, PCI_COMMAND); + val |= PCI_COMMAND_MEMORY; + dw_pcie_writel_rc(pp, PCI_COMMAND, val); + + return 0; +} + +static int amlogic_pcie_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct amlogic_pcie *amlogic_pcie = platform_get_drvdata(pdev); + + if (!amlogic_pcie->pm_enable) { + dev_info(dev, "don't noirq suspend amlogic pcie\n"); + return 0; + } + + if (amlogic_pcie->phy->device_attch == 0) { + dev_info(dev, "PCIE phy power off, no suspend noirq\n"); + return 0; + } + + if (amlogic_pcie->device_attch == 0) { + dev_info(dev, "controller power off, no suspend noirq\n"); + if (amlogic_pcie->pcie_num == 1) { + writel(0x1d, pcie_aml_regs_v2.pcie_phy_r[0]); + amlogic_pcie->phy->power_state = 0; + } + return 0; + } + + dev_info(dev, "amlogic_pcie_suspend_noirq\n"); + + clk_disable_unprepare(amlogic_pcie->clk); + clk_disable_unprepare(amlogic_pcie->bus_clk); + clk_disable_unprepare(amlogic_pcie->phy_clk); + amlogic_pcie->phy->reset_state = 0; + + if (amlogic_pcie->pcie_num == 1) { + writel(0x1d, pcie_aml_regs_v2.pcie_phy_r[0]); + amlogic_pcie->phy->power_state = 0; + } + + return 0; +} + +static int amlogic_pcie_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct amlogic_pcie *amlogic_pcie = platform_get_drvdata(pdev); + unsigned long rate = 100000000; + + if (!amlogic_pcie->pm_enable) { + dev_info(dev, "don't noirq resume amlogic pcie\n"); + return 0; + } + + if (amlogic_pcie->phy->device_attch == 0) { + dev_info(dev, "PCIE phy power off, no resume noirq\n"); + return 0; + } + + if (amlogic_pcie->pcie_num == 1) { + writel(0x1c, pcie_aml_regs_v2.pcie_phy_r[0]); + amlogic_pcie->phy->power_state = 1; + udelay(500); + } + + if (amlogic_pcie->device_attch == 0) { + dev_info(dev, "controller power off, no resume noirq\n"); + return 0; + } + + dev_info(dev, "amlogic_pcie_resume_noirq\n"); + if (!amlogic_pcie->phy->reset_state) + clk_set_rate(amlogic_pcie->bus_clk, rate); + + amlogic_pcie->phy->reset_state = 1; + + clk_prepare_enable(amlogic_pcie->phy_clk); + clk_prepare_enable(amlogic_pcie->bus_clk); + clk_prepare_enable(amlogic_pcie->clk); + udelay(500); + + return 0; +} + +#else +#define amlogic_pcie_suspend NULL +#define amlogic_pcie_resume NULL +#define amlogic_pcie_suspend_noirq NULL +#define amlogic_pcie_resume_noirq NULL +#endif + +static const struct dev_pm_ops amlogic_pcie_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(amlogic_pcie_suspend, amlogic_pcie_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(amlogic_pcie_suspend_noirq, + amlogic_pcie_resume_noirq) +}; + + + +static const struct of_device_id amlogic_pcie_of_match[] = { + { .compatible = "amlogic, amlogic-pcie-v2", }, + {}, +}; + +static struct platform_driver amlogic_pcie_driver = { + .remove = __exit_p(amlogic_pcie_remove), + .driver = { + .name = "amlogic-pcie-v2", + .of_match_table = amlogic_pcie_of_match, + .pm = &amlogic_pcie_pm_ops, + }, +}; + +/* AMLOGIC PCIe driver does not allow module unload */ +static int __init amlogic_pcie_init(void) +{ + return platform_driver_probe(&amlogic_pcie_driver, amlogic_pcie_probe); +} +subsys_initcall(amlogic_pcie_init); -- 2.7.4