dwc3-pci.c 11.1 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2 3 4 5 6 7 8 9 10 11
/**
 * dwc3-pci.c - PCI Specific glue layer
 *
 * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
 *
 * Authors: Felipe Balbi <balbi@ti.com>,
 *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
 */

#include <linux/kernel.h>
12
#include <linux/module.h>
13 14
#include <linux/slab.h>
#include <linux/pci.h>
15
#include <linux/workqueue.h>
16
#include <linux/pm_runtime.h>
17
#include <linux/platform_device.h>
18
#include <linux/gpio/consumer.h>
19
#include <linux/gpio/machine.h>
20
#include <linux/acpi.h>
21
#include <linux/delay.h>
22

J
John Youn 已提交
23 24 25 26 27
#define PCI_DEVICE_ID_INTEL_BYT			0x0f37
#define PCI_DEVICE_ID_INTEL_MRFLD		0x119e
#define PCI_DEVICE_ID_INTEL_BSW			0x22b7
#define PCI_DEVICE_ID_INTEL_SPTLP		0x9d30
#define PCI_DEVICE_ID_INTEL_SPTH		0xa130
28
#define PCI_DEVICE_ID_INTEL_BXT			0x0aaa
29
#define PCI_DEVICE_ID_INTEL_BXT_M		0x1aaa
30
#define PCI_DEVICE_ID_INTEL_APL			0x5aaa
31
#define PCI_DEVICE_ID_INTEL_KBP			0xa2b0
32
#define PCI_DEVICE_ID_INTEL_GLK			0x31aa
33 34
#define PCI_DEVICE_ID_INTEL_CNPLP		0x9dee
#define PCI_DEVICE_ID_INTEL_CNPH		0xa36e
35
#define PCI_DEVICE_ID_INTEL_ICLLP		0x34ee
36

37
#define PCI_INTEL_BXT_DSM_GUID		"732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511"
38 39 40 41
#define PCI_INTEL_BXT_FUNC_PMU_PWR	4
#define PCI_INTEL_BXT_STATE_D0		0
#define PCI_INTEL_BXT_STATE_D3		3

42 43 44 45
#define GP_RWBAR			1
#define GP_RWREG1			0xa0
#define GP_RWREG1_ULPI_REFCLK_DISABLE	(1 << 17)

46 47 48 49
/**
 * struct dwc3_pci - Driver private structure
 * @dwc3: child dwc3 platform_device
 * @pci: our link to PCI bus
50
 * @guid: _DSM GUID
51
 * @has_dsm_for_pm: true for devices which need to run _DSM on runtime PM
52
 * @wakeup_work: work for asynchronous resume
53 54 55 56
 */
struct dwc3_pci {
	struct platform_device *dwc3;
	struct pci_dev *pci;
57

58
	guid_t guid;
59 60

	unsigned int has_dsm_for_pm:1;
61
	struct work_struct wakeup_work;
62 63
};

64 65 66 67 68 69 70 71 72
static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
static const struct acpi_gpio_params cs_gpios = { 1, 0, false };

static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = {
	{ "reset-gpios", &reset_gpios, 1 },
	{ "cs-gpios", &cs_gpios, 1 },
	{ },
};

73 74 75 76 77 78 79 80 81
static struct gpiod_lookup_table platform_bytcr_gpios = {
	.dev_id		= "0000:00:16.0",
	.table		= {
		GPIO_LOOKUP("INT33FC:00", 54, "reset", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("INT33FC:02", 14, "cs", GPIO_ACTIVE_HIGH),
		{}
	},
};

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
static int dwc3_byt_enable_ulpi_refclock(struct pci_dev *pci)
{
	void __iomem	*reg;
	u32		value;

	reg = pcim_iomap(pci, GP_RWBAR, 0);
	if (IS_ERR(reg))
		return PTR_ERR(reg);

	value = readl(reg + GP_RWREG1);
	if (!(value & GP_RWREG1_ULPI_REFCLK_DISABLE))
		goto unmap; /* ULPI refclk already enabled */

	value &= ~GP_RWREG1_ULPI_REFCLK_DISABLE;
	writel(value, reg + GP_RWREG1);
	/* This comes from the Intel Android x86 tree w/o any explanation */
	msleep(100);
unmap:
	pcim_iounmap(pci, reg);
	return 0;
}

104 105 106 107 108 109
static const struct property_entry dwc3_pci_intel_properties[] = {
	PROPERTY_ENTRY_STRING("dr_mode", "peripheral"),
	PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
	{}
};

110 111 112 113 114 115
static const struct property_entry dwc3_pci_mrfld_properties[] = {
	PROPERTY_ENTRY_STRING("dr_mode", "otg"),
	PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
	{}
};

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
static const struct property_entry dwc3_pci_amd_properties[] = {
	PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
	PROPERTY_ENTRY_U8("snps,lpm-nyet-threshold", 0xf),
	PROPERTY_ENTRY_BOOL("snps,u2exit_lfps_quirk"),
	PROPERTY_ENTRY_BOOL("snps,u2ss_inp3_quirk"),
	PROPERTY_ENTRY_BOOL("snps,req_p1p2p3_quirk"),
	PROPERTY_ENTRY_BOOL("snps,del_p1p2p3_quirk"),
	PROPERTY_ENTRY_BOOL("snps,del_phy_power_chg_quirk"),
	PROPERTY_ENTRY_BOOL("snps,lfps_filter_quirk"),
	PROPERTY_ENTRY_BOOL("snps,rx_detect_poll_quirk"),
	PROPERTY_ENTRY_BOOL("snps,tx_de_emphasis_quirk"),
	PROPERTY_ENTRY_U8("snps,tx_de_emphasis", 1),
	/* FIXME these quirks should be removed when AMD NL tapes out */
	PROPERTY_ENTRY_BOOL("snps,disable_scramble_quirk"),
	PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
	PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
	PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
	{}
};

136
static int dwc3_pci_quirks(struct dwc3_pci *dwc)
H
Heikki Krogerus 已提交
137
{
138 139
	struct pci_dev			*pdev = dwc->pci;

140
	if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
141 142
		if (pdev->device == PCI_DEVICE_ID_INTEL_BXT ||
				pdev->device == PCI_DEVICE_ID_INTEL_BXT_M) {
143
			guid_parse(PCI_INTEL_BXT_DSM_GUID, &dwc->guid);
144 145 146
			dwc->has_dsm_for_pm = true;
		}

147 148
		if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
			struct gpio_desc *gpio;
149
			int ret;
150

151 152 153 154 155
			/* On BYT the FW does not always enable the refclock */
			ret = dwc3_byt_enable_ulpi_refclock(pdev);
			if (ret)
				return ret;

156
			ret = devm_acpi_dev_add_driver_gpios(&pdev->dev,
157
					acpi_dwc3_byt_gpios);
158 159
			if (ret)
				dev_dbg(&pdev->dev, "failed to add mapping table\n");
160

161 162 163 164 165 166 167
			/*
			 * A lot of BYT devices lack ACPI resource entries for
			 * the GPIOs, add a fallback mapping to the reference
			 * design GPIOs which all boards seem to use.
			 */
			gpiod_add_lookup_table(&platform_bytcr_gpios);

168 169 170 171 172
			/*
			 * These GPIOs will turn on the USB2 PHY. Note that we have to
			 * put the gpio descriptors again here because the phy driver
			 * might want to grab them, too.
			 */
173 174
			gpio = devm_gpiod_get_optional(&pdev->dev, "cs",
						       GPIOD_OUT_LOW);
175 176 177
			if (IS_ERR(gpio))
				return PTR_ERR(gpio);

178
			gpiod_set_value_cansleep(gpio, 1);
179

180 181
			gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
						       GPIOD_OUT_LOW);
182 183 184 185 186 187 188
			if (IS_ERR(gpio))
				return PTR_ERR(gpio);

			if (gpio) {
				gpiod_set_value_cansleep(gpio, 1);
				usleep_range(10000, 11000);
			}
189 190 191
		}
	}

H
Heikki Krogerus 已提交
192 193
	return 0;
}
194

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
#ifdef CONFIG_PM
static void dwc3_pci_resume_work(struct work_struct *work)
{
	struct dwc3_pci *dwc = container_of(work, struct dwc3_pci, wakeup_work);
	struct platform_device *dwc3 = dwc->dwc3;
	int ret;

	ret = pm_runtime_get_sync(&dwc3->dev);
	if (ret)
		return;

	pm_runtime_mark_last_busy(&dwc3->dev);
	pm_runtime_put_sync_autosuspend(&dwc3->dev);
}
#endif

211
static int dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
212
{
213
	struct property_entry *p = (struct property_entry *)id->driver_data;
214
	struct dwc3_pci		*dwc;
215
	struct resource		res[2];
216
	int			ret;
C
Chanho Park 已提交
217
	struct device		*dev = &pci->dev;
218

219
	ret = pcim_enable_device(pci);
220
	if (ret) {
C
Chanho Park 已提交
221 222
		dev_err(dev, "failed to enable pci device\n");
		return -ENODEV;
223 224 225 226
	}

	pci_set_master(pci);

227 228 229 230 231 232
	dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
	if (!dwc)
		return -ENOMEM;

	dwc->dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
	if (!dwc->dwc3)
233
		return -ENOMEM;
234 235 236 237 238 239 240 241 242 243 244 245

	memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));

	res[0].start	= pci_resource_start(pci, 0);
	res[0].end	= pci_resource_end(pci, 0);
	res[0].name	= "dwc_usb3";
	res[0].flags	= IORESOURCE_MEM;

	res[1].start	= pci->irq;
	res[1].name	= "dwc_usb3";
	res[1].flags	= IORESOURCE_IRQ;

246
	ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res));
247
	if (ret) {
C
Chanho Park 已提交
248
		dev_err(dev, "couldn't add resources to dwc3 device\n");
249
		goto err;
250 251
	}

252 253 254
	dwc->pci = pci;
	dwc->dwc3->dev.parent = dev;
	ACPI_COMPANION_SET(&dwc->dwc3->dev, ACPI_COMPANION(dev));
255

256 257 258 259
	ret = platform_device_add_properties(dwc->dwc3, p);
	if (ret < 0)
		return ret;

260
	ret = dwc3_pci_quirks(dwc);
261 262 263
	if (ret)
		goto err;

264
	ret = platform_device_add(dwc->dwc3);
265
	if (ret) {
C
Chanho Park 已提交
266
		dev_err(dev, "failed to register dwc3 device\n");
H
Heikki Krogerus 已提交
267
		goto err;
268 269
	}

270
	device_init_wakeup(dev, true);
271
	pci_set_drvdata(pci, dwc);
272
	pm_runtime_put(dev);
273 274 275
#ifdef CONFIG_PM
	INIT_WORK(&dwc->wakeup_work, dwc3_pci_resume_work);
#endif
276

277
	return 0;
H
Heikki Krogerus 已提交
278
err:
279
	platform_device_put(dwc->dwc3);
280 281 282
	return ret;
}

B
Bill Pemberton 已提交
283
static void dwc3_pci_remove(struct pci_dev *pci)
284
{
285 286
	struct dwc3_pci		*dwc = pci_get_drvdata(pci);

287
	gpiod_remove_lookup_table(&platform_bytcr_gpios);
288 289 290
#ifdef CONFIG_PM
	cancel_work_sync(&dwc->wakeup_work);
#endif
291 292
	device_init_wakeup(&pci->dev, false);
	pm_runtime_get(&pci->dev);
293
	platform_device_unregister(dwc->dwc3);
294 295
}

296
static const struct pci_device_id dwc3_pci_id_table[] = {
297 298 299 300 301 302 303
	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BSW),
	  (kernel_ulong_t) &dwc3_pci_intel_properties },

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BYT),
	  (kernel_ulong_t) &dwc3_pci_intel_properties, },

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MRFLD),
304
	  (kernel_ulong_t) &dwc3_pci_mrfld_properties, },
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SPTLP),
	  (kernel_ulong_t) &dwc3_pci_intel_properties, },

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SPTH),
	  (kernel_ulong_t) &dwc3_pci_intel_properties, },

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BXT),
	  (kernel_ulong_t) &dwc3_pci_intel_properties, },

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BXT_M),
	  (kernel_ulong_t) &dwc3_pci_intel_properties, },

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_APL),
	  (kernel_ulong_t) &dwc3_pci_intel_properties, },

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_KBP),
	  (kernel_ulong_t) &dwc3_pci_intel_properties, },

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_GLK),
	  (kernel_ulong_t) &dwc3_pci_intel_properties, },

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPLP),
	  (kernel_ulong_t) &dwc3_pci_intel_properties, },

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPH),
	  (kernel_ulong_t) &dwc3_pci_intel_properties, },

	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICLLP),
	  (kernel_ulong_t) &dwc3_pci_intel_properties, },

	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_NL_USB),
	  (kernel_ulong_t) &dwc3_pci_amd_properties, },
338 339 340 341
	{  }	/* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);

F
Felipe Balbi 已提交
342 343 344 345 346 347 348 349 350 351 352 353 354
#if defined(CONFIG_PM) || defined(CONFIG_PM_SLEEP)
static int dwc3_pci_dsm(struct dwc3_pci *dwc, int param)
{
	union acpi_object *obj;
	union acpi_object tmp;
	union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);

	if (!dwc->has_dsm_for_pm)
		return 0;

	tmp.type = ACPI_TYPE_INTEGER;
	tmp.integer.value = param;

355
	obj = acpi_evaluate_dsm(ACPI_HANDLE(&dwc->pci->dev), &dwc->guid,
F
Felipe Balbi 已提交
356 357 358 359 360 361 362 363 364 365 366 367
			1, PCI_INTEL_BXT_FUNC_PMU_PWR, &argv4);
	if (!obj) {
		dev_err(&dwc->pci->dev, "failed to evaluate _DSM\n");
		return -EIO;
	}

	ACPI_FREE(obj);

	return 0;
}
#endif /* CONFIG_PM || CONFIG_PM_SLEEP */

368 369 370
#ifdef CONFIG_PM
static int dwc3_pci_runtime_suspend(struct device *dev)
{
371 372
	struct dwc3_pci		*dwc = dev_get_drvdata(dev);

373
	if (device_can_wakeup(dev))
374
		return dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D3);
375 376 377 378

	return -EBUSY;
}

379 380
static int dwc3_pci_runtime_resume(struct device *dev)
{
381
	struct dwc3_pci		*dwc = dev_get_drvdata(dev);
382 383 384 385 386
	int			ret;

	ret = dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D0);
	if (ret)
		return ret;
387

388 389 390
	queue_work(pm_wq, &dwc->wakeup_work);

	return 0;
391
}
392
#endif /* CONFIG_PM */
393

394
#ifdef CONFIG_PM_SLEEP
395
static int dwc3_pci_suspend(struct device *dev)
396
{
397 398 399 400 401 402 403 404 405 406
	struct dwc3_pci		*dwc = dev_get_drvdata(dev);

	return dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D3);
}

static int dwc3_pci_resume(struct device *dev)
{
	struct dwc3_pci		*dwc = dev_get_drvdata(dev);

	return dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D0);
407
}
408
#endif /* CONFIG_PM_SLEEP */
409

D
Doug Wilson 已提交
410
static const struct dev_pm_ops dwc3_pci_dev_pm_ops = {
411
	SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume)
412
	SET_RUNTIME_PM_OPS(dwc3_pci_runtime_suspend, dwc3_pci_runtime_resume,
413 414 415
		NULL)
};

416
static struct pci_driver dwc3_pci_driver = {
417
	.name		= "dwc3-pci",
418 419
	.id_table	= dwc3_pci_id_table,
	.probe		= dwc3_pci_probe,
B
Bill Pemberton 已提交
420
	.remove		= dwc3_pci_remove,
421 422 423
	.driver		= {
		.pm	= &dwc3_pci_dev_pm_ops,
	}
424 425 426
};

MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
F
Felipe Balbi 已提交
427
MODULE_LICENSE("GPL v2");
428 429
MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer");

G
Greg Kroah-Hartman 已提交
430
module_pci_driver(dwc3_pci_driver);