blob: 2afcb6bdcdd74ceb8da1a20831c9cb166cd7314c [file] [log] [blame]
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/reset.h>
#include "pcie-designware.h"
#define to_ls1024a_pcie(x) container_of(x, struct ls1024a_pcie, pp)
struct ls1024a_pcie {
int reset_gpio;
struct reset_control *axi_rst;
struct clk *pcie;
struct phy *phy;
struct pcie_port pp;
struct regmap *app;
int app_profile;
struct gpio_desc *gpiod_reset;
};
/* The following register definitions are as per "DWC_regs_rev04.doc" document */
struct pcie_app_reg {
u32 cfg0;
u32 cfg1;
u32 cfg2;
u32 cfg3;
u32 cfg4;
u32 cfg5;
u32 cfg6;
u32 sts0;
u32 sts1;
u32 sts2;
u32 sts3;
u32 pwr_cfg_bdgt_data;
u32 pwr_cfg_bdgt_fn;
u32 radm_sts;
u32 pwr_sts_bdgt;
u32 intr_sts;
u32 intr_en;
u32 intr_msi_sts;
u32 intr_msi_en;
};
#define MAX_PCIE_PORTS 2
/* DWC PCIEe configuration register offsets on APB */
struct pcie_app_reg app_regs[MAX_PCIE_PORTS] = {
/* PCIe0 */
{
0x00000000, /* cfg0 */
0x00000004,
0x00000008,
0x0000000C,
0x00000010,
0x00000014,
0x00000018, /* cfg6 */
0x00000040, /* sts0 */
0x00000044,
0x00000048,
0x00000058, /* sts3 */
0x00000080,
0x00000084,
0x000000C0, /* radm_sts */
0x000000C4,
0x00000100, /* intr_sts */
0x00000104,
0x00000108,
0x0000010C
},
/* PCIe1 */
{
0x00000020,
0x00000024,
0x00000028,
0x0000002C,
0x00000030,
0x00000034,
0x00000038,
0x0000004C,
0x00000050,
0x00000054,
0x0000005C,
0x00000088,
0x0000008C,
0x000000C8,
0x000000CC,
0x00000110,
0x00000114,
0x00000118,
0x0000011C
}
};
/* INTR_STS and INTR_EN Register definitions */
#define INTR_CTRL_INTA_ASSERT 0x0001
#define INTR_CTRL_INTA_DEASSERT 0x0002
#define INTR_CTRL_INTB_ASSERT 0x0004
#define INTR_CTRL_INTB_DEASSERT 0x0008
#define INTR_CTRL_INTC_ASSERT 0x0010
#define INTR_CTRL_INTC_DEASSERT 0x0020
#define INTR_CTRL_INTD_ASSERT 0x0040
#define INTR_CTRL_INTD_DEASSERT 0x0080
#define INTR_CTRL_AER 0x0100
#define INTR_CTRL_PME 0x0200
#define INTR_CTRL_HP 0x0400
#define INTR_CTRL_LINK_AUTO_BW 0x0800
#define INTR_CTRL_MSI 0x1000
static irqreturn_t ls1024a_pcie_msi_handler(int irq, void *arg)
{
struct pcie_port *pp = arg;
struct ls1024a_pcie *ls1024a_pcie = to_ls1024a_pcie(pp);
irqreturn_t ret = IRQ_NONE;
u32 status = 0;
regmap_read(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].intr_sts, &status);
if (status & INTR_CTRL_MSI) {
regmap_write(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].intr_sts, INTR_CTRL_MSI);
status &= ~INTR_CTRL_MSI;
ret = dw_handle_msi_irq(pp);
}
regmap_write(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].intr_sts, status);
if (status)
return IRQ_NONE;
else
return ret;
}
static int ls1024a_pcie_wait_for_link(struct pcie_port *pp)
{
int count = 200;
while (!dw_pcie_link_up(pp)) {
usleep_range(100, 1000);
if (--count)
continue;
dev_err(pp->dev, "phy link never came up\n");
return -EINVAL;
}
return 0;
}
/* Port Logic Registers */
#define PCIE_ALRT_REG 0x700
#define PCIE_AFL0L1_REG 0x70C
#define PCIE_SYMNUM_REG 0x718
#define PCIE_G2CTRL_REG 0x80C
#define PCIE_CAP_BASE 0x70
#define PCIE_LCAP_REG (PCIE_CAP_BASE + 0x0C)
#define PCIE_LCNT2_REG (PCIE_CAP_BASE + 0x30)
static void ls1024a_pcie_host_init(struct pcie_port *pp)
{
struct ls1024a_pcie *ls1024a_pcie = to_ls1024a_pcie(pp);
int ret;
u32 val;
ret = reset_control_deassert(ls1024a_pcie->axi_rst);
ret = phy_init(ls1024a_pcie->phy);
/* TODO: Do we really need this delay */
mdelay(1); //After CMU locks wait for sometime
/* TODO: PCIe_SATA_RST_CNTRL */
#define CFG5_APP_INIT_RST 0x01
#define CFG5_LTSSM_ENABLE 0x02
//Hold the LTSSM in detect state
regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].cfg5, CFG5_LTSSM_ENABLE, 0);
//FIXME : Bit:27 definition is not clear from document
// This register setting is copied from simulation code.
regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].cfg0, 0x08007FF0, 0x08007FF0);
val = readl(pp->dbi_base + PCIE_AFL0L1_REG);
val &= ~(0x00FFFF00);
val |= 0x00F1F100;
writel(val, pp->dbi_base + PCIE_AFL0L1_REG);
if(0) // if (pcie_gen1_only)
{
writel(0x1, pp->dbi_base + PCIE_LCNT2_REG);
writel(0x1, pp->dbi_base + PCIE_LCAP_REG);
}
else
{
val = readl(pp->dbi_base + PCIE_G2CTRL_REG);
val &= ~(0xFF);
val |= 0xF1;
writel(val, pp->dbi_base + PCIE_G2CTRL_REG);
// instruct pcie to switch to gen2 after init
val = readl(pp->dbi_base + PCIE_G2CTRL_REG);
val |= (1 << 17);
writel(val, pp->dbi_base + PCIE_G2CTRL_REG);
}
dw_pcie_setup_rc(pp);
//Enable LTSSM to start link initialization
regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].cfg5,
(CFG5_APP_INIT_RST | CFG5_LTSSM_ENABLE),
(CFG5_APP_INIT_RST | CFG5_LTSSM_ENABLE));
ls1024a_pcie_wait_for_link(&ls1024a_pcie->pp);
if (IS_ENABLED(CONFIG_PCI_MSI))
dw_pcie_msi_init(pp);
}
#define STS0_RDLH_LINK_UP 0x10000
static int ls1024a_pcie_link_up(struct pcie_port *pp)
{
struct ls1024a_pcie *ls1024a_pcie = to_ls1024a_pcie(pp);
u32 rc = 0;
regmap_read(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].sts0, &rc);
return !!(rc & STS0_RDLH_LINK_UP);
}
static struct pcie_host_ops ls1024a_pcie_host_ops = {
.link_up = ls1024a_pcie_link_up,
.host_init = ls1024a_pcie_host_init,
};
static int __init ls1024a_add_pcie_port(struct pcie_port *pp,
struct platform_device *pdev)
{
int ret;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
pp->msi_irq = platform_get_irq_byname(pdev, "msi");
if (pp->msi_irq <= 0) {
dev_err(&pdev->dev, "failed to get MSI irq\n");
return -ENODEV;
}
ret = devm_request_irq(&pdev->dev, pp->msi_irq,
ls1024a_pcie_msi_handler,
IRQF_SHARED, "ls1024a-pcie-msi", pp);
if (ret) {
dev_err(&pdev->dev, "failed to request MSI irq\n");
return -ENODEV;
}
}
pp->root_bus_nr = -1;
pp->ops = &ls1024a_pcie_host_ops;
ret = dw_pcie_host_init(pp);
if (ret) {
dev_err(&pdev->dev, "failed to initialize host\n");
return ret;
}
return 0;
}
static int __init ls1024a_pcie_probe(struct platform_device *pdev)
{
struct ls1024a_pcie *ls1024a_pcie;
struct pcie_port *pp;
struct device_node *np = pdev->dev.of_node;
struct resource *dbi_base;
int ret;
ls1024a_pcie = devm_kzalloc(&pdev->dev, sizeof(*ls1024a_pcie), GFP_KERNEL);
if (!ls1024a_pcie)
return -ENOMEM;
pp = &ls1024a_pcie->pp;
pp->dev = &pdev->dev;
/* Added for PCI abort handling */
#if 0
hook_fault_code(16 + 6, ls1024aq_pcie_abort_handler, SIGBUS, 0,
"imprecise external abort");
#endif
dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base);
if (IS_ERR(pp->dbi_base))
return PTR_ERR(pp->dbi_base);
#if 0
app = platform_get_resource_byname(pdev, IORESOURCE_MEM, "app");
ls1024a_pcie->app = devm_ioremap_resource(&pdev->dev, app);
if (IS_ERR(ls1024a_pcie->app))
return PTR_ERR(ls1024a_pcie->app);
#endif
ls1024a_pcie->app = syscon_regmap_lookup_by_phandle(np, "app-syscon");
if (IS_ERR(ls1024a_pcie->app)) {
dev_err(&pdev->dev,
"Error retrieving pci_sata_usb_ctrl syscon\n");
return PTR_ERR(ls1024a_pcie->app);
}
ls1024a_pcie->app_profile = 0;
of_property_read_u32(np, "app-profile", &ls1024a_pcie->app_profile);
/* Fetch clocks */
ls1024a_pcie->pcie = devm_clk_get(&pdev->dev, "pcie");
if (IS_ERR(ls1024a_pcie->pcie)) {
dev_err(&pdev->dev,
"pcie clock source missing or invalid\n");
return PTR_ERR(ls1024a_pcie->pcie);
}
ls1024a_pcie->phy = devm_of_phy_get(pp->dev, np, NULL);
if (IS_ERR(ls1024a_pcie->phy)) {
ret = PTR_ERR(ls1024a_pcie->phy);
if (ret == -EPROBE_DEFER) {
return ret;
} else if (ret != -ENOSYS && ret != -ENODEV) {
dev_err(pp->dev,
"Error retrieving PCIe phy: %d\n", ret);
return ret;
}
}
ls1024a_pcie->axi_rst = devm_reset_control_get(pp->dev, "axi");
if (IS_ERR(ls1024a_pcie->axi_rst))
return PTR_ERR(ls1024a_pcie->axi_rst);
ls1024a_pcie->gpiod_reset = devm_gpiod_get_optional(pp->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ls1024a_pcie->gpiod_reset)) {
ls1024a_pcie->gpiod_reset = 0;
}
if (ls1024a_pcie->gpiod_reset) {
pr_err("Pulsing GPIO reset line\n");
gpiod_set_value(ls1024a_pcie->gpiod_reset, 1);
msleep(1);
gpiod_set_value(ls1024a_pcie->gpiod_reset, 0);
msleep(1);
}
/* TODO */
#define CFG0_DEV_TYPE_RC 0x04
regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].cfg0, 0xf, CFG0_DEV_TYPE_RC);
ret = ls1024a_add_pcie_port(pp, pdev);
if (ret < 0)
return ret;
/* TODO */
regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].intr_en, INTR_CTRL_INTA_ASSERT, INTR_CTRL_INTA_ASSERT);
regmap_update_bits(ls1024a_pcie->app, app_regs[ls1024a_pcie->app_profile].intr_en, INTR_CTRL_MSI, INTR_CTRL_MSI);
platform_set_drvdata(pdev, ls1024a_pcie);
return 0;
}
static const struct of_device_id ls1024a_pcie_of_match[] = {
{ .compatible = "fsl,ls1024a-pcie", },
{},
};
MODULE_DEVICE_TABLE(of, ls1024a_pcie_of_match);
static struct platform_driver ls1024a_pcie_driver = {
.driver = {
.name = "ls1024a-pcie",
.of_match_table = ls1024a_pcie_of_match,
},
};
static int __init ls1024a_pcie_init(void)
{
return platform_driver_probe(&ls1024a_pcie_driver, ls1024a_pcie_probe);
}
module_init(ls1024a_pcie_init);
MODULE_AUTHOR("Daniel Mentz <danielmentz@google.com>");
MODULE_DESCRIPTION("Freescale LS1024A PCIe host controller driver");
MODULE_LICENSE("GPL v2");