xref: /freebsd/sys/dev/aic7xxx/ahd_pci.c (revision 97cae63d7f4d89a31b5a364749bf6543f595b5f1)
117d24755SJustin T. Gibbs /*
217d24755SJustin T. Gibbs  * FreeBSD, PCI product support functions
317d24755SJustin T. Gibbs  *
417d24755SJustin T. Gibbs  * Copyright (c) 1995-2001 Justin T. Gibbs
517d24755SJustin T. Gibbs  * All rights reserved.
617d24755SJustin T. Gibbs  *
717d24755SJustin T. Gibbs  * Redistribution and use in source and binary forms, with or without
817d24755SJustin T. Gibbs  * modification, are permitted provided that the following conditions
917d24755SJustin T. Gibbs  * are met:
1017d24755SJustin T. Gibbs  * 1. Redistributions of source code must retain the above copyright
1117d24755SJustin T. Gibbs  *    notice, this list of conditions, and the following disclaimer,
1217d24755SJustin T. Gibbs  *    without modification, immediately at the beginning of the file.
1317d24755SJustin T. Gibbs  * 2. The name of the author may not be used to endorse or promote products
1417d24755SJustin T. Gibbs  *    derived from this software without specific prior written permission.
1517d24755SJustin T. Gibbs  *
1617d24755SJustin T. Gibbs  * Alternatively, this software may be distributed under the terms of the
1717d24755SJustin T. Gibbs  * GNU Public License ("GPL").
1817d24755SJustin T. Gibbs  *
1917d24755SJustin T. Gibbs  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2017d24755SJustin T. Gibbs  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2117d24755SJustin T. Gibbs  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2217d24755SJustin T. Gibbs  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2317d24755SJustin T. Gibbs  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2417d24755SJustin T. Gibbs  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2517d24755SJustin T. Gibbs  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2617d24755SJustin T. Gibbs  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2717d24755SJustin T. Gibbs  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2817d24755SJustin T. Gibbs  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2917d24755SJustin T. Gibbs  * SUCH DAMAGE.
3017d24755SJustin T. Gibbs  *
3197cae63dSScott Long  * $Id: //depot/aic7xxx/freebsd/dev/aic7xxx/ahd_pci.c#10 $
3217d24755SJustin T. Gibbs  *
3317d24755SJustin T. Gibbs  * $FreeBSD$
3417d24755SJustin T. Gibbs  */
3517d24755SJustin T. Gibbs 
3617d24755SJustin T. Gibbs #include <dev/aic7xxx/aic79xx_osm.h>
3717d24755SJustin T. Gibbs 
3817d24755SJustin T. Gibbs #define	AHD_PCI_IOADDR0 PCIR_MAPS	/* Primary I/O BAR */
3917d24755SJustin T. Gibbs #define	AHD_PCI_MEMADDR (PCIR_MAPS + 4) /* Mem I/O Address */
4017d24755SJustin T. Gibbs #define	AHD_PCI_IOADDR1 (PCIR_MAPS + 12)/* Secondary I/O BAR */
4117d24755SJustin T. Gibbs 
4217d24755SJustin T. Gibbs static int ahd_pci_probe(device_t dev);
4317d24755SJustin T. Gibbs static int ahd_pci_attach(device_t dev);
4417d24755SJustin T. Gibbs 
4517d24755SJustin T. Gibbs static device_method_t ahd_pci_device_methods[] = {
4617d24755SJustin T. Gibbs 	/* Device interface */
4717d24755SJustin T. Gibbs 	DEVMETHOD(device_probe,		ahd_pci_probe),
4817d24755SJustin T. Gibbs 	DEVMETHOD(device_attach,	ahd_pci_attach),
4917d24755SJustin T. Gibbs 	DEVMETHOD(device_detach,	ahd_detach),
5017d24755SJustin T. Gibbs 	{ 0, 0 }
5117d24755SJustin T. Gibbs };
5217d24755SJustin T. Gibbs 
5317d24755SJustin T. Gibbs static driver_t ahd_pci_driver = {
5417d24755SJustin T. Gibbs 	"ahd",
5517d24755SJustin T. Gibbs 	ahd_pci_device_methods,
5617d24755SJustin T. Gibbs 	sizeof(struct ahd_softc)
5717d24755SJustin T. Gibbs };
5817d24755SJustin T. Gibbs 
5917d24755SJustin T. Gibbs static devclass_t ahd_devclass;
6017d24755SJustin T. Gibbs 
6117d24755SJustin T. Gibbs DRIVER_MODULE(ahd, pci, ahd_pci_driver, ahd_devclass, 0, 0);
6217d24755SJustin T. Gibbs DRIVER_MODULE(ahd, cardbus, ahd_pci_driver, ahd_devclass, 0, 0);
6317d24755SJustin T. Gibbs MODULE_DEPEND(ahd_pci, ahd, 1, 1, 1);
6417d24755SJustin T. Gibbs MODULE_VERSION(ahd_pci, 1);
6517d24755SJustin T. Gibbs 
6617d24755SJustin T. Gibbs static int
6717d24755SJustin T. Gibbs ahd_pci_probe(device_t dev)
6817d24755SJustin T. Gibbs {
6917d24755SJustin T. Gibbs 	struct	ahd_pci_identity *entry;
7017d24755SJustin T. Gibbs 
7117d24755SJustin T. Gibbs 	entry = ahd_find_pci_device(dev);
7217d24755SJustin T. Gibbs 	if (entry != NULL) {
7317d24755SJustin T. Gibbs 		device_set_desc(dev, entry->name);
7417d24755SJustin T. Gibbs 		return (0);
7517d24755SJustin T. Gibbs 	}
7617d24755SJustin T. Gibbs 	return (ENXIO);
7717d24755SJustin T. Gibbs }
7817d24755SJustin T. Gibbs 
7917d24755SJustin T. Gibbs static int
8017d24755SJustin T. Gibbs ahd_pci_attach(device_t dev)
8117d24755SJustin T. Gibbs {
8217d24755SJustin T. Gibbs 	struct	 ahd_pci_identity *entry;
8317d24755SJustin T. Gibbs 	struct	 ahd_softc *ahd;
8417d24755SJustin T. Gibbs 	char	*name;
8517d24755SJustin T. Gibbs 	int	 error;
8617d24755SJustin T. Gibbs 
8717d24755SJustin T. Gibbs 	entry = ahd_find_pci_device(dev);
8817d24755SJustin T. Gibbs 	if (entry == NULL)
8917d24755SJustin T. Gibbs 		return (ENXIO);
9017d24755SJustin T. Gibbs 
9117d24755SJustin T. Gibbs 	/*
9217d24755SJustin T. Gibbs 	 * Allocate a softc for this card and
9317d24755SJustin T. Gibbs 	 * set it up for attachment by our
9417d24755SJustin T. Gibbs 	 * common detect routine.
9517d24755SJustin T. Gibbs 	 */
9617d24755SJustin T. Gibbs 	name = malloc(strlen(device_get_nameunit(dev)) + 1, M_DEVBUF, M_NOWAIT);
9717d24755SJustin T. Gibbs 	if (name == NULL)
9817d24755SJustin T. Gibbs 		return (ENOMEM);
9917d24755SJustin T. Gibbs 	strcpy(name, device_get_nameunit(dev));
10017d24755SJustin T. Gibbs 	ahd = ahd_alloc(dev, name);
10117d24755SJustin T. Gibbs 	if (ahd == NULL)
10217d24755SJustin T. Gibbs 		return (ENOMEM);
10317d24755SJustin T. Gibbs 
10417d24755SJustin T. Gibbs 	ahd_set_unit(ahd, device_get_unit(dev));
10517d24755SJustin T. Gibbs 
10617d24755SJustin T. Gibbs 	/*
10717d24755SJustin T. Gibbs 	 * Should we bother disabling 39Bit addressing
10817d24755SJustin T. Gibbs 	 * based on installed memory?
10917d24755SJustin T. Gibbs 	 */
11017d24755SJustin T. Gibbs 	if (sizeof(bus_addr_t) > 4)
11117d24755SJustin T. Gibbs                 ahd->flags |= AHD_39BIT_ADDRESSING;
11217d24755SJustin T. Gibbs 
11317d24755SJustin T. Gibbs 	/* Allocate a dmatag for our SCB DMA maps */
11417d24755SJustin T. Gibbs 	/* XXX Should be a child of the PCI bus dma tag */
11517d24755SJustin T. Gibbs 	error = bus_dma_tag_create(/*parent*/NULL, /*alignment*/1,
11617d24755SJustin T. Gibbs 				   /*boundary*/0,
11717d24755SJustin T. Gibbs 				   (ahd->flags & AHD_39BIT_ADDRESSING)
11817d24755SJustin T. Gibbs 				   ? 0x7FFFFFFFFF
11917d24755SJustin T. Gibbs 				   : BUS_SPACE_MAXADDR_32BIT,
12017d24755SJustin T. Gibbs 				   /*highaddr*/BUS_SPACE_MAXADDR,
12117d24755SJustin T. Gibbs 				   /*filter*/NULL, /*filterarg*/NULL,
12217d24755SJustin T. Gibbs 				   /*maxsize*/MAXBSIZE, /*nsegments*/AHD_NSEG,
12317d24755SJustin T. Gibbs 				   /*maxsegsz*/AHD_MAXTRANSFER_SIZE,
12417d24755SJustin T. Gibbs 				   /*flags*/BUS_DMA_ALLOCNOW,
12517d24755SJustin T. Gibbs 				   &ahd->parent_dmat);
12617d24755SJustin T. Gibbs 
12717d24755SJustin T. Gibbs 	if (error != 0) {
12817d24755SJustin T. Gibbs 		printf("ahd_pci_attach: Could not allocate DMA tag "
12917d24755SJustin T. Gibbs 		       "- error %d\n", error);
13017d24755SJustin T. Gibbs 		ahd_free(ahd);
13117d24755SJustin T. Gibbs 		return (ENOMEM);
13217d24755SJustin T. Gibbs 	}
13317d24755SJustin T. Gibbs 	ahd->dev_softc = dev;
13417d24755SJustin T. Gibbs 	error = ahd_pci_config(ahd, entry);
13517d24755SJustin T. Gibbs 	if (error != 0) {
13617d24755SJustin T. Gibbs 		ahd_free(ahd);
13717d24755SJustin T. Gibbs 		return (error);
13817d24755SJustin T. Gibbs 	}
13917d24755SJustin T. Gibbs 
14017d24755SJustin T. Gibbs 	ahd_attach(ahd);
14117d24755SJustin T. Gibbs 	return (0);
14217d24755SJustin T. Gibbs }
14317d24755SJustin T. Gibbs 
14417d24755SJustin T. Gibbs int
14517d24755SJustin T. Gibbs ahd_pci_map_registers(struct ahd_softc *ahd)
14617d24755SJustin T. Gibbs {
14717d24755SJustin T. Gibbs 	struct	resource *regs;
14817d24755SJustin T. Gibbs 	struct	resource *regs2;
14917d24755SJustin T. Gibbs 	u_int	command;
15017d24755SJustin T. Gibbs 	int	regs_type;
15117d24755SJustin T. Gibbs 	int	regs_id;
15217d24755SJustin T. Gibbs 	int	regs_id2;
15397cae63dSScott Long 	int	allow_memio;
15417d24755SJustin T. Gibbs 
15517d24755SJustin T. Gibbs 	command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/1);
15617d24755SJustin T. Gibbs 	regs = NULL;
15717d24755SJustin T. Gibbs 	regs2 = NULL;
15817d24755SJustin T. Gibbs 	regs_type = 0;
15917d24755SJustin T. Gibbs 	regs_id = 0;
16097cae63dSScott Long 
16197cae63dSScott Long 	/* Retrieve the per-device 'allow_memio' hint */
16297cae63dSScott Long 	if (resource_int_value(device_get_name(ahd->dev_softc),
16397cae63dSScott Long 			       device_get_unit(ahd->dev_softc),
16497cae63dSScott Long 			       "allow_memio", &allow_memio) != 0) {
16597cae63dSScott Long 		if (bootverbose)
16697cae63dSScott Long 			device_printf(ahd->dev_softc,
16797cae63dSScott Long 				      "Defaulting to MEMIO on\n");
16897cae63dSScott Long 	}
16997cae63dSScott Long 
170ccbe423cSJustin T. Gibbs 	if ((command & PCIM_CMD_MEMEN) != 0
17197cae63dSScott Long 	 && (ahd->bugs & AHD_PCIX_MMAPIO_BUG) == 0
17297cae63dSScott Long 	 && allow_memio != 0) {
17317d24755SJustin T. Gibbs 
17417d24755SJustin T. Gibbs 		regs_type = SYS_RES_MEMORY;
17517d24755SJustin T. Gibbs 		regs_id = AHD_PCI_MEMADDR;
17617d24755SJustin T. Gibbs 		regs = bus_alloc_resource(ahd->dev_softc, regs_type,
17717d24755SJustin T. Gibbs 					  &regs_id, 0, ~0, 1, RF_ACTIVE);
17817d24755SJustin T. Gibbs 		if (regs != NULL) {
17917d24755SJustin T. Gibbs 			int error;
18017d24755SJustin T. Gibbs 
18117d24755SJustin T. Gibbs 			ahd->tags[0] = rman_get_bustag(regs);
18217d24755SJustin T. Gibbs 			ahd->bshs[0] = rman_get_bushandle(regs);
18317d24755SJustin T. Gibbs 			ahd->tags[1] = ahd->tags[0];
18417d24755SJustin T. Gibbs 			error = bus_space_subregion(ahd->tags[0], ahd->bshs[0],
18517d24755SJustin T. Gibbs 						    /*offset*/0x100,
18617d24755SJustin T. Gibbs 						    /*size*/0x100,
18717d24755SJustin T. Gibbs 						    &ahd->bshs[1]);
18817d24755SJustin T. Gibbs 			/*
18917d24755SJustin T. Gibbs 			 * Do a quick test to see if memory mapped
19017d24755SJustin T. Gibbs 			 * I/O is functioning correctly.
19117d24755SJustin T. Gibbs 			 */
192add68794SScott Long 			if (error != 0
193add68794SScott Long 			 || ahd_pci_test_register_access(ahd) != 0) {
19417d24755SJustin T. Gibbs 				device_printf(ahd->dev_softc,
19517d24755SJustin T. Gibbs 				       "PCI Device %d:%d:%d failed memory "
19617d24755SJustin T. Gibbs 				       "mapped test.  Using PIO.\n",
19717d24755SJustin T. Gibbs 				       ahd_get_pci_bus(ahd->dev_softc),
19817d24755SJustin T. Gibbs 				       ahd_get_pci_slot(ahd->dev_softc),
19917d24755SJustin T. Gibbs 				       ahd_get_pci_function(ahd->dev_softc));
20017d24755SJustin T. Gibbs 				bus_release_resource(ahd->dev_softc, regs_type,
20117d24755SJustin T. Gibbs 						     regs_id, regs);
20217d24755SJustin T. Gibbs 				regs = NULL;
20317d24755SJustin T. Gibbs 			} else {
20417d24755SJustin T. Gibbs 				command &= ~PCIM_CMD_PORTEN;
20517d24755SJustin T. Gibbs 				ahd_pci_write_config(ahd->dev_softc,
20617d24755SJustin T. Gibbs 						     PCIR_COMMAND,
20717d24755SJustin T. Gibbs 						     command, /*bytes*/1);
20817d24755SJustin T. Gibbs 			}
20917d24755SJustin T. Gibbs 		}
21017d24755SJustin T. Gibbs 	}
21117d24755SJustin T. Gibbs 	if (regs == NULL && (command & PCIM_CMD_PORTEN) != 0) {
21217d24755SJustin T. Gibbs 		regs_type = SYS_RES_IOPORT;
21317d24755SJustin T. Gibbs 		regs_id = AHD_PCI_IOADDR0;
21417d24755SJustin T. Gibbs 		regs = bus_alloc_resource(ahd->dev_softc, regs_type,
21517d24755SJustin T. Gibbs 					  &regs_id, 0, ~0, 1, RF_ACTIVE);
21617d24755SJustin T. Gibbs 		if (regs == NULL) {
21717d24755SJustin T. Gibbs 			device_printf(ahd->dev_softc,
21817d24755SJustin T. Gibbs 				      "can't allocate register resources\n");
21917d24755SJustin T. Gibbs 			return (ENOMEM);
22017d24755SJustin T. Gibbs 		}
22117d24755SJustin T. Gibbs 		ahd->tags[0] = rman_get_bustag(regs);
22217d24755SJustin T. Gibbs 		ahd->bshs[0] = rman_get_bushandle(regs);
22317d24755SJustin T. Gibbs 
22417d24755SJustin T. Gibbs 		/* And now the second BAR */
22517d24755SJustin T. Gibbs 		regs_id2 = AHD_PCI_IOADDR1;
22617d24755SJustin T. Gibbs 		regs2 = bus_alloc_resource(ahd->dev_softc, regs_type,
22717d24755SJustin T. Gibbs 					   &regs_id2, 0, ~0, 1, RF_ACTIVE);
22817d24755SJustin T. Gibbs 		if (regs2 == NULL) {
22917d24755SJustin T. Gibbs 			device_printf(ahd->dev_softc,
23017d24755SJustin T. Gibbs 				      "can't allocate register resources\n");
23117d24755SJustin T. Gibbs 			return (ENOMEM);
23217d24755SJustin T. Gibbs 		}
23317d24755SJustin T. Gibbs 		ahd->tags[1] = rman_get_bustag(regs2);
23417d24755SJustin T. Gibbs 		ahd->bshs[1] = rman_get_bushandle(regs2);
23517d24755SJustin T. Gibbs 		command &= ~PCIM_CMD_MEMEN;
23617d24755SJustin T. Gibbs 		ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
23717d24755SJustin T. Gibbs 				     command, /*bytes*/1);
23817d24755SJustin T. Gibbs 		ahd->platform_data->regs_res_type[1] = regs_type;
23917d24755SJustin T. Gibbs 		ahd->platform_data->regs_res_id[1] = regs_id2;
24017d24755SJustin T. Gibbs 		ahd->platform_data->regs[1] = regs2;
24117d24755SJustin T. Gibbs 	}
24217d24755SJustin T. Gibbs 	ahd->platform_data->regs_res_type[0] = regs_type;
24317d24755SJustin T. Gibbs 	ahd->platform_data->regs_res_id[0] = regs_id;
24417d24755SJustin T. Gibbs 	ahd->platform_data->regs[0] = regs;
24517d24755SJustin T. Gibbs 	return (0);
24617d24755SJustin T. Gibbs }
24717d24755SJustin T. Gibbs 
24817d24755SJustin T. Gibbs int
24917d24755SJustin T. Gibbs ahd_pci_map_int(struct ahd_softc *ahd)
25017d24755SJustin T. Gibbs {
25117d24755SJustin T. Gibbs 	int zero;
25217d24755SJustin T. Gibbs 
25317d24755SJustin T. Gibbs 	zero = 0;
25417d24755SJustin T. Gibbs 	ahd->platform_data->irq =
25517d24755SJustin T. Gibbs 	    bus_alloc_resource(ahd->dev_softc, SYS_RES_IRQ, &zero,
25617d24755SJustin T. Gibbs 			       0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
25717d24755SJustin T. Gibbs 	if (ahd->platform_data->irq == NULL)
25817d24755SJustin T. Gibbs 		return (ENOMEM);
25917d24755SJustin T. Gibbs 	ahd->platform_data->irq_res_type = SYS_RES_IRQ;
26017d24755SJustin T. Gibbs 	return (ahd_map_int(ahd));
26117d24755SJustin T. Gibbs }
26217d24755SJustin T. Gibbs 
26317d24755SJustin T. Gibbs void
26417d24755SJustin T. Gibbs ahd_power_state_change(struct ahd_softc *ahd, ahd_power_state new_state)
26517d24755SJustin T. Gibbs {
26617d24755SJustin T. Gibbs 	uint32_t cap;
26717d24755SJustin T. Gibbs 	u_int cap_offset;
26817d24755SJustin T. Gibbs 
26917d24755SJustin T. Gibbs 	/*
27017d24755SJustin T. Gibbs 	 * Traverse the capability list looking for
27117d24755SJustin T. Gibbs 	 * the power management capability.
27217d24755SJustin T. Gibbs 	 */
27317d24755SJustin T. Gibbs 	cap = 0;
27417d24755SJustin T. Gibbs 	cap_offset = ahd_pci_read_config(ahd->dev_softc,
27517d24755SJustin T. Gibbs 					 PCIR_CAP_PTR, /*bytes*/1);
27617d24755SJustin T. Gibbs 	while (cap_offset != 0) {
27717d24755SJustin T. Gibbs 
27817d24755SJustin T. Gibbs 		cap = ahd_pci_read_config(ahd->dev_softc,
27917d24755SJustin T. Gibbs 					  cap_offset, /*bytes*/4);
28017d24755SJustin T. Gibbs 		if ((cap & 0xFF) == 1
28117d24755SJustin T. Gibbs 		 && ((cap >> 16) & 0x3) > 0) {
28217d24755SJustin T. Gibbs 			uint32_t pm_control;
28317d24755SJustin T. Gibbs 
28417d24755SJustin T. Gibbs 			pm_control = ahd_pci_read_config(ahd->dev_softc,
28517d24755SJustin T. Gibbs 							 cap_offset + 4,
28617d24755SJustin T. Gibbs 							 /*bytes*/2);
28717d24755SJustin T. Gibbs 			pm_control &= ~0x3;
28817d24755SJustin T. Gibbs 			pm_control |= new_state;
28917d24755SJustin T. Gibbs 			ahd_pci_write_config(ahd->dev_softc,
29017d24755SJustin T. Gibbs 					     cap_offset + 4,
29117d24755SJustin T. Gibbs 					     pm_control, /*bytes*/2);
29217d24755SJustin T. Gibbs 			break;
29317d24755SJustin T. Gibbs 		}
29417d24755SJustin T. Gibbs 		cap_offset = (cap >> 8) & 0xFF;
29517d24755SJustin T. Gibbs 	}
29617d24755SJustin T. Gibbs }
297