123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * PCIe host controller driver for Mobiveil PCIe Host controller
- *
- * Copyright (c) 2018 Mobiveil Inc.
- * Copyright 2019 NXP
- *
- * Author: Subrahmanya Lingappa <[email protected]>
- * Hou Zhiqiang <[email protected]>
- */
- #include <linux/delay.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/pci.h>
- #include <linux/platform_device.h>
- #include "pcie-mobiveil.h"
- /*
- * mobiveil_pcie_sel_page - routine to access paged register
- *
- * Registers whose address greater than PAGED_ADDR_BNDRY (0xc00) are paged,
- * for this scheme to work extracted higher 6 bits of the offset will be
- * written to pg_sel field of PAB_CTRL register and rest of the lower 10
- * bits enabled with PAGED_ADDR_BNDRY are used as offset of the register.
- */
- static void mobiveil_pcie_sel_page(struct mobiveil_pcie *pcie, u8 pg_idx)
- {
- u32 val;
- val = readl(pcie->csr_axi_slave_base + PAB_CTRL);
- val &= ~(PAGE_SEL_MASK << PAGE_SEL_SHIFT);
- val |= (pg_idx & PAGE_SEL_MASK) << PAGE_SEL_SHIFT;
- writel(val, pcie->csr_axi_slave_base + PAB_CTRL);
- }
- static void __iomem *mobiveil_pcie_comp_addr(struct mobiveil_pcie *pcie,
- u32 off)
- {
- if (off < PAGED_ADDR_BNDRY) {
- /* For directly accessed registers, clear the pg_sel field */
- mobiveil_pcie_sel_page(pcie, 0);
- return pcie->csr_axi_slave_base + off;
- }
- mobiveil_pcie_sel_page(pcie, OFFSET_TO_PAGE_IDX(off));
- return pcie->csr_axi_slave_base + OFFSET_TO_PAGE_ADDR(off);
- }
- static int mobiveil_pcie_read(void __iomem *addr, int size, u32 *val)
- {
- if ((uintptr_t)addr & (size - 1)) {
- *val = 0;
- return PCIBIOS_BAD_REGISTER_NUMBER;
- }
- switch (size) {
- case 4:
- *val = readl(addr);
- break;
- case 2:
- *val = readw(addr);
- break;
- case 1:
- *val = readb(addr);
- break;
- default:
- *val = 0;
- return PCIBIOS_BAD_REGISTER_NUMBER;
- }
- return PCIBIOS_SUCCESSFUL;
- }
- static int mobiveil_pcie_write(void __iomem *addr, int size, u32 val)
- {
- if ((uintptr_t)addr & (size - 1))
- return PCIBIOS_BAD_REGISTER_NUMBER;
- switch (size) {
- case 4:
- writel(val, addr);
- break;
- case 2:
- writew(val, addr);
- break;
- case 1:
- writeb(val, addr);
- break;
- default:
- return PCIBIOS_BAD_REGISTER_NUMBER;
- }
- return PCIBIOS_SUCCESSFUL;
- }
- u32 mobiveil_csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size)
- {
- void __iomem *addr;
- u32 val;
- int ret;
- addr = mobiveil_pcie_comp_addr(pcie, off);
- ret = mobiveil_pcie_read(addr, size, &val);
- if (ret)
- dev_err(&pcie->pdev->dev, "read CSR address failed\n");
- return val;
- }
- void mobiveil_csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off,
- size_t size)
- {
- void __iomem *addr;
- int ret;
- addr = mobiveil_pcie_comp_addr(pcie, off);
- ret = mobiveil_pcie_write(addr, size, val);
- if (ret)
- dev_err(&pcie->pdev->dev, "write CSR address failed\n");
- }
- bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie)
- {
- if (pcie->ops->link_up)
- return pcie->ops->link_up(pcie);
- return (mobiveil_csr_readl(pcie, LTSSM_STATUS) &
- LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0;
- }
- void program_ib_windows(struct mobiveil_pcie *pcie, int win_num,
- u64 cpu_addr, u64 pci_addr, u32 type, u64 size)
- {
- u32 value;
- u64 size64 = ~(size - 1);
- if (win_num >= pcie->ppio_wins) {
- dev_err(&pcie->pdev->dev,
- "ERROR: max inbound windows reached !\n");
- return;
- }
- value = mobiveil_csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num));
- value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT | WIN_SIZE_MASK);
- value |= type << AMAP_CTRL_TYPE_SHIFT | 1 << AMAP_CTRL_EN_SHIFT |
- (lower_32_bits(size64) & WIN_SIZE_MASK);
- mobiveil_csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num));
- mobiveil_csr_writel(pcie, upper_32_bits(size64),
- PAB_EXT_PEX_AMAP_SIZEN(win_num));
- mobiveil_csr_writel(pcie, lower_32_bits(cpu_addr),
- PAB_PEX_AMAP_AXI_WIN(win_num));
- mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr),
- PAB_EXT_PEX_AMAP_AXI_WIN(win_num));
- mobiveil_csr_writel(pcie, lower_32_bits(pci_addr),
- PAB_PEX_AMAP_PEX_WIN_L(win_num));
- mobiveil_csr_writel(pcie, upper_32_bits(pci_addr),
- PAB_PEX_AMAP_PEX_WIN_H(win_num));
- pcie->ib_wins_configured++;
- }
- /*
- * routine to program the outbound windows
- */
- void program_ob_windows(struct mobiveil_pcie *pcie, int win_num,
- u64 cpu_addr, u64 pci_addr, u32 type, u64 size)
- {
- u32 value;
- u64 size64 = ~(size - 1);
- if (win_num >= pcie->apio_wins) {
- dev_err(&pcie->pdev->dev,
- "ERROR: max outbound windows reached !\n");
- return;
- }
- /*
- * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit
- * to 4 KB in PAB_AXI_AMAP_CTRL register
- */
- value = mobiveil_csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
- value &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT | WIN_SIZE_MASK);
- value |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT |
- (lower_32_bits(size64) & WIN_SIZE_MASK);
- mobiveil_csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num));
- mobiveil_csr_writel(pcie, upper_32_bits(size64),
- PAB_EXT_AXI_AMAP_SIZE(win_num));
- /*
- * program AXI window base with appropriate value in
- * PAB_AXI_AMAP_AXI_WIN0 register
- */
- mobiveil_csr_writel(pcie,
- lower_32_bits(cpu_addr) & (~AXI_WINDOW_ALIGN_MASK),
- PAB_AXI_AMAP_AXI_WIN(win_num));
- mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr),
- PAB_EXT_AXI_AMAP_AXI_WIN(win_num));
- mobiveil_csr_writel(pcie, lower_32_bits(pci_addr),
- PAB_AXI_AMAP_PEX_WIN_L(win_num));
- mobiveil_csr_writel(pcie, upper_32_bits(pci_addr),
- PAB_AXI_AMAP_PEX_WIN_H(win_num));
- pcie->ob_wins_configured++;
- }
- int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
- {
- int retries;
- /* check if the link is up or not */
- for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
- if (mobiveil_pcie_link_up(pcie))
- return 0;
- usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX);
- }
- dev_err(&pcie->pdev->dev, "link never came up\n");
- return -ETIMEDOUT;
- }
|