summaryrefslogtreecommitdiff
path: root/src/drivers/usb/pci_ehci.c
blob: 34684cb09a9a9e13d080ed257497c9792b61a3a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2006 Eric Biederman (ebiederm@xmission.com)
 * Copyright (C) 2007 AMD
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <stddef.h>
#include <console/console.h>
#include <device/pci_ehci.h>
#include <device/mmio.h>
#include <device/pci_ops.h>
#include <device/pci.h>
#include <device/pci_def.h>
#include <string.h>

#include "ehci_debug.h"
#include "ehci.h"

#if ENV_RAMSTAGE
static struct device_operations *ehci_drv_ops;
static struct device_operations ehci_dbg_ops;
#endif

int ehci_debug_hw_enable(unsigned int *base, unsigned int *dbg_offset)
{
	pci_devfn_t dbg_dev = pci_ehci_dbg_dev(CONFIG_USBDEBUG_HCD_INDEX);

	/* We only support controllers on bus 0. */
	if (PCI_DEV2SEGBUS(dbg_dev) != 0)
		return -1;

#ifdef __SIMPLE_DEVICE__
	pci_devfn_t dev = dbg_dev;
#else
	struct device *dev = pcidev_path_on_root(PCI_DEV2DEVFN(dbg_dev));
#endif

	u32 class = pci_read_config32(dev, PCI_CLASS_REVISION) >> 8;
	if (class != PCI_EHCI_CLASSCODE)
		return -1;

	u8 pm_cap = pci_s_find_capability(dbg_dev, PCI_CAP_ID_PM);
	if (pm_cap) {
		u16 pm_ctrl = pci_read_config16(dev, pm_cap + PCI_PM_CTRL);
		/* Set to D0 and disable PM events. */
		pm_ctrl &= ~PCI_PM_CTRL_PME_ENABLE;
		pm_ctrl &= ~PCI_PM_CTRL_STATE_MASK;
		pci_write_config16(dev, pm_cap + PCI_PM_CTRL, pm_ctrl);
	}

	u8 pos = pci_s_find_capability(dbg_dev, PCI_CAP_ID_EHCI_DEBUG);
	if (!pos)
		return -1;

	u32 cap = pci_read_config32(dev, pos);

	/* FIXME: We should remove static EHCI_BAR_INDEX. */
	u8 ehci_bar = 0x10 + 4 * ((cap >> 29) - 1);
	if (ehci_bar != EHCI_BAR_INDEX)
		return -1;

	pci_write_config32(dev, ehci_bar, CONFIG_EHCI_BAR);

	pci_write_config8(dev, PCI_COMMAND, PCI_COMMAND_MEMORY |
		PCI_COMMAND_MASTER);

	*base = CONFIG_EHCI_BAR;
	*dbg_offset = (cap>>16) & 0x1ffc;

	return 0;
}

void ehci_debug_select_port(unsigned int port)
{
	pci_devfn_t dbg_dev = pci_ehci_dbg_dev(CONFIG_USBDEBUG_HCD_INDEX);
	pci_ehci_dbg_set_port(dbg_dev, port);
}

#if ENV_RAMSTAGE
static void pci_ehci_set_resources(struct device *dev)
{
	struct resource *res;

	printk(BIOS_DEBUG, "%s EHCI Debug Port hook triggered\n", dev_path(dev));
	usbdebug_disable();

	if (ehci_drv_ops->set_resources)
		ehci_drv_ops->set_resources(dev);
	res = find_resource(dev, EHCI_BAR_INDEX);
	if (!res)
		return;

	usbdebug_re_enable((u32)res->base);
	report_resource_stored(dev, res, "");
	printk(BIOS_DEBUG, "%s EHCI Debug Port relocated\n", dev_path(dev));
}

void pci_ehci_read_resources(struct device *dev)
{
	pci_devfn_t dbg_dev = pci_ehci_dbg_dev(CONFIG_USBDEBUG_HCD_INDEX);

	if (!ehci_drv_ops && pci_match_simple_dev(dev, dbg_dev)) {
		memcpy(&ehci_dbg_ops, dev->ops, sizeof(ehci_dbg_ops));
		ehci_drv_ops = dev->ops;
		ehci_dbg_ops.set_resources = pci_ehci_set_resources;
		dev->ops = &ehci_dbg_ops;
		printk(BIOS_DEBUG, "%s EHCI BAR hook registered\n", dev_path(dev));
	} else {
		printk(BIOS_DEBUG, "More than one caller of %s from %s\n", __func__, dev_path(dev));
	}

	pci_dev_read_resources(dev);
}
#endif

u8 *pci_ehci_base_regs(pci_devfn_t sdev)
{
#ifdef __SIMPLE_DEVICE__
	u8 *base = (u8 *)(pci_read_config32(sdev, EHCI_BAR_INDEX) & ~0x0f);
#else
	struct device *dev = pcidev_path_on_root(PCI_DEV2DEVFN(sdev));
	u8 *base = (u8 *)(pci_read_config32(dev, EHCI_BAR_INDEX) & ~0x0f);
#endif
	return base + HC_LENGTH(read32(base));
}