pcie-hisi.c 9.3 KB
Newer Older
1
/*
2
 * PCIe host controller driver for HiSilicon SoCs
3 4 5
 *
 * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com
 *
6 7 8
 * Authors: Zhou Wang <wangzhou1@hisilicon.com>
 *          Dacai Zhu <zhudacai@hisilicon.com>
 *          Gabriele Paoloni <gabriele.paoloni@huawei.com>
9 10 11 12 13 14
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/interrupt.h>
15
#include <linux/init.h>
16 17 18 19
#include <linux/mfd/syscon.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/platform_device.h>
20
#include <linux/of_device.h>
21 22 23
#include <linux/pci.h>
#include <linux/pci-acpi.h>
#include <linux/pci-ecam.h>
24
#include <linux/regmap.h>
25 26
#include "../pci.h"

27
#if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
28

29 30
static int hisi_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
			     int size, u32 *val)
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
{
	struct pci_config_window *cfg = bus->sysdata;
	int dev = PCI_SLOT(devfn);

	if (bus->number == cfg->busr.start) {
		/* access only one slot on each root port */
		if (dev > 0)
			return PCIBIOS_DEVICE_NOT_FOUND;
		else
			return pci_generic_config_read32(bus, devfn, where,
							 size, val);
	}

	return pci_generic_config_read(bus, devfn, where, size, val);
}

47 48
static int hisi_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
			     int where, int size, u32 val)
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
{
	struct pci_config_window *cfg = bus->sysdata;
	int dev = PCI_SLOT(devfn);

	if (bus->number == cfg->busr.start) {
		/* access only one slot on each root port */
		if (dev > 0)
			return PCIBIOS_DEVICE_NOT_FOUND;
		else
			return pci_generic_config_write32(bus, devfn, where,
							  size, val);
	}

	return pci_generic_config_write(bus, devfn, where, size, val);
}

static void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
				       int where)
{
	struct pci_config_window *cfg = bus->sysdata;
	void __iomem *reg_base = cfg->priv;

	if (bus->number == cfg->busr.start)
		return reg_base + where;
	else
		return pci_ecam_map_bus(bus, devfn, where);
}

77 78
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
static int hisi_pcie_init(struct pci_config_window *cfg)
{
	struct device *dev = cfg->parent;
	struct acpi_device *adev = to_acpi_device(dev);
	struct acpi_pci_root *root = acpi_driver_data(adev);
	struct resource *res;
	void __iomem *reg_base;
	int ret;

	/*
	 * Retrieve RC base and size from a HISI0081 device with _UID
	 * matching our segment.
	 */
	res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL);
	if (!res)
		return -ENOMEM;

	ret = acpi_get_rc_resources(dev, "HISI0081", root->segment, res);
	if (ret) {
		dev_err(dev, "can't get rc base address\n");
		return -ENOMEM;
	}

102
	reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res));
103 104 105 106 107 108 109 110 111 112 113 114
	if (!reg_base)
		return -ENOMEM;

	cfg->priv = reg_base;
	return 0;
}

struct pci_ecam_ops hisi_pcie_ops = {
	.bus_shift    = 20,
	.init         =  hisi_pcie_init,
	.pci_ops      = {
		.map_bus    = hisi_pcie_map_bus,
115 116
		.read       = hisi_pcie_rd_conf,
		.write      = hisi_pcie_wr_conf,
117 118 119 120 121 122
	}
};

#endif

#ifdef CONFIG_PCI_HISI
123 124 125

#include "pcie-designware.h"

126 127 128 129 130
#define PCIE_SUBCTRL_SYS_STATE4_REG		0x6818
#define PCIE_HIP06_CTRL_OFF			0x1000
#define PCIE_SYS_STATE4				(PCIE_HIP06_CTRL_OFF + 0x31c)
#define PCIE_LTSSM_LINKUP_STATE			0x11
#define PCIE_LTSSM_STATE_MASK			0x3F
131

132
#define to_hisi_pcie(x)	dev_get_drvdata((x)->dev)
133

134 135 136
struct hisi_pcie;

struct pcie_soc_ops {
137
	int (*hisi_pcie_link_up)(struct hisi_pcie *hisi_pcie);
138 139
};

140
struct hisi_pcie {
141
	struct dw_pcie *pci;
142 143
	struct regmap *subctrl;
	u32 port_id;
144
	const struct pcie_soc_ops *soc_ops;
145 146
};

147
/* HipXX PCIe host only supports 32-bit config access */
148 149 150 151 152 153
static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
			      u32 *val)
{
	u32 reg;
	u32 reg_val;
	void *walker = &reg_val;
154
	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
155 156 157

	walker += (where & 0x3);
	reg = where & ~0x3;
158
	reg_val = dw_pcie_readl_dbi(pci, reg);
159 160 161 162 163

	if (size == 1)
		*val = *(u8 __force *) walker;
	else if (size == 2)
		*val = *(u16 __force *) walker;
164 165 166
	else if (size == 4)
		*val = reg_val;
	else
167 168 169 170 171
		return PCIBIOS_BAD_REGISTER_NUMBER;

	return PCIBIOS_SUCCESSFUL;
}

172
/* HipXX PCIe host only supports 32-bit config access */
173 174 175 176 177 178
static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int  size,
				u32 val)
{
	u32 reg_val;
	u32 reg;
	void *walker = &reg_val;
179
	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
180 181 182 183

	walker += (where & 0x3);
	reg = where & ~0x3;
	if (size == 4)
184
		dw_pcie_writel_dbi(pci, reg, val);
185
	else if (size == 2) {
186
		reg_val = dw_pcie_readl_dbi(pci, reg);
187
		*(u16 __force *) walker = val;
188
		dw_pcie_writel_dbi(pci, reg, reg_val);
189
	} else if (size == 1) {
190
		reg_val = dw_pcie_readl_dbi(pci, reg);
191
		*(u8 __force *) walker = val;
192
		dw_pcie_writel_dbi(pci, reg, reg_val);
193 194 195 196 197 198
	} else
		return PCIBIOS_BAD_REGISTER_NUMBER;

	return PCIBIOS_SUCCESSFUL;
}

199
static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie)
200 201 202 203 204 205 206 207 208
{
	u32 val;

	regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG +
		    0x100 * hisi_pcie->port_id, &val);

	return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
}

209 210
static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie)
{
211
	struct dw_pcie *pci = hisi_pcie->pci;
212 213
	u32 val;

214
	val = dw_pcie_readl_dbi(pci, PCIE_SYS_STATE4);
215 216 217 218

	return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
}

219
static int hisi_pcie_link_up(struct dw_pcie *pci)
220
{
221
	struct hisi_pcie *hisi_pcie = to_hisi_pcie(pci);
222 223 224 225

	return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie);
}

226
static struct dw_pcie_host_ops hisi_pcie_host_ops = {
227 228 229 230
	.rd_own_conf = hisi_pcie_cfg_read,
	.wr_own_conf = hisi_pcie_cfg_write,
};

231 232
static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie,
			      struct platform_device *pdev)
233
{
234 235 236
	struct dw_pcie *pci = hisi_pcie->pci;
	struct pcie_port *pp = &pci->pp;
	struct device *dev = &pdev->dev;
237 238 239
	int ret;
	u32 port_id;

240 241
	if (of_property_read_u32(dev->of_node, "port-id", &port_id)) {
		dev_err(dev, "failed to read port-id\n");
242 243 244
		return -EINVAL;
	}
	if (port_id > 3) {
245
		dev_err(dev, "Invalid port-id: %d\n", port_id);
246 247 248 249 250 251 252 253
		return -EINVAL;
	}
	hisi_pcie->port_id = port_id;

	pp->ops = &hisi_pcie_host_ops;

	ret = dw_pcie_host_init(pp);
	if (ret) {
254
		dev_err(dev, "failed to initialize host\n");
255 256 257 258 259 260
		return ret;
	}

	return 0;
}

261 262 263 264
static const struct dw_pcie_ops dw_pcie_ops = {
	.link_up = hisi_pcie_link_up,
};

A
Arnd Bergmann 已提交
265
static int hisi_pcie_probe(struct platform_device *pdev)
266
{
267
	struct device *dev = &pdev->dev;
268
	struct dw_pcie *pci;
269 270
	struct hisi_pcie *hisi_pcie;
	struct resource *reg;
271
	struct device_driver *driver;
272 273
	int ret;

274
	hisi_pcie = devm_kzalloc(dev, sizeof(*hisi_pcie), GFP_KERNEL);
275 276 277
	if (!hisi_pcie)
		return -ENOMEM;

278 279 280 281 282 283 284
	pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
	if (!pci)
		return -ENOMEM;

	pci->dev = dev;
	pci->ops = &dw_pcie_ops;

285
	driver = dev->driver;
286

287 288
	hisi_pcie->pci = pci;

289
	hisi_pcie->soc_ops = of_device_get_match_data(dev);
290 291

	hisi_pcie->subctrl =
292
	    syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl");
293
	if (IS_ERR(hisi_pcie->subctrl)) {
294
		dev_err(dev, "cannot get subctrl base\n");
295 296 297 298
		return PTR_ERR(hisi_pcie->subctrl);
	}

	reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi");
299
	pci->dbi_base = devm_pci_remap_cfg_resource(dev, reg);
300 301
	if (IS_ERR(pci->dbi_base))
		return PTR_ERR(pci->dbi_base);
302 303
	platform_set_drvdata(pdev, hisi_pcie);

304
	ret = hisi_add_pcie_port(hisi_pcie, pdev);
305 306 307 308 309 310
	if (ret)
		return ret;

	return 0;
}

311 312 313 314 315 316 317 318
static struct pcie_soc_ops hip05_ops = {
		&hisi_pcie_link_up_hip05
};

static struct pcie_soc_ops hip06_ops = {
		&hisi_pcie_link_up_hip06
};

319
static const struct of_device_id hisi_pcie_of_match[] = {
320 321 322 323 324 325 326 327
	{
			.compatible = "hisilicon,hip05-pcie",
			.data	    = (void *) &hip05_ops,
	},
	{
			.compatible = "hisilicon,hip06-pcie",
			.data	    = (void *) &hip06_ops,
	},
328 329 330 331 332 333 334 335 336 337
	{},
};

static struct platform_driver hisi_pcie_driver = {
	.probe  = hisi_pcie_probe,
	.driver = {
		   .name = "hisi-pcie",
		   .of_match_table = hisi_pcie_of_match,
	},
};
338
builtin_platform_driver(hisi_pcie_driver);
339

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
static int hisi_pcie_almost_ecam_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct pci_ecam_ops *ops;

	ops = (struct pci_ecam_ops *)of_device_get_match_data(dev);
	return pci_host_common_probe(pdev, ops);
}

static int hisi_pcie_platform_init(struct pci_config_window *cfg)
{
	struct device *dev = cfg->parent;
	struct platform_device *pdev = to_platform_device(dev);
	struct resource *res;
	void __iomem *reg_base;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	if (!res) {
		dev_err(dev, "missing \"reg[1]\"property\n");
		return -EINVAL;
	}

362
	reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res));
363 364 365 366 367 368 369 370 371 372 373 374
	if (!reg_base)
		return -ENOMEM;

	cfg->priv = reg_base;
	return 0;
}

struct pci_ecam_ops hisi_pcie_platform_ops = {
	.bus_shift    = 20,
	.init         =  hisi_pcie_platform_init,
	.pci_ops      = {
		.map_bus    = hisi_pcie_map_bus,
375 376
		.read       = hisi_pcie_rd_conf,
		.write      = hisi_pcie_wr_conf,
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
	}
};

static const struct of_device_id hisi_pcie_almost_ecam_of_match[] = {
	{
		.compatible = "hisilicon,pcie-almost-ecam",
		.data	    = (void *) &hisi_pcie_platform_ops,
	},
	{},
};

static struct platform_driver hisi_pcie_almost_ecam_driver = {
	.probe  = hisi_pcie_almost_ecam_probe,
	.driver = {
		   .name = "hisi-pcie-almost-ecam",
		   .of_match_table = hisi_pcie_almost_ecam_of_match,
	},
};
builtin_platform_driver(hisi_pcie_almost_ecam_driver);

#endif
398
#endif