bcma: add PCIe host controller
[linux-3.10.git] / 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
20 u32 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
27 #if 0
28 static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data)
29 {
30         pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
31         pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
32         pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data);
33 }
34 #endif
35
36 static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy)
37 {
38         u32 v;
39         int i;
40
41         v = BCMA_CORE_PCI_MDIODATA_START;
42         v |= BCMA_CORE_PCI_MDIODATA_WRITE;
43         v |= (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
44               BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
45         v |= (BCMA_CORE_PCI_MDIODATA_BLK_ADDR <<
46               BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
47         v |= BCMA_CORE_PCI_MDIODATA_TA;
48         v |= (phy << 4);
49         pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
50
51         udelay(10);
52         for (i = 0; i < 200; i++) {
53                 v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
54                 if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
55                         break;
56                 msleep(1);
57         }
58 }
59
60 static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address)
61 {
62         int max_retries = 10;
63         u16 ret = 0;
64         u32 v;
65         int i;
66
67         /* enable mdio access to SERDES */
68         v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
69         v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
70         pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
71
72         if (pc->core->id.rev >= 10) {
73                 max_retries = 200;
74                 bcma_pcie_mdio_set_phy(pc, device);
75                 v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
76                      BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
77                 v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
78         } else {
79                 v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
80                 v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
81         }
82
83         v = BCMA_CORE_PCI_MDIODATA_START;
84         v |= BCMA_CORE_PCI_MDIODATA_READ;
85         v |= BCMA_CORE_PCI_MDIODATA_TA;
86
87         pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
88         /* Wait for the device to complete the transaction */
89         udelay(10);
90         for (i = 0; i < max_retries; i++) {
91                 v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
92                 if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) {
93                         udelay(10);
94                         ret = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_DATA);
95                         break;
96                 }
97                 msleep(1);
98         }
99         pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
100         return ret;
101 }
102
103 static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device,
104                                 u8 address, u16 data)
105 {
106         int max_retries = 10;
107         u32 v;
108         int i;
109
110         /* enable mdio access to SERDES */
111         v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
112         v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
113         pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
114
115         if (pc->core->id.rev >= 10) {
116                 max_retries = 200;
117                 bcma_pcie_mdio_set_phy(pc, device);
118                 v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
119                      BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
120                 v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
121         } else {
122                 v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
123                 v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
124         }
125
126         v = BCMA_CORE_PCI_MDIODATA_START;
127         v |= BCMA_CORE_PCI_MDIODATA_WRITE;
128         v |= BCMA_CORE_PCI_MDIODATA_TA;
129         v |= data;
130         pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
131         /* Wait for the device to complete the transaction */
132         udelay(10);
133         for (i = 0; i < max_retries; i++) {
134                 v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
135                 if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
136                         break;
137                 msleep(1);
138         }
139         pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
140 }
141
142 /**************************************************
143  * Workarounds.
144  **************************************************/
145
146 static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc)
147 {
148         u32 tmp;
149
150         tmp = bcma_pcie_read(pc, BCMA_CORE_PCI_PLP_STATUSREG);
151         if (tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT)
152                 return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE |
153                        BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY;
154         else
155                 return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE;
156 }
157
158 static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
159 {
160         u16 tmp;
161
162         bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_RX,
163                              BCMA_CORE_PCI_SERDES_RX_CTRL,
164                              bcma_pcicore_polarity_workaround(pc));
165         tmp = bcma_pcie_mdio_read(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
166                                   BCMA_CORE_PCI_SERDES_PLL_CTRL);
167         if (tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN)
168                 bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
169                                      BCMA_CORE_PCI_SERDES_PLL_CTRL,
170                                      tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN);
171 }
172
173 /**************************************************
174  * Init.
175  **************************************************/
176
177 static void __devinit bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
178 {
179         bcma_pcicore_serdes_workaround(pc);
180 }
181
182 void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc)
183 {
184         if (pc->setup_done)
185                 return;
186
187 #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
188         pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
189         if (pc->hostmode)
190                 bcma_core_pci_hostmode_init(pc);
191 #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
192
193         if (!pc->hostmode)
194                 bcma_core_pci_clientmode_init(pc);
195 }
196
197 int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
198                           bool enable)
199 {
200         struct pci_dev *pdev = pc->core->bus->host_pci;
201         u32 coremask, tmp;
202         int err = 0;
203
204         if (core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
205                 /* This bcma device is not on a PCI host-bus. So the IRQs are
206                  * not routed through the PCI core.
207                  * So we must not enable routing through the PCI core. */
208                 goto out;
209         }
210
211         err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
212         if (err)
213                 goto out;
214
215         coremask = BIT(core->core_index) << 8;
216         if (enable)
217                 tmp |= coremask;
218         else
219                 tmp &= ~coremask;
220
221         err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
222
223 out:
224         return err;
225 }
226 EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);