Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * PCIe host controller driver for Mobiveil PCIe Host controller
  4 *
  5 * Copyright (c) 2018 Mobiveil Inc.
  6 * Copyright 2019 NXP
  7 *
  8 * Author: Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>
  9 *	   Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
 10 */
 11
 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>
 17
 18#include "pcie-mobiveil.h"
 19
 20/*
 21 * mobiveil_pcie_sel_page - routine to access paged register
 22 *
 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.
 27 */
 28static void mobiveil_pcie_sel_page(struct mobiveil_pcie *pcie, u8 pg_idx)
 29{
 30	u32 val;
 31
 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;
 35
 36	writel(val, pcie->csr_axi_slave_base + PAB_CTRL);
 37}
 38
 39static void __iomem *mobiveil_pcie_comp_addr(struct mobiveil_pcie *pcie,
 40					     u32 off)
 41{
 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;
 46	}
 47
 48	mobiveil_pcie_sel_page(pcie, OFFSET_TO_PAGE_IDX(off));
 49	return pcie->csr_axi_slave_base + OFFSET_TO_PAGE_ADDR(off);
 50}
 51
 52static int mobiveil_pcie_read(void __iomem *addr, int size, u32 *val)
 53{
 54	if ((uintptr_t)addr & (size - 1)) {
 55		*val = 0;
 56		return PCIBIOS_BAD_REGISTER_NUMBER;
 57	}
 58
 59	switch (size) {
 60	case 4:
 61		*val = readl(addr);
 62		break;
 63	case 2:
 64		*val = readw(addr);
 65		break;
 66	case 1:
 67		*val = readb(addr);
 68		break;
 69	default:
 70		*val = 0;
 71		return PCIBIOS_BAD_REGISTER_NUMBER;
 72	}
 73
 74	return PCIBIOS_SUCCESSFUL;
 75}
 76
 77static int mobiveil_pcie_write(void __iomem *addr, int size, u32 val)
 78{
 79	if ((uintptr_t)addr & (size - 1))
 80		return PCIBIOS_BAD_REGISTER_NUMBER;
 81
 82	switch (size) {
 83	case 4:
 84		writel(val, addr);
 85		break;
 86	case 2:
 87		writew(val, addr);
 88		break;
 89	case 1:
 90		writeb(val, addr);
 91		break;
 92	default:
 93		return PCIBIOS_BAD_REGISTER_NUMBER;
 94	}
 95
 96	return PCIBIOS_SUCCESSFUL;
 97}
 98
 99u32 mobiveil_csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size)
100{
101	void __iomem *addr;
102	u32 val;
103	int ret;
104
105	addr = mobiveil_pcie_comp_addr(pcie, off);
106
107	ret = mobiveil_pcie_read(addr, size, &val);
108	if (ret)
109		dev_err(&pcie->pdev->dev, "read CSR address failed\n");
110
111	return val;
112}
113
114void mobiveil_csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off,
115			       size_t size)
116{
117	void __iomem *addr;
118	int ret;
119
120	addr = mobiveil_pcie_comp_addr(pcie, off);
121
122	ret = mobiveil_pcie_write(addr, size, val);
123	if (ret)
124		dev_err(&pcie->pdev->dev, "write CSR address failed\n");
125}
126
127bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie)
128{
129	if (pcie->ops->link_up)
130		return pcie->ops->link_up(pcie);
131
132	return (mobiveil_csr_readl(pcie, LTSSM_STATUS) &
133		LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0;
134}
135
136void program_ib_windows(struct mobiveil_pcie *pcie, int win_num,
137			u64 cpu_addr, u64 pci_addr, u32 type, u64 size)
138{
139	u32 value;
140	u64 size64 = ~(size - 1);
141
142	if (win_num >= pcie->ppio_wins) {
143		dev_err(&pcie->pdev->dev,
144			"ERROR: max inbound windows reached !\n");
145		return;
146	}
147
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));
153
154	mobiveil_csr_writel(pcie, upper_32_bits(size64),
155			    PAB_EXT_PEX_AMAP_SIZEN(win_num));
156
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));
161
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));
166
167	pcie->ib_wins_configured++;
168}
169
170/*
171 * routine to program the outbound windows
172 */
173void program_ob_windows(struct mobiveil_pcie *pcie, int win_num,
174			u64 cpu_addr, u64 pci_addr, u32 type, u64 size)
175{
176	u32 value;
177	u64 size64 = ~(size - 1);
178
179	if (win_num >= pcie->apio_wins) {
180		dev_err(&pcie->pdev->dev,
181			"ERROR: max outbound windows reached !\n");
182		return;
183	}
184
185	/*
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
188	 */
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));
194
195	mobiveil_csr_writel(pcie, upper_32_bits(size64),
196			    PAB_EXT_AXI_AMAP_SIZE(win_num));
197
198	/*
199	 * program AXI window base with appropriate value in
200	 * PAB_AXI_AMAP_AXI_WIN0 register
201	 */
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));
207
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));
212
213	pcie->ob_wins_configured++;
214}
215
216int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
217{
218	int retries;
219
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))
223			return 0;
224
225		usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX);
226	}
227
228	dev_err(&pcie->pdev->dev, "link never came up\n");
229
230	return -ETIMEDOUT;
231}