Commit | Line | Data |
---|---|---|
50ee11fe BB |
1 | /* Copyright (C) 2007 One Stop Systems |
2 | * Copyright (C) 2003-2005 SBE, Inc. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | */ | |
14 | ||
e6e4d05d JP |
15 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
16 | ||
50ee11fe BB |
17 | #include <linux/netdevice.h> |
18 | #include <linux/hdlc.h> | |
19 | #include <linux/if_arp.h> | |
20 | #include <asm/uaccess.h> | |
21 | #include <linux/rtnetlink.h> | |
22 | #include <linux/pci.h> | |
23 | #include "pmcc4_sysdep.h" | |
24 | #include "sbecom_inline_linux.h" | |
25 | #include "libsbew.h" | |
26 | #include "pmcc4_private.h" | |
27 | #include "pmcc4.h" | |
28 | #include "pmcc4_ioctls.h" | |
29 | #include "pmc93x6_eeprom.h" | |
30 | #ifdef CONFIG_PROC_FS | |
31 | #include "sbeproc.h" | |
32 | #endif | |
33 | ||
b8b73994 | 34 | extern int cxt1e1_log_level; |
50ee11fe BB |
35 | extern int error_flag; |
36 | extern int drvr_state; | |
37 | ||
38 | /* forward references */ | |
39 | void c4_stopwd (ci_t *); | |
40 | struct net_device * __init c4_add_dev (hdw_info_t *, int, unsigned long, unsigned long, int, int); | |
41 | ||
42 | ||
43 | struct s_hdw_info hdw_info[MAX_BOARDS]; | |
44 | ||
45 | ||
46 | void __init | |
ee1803cf | 47 | show_two (hdw_info_t *hi, int brdno) |
50ee11fe BB |
48 | { |
49 | ci_t *ci; | |
50 | struct pci_dev *pdev; | |
51 | char *bid; | |
52 | char *bp, banner[80]; | |
53 | char sn[6]; | |
54 | ||
55 | bp = banner; | |
56 | memset (banner, 0, 80); /* clear print buffer */ | |
57 | ||
58 | ci = (ci_t *)(netdev_priv(hi->ndev)); | |
59 | bid = sbeid_get_bdname (ci); | |
60 | switch (hi->promfmt) | |
61 | { | |
62 | case PROM_FORMAT_TYPE1: | |
63 | memcpy (sn, (FLD_TYPE1 *) (hi->mfg_info.pft1.Serial), 6); | |
64 | break; | |
65 | case PROM_FORMAT_TYPE2: | |
66 | memcpy (sn, (FLD_TYPE2 *) (hi->mfg_info.pft2.Serial), 6); | |
67 | break; | |
68 | default: | |
69 | memset (sn, 0, 6); | |
70 | break; | |
71 | } | |
72 | ||
73 | sprintf (banner, "%s: %s S/N %06X, MUSYCC Rev %02X", | |
74 | hi->devname, bid, | |
75 | ((sn[3] << 16) & 0xff0000) | | |
76 | ((sn[4] << 8) & 0x00ff00) | | |
77 | (sn[5] & 0x0000ff), | |
78 | (u_int8_t) hi->revid[0]); | |
79 | ||
694a9807 | 80 | pr_info("%s\n", banner); |
50ee11fe BB |
81 | |
82 | pdev = hi->pdev[0]; | |
694a9807 | 83 | pr_info("%s: %s at v/p=%lx/%lx (%02x:%02x.%x) irq %d\n", |
50ee11fe BB |
84 | hi->devname, "MUSYCC", |
85 | (unsigned long) hi->addr_mapped[0], hi->addr[0], | |
86 | hi->pci_busno, (u_int8_t) PCI_SLOT (pdev->devfn), | |
87 | (u_int8_t) PCI_FUNC (pdev->devfn), pdev->irq); | |
88 | ||
89 | pdev = hi->pdev[1]; | |
694a9807 | 90 | pr_info("%s: %s at v/p=%lx/%lx (%02x:%02x.%x) irq %d\n", |
50ee11fe BB |
91 | hi->devname, "EBUS ", |
92 | (unsigned long) hi->addr_mapped[1], hi->addr[1], | |
93 | hi->pci_busno, (u_int8_t) PCI_SLOT (pdev->devfn), | |
94 | (u_int8_t) PCI_FUNC (pdev->devfn), pdev->irq); | |
95 | } | |
96 | ||
97 | ||
98 | void __init | |
ee1803cf | 99 | hdw_sn_get (hdw_info_t *hi, int brdno) |
50ee11fe BB |
100 | { |
101 | /* obtain hardware EEPROM information */ | |
102 | long addr; | |
103 | ||
104 | addr = (long) hi->addr_mapped[1] + EEPROM_OFFSET; | |
105 | ||
106 | /* read EEPROM with largest known format size... */ | |
107 | pmc_eeprom_read_buffer (addr, 0, (char *) hi->mfg_info.data, sizeof (FLD_TYPE2)); | |
108 | ||
109 | #if 0 | |
110 | { | |
111 | unsigned char *ucp = (unsigned char *) &hi->mfg_info.data; | |
112 | ||
694a9807 | 113 | pr_info("eeprom[00]: %02x %02x %02x %02x %02x %02x %02x %02x\n", |
50ee11fe | 114 | *(ucp + 0), *(ucp + 1), *(ucp + 2), *(ucp + 3), *(ucp + 4), *(ucp + 5), *(ucp + 6), *(ucp + 7)); |
694a9807 | 115 | pr_info("eeprom[08]: %02x %02x %02x %02x %02x %02x %02x %02x\n", |
50ee11fe | 116 | *(ucp + 8), *(ucp + 9), *(ucp + 10), *(ucp + 11), *(ucp + 12), *(ucp + 13), *(ucp + 14), *(ucp + 15)); |
694a9807 | 117 | pr_info("eeprom[16]: %02x %02x %02x %02x %02x %02x %02x %02x\n", |
50ee11fe | 118 | *(ucp + 16), *(ucp + 17), *(ucp + 18), *(ucp + 19), *(ucp + 20), *(ucp + 21), *(ucp + 22), *(ucp + 23)); |
694a9807 | 119 | pr_info("eeprom[24]: %02x %02x %02x %02x %02x %02x %02x %02x\n", |
50ee11fe | 120 | *(ucp + 24), *(ucp + 25), *(ucp + 26), *(ucp + 27), *(ucp + 28), *(ucp + 29), *(ucp + 30), *(ucp + 31)); |
694a9807 | 121 | pr_info("eeprom[32]: %02x %02x %02x %02x %02x %02x %02x %02x\n", |
50ee11fe | 122 | *(ucp + 32), *(ucp + 33), *(ucp + 34), *(ucp + 35), *(ucp + 36), *(ucp + 37), *(ucp + 38), *(ucp + 39)); |
694a9807 | 123 | pr_info("eeprom[40]: %02x %02x %02x %02x %02x %02x %02x %02x\n", |
50ee11fe BB |
124 | *(ucp + 40), *(ucp + 41), *(ucp + 42), *(ucp + 43), *(ucp + 44), *(ucp + 45), *(ucp + 46), *(ucp + 47)); |
125 | } | |
126 | #endif | |
127 | #if 0 | |
694a9807 | 128 | pr_info("sn: %x %x %x %x %x %x\n", |
50ee11fe BB |
129 | hi->mfg_info.Serial[0], |
130 | hi->mfg_info.Serial[1], | |
131 | hi->mfg_info.Serial[2], | |
132 | hi->mfg_info.Serial[3], | |
133 | hi->mfg_info.Serial[4], | |
134 | hi->mfg_info.Serial[5]); | |
135 | #endif | |
136 | ||
137 | if ((hi->promfmt = pmc_verify_cksum (&hi->mfg_info.data)) == PROM_FORMAT_Unk) | |
138 | { | |
139 | /* bad crc, data is suspect */ | |
b8b73994 | 140 | if (cxt1e1_log_level >= LOG_WARN) |
694a9807 | 141 | pr_info("%s: EEPROM cksum error\n", hi->devname); |
50ee11fe BB |
142 | hi->mfg_info_sts = EEPROM_CRCERR; |
143 | } else | |
144 | hi->mfg_info_sts = EEPROM_OK; | |
145 | } | |
146 | ||
147 | ||
148 | void __init | |
149 | prep_hdw_info (void) | |
150 | { | |
151 | hdw_info_t *hi; | |
152 | int i; | |
153 | ||
154 | for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) | |
155 | { | |
156 | hi->pci_busno = 0xff; | |
157 | hi->pci_slot = 0xff; | |
158 | hi->pci_pin[0] = 0; | |
159 | hi->pci_pin[1] = 0; | |
bb0a9747 | 160 | hi->ndev = NULL; |
50ee11fe BB |
161 | hi->addr[0] = 0L; |
162 | hi->addr[1] = 0L; | |
163 | hi->addr_mapped[0] = 0L; | |
164 | hi->addr_mapped[1] = 0L; | |
165 | } | |
166 | } | |
167 | ||
168 | void | |
169 | cleanup_ioremap (void) | |
170 | { | |
171 | hdw_info_t *hi; | |
172 | int i; | |
173 | ||
174 | for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) | |
175 | { | |
176 | if (hi->pci_slot == 0xff) | |
177 | break; | |
178 | if (hi->addr_mapped[0]) | |
179 | { | |
180 | iounmap ((void *) (hi->addr_mapped[0])); | |
181 | release_mem_region ((long) hi->addr[0], hi->len[0]); | |
182 | hi->addr_mapped[0] = 0; | |
183 | } | |
184 | if (hi->addr_mapped[1]) | |
185 | { | |
186 | iounmap ((void *) (hi->addr_mapped[1])); | |
187 | release_mem_region ((long) hi->addr[1], hi->len[1]); | |
188 | hi->addr_mapped[1] = 0; | |
189 | } | |
190 | } | |
191 | } | |
192 | ||
193 | ||
194 | void | |
195 | cleanup_devs (void) | |
196 | { | |
197 | hdw_info_t *hi; | |
198 | int i; | |
199 | ||
200 | for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) | |
201 | { | |
202 | if (hi->pci_slot == 0xff || !hi->ndev) | |
203 | break; | |
204 | c4_stopwd(netdev_priv(hi->ndev)); | |
205 | #ifdef CONFIG_PROC_FS | |
206 | sbecom_proc_brd_cleanup(netdev_priv(hi->ndev)); | |
207 | #endif | |
208 | unregister_netdev (hi->ndev); | |
209 | free_irq (hi->pdev[0]->irq, hi->ndev); | |
210 | #ifdef CONFIG_SBE_PMCC4_NCOMM | |
211 | free_irq (hi->pdev[1]->irq, hi->ndev); | |
212 | #endif | |
213 | OS_kfree (hi->ndev); | |
214 | } | |
215 | } | |
216 | ||
217 | ||
37ca35c4 | 218 | static int __init |
ee1803cf | 219 | c4_hdw_init (struct pci_dev *pdev, int found) |
50ee11fe BB |
220 | { |
221 | hdw_info_t *hi; | |
222 | int i; | |
223 | int fun, slot; | |
224 | unsigned char busno = 0xff; | |
225 | ||
226 | /* our MUSYCC chip supports two functions, 0 & 1 */ | |
227 | if ((fun = PCI_FUNC (pdev->devfn)) > 1) | |
228 | { | |
e6e4d05d | 229 | pr_warning("unexpected devfun: 0x%x\n", pdev->devfn); |
50ee11fe BB |
230 | return 0; |
231 | } | |
232 | if (pdev->bus) /* obtain bus number */ | |
233 | busno = pdev->bus->number; | |
234 | else | |
235 | busno = 0; /* default for system PCI inconsistency */ | |
236 | slot = pdev->devfn & ~0x07; | |
237 | ||
238 | /* | |
239 | * Functions 0 & 1 for a given board (identified by same bus(busno) and | |
240 | * slot(slot)) are placed into the same 'hardware' structure. The first | |
241 | * part of the board's functionality will be placed into an unpopulated | |
242 | * element, identified by "slot==(0xff)". The second part of a board's | |
243 | * functionality will match the previously loaded slot/busno. | |
244 | */ | |
245 | for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) | |
246 | { | |
247 | /* | |
248 | * match with board's first found interface, otherwise this is first | |
249 | * found | |
250 | */ | |
251 | if ((hi->pci_slot == 0xff) || /* new board */ | |
252 | ((hi->pci_slot == slot) && (hi->bus == pdev->bus))) | |
253 | break; /* found for-loop exit */ | |
254 | } | |
255 | if (i == MAX_BOARDS) /* no match in above loop means MAX | |
256 | * exceeded */ | |
257 | { | |
e6e4d05d | 258 | pr_warning("exceeded number of allowed devices (>%d)?\n", MAX_BOARDS); |
50ee11fe BB |
259 | return 0; |
260 | } | |
261 | if (pdev->bus) | |
262 | hi->pci_busno = pdev->bus->number; | |
263 | else | |
264 | hi->pci_busno = 0; /* default for system PCI inconsistency */ | |
265 | hi->pci_slot = slot; | |
266 | pci_read_config_byte (pdev, PCI_INTERRUPT_PIN, &hi->pci_pin[fun]); | |
267 | pci_read_config_byte (pdev, PCI_REVISION_ID, &hi->revid[fun]); | |
268 | hi->bus = pdev->bus; | |
269 | hi->addr[fun] = pci_resource_start (pdev, 0); | |
270 | hi->len[fun] = pci_resource_end (pdev, 0) - hi->addr[fun] + 1; | |
271 | hi->pdev[fun] = pdev; | |
272 | ||
273 | { | |
274 | /* | |
275 | * create device name from module name, plus add the appropriate | |
276 | * board number | |
277 | */ | |
278 | char *cp = hi->devname; | |
279 | ||
40447df8 | 280 | strcpy (cp, KBUILD_MODNAME); |
50ee11fe BB |
281 | cp += strlen (cp); /* reposition */ |
282 | *cp++ = '-'; | |
283 | *cp++ = '0' + (found / 2); /* there are two found interfaces per | |
284 | * board */ | |
285 | *cp = 0; /* termination */ | |
286 | } | |
287 | ||
288 | return 1; | |
289 | } | |
290 | ||
291 | ||
292 | status_t __init | |
293 | c4hw_attach_all (void) | |
294 | { | |
295 | hdw_info_t *hi; | |
296 | struct pci_dev *pdev = NULL; | |
297 | int found = 0, i, j; | |
298 | ||
299 | error_flag = 0; | |
300 | prep_hdw_info (); | |
301 | /*** scan PCI bus for all possible boards */ | |
50ee11fe | 302 | while ((pdev = pci_get_device (PCI_VENDOR_ID_CONEXANT, |
50ee11fe BB |
303 | PCI_DEVICE_ID_CN8474, |
304 | pdev))) | |
50ee11fe BB |
305 | { |
306 | if (c4_hdw_init (pdev, found)) | |
307 | found++; | |
308 | } | |
309 | if (!found) | |
310 | { | |
e6e4d05d | 311 | pr_warning("No boards found\n"); |
8654cda0 | 312 | return -ENODEV; |
50ee11fe | 313 | } |
9e36261d | 314 | /* sanity check for consistent hardware found */ |
50ee11fe BB |
315 | for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) |
316 | { | |
317 | if (hi->pci_slot != 0xff && (!hi->addr[0] || !hi->addr[1])) | |
318 | { | |
e6e4d05d JP |
319 | pr_warning("%s: something very wrong with pci_get_device\n", |
320 | hi->devname); | |
8654cda0 | 321 | return -EIO; |
50ee11fe BB |
322 | } |
323 | } | |
324 | /* bring board's memory regions on/line */ | |
325 | for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) | |
326 | { | |
327 | if (hi->pci_slot == 0xff) | |
328 | break; | |
329 | for (j = 0; j < 2; j++) | |
330 | { | |
bb0a9747 | 331 | if (!request_mem_region (hi->addr[j], hi->len[j], hi->devname)) |
50ee11fe | 332 | { |
e6e4d05d JP |
333 | pr_warning("%s: memory in use, addr=0x%lx, len=0x%lx ?\n", |
334 | hi->devname, hi->addr[j], hi->len[j]); | |
50ee11fe | 335 | cleanup_ioremap (); |
8654cda0 | 336 | return -ENOMEM; |
50ee11fe BB |
337 | } |
338 | hi->addr_mapped[j] = (unsigned long) ioremap (hi->addr[j], hi->len[j]); | |
339 | if (!hi->addr_mapped[j]) | |
340 | { | |
e6e4d05d JP |
341 | pr_warning("%s: ioremap fails, addr=0x%lx, len=0x%lx ?\n", |
342 | hi->devname, hi->addr[j], hi->len[j]); | |
50ee11fe | 343 | cleanup_ioremap (); |
8654cda0 | 344 | return -ENOMEM; |
50ee11fe BB |
345 | } |
346 | #ifdef SBE_MAP_DEBUG | |
e6e4d05d JP |
347 | pr_warning("%s: io remapped from phys %x to virt %x\n", |
348 | hi->devname, (u_int32_t) hi->addr[j], (u_int32_t) hi->addr_mapped[j]); | |
50ee11fe BB |
349 | #endif |
350 | } | |
351 | } | |
352 | ||
353 | drvr_state = SBE_DRVR_AVAILABLE; | |
354 | ||
355 | /* Have now memory mapped all boards. Now allow board's access to system */ | |
356 | for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) | |
357 | { | |
358 | if (hi->pci_slot == 0xff) | |
359 | break; | |
360 | if (pci_enable_device (hi->pdev[0]) || | |
361 | pci_enable_device (hi->pdev[1])) | |
362 | { | |
363 | drvr_state = SBE_DRVR_DOWN; | |
e6e4d05d JP |
364 | pr_warning("%s: failed to enable card %d slot %d\n", |
365 | hi->devname, i, hi->pci_slot); | |
50ee11fe BB |
366 | cleanup_devs (); |
367 | cleanup_ioremap (); | |
8654cda0 | 368 | return -EIO; |
50ee11fe BB |
369 | } |
370 | pci_set_master (hi->pdev[0]); | |
371 | pci_set_master (hi->pdev[1]); | |
372 | if (!(hi->ndev = c4_add_dev (hi, i, (long) hi->addr_mapped[0], | |
373 | (long) hi->addr_mapped[1], | |
374 | hi->pdev[0]->irq, | |
375 | hi->pdev[1]->irq))) | |
376 | { | |
377 | drvr_state = SBE_DRVR_DOWN; | |
378 | cleanup_ioremap (); | |
379 | /* NOTE: c4_add_dev() does its own device cleanup */ | |
380 | #if 0 | |
381 | cleanup_devs (); | |
382 | #endif | |
383 | return error_flag; /* error_flag set w/in add_dev() */ | |
384 | } | |
385 | show_two (hi, i); /* displays found information */ | |
386 | } | |
387 | return 0; | |
388 | } | |
389 | ||
390 | /*** End-of-File ***/ |