diff options
Diffstat (limited to 'drivers/ssb/driver_gige.c')
-rw-r--r-- | drivers/ssb/driver_gige.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/drivers/ssb/driver_gige.c b/drivers/ssb/driver_gige.c new file mode 100644 index 000000000000..172f90407b93 --- /dev/null +++ b/drivers/ssb/driver_gige.c | |||
@@ -0,0 +1,294 @@ | |||
1 | /* | ||
2 | * Sonics Silicon Backplane | ||
3 | * Broadcom Gigabit Ethernet core driver | ||
4 | * | ||
5 | * Copyright 2008, Broadcom Corporation | ||
6 | * Copyright 2008, Michael Buesch <mb@bu3sch.de> | ||
7 | * | ||
8 | * Licensed under the GNU/GPL. See COPYING for details. | ||
9 | */ | ||
10 | |||
11 | #include <linux/ssb/ssb.h> | ||
12 | #include <linux/ssb/ssb_driver_gige.h> | ||
13 | #include <linux/pci.h> | ||
14 | #include <linux/pci_regs.h> | ||
15 | |||
16 | |||
17 | /* | ||
18 | MODULE_DESCRIPTION("SSB Broadcom Gigabit Ethernet driver"); | ||
19 | MODULE_AUTHOR("Michael Buesch"); | ||
20 | MODULE_LICENSE("GPL"); | ||
21 | */ | ||
22 | |||
23 | static const struct ssb_device_id ssb_gige_tbl[] = { | ||
24 | SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET_GBIT, SSB_ANY_REV), | ||
25 | SSB_DEVTABLE_END | ||
26 | }; | ||
27 | /* MODULE_DEVICE_TABLE(ssb, ssb_gige_tbl); */ | ||
28 | |||
29 | |||
30 | static inline u8 gige_read8(struct ssb_gige *dev, u16 offset) | ||
31 | { | ||
32 | return ssb_read8(dev->dev, offset); | ||
33 | } | ||
34 | |||
35 | static inline u16 gige_read16(struct ssb_gige *dev, u16 offset) | ||
36 | { | ||
37 | return ssb_read16(dev->dev, offset); | ||
38 | } | ||
39 | |||
40 | static inline u32 gige_read32(struct ssb_gige *dev, u16 offset) | ||
41 | { | ||
42 | return ssb_read32(dev->dev, offset); | ||
43 | } | ||
44 | |||
45 | static inline void gige_write8(struct ssb_gige *dev, | ||
46 | u16 offset, u8 value) | ||
47 | { | ||
48 | ssb_write8(dev->dev, offset, value); | ||
49 | } | ||
50 | |||
51 | static inline void gige_write16(struct ssb_gige *dev, | ||
52 | u16 offset, u16 value) | ||
53 | { | ||
54 | ssb_write16(dev->dev, offset, value); | ||
55 | } | ||
56 | |||
57 | static inline void gige_write32(struct ssb_gige *dev, | ||
58 | u16 offset, u32 value) | ||
59 | { | ||
60 | ssb_write32(dev->dev, offset, value); | ||
61 | } | ||
62 | |||
63 | static inline | ||
64 | u8 gige_pcicfg_read8(struct ssb_gige *dev, unsigned int offset) | ||
65 | { | ||
66 | BUG_ON(offset >= 256); | ||
67 | return gige_read8(dev, SSB_GIGE_PCICFG + offset); | ||
68 | } | ||
69 | |||
70 | static inline | ||
71 | u16 gige_pcicfg_read16(struct ssb_gige *dev, unsigned int offset) | ||
72 | { | ||
73 | BUG_ON(offset >= 256); | ||
74 | return gige_read16(dev, SSB_GIGE_PCICFG + offset); | ||
75 | } | ||
76 | |||
77 | static inline | ||
78 | u32 gige_pcicfg_read32(struct ssb_gige *dev, unsigned int offset) | ||
79 | { | ||
80 | BUG_ON(offset >= 256); | ||
81 | return gige_read32(dev, SSB_GIGE_PCICFG + offset); | ||
82 | } | ||
83 | |||
84 | static inline | ||
85 | void gige_pcicfg_write8(struct ssb_gige *dev, | ||
86 | unsigned int offset, u8 value) | ||
87 | { | ||
88 | BUG_ON(offset >= 256); | ||
89 | gige_write8(dev, SSB_GIGE_PCICFG + offset, value); | ||
90 | } | ||
91 | |||
92 | static inline | ||
93 | void gige_pcicfg_write16(struct ssb_gige *dev, | ||
94 | unsigned int offset, u16 value) | ||
95 | { | ||
96 | BUG_ON(offset >= 256); | ||
97 | gige_write16(dev, SSB_GIGE_PCICFG + offset, value); | ||
98 | } | ||
99 | |||
100 | static inline | ||
101 | void gige_pcicfg_write32(struct ssb_gige *dev, | ||
102 | unsigned int offset, u32 value) | ||
103 | { | ||
104 | BUG_ON(offset >= 256); | ||
105 | gige_write32(dev, SSB_GIGE_PCICFG + offset, value); | ||
106 | } | ||
107 | |||
108 | static int ssb_gige_pci_read_config(struct pci_bus *bus, unsigned int devfn, | ||
109 | int reg, int size, u32 *val) | ||
110 | { | ||
111 | struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops); | ||
112 | unsigned long flags; | ||
113 | |||
114 | if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0)) | ||
115 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
116 | if (reg >= 256) | ||
117 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
118 | |||
119 | spin_lock_irqsave(&dev->lock, flags); | ||
120 | switch (size) { | ||
121 | case 1: | ||
122 | *val = gige_pcicfg_read8(dev, reg); | ||
123 | break; | ||
124 | case 2: | ||
125 | *val = gige_pcicfg_read16(dev, reg); | ||
126 | break; | ||
127 | case 4: | ||
128 | *val = gige_pcicfg_read32(dev, reg); | ||
129 | break; | ||
130 | default: | ||
131 | WARN_ON(1); | ||
132 | } | ||
133 | spin_unlock_irqrestore(&dev->lock, flags); | ||
134 | |||
135 | return PCIBIOS_SUCCESSFUL; | ||
136 | } | ||
137 | |||
138 | static int ssb_gige_pci_write_config(struct pci_bus *bus, unsigned int devfn, | ||
139 | int reg, int size, u32 val) | ||
140 | { | ||
141 | struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops); | ||
142 | unsigned long flags; | ||
143 | |||
144 | if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0)) | ||
145 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
146 | if (reg >= 256) | ||
147 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
148 | |||
149 | spin_lock_irqsave(&dev->lock, flags); | ||
150 | switch (size) { | ||
151 | case 1: | ||
152 | gige_pcicfg_write8(dev, reg, val); | ||
153 | break; | ||
154 | case 2: | ||
155 | gige_pcicfg_write16(dev, reg, val); | ||
156 | break; | ||
157 | case 4: | ||
158 | gige_pcicfg_write32(dev, reg, val); | ||
159 | break; | ||
160 | default: | ||
161 | WARN_ON(1); | ||
162 | } | ||
163 | spin_unlock_irqrestore(&dev->lock, flags); | ||
164 | |||
165 | return PCIBIOS_SUCCESSFUL; | ||
166 | } | ||
167 | |||
168 | static int ssb_gige_probe(struct ssb_device *sdev, const struct ssb_device_id *id) | ||
169 | { | ||
170 | struct ssb_gige *dev; | ||
171 | u32 base, tmslow, tmshigh; | ||
172 | |||
173 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
174 | if (!dev) | ||
175 | return -ENOMEM; | ||
176 | dev->dev = sdev; | ||
177 | |||
178 | spin_lock_init(&dev->lock); | ||
179 | dev->pci_controller.pci_ops = &dev->pci_ops; | ||
180 | dev->pci_controller.io_resource = &dev->io_resource; | ||
181 | dev->pci_controller.mem_resource = &dev->mem_resource; | ||
182 | dev->pci_controller.io_map_base = 0x800; | ||
183 | dev->pci_ops.read = ssb_gige_pci_read_config; | ||
184 | dev->pci_ops.write = ssb_gige_pci_write_config; | ||
185 | |||
186 | dev->io_resource.name = SSB_GIGE_IO_RES_NAME; | ||
187 | dev->io_resource.start = 0x800; | ||
188 | dev->io_resource.end = 0x8FF; | ||
189 | dev->io_resource.flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED; | ||
190 | |||
191 | if (!ssb_device_is_enabled(sdev)) | ||
192 | ssb_device_enable(sdev, 0); | ||
193 | |||
194 | /* Setup BAR0. This is a 64k MMIO region. */ | ||
195 | base = ssb_admatch_base(ssb_read32(sdev, SSB_ADMATCH1)); | ||
196 | gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_0, base); | ||
197 | gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_1, 0); | ||
198 | |||
199 | dev->mem_resource.name = SSB_GIGE_MEM_RES_NAME; | ||
200 | dev->mem_resource.start = base; | ||
201 | dev->mem_resource.end = base + 0x10000 - 1; | ||
202 | dev->mem_resource.flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED; | ||
203 | |||
204 | /* Enable the memory region. */ | ||
205 | gige_pcicfg_write16(dev, PCI_COMMAND, | ||
206 | gige_pcicfg_read16(dev, PCI_COMMAND) | ||
207 | | PCI_COMMAND_MEMORY); | ||
208 | |||
209 | /* Write flushing is controlled by the Flush Status Control register. | ||
210 | * We want to flush every register write with a timeout and we want | ||
211 | * to disable the IRQ mask while flushing to avoid concurrency. | ||
212 | * Note that automatic write flushing does _not_ work from | ||
213 | * an IRQ handler. The driver must flush manually by reading a register. | ||
214 | */ | ||
215 | gige_write32(dev, SSB_GIGE_SHIM_FLUSHSTAT, 0x00000068); | ||
216 | |||
217 | /* Check if we have an RGMII or GMII PHY-bus. | ||
218 | * On RGMII do not bypass the DLLs */ | ||
219 | tmslow = ssb_read32(sdev, SSB_TMSLOW); | ||
220 | tmshigh = ssb_read32(sdev, SSB_TMSHIGH); | ||
221 | if (tmshigh & SSB_GIGE_TMSHIGH_RGMII) { | ||
222 | tmslow &= ~SSB_GIGE_TMSLOW_TXBYPASS; | ||
223 | tmslow &= ~SSB_GIGE_TMSLOW_RXBYPASS; | ||
224 | dev->has_rgmii = 1; | ||
225 | } else { | ||
226 | tmslow |= SSB_GIGE_TMSLOW_TXBYPASS; | ||
227 | tmslow |= SSB_GIGE_TMSLOW_RXBYPASS; | ||
228 | dev->has_rgmii = 0; | ||
229 | } | ||
230 | tmslow |= SSB_GIGE_TMSLOW_DLLEN; | ||
231 | ssb_write32(sdev, SSB_TMSLOW, tmslow); | ||
232 | |||
233 | ssb_set_drvdata(sdev, dev); | ||
234 | register_pci_controller(&dev->pci_controller); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | bool pdev_is_ssb_gige_core(struct pci_dev *pdev) | ||
240 | { | ||
241 | if (!pdev->resource[0].name) | ||
242 | return 0; | ||
243 | return (strcmp(pdev->resource[0].name, SSB_GIGE_MEM_RES_NAME) == 0); | ||
244 | } | ||
245 | EXPORT_SYMBOL(pdev_is_ssb_gige_core); | ||
246 | |||
247 | int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev, | ||
248 | struct pci_dev *pdev) | ||
249 | { | ||
250 | struct ssb_gige *dev = ssb_get_drvdata(sdev); | ||
251 | struct resource *res; | ||
252 | |||
253 | if (pdev->bus->ops != &dev->pci_ops) { | ||
254 | /* The PCI device is not on this SSB GigE bridge device. */ | ||
255 | return -ENODEV; | ||
256 | } | ||
257 | |||
258 | /* Fixup the PCI resources. */ | ||
259 | res = &(pdev->resource[0]); | ||
260 | res->flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED; | ||
261 | res->name = dev->mem_resource.name; | ||
262 | res->start = dev->mem_resource.start; | ||
263 | res->end = dev->mem_resource.end; | ||
264 | |||
265 | /* Fixup interrupt lines. */ | ||
266 | pdev->irq = ssb_mips_irq(sdev) + 2; | ||
267 | pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, pdev->irq); | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | int ssb_gige_map_irq(struct ssb_device *sdev, | ||
273 | const struct pci_dev *pdev) | ||
274 | { | ||
275 | struct ssb_gige *dev = ssb_get_drvdata(sdev); | ||
276 | |||
277 | if (pdev->bus->ops != &dev->pci_ops) { | ||
278 | /* The PCI device is not on this SSB GigE bridge device. */ | ||
279 | return -ENODEV; | ||
280 | } | ||
281 | |||
282 | return ssb_mips_irq(sdev) + 2; | ||
283 | } | ||
284 | |||
285 | static struct ssb_driver ssb_gige_driver = { | ||
286 | .name = "BCM-GigE", | ||
287 | .id_table = ssb_gige_tbl, | ||
288 | .probe = ssb_gige_probe, | ||
289 | }; | ||
290 | |||
291 | int ssb_gige_init(void) | ||
292 | { | ||
293 | return ssb_driver_register(&ssb_gige_driver); | ||
294 | } | ||