driver_pci.c 7.47 KB
Newer Older
1
2
3
4
/*
 * Broadcom specific AMBA
 * PCI Core
 *
5
 * Copyright 2005, 2011, Broadcom Corporation
Michael Büsch's avatar
Michael Büsch committed
6
 * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
7
 * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de>
8
9
10
11
12
 *
 * Licensed under the GNU/GPL. See COPYING for details.
 */

#include "bcma_private.h"
13
#include <linux/export.h>
14
15
16
17
18
19
#include <linux/bcma/bcma.h>

/**************************************************
 * R/W ops.
 **************************************************/

Hauke Mehrtens's avatar
Hauke Mehrtens committed
20
u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address)
21
{
22
23
24
	pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
	pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
	return pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_DATA);
25
26
27
28
}

static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data)
{
29
30
31
	pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
	pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
	pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data);
32
33
34
35
36
37
38
}

static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy)
{
	u32 v;
	int i;

39
40
41
42
43
44
45
	v = BCMA_CORE_PCI_MDIODATA_START;
	v |= BCMA_CORE_PCI_MDIODATA_WRITE;
	v |= (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
	      BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
	v |= (BCMA_CORE_PCI_MDIODATA_BLK_ADDR <<
	      BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
	v |= BCMA_CORE_PCI_MDIODATA_TA;
46
	v |= (phy << 4);
47
	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
48
49
50

	udelay(10);
	for (i = 0; i < 200; i++) {
51
52
		v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
		if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
53
54
55
56
57
58
59
60
61
62
63
64
			break;
		msleep(1);
	}
}

static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address)
{
	int max_retries = 10;
	u16 ret = 0;
	u32 v;
	int i;

65
66
67
68
	/* enable mdio access to SERDES */
	v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
	v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
69
70
71
72

	if (pc->core->id.rev >= 10) {
		max_retries = 200;
		bcma_pcie_mdio_set_phy(pc, device);
73
74
75
76
77
78
		v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
		     BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
		v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
	} else {
		v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
		v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
79
80
	}

81
82
83
84
85
	v = BCMA_CORE_PCI_MDIODATA_START;
	v |= BCMA_CORE_PCI_MDIODATA_READ;
	v |= BCMA_CORE_PCI_MDIODATA_TA;

	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
86
87
	/* Wait for the device to complete the transaction */
	udelay(10);
88
	for (i = 0; i < max_retries; i++) {
89
90
		v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
		if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) {
91
			udelay(10);
92
			ret = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_DATA);
93
94
95
96
			break;
		}
		msleep(1);
	}
97
	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
98
99
100
101
102
103
104
105
106
107
	return ret;
}

static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device,
				u8 address, u16 data)
{
	int max_retries = 10;
	u32 v;
	int i;

108
109
110
111
	/* enable mdio access to SERDES */
	v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
	v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
112
113
114
115

	if (pc->core->id.rev >= 10) {
		max_retries = 200;
		bcma_pcie_mdio_set_phy(pc, device);
116
117
118
119
120
121
		v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
		     BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
		v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
	} else {
		v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
		v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
122
123
	}

124
125
126
	v = BCMA_CORE_PCI_MDIODATA_START;
	v |= BCMA_CORE_PCI_MDIODATA_WRITE;
	v |= BCMA_CORE_PCI_MDIODATA_TA;
127
	v |= data;
128
	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
129
130
131
	/* Wait for the device to complete the transaction */
	udelay(10);
	for (i = 0; i < max_retries; i++) {
132
133
		v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
		if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
134
135
136
			break;
		msleep(1);
	}
137
	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
138
139
140
141
142
143
144
145
}

/**************************************************
 * Workarounds.
 **************************************************/

static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc)
{
146
147
148
149
150
151
152
153
	u32 tmp;

	tmp = bcma_pcie_read(pc, BCMA_CORE_PCI_PLP_STATUSREG);
	if (tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT)
		return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE |
		       BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY;
	else
		return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE;
154
155
156
157
158
159
}

static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
{
	u16 tmp;

160
161
162
163
164
165
166
167
168
	bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_RX,
	                     BCMA_CORE_PCI_SERDES_RX_CTRL,
			     bcma_pcicore_polarity_workaround(pc));
	tmp = bcma_pcie_mdio_read(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
	                          BCMA_CORE_PCI_SERDES_PLL_CTRL);
	if (tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN)
		bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
		                     BCMA_CORE_PCI_SERDES_PLL_CTRL,
		                     tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN);
169
170
}

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
{
	struct bcma_device *core = pc->core;
	u16 val16, core_index;
	uint regoff;

	regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
	core_index = (u16)core->core_index;

	val16 = pcicore_read16(pc, regoff);
	if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
	     != core_index) {
		val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
			(val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
		pcicore_write16(pc, regoff, val16);
	}
}

189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
/* Needs to happen when coming out of 'standby'/'hibernate' */
static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
{
	u16 val16;
	uint regoff;

	regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_MISC_CONFIG);

	val16 = pcicore_read16(pc, regoff);

	if (!(val16 & BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST)) {
		val16 |= BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST;
		pcicore_write16(pc, regoff, val16);
	}
}

206
207
208
209
/**************************************************
 * Init.
 **************************************************/

210
static void __devinit bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
211
{
212
	bcma_core_pci_fixcfg(pc);
213
	bcma_pcicore_serdes_workaround(pc);
214
	bcma_core_pci_config_fixup(pc);
215
}
216

217
void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc)
218
{
219
220
221
	if (pc->setup_done)
		return;

222
#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
223
224
	pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
	if (pc->hostmode)
225
226
		bcma_core_pci_hostmode_init(pc);
#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
227

228
229
	if (!pc->hostmode)
		bcma_core_pci_clientmode_init(pc);
230
231
}

232
233
234
235
236
int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
			  bool enable)
{
	struct pci_dev *pdev = pc->core->bus->host_pci;
	u32 coremask, tmp;
Hauke Mehrtens's avatar
Hauke Mehrtens committed
237
238
239
240
241
242
243
244
	int err = 0;

	if (core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
		/* This bcma device is not on a PCI host-bus. So the IRQs are
		 * not routed through the PCI core.
		 * So we must not enable routing through the PCI core. */
		goto out;
	}
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

	err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
	if (err)
		goto out;

	coremask = BIT(core->core_index) << 8;
	if (enable)
		tmp |= coremask;
	else
		tmp &= ~coremask;

	err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);

out:
	return err;
}
261
EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
262
263
264
265
266
267
268
269
270
271
272
273
274
275

void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
{
	u32 w;

	w = bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
	if (extend)
		w |= BCMA_CORE_PCI_ASPMTIMER_EXTEND;
	else
		w &= ~BCMA_CORE_PCI_ASPMTIMER_EXTEND;
	bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w);
	bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
}
EXPORT_SYMBOL_GPL(bcma_core_pci_extend_L1timer);