Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1/*
  2 * PCIe host controller driver for HiSilicon SoCs
  3 *
  4 * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com
  5 *
  6 * Authors: Zhou Wang <wangzhou1@hisilicon.com>
  7 *          Dacai Zhu <zhudacai@hisilicon.com>
  8 *          Gabriele Paoloni <gabriele.paoloni@huawei.com>
  9 *
 10 * This program is free software; you can redistribute it and/or modify
 11 * it under the terms of the GNU General Public License version 2 as
 12 * published by the Free Software Foundation.
 13 */
 14#include <linux/interrupt.h>
 15#include <linux/module.h>
 16#include <linux/mfd/syscon.h>
 17#include <linux/of_address.h>
 18#include <linux/of_pci.h>
 19#include <linux/platform_device.h>
 20#include <linux/of_device.h>
 21#include <linux/regmap.h>
 22
 23#include "pcie-designware.h"
 24
 25#define PCIE_LTSSM_LINKUP_STATE				0x11
 26#define PCIE_LTSSM_STATE_MASK				0x3F
 27#define PCIE_SUBCTRL_SYS_STATE4_REG			0x6818
 28#define PCIE_SYS_STATE4						0x31c
 29#define PCIE_HIP06_CTRL_OFF					0x1000
 30
 31#define to_hisi_pcie(x)	container_of(x, struct hisi_pcie, pp)
 32
 33struct hisi_pcie;
 34
 35struct pcie_soc_ops {
 36	int (*hisi_pcie_link_up)(struct hisi_pcie *pcie);
 37};
 38
 39struct hisi_pcie {
 40	struct regmap *subctrl;
 41	void __iomem *reg_base;
 42	u32 port_id;
 43	struct pcie_port pp;
 44	struct pcie_soc_ops *soc_ops;
 45};
 46
 47static inline void hisi_pcie_apb_writel(struct hisi_pcie *pcie,
 48					u32 val, u32 reg)
 49{
 50	writel(val, pcie->reg_base + reg);
 51}
 52
 53static inline u32 hisi_pcie_apb_readl(struct hisi_pcie *pcie, u32 reg)
 54{
 55	return readl(pcie->reg_base + reg);
 56}
 57
 58/* HipXX PCIe host only supports 32-bit config access */
 59static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
 60			      u32 *val)
 61{
 62	u32 reg;
 63	u32 reg_val;
 64	struct hisi_pcie *pcie = to_hisi_pcie(pp);
 65	void *walker = &reg_val;
 66
 67	walker += (where & 0x3);
 68	reg = where & ~0x3;
 69	reg_val = hisi_pcie_apb_readl(pcie, reg);
 70
 71	if (size == 1)
 72		*val = *(u8 __force *) walker;
 73	else if (size == 2)
 74		*val = *(u16 __force *) walker;
 75	else if (size == 4)
 76		*val = reg_val;
 77	else
 78		return PCIBIOS_BAD_REGISTER_NUMBER;
 79
 80	return PCIBIOS_SUCCESSFUL;
 81}
 82
 83/* HipXX PCIe host only supports 32-bit config access */
 84static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int  size,
 85				u32 val)
 86{
 87	u32 reg_val;
 88	u32 reg;
 89	struct hisi_pcie *pcie = to_hisi_pcie(pp);
 90	void *walker = &reg_val;
 91
 92	walker += (where & 0x3);
 93	reg = where & ~0x3;
 94	if (size == 4)
 95		hisi_pcie_apb_writel(pcie, val, reg);
 96	else if (size == 2) {
 97		reg_val = hisi_pcie_apb_readl(pcie, reg);
 98		*(u16 __force *) walker = val;
 99		hisi_pcie_apb_writel(pcie, reg_val, reg);
100	} else if (size == 1) {
101		reg_val = hisi_pcie_apb_readl(pcie, reg);
102		*(u8 __force *) walker = val;
103		hisi_pcie_apb_writel(pcie, reg_val, reg);
104	} else
105		return PCIBIOS_BAD_REGISTER_NUMBER;
106
107	return PCIBIOS_SUCCESSFUL;
108}
109
110static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie)
111{
112	u32 val;
113
114	regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG +
115		    0x100 * hisi_pcie->port_id, &val);
116
117	return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
118}
119
120static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie)
121{
122	u32 val;
123
124	val = hisi_pcie_apb_readl(hisi_pcie, PCIE_HIP06_CTRL_OFF +
125			PCIE_SYS_STATE4);
126
127	return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
128}
129
130static int hisi_pcie_link_up(struct pcie_port *pp)
131{
132	struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp);
133
134	return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie);
135}
136
137static struct pcie_host_ops hisi_pcie_host_ops = {
138	.rd_own_conf = hisi_pcie_cfg_read,
139	.wr_own_conf = hisi_pcie_cfg_write,
140	.link_up = hisi_pcie_link_up,
141};
142
143static int hisi_add_pcie_port(struct pcie_port *pp,
144				     struct platform_device *pdev)
145{
146	int ret;
147	u32 port_id;
148	struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp);
149
150	if (of_property_read_u32(pdev->dev.of_node, "port-id", &port_id)) {
151		dev_err(&pdev->dev, "failed to read port-id\n");
152		return -EINVAL;
153	}
154	if (port_id > 3) {
155		dev_err(&pdev->dev, "Invalid port-id: %d\n", port_id);
156		return -EINVAL;
157	}
158	hisi_pcie->port_id = port_id;
159
160	pp->ops = &hisi_pcie_host_ops;
161
162	ret = dw_pcie_host_init(pp);
163	if (ret) {
164		dev_err(&pdev->dev, "failed to initialize host\n");
165		return ret;
166	}
167
168	return 0;
169}
170
171static int hisi_pcie_probe(struct platform_device *pdev)
172{
173	struct hisi_pcie *hisi_pcie;
174	struct pcie_port *pp;
175	const struct of_device_id *match;
176	struct resource *reg;
177	struct device_driver *driver;
178	int ret;
179
180	hisi_pcie = devm_kzalloc(&pdev->dev, sizeof(*hisi_pcie), GFP_KERNEL);
181	if (!hisi_pcie)
182		return -ENOMEM;
183
184	pp = &hisi_pcie->pp;
185	pp->dev = &pdev->dev;
186	driver = (pdev->dev).driver;
187
188	match = of_match_device(driver->of_match_table, &pdev->dev);
189	hisi_pcie->soc_ops = (struct pcie_soc_ops *) match->data;
190
191	hisi_pcie->subctrl =
192	syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl");
193	if (IS_ERR(hisi_pcie->subctrl)) {
194		dev_err(pp->dev, "cannot get subctrl base\n");
195		return PTR_ERR(hisi_pcie->subctrl);
196	}
197
198	reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi");
199	hisi_pcie->reg_base = devm_ioremap_resource(&pdev->dev, reg);
200	if (IS_ERR(hisi_pcie->reg_base)) {
201		dev_err(pp->dev, "cannot get rc_dbi base\n");
202		return PTR_ERR(hisi_pcie->reg_base);
203	}
204
205	hisi_pcie->pp.dbi_base = hisi_pcie->reg_base;
206
207	ret = hisi_add_pcie_port(pp, pdev);
208	if (ret)
209		return ret;
210
211	platform_set_drvdata(pdev, hisi_pcie);
212
213	dev_warn(pp->dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
214
215	return 0;
216}
217
218static struct pcie_soc_ops hip05_ops = {
219		&hisi_pcie_link_up_hip05
220};
221
222static struct pcie_soc_ops hip06_ops = {
223		&hisi_pcie_link_up_hip06
224};
225
226static const struct of_device_id hisi_pcie_of_match[] = {
227	{
228			.compatible = "hisilicon,hip05-pcie",
229			.data	    = (void *) &hip05_ops,
230	},
231	{
232			.compatible = "hisilicon,hip06-pcie",
233			.data	    = (void *) &hip06_ops,
234	},
235	{},
236};
237
238
239MODULE_DEVICE_TABLE(of, hisi_pcie_of_match);
240
241static struct platform_driver hisi_pcie_driver = {
242	.probe  = hisi_pcie_probe,
243	.driver = {
244		   .name = "hisi-pcie",
245		   .of_match_table = hisi_pcie_of_match,
246	},
247};
248
249module_platform_driver(hisi_pcie_driver);
250
251MODULE_AUTHOR("Zhou Wang <wangzhou1@hisilicon.com>");
252MODULE_AUTHOR("Dacai Zhu <zhudacai@hisilicon.com>");
253MODULE_AUTHOR("Gabriele Paoloni <gabriele.paoloni@huawei.com>");
254MODULE_LICENSE("GPL v2");