103bdc388SHou Zhiqiang // SPDX-License-Identifier: GPL-2.0
203bdc388SHou Zhiqiang /*
303bdc388SHou Zhiqiang * PCIe host controller driver for Mobiveil PCIe Host controller
403bdc388SHou Zhiqiang *
503bdc388SHou Zhiqiang * Copyright (c) 2018 Mobiveil Inc.
603bdc388SHou Zhiqiang * Copyright 2019 NXP
703bdc388SHou Zhiqiang *
803bdc388SHou Zhiqiang * Author: Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>
903bdc388SHou Zhiqiang * Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
1003bdc388SHou Zhiqiang */
1103bdc388SHou Zhiqiang
1203bdc388SHou Zhiqiang #include <linux/delay.h>
1303bdc388SHou Zhiqiang #include <linux/init.h>
1403bdc388SHou Zhiqiang #include <linux/kernel.h>
1503bdc388SHou Zhiqiang #include <linux/pci.h>
1603bdc388SHou Zhiqiang #include <linux/platform_device.h>
1703bdc388SHou Zhiqiang
1803bdc388SHou Zhiqiang #include "pcie-mobiveil.h"
1903bdc388SHou Zhiqiang
2003bdc388SHou Zhiqiang /*
2103bdc388SHou Zhiqiang * mobiveil_pcie_sel_page - routine to access paged register
2203bdc388SHou Zhiqiang *
2303bdc388SHou Zhiqiang * Registers whose address greater than PAGED_ADDR_BNDRY (0xc00) are paged,
2403bdc388SHou Zhiqiang * for this scheme to work extracted higher 6 bits of the offset will be
2503bdc388SHou Zhiqiang * written to pg_sel field of PAB_CTRL register and rest of the lower 10
2603bdc388SHou Zhiqiang * bits enabled with PAGED_ADDR_BNDRY are used as offset of the register.
2703bdc388SHou Zhiqiang */
mobiveil_pcie_sel_page(struct mobiveil_pcie * pcie,u8 pg_idx)2803bdc388SHou Zhiqiang static void mobiveil_pcie_sel_page(struct mobiveil_pcie *pcie, u8 pg_idx)
2903bdc388SHou Zhiqiang {
3003bdc388SHou Zhiqiang u32 val;
3103bdc388SHou Zhiqiang
3203bdc388SHou Zhiqiang val = readl(pcie->csr_axi_slave_base + PAB_CTRL);
3303bdc388SHou Zhiqiang val &= ~(PAGE_SEL_MASK << PAGE_SEL_SHIFT);
3403bdc388SHou Zhiqiang val |= (pg_idx & PAGE_SEL_MASK) << PAGE_SEL_SHIFT;
3503bdc388SHou Zhiqiang
3603bdc388SHou Zhiqiang writel(val, pcie->csr_axi_slave_base + PAB_CTRL);
3703bdc388SHou Zhiqiang }
3803bdc388SHou Zhiqiang
mobiveil_pcie_comp_addr(struct mobiveil_pcie * pcie,u32 off)39*92a17e5cSHou Zhiqiang static void __iomem *mobiveil_pcie_comp_addr(struct mobiveil_pcie *pcie,
40*92a17e5cSHou Zhiqiang u32 off)
4103bdc388SHou Zhiqiang {
4203bdc388SHou Zhiqiang if (off < PAGED_ADDR_BNDRY) {
4303bdc388SHou Zhiqiang /* For directly accessed registers, clear the pg_sel field */
4403bdc388SHou Zhiqiang mobiveil_pcie_sel_page(pcie, 0);
4503bdc388SHou Zhiqiang return pcie->csr_axi_slave_base + off;
4603bdc388SHou Zhiqiang }
4703bdc388SHou Zhiqiang
4803bdc388SHou Zhiqiang mobiveil_pcie_sel_page(pcie, OFFSET_TO_PAGE_IDX(off));
4903bdc388SHou Zhiqiang return pcie->csr_axi_slave_base + OFFSET_TO_PAGE_ADDR(off);
5003bdc388SHou Zhiqiang }
5103bdc388SHou Zhiqiang
mobiveil_pcie_read(void __iomem * addr,int size,u32 * val)5203bdc388SHou Zhiqiang static int mobiveil_pcie_read(void __iomem *addr, int size, u32 *val)
5303bdc388SHou Zhiqiang {
5403bdc388SHou Zhiqiang if ((uintptr_t)addr & (size - 1)) {
5503bdc388SHou Zhiqiang *val = 0;
5603bdc388SHou Zhiqiang return PCIBIOS_BAD_REGISTER_NUMBER;
5703bdc388SHou Zhiqiang }
5803bdc388SHou Zhiqiang
5903bdc388SHou Zhiqiang switch (size) {
6003bdc388SHou Zhiqiang case 4:
6103bdc388SHou Zhiqiang *val = readl(addr);
6203bdc388SHou Zhiqiang break;
6303bdc388SHou Zhiqiang case 2:
6403bdc388SHou Zhiqiang *val = readw(addr);
6503bdc388SHou Zhiqiang break;
6603bdc388SHou Zhiqiang case 1:
6703bdc388SHou Zhiqiang *val = readb(addr);
6803bdc388SHou Zhiqiang break;
6903bdc388SHou Zhiqiang default:
7003bdc388SHou Zhiqiang *val = 0;
7103bdc388SHou Zhiqiang return PCIBIOS_BAD_REGISTER_NUMBER;
7203bdc388SHou Zhiqiang }
7303bdc388SHou Zhiqiang
7403bdc388SHou Zhiqiang return PCIBIOS_SUCCESSFUL;
7503bdc388SHou Zhiqiang }
7603bdc388SHou Zhiqiang
mobiveil_pcie_write(void __iomem * addr,int size,u32 val)7703bdc388SHou Zhiqiang static int mobiveil_pcie_write(void __iomem *addr, int size, u32 val)
7803bdc388SHou Zhiqiang {
7903bdc388SHou Zhiqiang if ((uintptr_t)addr & (size - 1))
8003bdc388SHou Zhiqiang return PCIBIOS_BAD_REGISTER_NUMBER;
8103bdc388SHou Zhiqiang
8203bdc388SHou Zhiqiang switch (size) {
8303bdc388SHou Zhiqiang case 4:
8403bdc388SHou Zhiqiang writel(val, addr);
8503bdc388SHou Zhiqiang break;
8603bdc388SHou Zhiqiang case 2:
8703bdc388SHou Zhiqiang writew(val, addr);
8803bdc388SHou Zhiqiang break;
8903bdc388SHou Zhiqiang case 1:
9003bdc388SHou Zhiqiang writeb(val, addr);
9103bdc388SHou Zhiqiang break;
9203bdc388SHou Zhiqiang default:
9303bdc388SHou Zhiqiang return PCIBIOS_BAD_REGISTER_NUMBER;
9403bdc388SHou Zhiqiang }
9503bdc388SHou Zhiqiang
9603bdc388SHou Zhiqiang return PCIBIOS_SUCCESSFUL;
9703bdc388SHou Zhiqiang }
9803bdc388SHou Zhiqiang
mobiveil_csr_read(struct mobiveil_pcie * pcie,u32 off,size_t size)9903bdc388SHou Zhiqiang u32 mobiveil_csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size)
10003bdc388SHou Zhiqiang {
101*92a17e5cSHou Zhiqiang void __iomem *addr;
10203bdc388SHou Zhiqiang u32 val;
10303bdc388SHou Zhiqiang int ret;
10403bdc388SHou Zhiqiang
10503bdc388SHou Zhiqiang addr = mobiveil_pcie_comp_addr(pcie, off);
10603bdc388SHou Zhiqiang
10703bdc388SHou Zhiqiang ret = mobiveil_pcie_read(addr, size, &val);
10803bdc388SHou Zhiqiang if (ret)
10903bdc388SHou Zhiqiang dev_err(&pcie->pdev->dev, "read CSR address failed\n");
11003bdc388SHou Zhiqiang
11103bdc388SHou Zhiqiang return val;
11203bdc388SHou Zhiqiang }
11303bdc388SHou Zhiqiang
mobiveil_csr_write(struct mobiveil_pcie * pcie,u32 val,u32 off,size_t size)11403bdc388SHou Zhiqiang void mobiveil_csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off,
11503bdc388SHou Zhiqiang size_t size)
11603bdc388SHou Zhiqiang {
117*92a17e5cSHou Zhiqiang void __iomem *addr;
11803bdc388SHou Zhiqiang int ret;
11903bdc388SHou Zhiqiang
12003bdc388SHou Zhiqiang addr = mobiveil_pcie_comp_addr(pcie, off);
12103bdc388SHou Zhiqiang
12203bdc388SHou Zhiqiang ret = mobiveil_pcie_write(addr, size, val);
12303bdc388SHou Zhiqiang if (ret)
12403bdc388SHou Zhiqiang dev_err(&pcie->pdev->dev, "write CSR address failed\n");
12503bdc388SHou Zhiqiang }
12603bdc388SHou Zhiqiang
mobiveil_pcie_link_up(struct mobiveil_pcie * pcie)12703bdc388SHou Zhiqiang bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie)
12803bdc388SHou Zhiqiang {
129fc99b331SHou Zhiqiang if (pcie->ops->link_up)
130fc99b331SHou Zhiqiang return pcie->ops->link_up(pcie);
131fc99b331SHou Zhiqiang
13203bdc388SHou Zhiqiang return (mobiveil_csr_readl(pcie, LTSSM_STATUS) &
13303bdc388SHou Zhiqiang LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0;
13403bdc388SHou Zhiqiang }
13503bdc388SHou Zhiqiang
program_ib_windows(struct mobiveil_pcie * pcie,int win_num,u64 cpu_addr,u64 pci_addr,u32 type,u64 size)13603bdc388SHou Zhiqiang void program_ib_windows(struct mobiveil_pcie *pcie, int win_num,
13703bdc388SHou Zhiqiang u64 cpu_addr, u64 pci_addr, u32 type, u64 size)
13803bdc388SHou Zhiqiang {
13903bdc388SHou Zhiqiang u32 value;
14003bdc388SHou Zhiqiang u64 size64 = ~(size - 1);
14103bdc388SHou Zhiqiang
14203bdc388SHou Zhiqiang if (win_num >= pcie->ppio_wins) {
14303bdc388SHou Zhiqiang dev_err(&pcie->pdev->dev,
14403bdc388SHou Zhiqiang "ERROR: max inbound windows reached !\n");
14503bdc388SHou Zhiqiang return;
14603bdc388SHou Zhiqiang }
14703bdc388SHou Zhiqiang
14803bdc388SHou Zhiqiang value = mobiveil_csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num));
14903bdc388SHou Zhiqiang value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT | WIN_SIZE_MASK);
15003bdc388SHou Zhiqiang value |= type << AMAP_CTRL_TYPE_SHIFT | 1 << AMAP_CTRL_EN_SHIFT |
15103bdc388SHou Zhiqiang (lower_32_bits(size64) & WIN_SIZE_MASK);
15203bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num));
15303bdc388SHou Zhiqiang
15403bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, upper_32_bits(size64),
15503bdc388SHou Zhiqiang PAB_EXT_PEX_AMAP_SIZEN(win_num));
15603bdc388SHou Zhiqiang
15703bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, lower_32_bits(cpu_addr),
15803bdc388SHou Zhiqiang PAB_PEX_AMAP_AXI_WIN(win_num));
15903bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr),
16003bdc388SHou Zhiqiang PAB_EXT_PEX_AMAP_AXI_WIN(win_num));
16103bdc388SHou Zhiqiang
16203bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, lower_32_bits(pci_addr),
16303bdc388SHou Zhiqiang PAB_PEX_AMAP_PEX_WIN_L(win_num));
16403bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, upper_32_bits(pci_addr),
16503bdc388SHou Zhiqiang PAB_PEX_AMAP_PEX_WIN_H(win_num));
16603bdc388SHou Zhiqiang
16703bdc388SHou Zhiqiang pcie->ib_wins_configured++;
16803bdc388SHou Zhiqiang }
16903bdc388SHou Zhiqiang
17003bdc388SHou Zhiqiang /*
17103bdc388SHou Zhiqiang * routine to program the outbound windows
17203bdc388SHou Zhiqiang */
program_ob_windows(struct mobiveil_pcie * pcie,int win_num,u64 cpu_addr,u64 pci_addr,u32 type,u64 size)17303bdc388SHou Zhiqiang void program_ob_windows(struct mobiveil_pcie *pcie, int win_num,
17403bdc388SHou Zhiqiang u64 cpu_addr, u64 pci_addr, u32 type, u64 size)
17503bdc388SHou Zhiqiang {
17603bdc388SHou Zhiqiang u32 value;
17703bdc388SHou Zhiqiang u64 size64 = ~(size - 1);
17803bdc388SHou Zhiqiang
17903bdc388SHou Zhiqiang if (win_num >= pcie->apio_wins) {
18003bdc388SHou Zhiqiang dev_err(&pcie->pdev->dev,
18103bdc388SHou Zhiqiang "ERROR: max outbound windows reached !\n");
18203bdc388SHou Zhiqiang return;
18303bdc388SHou Zhiqiang }
18403bdc388SHou Zhiqiang
18503bdc388SHou Zhiqiang /*
18603bdc388SHou Zhiqiang * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit
18703bdc388SHou Zhiqiang * to 4 KB in PAB_AXI_AMAP_CTRL register
18803bdc388SHou Zhiqiang */
18903bdc388SHou Zhiqiang value = mobiveil_csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
19003bdc388SHou Zhiqiang value &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT | WIN_SIZE_MASK);
19103bdc388SHou Zhiqiang value |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT |
19203bdc388SHou Zhiqiang (lower_32_bits(size64) & WIN_SIZE_MASK);
19303bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num));
19403bdc388SHou Zhiqiang
19503bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, upper_32_bits(size64),
19603bdc388SHou Zhiqiang PAB_EXT_AXI_AMAP_SIZE(win_num));
19703bdc388SHou Zhiqiang
19803bdc388SHou Zhiqiang /*
19903bdc388SHou Zhiqiang * program AXI window base with appropriate value in
20003bdc388SHou Zhiqiang * PAB_AXI_AMAP_AXI_WIN0 register
20103bdc388SHou Zhiqiang */
20203bdc388SHou Zhiqiang mobiveil_csr_writel(pcie,
20303bdc388SHou Zhiqiang lower_32_bits(cpu_addr) & (~AXI_WINDOW_ALIGN_MASK),
20403bdc388SHou Zhiqiang PAB_AXI_AMAP_AXI_WIN(win_num));
20503bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr),
20603bdc388SHou Zhiqiang PAB_EXT_AXI_AMAP_AXI_WIN(win_num));
20703bdc388SHou Zhiqiang
20803bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, lower_32_bits(pci_addr),
20903bdc388SHou Zhiqiang PAB_AXI_AMAP_PEX_WIN_L(win_num));
21003bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, upper_32_bits(pci_addr),
21103bdc388SHou Zhiqiang PAB_AXI_AMAP_PEX_WIN_H(win_num));
21203bdc388SHou Zhiqiang
21303bdc388SHou Zhiqiang pcie->ob_wins_configured++;
21403bdc388SHou Zhiqiang }
21503bdc388SHou Zhiqiang
mobiveil_bringup_link(struct mobiveil_pcie * pcie)21603bdc388SHou Zhiqiang int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
21703bdc388SHou Zhiqiang {
21803bdc388SHou Zhiqiang int retries;
21903bdc388SHou Zhiqiang
22003bdc388SHou Zhiqiang /* check if the link is up or not */
22103bdc388SHou Zhiqiang for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
22203bdc388SHou Zhiqiang if (mobiveil_pcie_link_up(pcie))
22303bdc388SHou Zhiqiang return 0;
22403bdc388SHou Zhiqiang
22503bdc388SHou Zhiqiang usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX);
22603bdc388SHou Zhiqiang }
22703bdc388SHou Zhiqiang
22803bdc388SHou Zhiqiang dev_err(&pcie->pdev->dev, "link never came up\n");
22903bdc388SHou Zhiqiang
23003bdc388SHou Zhiqiang return -ETIMEDOUT;
23103bdc388SHou Zhiqiang }
232