1 // SPDX-License-Identifier: GPL-2.0
3 * PCIe host controller driver for Mobiveil PCIe Host controller
5 * Copyright (c) 2018 Mobiveil Inc.
12 #include <linux/delay.h>
13 #include <linux/init.h>
14 #include <linux/kernel.h>
15 #include <linux/pci.h>
16 #include <linux/platform_device.h>
18 #include "pcie-mobiveil.h"
21 * mobiveil_pcie_sel_page - routine to access paged register
23 * Registers whose address greater than PAGED_ADDR_BNDRY (0xc00) are paged,
24 * for this scheme to work extracted higher 6 bits of the offset will be
25 * written to pg_sel field of PAB_CTRL register and rest of the lower 10
26 * bits enabled with PAGED_ADDR_BNDRY are used as offset of the register.
28 static void mobiveil_pcie_sel_page(struct mobiveil_pcie *pcie, u8 pg_idx)
32 val = readl(pcie->csr_axi_slave_base + PAB_CTRL);
33 val &= ~(PAGE_SEL_MASK << PAGE_SEL_SHIFT);
34 val |= (pg_idx & PAGE_SEL_MASK) << PAGE_SEL_SHIFT;
36 writel(val, pcie->csr_axi_slave_base + PAB_CTRL);
39 static void __iomem *mobiveil_pcie_comp_addr(struct mobiveil_pcie *pcie,
42 if (off < PAGED_ADDR_BNDRY) {
43 /* For directly accessed registers, clear the pg_sel field */
44 mobiveil_pcie_sel_page(pcie, 0);
45 return pcie->csr_axi_slave_base + off;
48 mobiveil_pcie_sel_page(pcie, OFFSET_TO_PAGE_IDX(off));
49 return pcie->csr_axi_slave_base + OFFSET_TO_PAGE_ADDR(off);
52 static int mobiveil_pcie_read(void __iomem *addr, int size, u32 *val)
54 if ((uintptr_t)addr & (size - 1)) {
56 return PCIBIOS_BAD_REGISTER_NUMBER;
71 return PCIBIOS_BAD_REGISTER_NUMBER;
74 return PCIBIOS_SUCCESSFUL;
77 static int mobiveil_pcie_write(void __iomem *addr, int size, u32 val)
79 if ((uintptr_t)addr & (size - 1))
80 return PCIBIOS_BAD_REGISTER_NUMBER;
93 return PCIBIOS_BAD_REGISTER_NUMBER;
96 return PCIBIOS_SUCCESSFUL;
99 u32 mobiveil_csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size)
105 addr = mobiveil_pcie_comp_addr(pcie, off);
107 ret = mobiveil_pcie_read(addr, size, &val);
109 dev_err(&pcie->pdev->dev, "read CSR address failed\n");
114 void mobiveil_csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off,
120 addr = mobiveil_pcie_comp_addr(pcie, off);
122 ret = mobiveil_pcie_write(addr, size, val);
124 dev_err(&pcie->pdev->dev, "write CSR address failed\n");
127 bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie)
129 if (pcie->ops->link_up)
130 return pcie->ops->link_up(pcie);
132 return (mobiveil_csr_readl(pcie, LTSSM_STATUS) &
133 LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0;
136 void program_ib_windows(struct mobiveil_pcie *pcie, int win_num,
137 u64 cpu_addr, u64 pci_addr, u32 type, u64 size)
140 u64 size64 = ~(size - 1);
142 if (win_num >= pcie->ppio_wins) {
143 dev_err(&pcie->pdev->dev,
144 "ERROR: max inbound windows reached !\n");
148 value = mobiveil_csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num));
149 value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT | WIN_SIZE_MASK);
150 value |= type << AMAP_CTRL_TYPE_SHIFT | 1 << AMAP_CTRL_EN_SHIFT |
151 (lower_32_bits(size64) & WIN_SIZE_MASK);
152 mobiveil_csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num));
154 mobiveil_csr_writel(pcie, upper_32_bits(size64),
155 PAB_EXT_PEX_AMAP_SIZEN(win_num));
157 mobiveil_csr_writel(pcie, lower_32_bits(cpu_addr),
158 PAB_PEX_AMAP_AXI_WIN(win_num));
159 mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr),
160 PAB_EXT_PEX_AMAP_AXI_WIN(win_num));
162 mobiveil_csr_writel(pcie, lower_32_bits(pci_addr),
163 PAB_PEX_AMAP_PEX_WIN_L(win_num));
164 mobiveil_csr_writel(pcie, upper_32_bits(pci_addr),
165 PAB_PEX_AMAP_PEX_WIN_H(win_num));
167 pcie->ib_wins_configured++;
171 * routine to program the outbound windows
173 void program_ob_windows(struct mobiveil_pcie *pcie, int win_num,
174 u64 cpu_addr, u64 pci_addr, u32 type, u64 size)
177 u64 size64 = ~(size - 1);
179 if (win_num >= pcie->apio_wins) {
180 dev_err(&pcie->pdev->dev,
181 "ERROR: max outbound windows reached !\n");
186 * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit
187 * to 4 KB in PAB_AXI_AMAP_CTRL register
189 value = mobiveil_csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
190 value &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT | WIN_SIZE_MASK);
191 value |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT |
192 (lower_32_bits(size64) & WIN_SIZE_MASK);
193 mobiveil_csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num));
195 mobiveil_csr_writel(pcie, upper_32_bits(size64),
196 PAB_EXT_AXI_AMAP_SIZE(win_num));
199 * program AXI window base with appropriate value in
200 * PAB_AXI_AMAP_AXI_WIN0 register
202 mobiveil_csr_writel(pcie,
203 lower_32_bits(cpu_addr) & (~AXI_WINDOW_ALIGN_MASK),
204 PAB_AXI_AMAP_AXI_WIN(win_num));
205 mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr),
206 PAB_EXT_AXI_AMAP_AXI_WIN(win_num));
208 mobiveil_csr_writel(pcie, lower_32_bits(pci_addr),
209 PAB_AXI_AMAP_PEX_WIN_L(win_num));
210 mobiveil_csr_writel(pcie, upper_32_bits(pci_addr),
211 PAB_AXI_AMAP_PEX_WIN_H(win_num));
213 pcie->ob_wins_configured++;
216 int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
220 /* check if the link is up or not */
221 for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
222 if (mobiveil_pcie_link_up(pcie))
225 usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX);
228 dev_err(&pcie->pdev->dev, "link never came up\n");