Root/drivers/bcma/driver_pci.c

1/*
2 * Broadcom specific AMBA
3 * PCI Core
4 *
5 * Copyright 2005, 2011, Broadcom Corporation
6 * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
7 * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de>
8 *
9 * Licensed under the GNU/GPL. See COPYING for details.
10 */
11
12#include "bcma_private.h"
13#include <linux/export.h>
14#include <linux/bcma/bcma.h>
15
16/**************************************************
17 * R/W ops.
18 **************************************************/
19
20u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address)
21{
22    pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
23    pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
24    return pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_DATA);
25}
26
27static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data)
28{
29    pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
30    pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
31    pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data);
32}
33
34static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy)
35{
36    u32 v;
37    int i;
38
39    v = BCMA_CORE_PCI_MDIODATA_START;
40    v |= BCMA_CORE_PCI_MDIODATA_WRITE;
41    v |= (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
42          BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
43    v |= (BCMA_CORE_PCI_MDIODATA_BLK_ADDR <<
44          BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
45    v |= BCMA_CORE_PCI_MDIODATA_TA;
46    v |= (phy << 4);
47    pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
48
49    udelay(10);
50    for (i = 0; i < 200; i++) {
51        v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
52        if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
53            break;
54        usleep_range(1000, 2000);
55    }
56}
57
58static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address)
59{
60    int max_retries = 10;
61    u16 ret = 0;
62    u32 v;
63    int i;
64
65    /* enable mdio access to SERDES */
66    v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
67    v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
68    pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
69
70    if (pc->core->id.rev >= 10) {
71        max_retries = 200;
72        bcma_pcie_mdio_set_phy(pc, device);
73        v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
74             BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
75        v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
76    } else {
77        v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
78        v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
79    }
80
81    v = BCMA_CORE_PCI_MDIODATA_START;
82    v |= BCMA_CORE_PCI_MDIODATA_READ;
83    v |= BCMA_CORE_PCI_MDIODATA_TA;
84
85    pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
86    /* Wait for the device to complete the transaction */
87    udelay(10);
88    for (i = 0; i < max_retries; i++) {
89        v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
90        if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) {
91            udelay(10);
92            ret = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_DATA);
93            break;
94        }
95        usleep_range(1000, 2000);
96    }
97    pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
98    return ret;
99}
100
101static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device,
102                u8 address, u16 data)
103{
104    int max_retries = 10;
105    u32 v;
106    int i;
107
108    /* enable mdio access to SERDES */
109    v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
110    v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
111    pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
112
113    if (pc->core->id.rev >= 10) {
114        max_retries = 200;
115        bcma_pcie_mdio_set_phy(pc, device);
116        v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
117             BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
118        v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
119    } else {
120        v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
121        v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
122    }
123
124    v = BCMA_CORE_PCI_MDIODATA_START;
125    v |= BCMA_CORE_PCI_MDIODATA_WRITE;
126    v |= BCMA_CORE_PCI_MDIODATA_TA;
127    v |= data;
128    pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
129    /* Wait for the device to complete the transaction */
130    udelay(10);
131    for (i = 0; i < max_retries; i++) {
132        v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
133        if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
134            break;
135        usleep_range(1000, 2000);
136    }
137    pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
138}
139
140/**************************************************
141 * Workarounds.
142 **************************************************/
143
144static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc)
145{
146    u32 tmp;
147
148    tmp = bcma_pcie_read(pc, BCMA_CORE_PCI_PLP_STATUSREG);
149    if (tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT)
150        return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE |
151               BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY;
152    else
153        return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE;
154}
155
156static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
157{
158    u16 tmp;
159
160    bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_RX,
161                         BCMA_CORE_PCI_SERDES_RX_CTRL,
162                 bcma_pcicore_polarity_workaround(pc));
163    tmp = bcma_pcie_mdio_read(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
164                              BCMA_CORE_PCI_SERDES_PLL_CTRL);
165    if (tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN)
166        bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
167                             BCMA_CORE_PCI_SERDES_PLL_CTRL,
168                             tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN);
169}
170
171static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
172{
173    struct bcma_device *core = pc->core;
174    u16 val16, core_index;
175    uint regoff;
176
177    regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
178    core_index = (u16)core->core_index;
179
180    val16 = pcicore_read16(pc, regoff);
181    if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
182         != core_index) {
183        val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
184            (val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
185        pcicore_write16(pc, regoff, val16);
186    }
187}
188
189/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
190/* Needs to happen when coming out of 'standby'/'hibernate' */
191static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
192{
193    u16 val16;
194    uint regoff;
195
196    regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_MISC_CONFIG);
197
198    val16 = pcicore_read16(pc, regoff);
199
200    if (!(val16 & BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST)) {
201        val16 |= BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST;
202        pcicore_write16(pc, regoff, val16);
203    }
204}
205
206/**************************************************
207 * Init.
208 **************************************************/
209
210static void 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
217void bcma_core_pci_init(struct bcma_drv_pci *pc)
218{
219    if (pc->setup_done)
220        return;
221
222#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
223    pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
224    if (pc->hostmode)
225        bcma_core_pci_hostmode_init(pc);
226#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
227
228    if (!pc->hostmode)
229        bcma_core_pci_clientmode_init(pc);
230}
231
232int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
233              bool enable)
234{
235    struct pci_dev *pdev;
236    u32 coremask, tmp;
237    int err = 0;
238
239    if (!pc || core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
240        /* This bcma device is not on a PCI host-bus. So the IRQs are
241         * not routed through the PCI core.
242         * So we must not enable routing through the PCI core. */
243        goto out;
244    }
245
246    pdev = pc->core->bus->host_pci;
247
248    err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
249    if (err)
250        goto out;
251
252    coremask = BIT(core->core_index) << 8;
253    if (enable)
254        tmp |= coremask;
255    else
256        tmp &= ~coremask;
257
258    err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
259
260out:
261    return err;
262}
263EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
264
265void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
266{
267    u32 w;
268
269    w = bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
270    if (extend)
271        w |= BCMA_CORE_PCI_ASPMTIMER_EXTEND;
272    else
273        w &= ~BCMA_CORE_PCI_ASPMTIMER_EXTEND;
274    bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w);
275    bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
276}
277EXPORT_SYMBOL_GPL(bcma_core_pci_extend_L1timer);
278

Archive Download this file



interactive