File: /usr/src/linux/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c

1     /*
2      * Linux driver attachment glue for PCI based controllers.
3      *
4      * Copyright (c) 2000, 2001 Adaptec Inc.
5      * All rights reserved.
6      *
7      * Redistribution and use in source and binary forms, with or without
8      * modification, are permitted provided that the following conditions
9      * are met:
10      * 1. Redistributions of source code must retain the above copyright
11      *    notice, this list of conditions, and the following disclaimer,
12      *    without modification.
13      * 2. The name of the author may not be used to endorse or promote products
14      *    derived from this software without specific prior written permission.
15      *
16      * Alternatively, this software may be distributed under the terms of the
17      * GNU General Public License ("GPL").
18      *
19      * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20      * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21      * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22      * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
23      * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24      * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25      * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26      * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27      * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28      * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29      * SUCH DAMAGE.
30      *
31      * $Id: //depot/src/linux/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c#23 $
32      */
33     
34     #include "aic7xxx_osm.h"
35     
36     #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
37     struct pci_device_id
38     {
39     };
40     #endif
41     
42     static int	ahc_linux_pci_dev_probe(struct pci_dev *pdev,
43     					const struct pci_device_id *ent);
44     #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
45     static void	ahc_linux_pci_dev_remove(struct pci_dev *pdev);
46     
47     /* We do our own ID filtering.  So, grab all SCSI storage class devices. */
48     static struct pci_device_id ahc_linux_pci_id_table[] = {
49     	{
50     		0x9004, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
51     		PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
52     	},
53     	{
54     		0x9005, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
55     		PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
56     	},
57     	{ 0 }
58     };
59     
60     struct pci_driver aic7xxx_pci_driver = {
61     	name:		"aic7xxx",
62     	probe:		ahc_linux_pci_dev_probe,
63     	remove:		ahc_linux_pci_dev_remove,
64     	id_table:	ahc_linux_pci_id_table
65     };
66     
67     static void
68     ahc_linux_pci_dev_remove(struct pci_dev *pdev)
69     {
70     	struct ahc_softc *ahc;
71     	struct ahc_softc *list_ahc;
72     
73     	/*
74     	 * We should be able to just perform
75     	 * the free directly, but check our
76     	 * list for extra sanity.
77     	 */
78     	ahc = (struct ahc_softc *)pdev->driver_data;
79     	TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
80     		if (list_ahc == ahc) {
81     			ahc_free(ahc);
82     			break;
83     		}
84     	}
85     }
86     #endif /* !LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) */
87     
88     static int
89     ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
90     {
91     	char	 buf[80];
92     	struct	 ahc_softc *ahc;
93     	ahc_dev_softc_t pci;
94     	struct	 ahc_pci_identity *entry;
95     	char	*name;
96     	int	 error;
97     
98     	pci = pdev;
99     	entry = ahc_find_pci_device(pci);
100     	if (entry == NULL)
101     		return (-ENODEV);
102     
103     	/*
104     	 * Allocate a softc for this card and
105     	 * set it up for attachment by our
106     	 * common detect routine.
107     	 */
108     	sprintf(buf, "ahc_pci:%d:%d:%d",
109     		ahc_get_pci_bus(pci),
110     		ahc_get_pci_slot(pci),
111     		ahc_get_pci_function(pci));
112     	name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
113     	if (name == NULL)
114     		return (-ENOMEM);
115     	strcpy(name, buf);
116     	ahc = ahc_alloc(NULL, name);
117     	if (ahc == NULL)
118     		return (-ENOMEM);
119     #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
120     	if (pci_enable_device(pdev)) {
121     		ahc_free(ahc);
122     		return (-ENODEV);
123     	}
124     	pci_set_master(pdev);
125     
126     	if (sizeof(bus_addr_t) > 4
127     #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
128     	 && ahc_linux_get_memsize() > 0x80000000
129     	 && pci_set_dma_mask(pdev, 0x7FFFFFFFFFULL) == 0) {
130     #else
131     	 && ahc_linux_get_memsize() > 0x80000000) {
132     		ahc->dev_softc->dma_mask = 
133     		    (bus_addr_t)(0x7FFFFFFFFFULL
134     			       & ((1ULL << (sizeof(bus_addr_t) * 8))-1));
135     #endif
136     		ahc->flags |= AHC_39BIT_ADDRESSING;
137     		ahc->platform_data->hw_dma_mask =
138     		    (bus_addr_t)(0x7FFFFFFFFFULL
139     			       & ((1ULL << (sizeof(bus_addr_t) * 8))-1));
140     	}
141     #endif
142     	ahc->dev_softc = pci;
143     	ahc->platform_data->irq = pdev->irq;
144     	error = ahc_pci_config(ahc, entry);
145     	if (error != 0) {
146     		ahc_free(ahc);
147     		return (-error);
148     	}
149     #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
150     	pdev->driver_data = ahc;
151     	if (aic7xxx_detect_complete)
152     		ahc_linux_register_host(ahc, aic7xxx_driver_template);
153     #endif
154     	return (0);
155     }
156     
157     int
158     ahc_linux_pci_probe(Scsi_Host_Template *template)
159     {
160     #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
161     	return (pci_module_init(&aic7xxx_pci_driver));
162     #else
163     	struct pci_dev *pdev;
164     	u_int class;
165     	int found;
166     
167     	/* If we don't have a PCI bus, we can't find any adapters. */
168     	if (pci_present() == 0)
169     		return (0);
170     
171     	found = 0;
172     	pdev = NULL;
173     	class = PCI_CLASS_STORAGE_SCSI << 8;
174     	while ((pdev = pci_find_class(class, pdev)) != NULL) {
175     		struct ahc_softc *ahc;
176     		ahc_dev_softc_t pci;
177     		int error;
178     
179     		pci = pdev;
180     
181     		/*
182     		 * Some BIOSen report the same device multiple times.
183     		 */
184     		TAILQ_FOREACH(ahc, &ahc_tailq, links) {
185     			struct pci_dev *probed_pdev;
186     
187     			probed_pdev = ahc->dev_softc;
188     			if (probed_pdev->bus->number == pdev->bus->number
189     			 && probed_pdev->devfn == pdev->devfn)
190     				break;
191     		}
192     		if (ahc != NULL) {
193     			/* Skip duplicate. */
194     			continue;
195     		}
196     
197     		error = ahc_linux_pci_dev_probe(pdev, /*pci_devid*/NULL);
198     		if (error == 0)
199     			found++;
200     	}
201     	return (found);
202     #endif
203     }
204     
205     int
206     ahc_pci_map_registers(struct ahc_softc *ahc)
207     {
208     	uint32_t command;
209     	u_long	 base;
210     #ifdef MMAPIO
211     	u_long	 start;
212     	u_long	 base_page;
213     	u_long	 base_offset;
214     #endif
215     	uint8_t *maddr;
216     
217     	command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, 4);
218     	base = 0;
219     	maddr = NULL;
220     #ifdef MMAPIO
221     #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
222     	start = pci_resource_start(ahc->dev_softc, 1);
223     	base_page = start & PAGE_MASK;
224     	base_offset = start - base_page;
225     #else
226     	start = ahc_pci_read_config(ahc->dev_softc, PCIR_MAPS+4, 4);
227     	base_offset = start & PCI_BASE_ADDRESS_MEM_MASK;
228     	base_page = base_offset & PAGE_MASK;
229     	base_offset -= base_page;
230     #endif
231     	if (start != 0) {
232     		ahc->platform_data->mem_busaddr = start;
233     #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
234     		if (request_mem_region(start, 0x1000, "aic7xxx") == 0) {
235     			printf("aic7xxx: PCI%d:%d:%d MEM region 0x%lx "
236     			       "in use. Cannot map device.\n",
237     			       ahc_get_pci_bus(ahc->dev_softc),
238     			       ahc_get_pci_slot(ahc->dev_softc),
239     			       ahc_get_pci_function(ahc->dev_softc),
240     			       start);
241     		} else
242     #endif
243     			maddr = ioremap_nocache(base_page, base_offset + 256);
244     		if (maddr != NULL) {
245     			ahc->tag = BUS_SPACE_MEMIO;
246     			ahc->bsh.maddr = maddr + base_offset;
247     			command |= PCIM_CMD_MEMEN;
248     			ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND,
249     					     command, 4);
250     
251     			/*
252     			 * Do a quick test to see if memory mapped
253     			 * I/O is functioning correctly.
254     			 */
255     			if (ahc_inb(ahc, HCNTRL) == 0xFF) {
256     				printf("aic7xxx: PCI Device %d:%d:%d "
257     				       "failed memory mapped test\n",
258     				       ahc_get_pci_bus(ahc->dev_softc),
259     				       ahc_get_pci_slot(ahc->dev_softc),
260     				       ahc_get_pci_function(ahc->dev_softc));
261     				iounmap((void *)base_page);
262     				maddr = NULL;
263     			} else {
264     				command &= ~PCIM_CMD_PORTEN;
265     				ahc_pci_write_config(ahc->dev_softc,
266     						    PCIR_COMMAND, command, 4);
267     			}
268     		}
269     	}
270     #endif
271     
272     	/*
273     	 * We always prefer memory mapped access.  Only
274     	 * complain about our ioport conflicting with
275     	 * another device if we are going to use it.
276     	 */
277     	if (maddr == NULL) {
278     		ahc->tag = BUS_SPACE_PIO;
279     		command &= ~(PCIM_CMD_MEMEN|PCIM_CMD_PORTEN);
280     		ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, 4);
281     #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
282     		base = pci_resource_start(ahc->dev_softc, 0);
283     #else
284     		base = ahc_pci_read_config(ahc->dev_softc, PCIR_MAPS, 4);
285     		base &= PCI_BASE_ADDRESS_IO_MASK;
286     #endif
287     		if (base == 0) {
288     			printf("aic7xxx: PCI%d:%d:%d No mapping available. "
289     			       "Cannot map device.\n",
290     			       ahc_get_pci_bus(ahc->dev_softc),
291     			       ahc_get_pci_slot(ahc->dev_softc),
292     			       ahc_get_pci_function(ahc->dev_softc));
293     			return (ENXIO);
294     		}
295     #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
296     		if (check_region(base, 256) != 0) {
297     #else
298     		if (request_region(base, 256, "aic7xxx") == 0) {
299     #endif
300     			printf("aic7xxx: PCI%d:%d:%d IO region 0x%lx[0..255] "
301     			       "in use. Cannot map device.\n",
302     			       ahc_get_pci_bus(ahc->dev_softc),
303     			       ahc_get_pci_slot(ahc->dev_softc),
304     			       ahc_get_pci_function(ahc->dev_softc),
305     			       base);
306     			base = 0;
307     			return (EBUSY);
308     		}
309     #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
310     		request_region(base, 256, "aic7xxx");
311     #endif
312     		ahc->bsh.ioport = base;
313     		command |= PCIM_CMD_PORTEN;
314     		ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, 4);
315     	}
316     	return (0);
317     }
318     
319     int
320     ahc_pci_map_int(struct ahc_softc *ahc)
321     {
322     	int error;
323     
324     	ahc->platform_data->irq = ahc->dev_softc->irq;
325     	error = request_irq(ahc->platform_data->irq, ahc_linux_isr,
326     			    SA_SHIRQ, "aic7xxx", ahc);
327     	
328     	return (-error);
329     }
330     
331     void
332     ahc_power_state_change(struct ahc_softc *ahc, ahc_power_state new_state)
333     {
334     #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
335     	pci_set_power_state(ahc->dev_softc, new_state);
336     #else
337     	uint32_t cap;
338     	u_int cap_offset;
339     
340     	/*
341     	 * Traverse the capability list looking for
342     	 * the power management capability.
343     	 */
344     	cap = 0;
345     	cap_offset = ahc_pci_read_config(ahc->dev_softc,
346     					 PCIR_CAP_PTR, /*bytes*/1);
347     	while (cap_offset != 0) {
348     
349     		cap = ahc_pci_read_config(ahc->dev_softc,
350     					  cap_offset, /*bytes*/4);
351     		if ((cap & 0xFF) == 1
352     		 && ((cap >> 16) & 0x3) > 0) {
353     			uint32_t pm_control;
354     
355     			pm_control = ahc_pci_read_config(ahc->dev_softc,
356     							 cap_offset + 4,
357     							 /*bytes*/4);
358     			pm_control &= ~0x3;
359     			pm_control |= new_state;
360     			ahc_pci_write_config(ahc->dev_softc,
361     					     cap_offset + 4,
362     					     pm_control, /*bytes*/2);
363     			break;
364     		}
365     		cap_offset = (cap >> 8) & 0xFF;
366     	}
367     #endif 
368     }
369