18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Linux driver attachment glue for PCI based controllers.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2000-2001 Adaptec Inc.
58c2ecf20Sopenharmony_ci * All rights reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
88c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
98c2ecf20Sopenharmony_ci * are met:
108c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
118c2ecf20Sopenharmony_ci *    notice, this list of conditions, and the following disclaimer,
128c2ecf20Sopenharmony_ci *    without modification.
138c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce at minimum a disclaimer
148c2ecf20Sopenharmony_ci *    substantially similar to the "NO WARRANTY" disclaimer below
158c2ecf20Sopenharmony_ci *    ("Disclaimer") and any redistribution must be conditioned upon
168c2ecf20Sopenharmony_ci *    including a substantially similar Disclaimer requirement for further
178c2ecf20Sopenharmony_ci *    binary redistribution.
188c2ecf20Sopenharmony_ci * 3. Neither the names of the above-listed copyright holders nor the names
198c2ecf20Sopenharmony_ci *    of any contributors may be used to endorse or promote products derived
208c2ecf20Sopenharmony_ci *    from this software without specific prior written permission.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the
238c2ecf20Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free
248c2ecf20Sopenharmony_ci * Software Foundation.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * NO WARRANTY
278c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
288c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
298c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
308c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
318c2ecf20Sopenharmony_ci * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
328c2ecf20Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
338c2ecf20Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
348c2ecf20Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
358c2ecf20Sopenharmony_ci * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
368c2ecf20Sopenharmony_ci * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
378c2ecf20Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGES.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c#47 $
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include "aic7xxx_osm.h"
438c2ecf20Sopenharmony_ci#include "aic7xxx_pci.h"
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* Define the macro locally since it's different for different class of chips.
468c2ecf20Sopenharmony_ci*/
478c2ecf20Sopenharmony_ci#define ID(x)	ID_C(x, PCI_CLASS_STORAGE_SCSI)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic const struct pci_device_id ahc_linux_pci_id_table[] = {
508c2ecf20Sopenharmony_ci	/* aic7850 based controllers */
518c2ecf20Sopenharmony_ci	ID(ID_AHA_2902_04_10_15_20C_30C),
528c2ecf20Sopenharmony_ci	/* aic7860 based controllers */
538c2ecf20Sopenharmony_ci	ID(ID_AHA_2930CU),
548c2ecf20Sopenharmony_ci	ID(ID_AHA_1480A & ID_DEV_VENDOR_MASK),
558c2ecf20Sopenharmony_ci	ID(ID_AHA_2940AU_0 & ID_DEV_VENDOR_MASK),
568c2ecf20Sopenharmony_ci	ID(ID_AHA_2940AU_CN & ID_DEV_VENDOR_MASK),
578c2ecf20Sopenharmony_ci	ID(ID_AHA_2930C_VAR & ID_DEV_VENDOR_MASK),
588c2ecf20Sopenharmony_ci	/* aic7870 based controllers */
598c2ecf20Sopenharmony_ci	ID(ID_AHA_2940),
608c2ecf20Sopenharmony_ci	ID(ID_AHA_3940),
618c2ecf20Sopenharmony_ci	ID(ID_AHA_398X),
628c2ecf20Sopenharmony_ci	ID(ID_AHA_2944),
638c2ecf20Sopenharmony_ci	ID(ID_AHA_3944),
648c2ecf20Sopenharmony_ci	ID(ID_AHA_4944),
658c2ecf20Sopenharmony_ci	/* aic7880 based controllers */
668c2ecf20Sopenharmony_ci	ID(ID_AHA_2940U & ID_DEV_VENDOR_MASK),
678c2ecf20Sopenharmony_ci	ID(ID_AHA_3940U & ID_DEV_VENDOR_MASK),
688c2ecf20Sopenharmony_ci	ID(ID_AHA_2944U & ID_DEV_VENDOR_MASK),
698c2ecf20Sopenharmony_ci	ID(ID_AHA_3944U & ID_DEV_VENDOR_MASK),
708c2ecf20Sopenharmony_ci	ID(ID_AHA_398XU & ID_DEV_VENDOR_MASK),
718c2ecf20Sopenharmony_ci	ID(ID_AHA_4944U & ID_DEV_VENDOR_MASK),
728c2ecf20Sopenharmony_ci	ID(ID_AHA_2930U & ID_DEV_VENDOR_MASK),
738c2ecf20Sopenharmony_ci	ID(ID_AHA_2940U_PRO & ID_DEV_VENDOR_MASK),
748c2ecf20Sopenharmony_ci	ID(ID_AHA_2940U_CN & ID_DEV_VENDOR_MASK),
758c2ecf20Sopenharmony_ci	/* aic7890 based controllers */
768c2ecf20Sopenharmony_ci	ID(ID_AHA_2930U2),
778c2ecf20Sopenharmony_ci	ID(ID_AHA_2940U2B),
788c2ecf20Sopenharmony_ci	ID(ID_AHA_2940U2_OEM),
798c2ecf20Sopenharmony_ci	ID(ID_AHA_2940U2),
808c2ecf20Sopenharmony_ci	ID(ID_AHA_2950U2B),
818c2ecf20Sopenharmony_ci	ID16(ID_AIC7890_ARO & ID_AIC7895_ARO_MASK),
828c2ecf20Sopenharmony_ci	ID(ID_AAA_131U2),
838c2ecf20Sopenharmony_ci	/* aic7890 based controllers */
848c2ecf20Sopenharmony_ci	ID(ID_AHA_29160),
858c2ecf20Sopenharmony_ci	ID(ID_AHA_29160_CPQ),
868c2ecf20Sopenharmony_ci	ID(ID_AHA_29160N),
878c2ecf20Sopenharmony_ci	ID(ID_AHA_29160C),
888c2ecf20Sopenharmony_ci	ID(ID_AHA_29160B),
898c2ecf20Sopenharmony_ci	ID(ID_AHA_19160B),
908c2ecf20Sopenharmony_ci	ID(ID_AIC7892_ARO),
918c2ecf20Sopenharmony_ci	/* aic7892 based controllers */
928c2ecf20Sopenharmony_ci	ID(ID_AHA_2940U_DUAL),
938c2ecf20Sopenharmony_ci	ID(ID_AHA_3940AU),
948c2ecf20Sopenharmony_ci	ID(ID_AHA_3944AU),
958c2ecf20Sopenharmony_ci	ID(ID_AIC7895_ARO),
968c2ecf20Sopenharmony_ci	ID(ID_AHA_3950U2B_0),
978c2ecf20Sopenharmony_ci	ID(ID_AHA_3950U2B_1),
988c2ecf20Sopenharmony_ci	ID(ID_AHA_3950U2D_0),
998c2ecf20Sopenharmony_ci	ID(ID_AHA_3950U2D_1),
1008c2ecf20Sopenharmony_ci	ID(ID_AIC7896_ARO),
1018c2ecf20Sopenharmony_ci	/* aic7899 based controllers */
1028c2ecf20Sopenharmony_ci	ID(ID_AHA_3960D),
1038c2ecf20Sopenharmony_ci	ID(ID_AHA_3960D_CPQ),
1048c2ecf20Sopenharmony_ci	ID(ID_AIC7899_ARO),
1058c2ecf20Sopenharmony_ci	/* Generic chip probes for devices we don't know exactly. */
1068c2ecf20Sopenharmony_ci	ID(ID_AIC7850 & ID_DEV_VENDOR_MASK),
1078c2ecf20Sopenharmony_ci	ID(ID_AIC7855 & ID_DEV_VENDOR_MASK),
1088c2ecf20Sopenharmony_ci	ID(ID_AIC7859 & ID_DEV_VENDOR_MASK),
1098c2ecf20Sopenharmony_ci	ID(ID_AIC7860 & ID_DEV_VENDOR_MASK),
1108c2ecf20Sopenharmony_ci	ID(ID_AIC7870 & ID_DEV_VENDOR_MASK),
1118c2ecf20Sopenharmony_ci	ID(ID_AIC7880 & ID_DEV_VENDOR_MASK),
1128c2ecf20Sopenharmony_ci 	ID16(ID_AIC7890 & ID_9005_GENERIC_MASK),
1138c2ecf20Sopenharmony_ci 	ID16(ID_AIC7892 & ID_9005_GENERIC_MASK),
1148c2ecf20Sopenharmony_ci	ID(ID_AIC7895 & ID_DEV_VENDOR_MASK),
1158c2ecf20Sopenharmony_ci	ID16(ID_AIC7896 & ID_9005_GENERIC_MASK),
1168c2ecf20Sopenharmony_ci	ID16(ID_AIC7899 & ID_9005_GENERIC_MASK),
1178c2ecf20Sopenharmony_ci	ID(ID_AIC7810 & ID_DEV_VENDOR_MASK),
1188c2ecf20Sopenharmony_ci	ID(ID_AIC7815 & ID_DEV_VENDOR_MASK),
1198c2ecf20Sopenharmony_ci	{ 0 }
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ahc_linux_pci_id_table);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
1258c2ecf20Sopenharmony_cistatic int
1268c2ecf20Sopenharmony_ciahc_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct ahc_softc *ahc = pci_get_drvdata(pdev);
1298c2ecf20Sopenharmony_ci	int rc;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if ((rc = ahc_suspend(ahc)))
1328c2ecf20Sopenharmony_ci		return rc;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	pci_save_state(pdev);
1358c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (mesg.event & PM_EVENT_SLEEP)
1388c2ecf20Sopenharmony_ci		pci_set_power_state(pdev, PCI_D3hot);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return rc;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic int
1448c2ecf20Sopenharmony_ciahc_linux_pci_dev_resume(struct pci_dev *pdev)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct ahc_softc *ahc = pci_get_drvdata(pdev);
1478c2ecf20Sopenharmony_ci	int rc;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	pci_set_power_state(pdev, PCI_D0);
1508c2ecf20Sopenharmony_ci	pci_restore_state(pdev);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if ((rc = pci_enable_device(pdev))) {
1538c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &pdev->dev,
1548c2ecf20Sopenharmony_ci			   "failed to enable device after resume (%d)\n", rc);
1558c2ecf20Sopenharmony_ci		return rc;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	pci_set_master(pdev);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	ahc_pci_resume(ahc);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return (ahc_resume(ahc));
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci#endif
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic void
1678c2ecf20Sopenharmony_ciahc_linux_pci_dev_remove(struct pci_dev *pdev)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct ahc_softc *ahc = pci_get_drvdata(pdev);
1708c2ecf20Sopenharmony_ci	u_long s;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (ahc->platform_data && ahc->platform_data->host)
1738c2ecf20Sopenharmony_ci			scsi_remove_host(ahc->platform_data->host);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	ahc_lock(ahc, &s);
1768c2ecf20Sopenharmony_ci	ahc_intr_enable(ahc, FALSE);
1778c2ecf20Sopenharmony_ci	ahc_unlock(ahc, &s);
1788c2ecf20Sopenharmony_ci	ahc_free(ahc);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic void
1828c2ecf20Sopenharmony_ciahc_linux_pci_inherit_flags(struct ahc_softc *ahc)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct pci_dev *pdev = ahc->dev_softc, *master_pdev;
1858c2ecf20Sopenharmony_ci	unsigned int master_devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	master_pdev = pci_get_slot(pdev->bus, master_devfn);
1888c2ecf20Sopenharmony_ci	if (master_pdev) {
1898c2ecf20Sopenharmony_ci		struct ahc_softc *master = pci_get_drvdata(master_pdev);
1908c2ecf20Sopenharmony_ci		if (master) {
1918c2ecf20Sopenharmony_ci			ahc->flags &= ~AHC_BIOS_ENABLED;
1928c2ecf20Sopenharmony_ci			ahc->flags |= master->flags & AHC_BIOS_ENABLED;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci			ahc->flags &= ~AHC_PRIMARY_CHANNEL;
1958c2ecf20Sopenharmony_ci			ahc->flags |= master->flags & AHC_PRIMARY_CHANNEL;
1968c2ecf20Sopenharmony_ci		} else
1978c2ecf20Sopenharmony_ci			printk(KERN_ERR "aic7xxx: no multichannel peer found!\n");
1988c2ecf20Sopenharmony_ci		pci_dev_put(master_pdev);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int
2038c2ecf20Sopenharmony_ciahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	char		 buf[80];
2068c2ecf20Sopenharmony_ci	const uint64_t	 mask_39bit = 0x7FFFFFFFFFULL;
2078c2ecf20Sopenharmony_ci	struct		 ahc_softc *ahc;
2088c2ecf20Sopenharmony_ci	ahc_dev_softc_t	 pci;
2098c2ecf20Sopenharmony_ci	const struct ahc_pci_identity *entry;
2108c2ecf20Sopenharmony_ci	char		*name;
2118c2ecf20Sopenharmony_ci	int		 error;
2128c2ecf20Sopenharmony_ci	struct device	*dev = &pdev->dev;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	pci = pdev;
2158c2ecf20Sopenharmony_ci	entry = ahc_find_pci_device(pci);
2168c2ecf20Sopenharmony_ci	if (entry == NULL)
2178c2ecf20Sopenharmony_ci		return (-ENODEV);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/*
2208c2ecf20Sopenharmony_ci	 * Allocate a softc for this card and
2218c2ecf20Sopenharmony_ci	 * set it up for attachment by our
2228c2ecf20Sopenharmony_ci	 * common detect routine.
2238c2ecf20Sopenharmony_ci	 */
2248c2ecf20Sopenharmony_ci	sprintf(buf, "ahc_pci:%d:%d:%d",
2258c2ecf20Sopenharmony_ci		ahc_get_pci_bus(pci),
2268c2ecf20Sopenharmony_ci		ahc_get_pci_slot(pci),
2278c2ecf20Sopenharmony_ci		ahc_get_pci_function(pci));
2288c2ecf20Sopenharmony_ci	name = kstrdup(buf, GFP_ATOMIC);
2298c2ecf20Sopenharmony_ci	if (name == NULL)
2308c2ecf20Sopenharmony_ci		return (-ENOMEM);
2318c2ecf20Sopenharmony_ci	ahc = ahc_alloc(NULL, name);
2328c2ecf20Sopenharmony_ci	if (ahc == NULL)
2338c2ecf20Sopenharmony_ci		return (-ENOMEM);
2348c2ecf20Sopenharmony_ci	if (pci_enable_device(pdev)) {
2358c2ecf20Sopenharmony_ci		ahc_free(ahc);
2368c2ecf20Sopenharmony_ci		return (-ENODEV);
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci	pci_set_master(pdev);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (sizeof(dma_addr_t) > 4
2418c2ecf20Sopenharmony_ci	    && ahc->features & AHC_LARGE_SCBS
2428c2ecf20Sopenharmony_ci	    && dma_set_mask(dev, mask_39bit) == 0
2438c2ecf20Sopenharmony_ci	    && dma_get_required_mask(dev) > DMA_BIT_MASK(32)) {
2448c2ecf20Sopenharmony_ci		ahc->flags |= AHC_39BIT_ADDRESSING;
2458c2ecf20Sopenharmony_ci	} else {
2468c2ecf20Sopenharmony_ci		if (dma_set_mask(dev, DMA_BIT_MASK(32))) {
2478c2ecf20Sopenharmony_ci			ahc_free(ahc);
2488c2ecf20Sopenharmony_ci			printk(KERN_WARNING "aic7xxx: No suitable DMA available.\n");
2498c2ecf20Sopenharmony_ci                	return (-ENODEV);
2508c2ecf20Sopenharmony_ci		}
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci	ahc->dev_softc = pci;
2538c2ecf20Sopenharmony_ci	ahc->dev = &pci->dev;
2548c2ecf20Sopenharmony_ci	error = ahc_pci_config(ahc, entry);
2558c2ecf20Sopenharmony_ci	if (error != 0) {
2568c2ecf20Sopenharmony_ci		ahc_free(ahc);
2578c2ecf20Sopenharmony_ci		return (-error);
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/*
2618c2ecf20Sopenharmony_ci	 * Second Function PCI devices need to inherit some
2628c2ecf20Sopenharmony_ci	 * settings from function 0.
2638c2ecf20Sopenharmony_ci	 */
2648c2ecf20Sopenharmony_ci	if ((ahc->features & AHC_MULTI_FUNC) && PCI_FUNC(pdev->devfn) != 0)
2658c2ecf20Sopenharmony_ci		ahc_linux_pci_inherit_flags(ahc);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, ahc);
2688c2ecf20Sopenharmony_ci	ahc_linux_register_host(ahc, &aic7xxx_driver_template);
2698c2ecf20Sopenharmony_ci	return (0);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/******************************* PCI Routines *********************************/
2738c2ecf20Sopenharmony_ciuint32_t
2748c2ecf20Sopenharmony_ciahc_pci_read_config(ahc_dev_softc_t pci, int reg, int width)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	switch (width) {
2778c2ecf20Sopenharmony_ci	case 1:
2788c2ecf20Sopenharmony_ci	{
2798c2ecf20Sopenharmony_ci		uint8_t retval;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		pci_read_config_byte(pci, reg, &retval);
2828c2ecf20Sopenharmony_ci		return (retval);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci	case 2:
2858c2ecf20Sopenharmony_ci	{
2868c2ecf20Sopenharmony_ci		uint16_t retval;
2878c2ecf20Sopenharmony_ci		pci_read_config_word(pci, reg, &retval);
2888c2ecf20Sopenharmony_ci		return (retval);
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	case 4:
2918c2ecf20Sopenharmony_ci	{
2928c2ecf20Sopenharmony_ci		uint32_t retval;
2938c2ecf20Sopenharmony_ci		pci_read_config_dword(pci, reg, &retval);
2948c2ecf20Sopenharmony_ci		return (retval);
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci	default:
2978c2ecf20Sopenharmony_ci		panic("ahc_pci_read_config: Read size too big");
2988c2ecf20Sopenharmony_ci		/* NOTREACHED */
2998c2ecf20Sopenharmony_ci		return (0);
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_civoid
3048c2ecf20Sopenharmony_ciahc_pci_write_config(ahc_dev_softc_t pci, int reg, uint32_t value, int width)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	switch (width) {
3078c2ecf20Sopenharmony_ci	case 1:
3088c2ecf20Sopenharmony_ci		pci_write_config_byte(pci, reg, value);
3098c2ecf20Sopenharmony_ci		break;
3108c2ecf20Sopenharmony_ci	case 2:
3118c2ecf20Sopenharmony_ci		pci_write_config_word(pci, reg, value);
3128c2ecf20Sopenharmony_ci		break;
3138c2ecf20Sopenharmony_ci	case 4:
3148c2ecf20Sopenharmony_ci		pci_write_config_dword(pci, reg, value);
3158c2ecf20Sopenharmony_ci		break;
3168c2ecf20Sopenharmony_ci	default:
3178c2ecf20Sopenharmony_ci		panic("ahc_pci_write_config: Write size too big");
3188c2ecf20Sopenharmony_ci		/* NOTREACHED */
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic struct pci_driver aic7xxx_pci_driver = {
3248c2ecf20Sopenharmony_ci	.name		= "aic7xxx",
3258c2ecf20Sopenharmony_ci	.probe		= ahc_linux_pci_dev_probe,
3268c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
3278c2ecf20Sopenharmony_ci	.suspend	= ahc_linux_pci_dev_suspend,
3288c2ecf20Sopenharmony_ci	.resume		= ahc_linux_pci_dev_resume,
3298c2ecf20Sopenharmony_ci#endif
3308c2ecf20Sopenharmony_ci	.remove		= ahc_linux_pci_dev_remove,
3318c2ecf20Sopenharmony_ci	.id_table	= ahc_linux_pci_id_table
3328c2ecf20Sopenharmony_ci};
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ciint
3358c2ecf20Sopenharmony_ciahc_linux_pci_init(void)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	return pci_register_driver(&aic7xxx_pci_driver);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_civoid
3418c2ecf20Sopenharmony_ciahc_linux_pci_exit(void)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	pci_unregister_driver(&aic7xxx_pci_driver);
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int
3478c2ecf20Sopenharmony_ciahc_linux_pci_reserve_io_region(struct ahc_softc *ahc, resource_size_t *base)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	if (aic7xxx_allow_memio == 0)
3508c2ecf20Sopenharmony_ci		return (ENOMEM);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	*base = pci_resource_start(ahc->dev_softc, 0);
3538c2ecf20Sopenharmony_ci	if (*base == 0)
3548c2ecf20Sopenharmony_ci		return (ENOMEM);
3558c2ecf20Sopenharmony_ci	if (!request_region(*base, 256, "aic7xxx"))
3568c2ecf20Sopenharmony_ci		return (ENOMEM);
3578c2ecf20Sopenharmony_ci	return (0);
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int
3618c2ecf20Sopenharmony_ciahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
3628c2ecf20Sopenharmony_ci				 resource_size_t *bus_addr,
3638c2ecf20Sopenharmony_ci				 uint8_t __iomem **maddr)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	resource_size_t	start;
3668c2ecf20Sopenharmony_ci	int	error;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	error = 0;
3698c2ecf20Sopenharmony_ci	start = pci_resource_start(ahc->dev_softc, 1);
3708c2ecf20Sopenharmony_ci	if (start != 0) {
3718c2ecf20Sopenharmony_ci		*bus_addr = start;
3728c2ecf20Sopenharmony_ci		if (!request_mem_region(start, 0x1000, "aic7xxx"))
3738c2ecf20Sopenharmony_ci			error = ENOMEM;
3748c2ecf20Sopenharmony_ci		if (error == 0) {
3758c2ecf20Sopenharmony_ci			*maddr = ioremap(start, 256);
3768c2ecf20Sopenharmony_ci			if (*maddr == NULL) {
3778c2ecf20Sopenharmony_ci				error = ENOMEM;
3788c2ecf20Sopenharmony_ci				release_mem_region(start, 0x1000);
3798c2ecf20Sopenharmony_ci			}
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci	} else
3828c2ecf20Sopenharmony_ci		error = ENOMEM;
3838c2ecf20Sopenharmony_ci	return (error);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ciint
3878c2ecf20Sopenharmony_ciahc_pci_map_registers(struct ahc_softc *ahc)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	uint32_t command;
3908c2ecf20Sopenharmony_ci	resource_size_t	base;
3918c2ecf20Sopenharmony_ci	uint8_t	__iomem *maddr;
3928c2ecf20Sopenharmony_ci	int	 error;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/*
3958c2ecf20Sopenharmony_ci	 * If its allowed, we prefer memory mapped access.
3968c2ecf20Sopenharmony_ci	 */
3978c2ecf20Sopenharmony_ci	command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, 4);
3988c2ecf20Sopenharmony_ci	command &= ~(PCIM_CMD_PORTEN|PCIM_CMD_MEMEN);
3998c2ecf20Sopenharmony_ci	base = 0;
4008c2ecf20Sopenharmony_ci	maddr = NULL;
4018c2ecf20Sopenharmony_ci	error = ahc_linux_pci_reserve_mem_region(ahc, &base, &maddr);
4028c2ecf20Sopenharmony_ci	if (error == 0) {
4038c2ecf20Sopenharmony_ci		ahc->platform_data->mem_busaddr = base;
4048c2ecf20Sopenharmony_ci		ahc->tag = BUS_SPACE_MEMIO;
4058c2ecf20Sopenharmony_ci		ahc->bsh.maddr = maddr;
4068c2ecf20Sopenharmony_ci		ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND,
4078c2ecf20Sopenharmony_ci				     command | PCIM_CMD_MEMEN, 4);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci		/*
4108c2ecf20Sopenharmony_ci		 * Do a quick test to see if memory mapped
4118c2ecf20Sopenharmony_ci		 * I/O is functioning correctly.
4128c2ecf20Sopenharmony_ci		 */
4138c2ecf20Sopenharmony_ci		if (ahc_pci_test_register_access(ahc) != 0) {
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci			printk("aic7xxx: PCI Device %d:%d:%d "
4168c2ecf20Sopenharmony_ci			       "failed memory mapped test.  Using PIO.\n",
4178c2ecf20Sopenharmony_ci			       ahc_get_pci_bus(ahc->dev_softc),
4188c2ecf20Sopenharmony_ci			       ahc_get_pci_slot(ahc->dev_softc),
4198c2ecf20Sopenharmony_ci			       ahc_get_pci_function(ahc->dev_softc));
4208c2ecf20Sopenharmony_ci			iounmap(maddr);
4218c2ecf20Sopenharmony_ci			release_mem_region(ahc->platform_data->mem_busaddr,
4228c2ecf20Sopenharmony_ci					   0x1000);
4238c2ecf20Sopenharmony_ci			ahc->bsh.maddr = NULL;
4248c2ecf20Sopenharmony_ci			maddr = NULL;
4258c2ecf20Sopenharmony_ci		} else
4268c2ecf20Sopenharmony_ci			command |= PCIM_CMD_MEMEN;
4278c2ecf20Sopenharmony_ci	} else {
4288c2ecf20Sopenharmony_ci		printk("aic7xxx: PCI%d:%d:%d MEM region 0x%llx "
4298c2ecf20Sopenharmony_ci		       "unavailable. Cannot memory map device.\n",
4308c2ecf20Sopenharmony_ci		       ahc_get_pci_bus(ahc->dev_softc),
4318c2ecf20Sopenharmony_ci		       ahc_get_pci_slot(ahc->dev_softc),
4328c2ecf20Sopenharmony_ci		       ahc_get_pci_function(ahc->dev_softc),
4338c2ecf20Sopenharmony_ci		       (unsigned long long)base);
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	/*
4378c2ecf20Sopenharmony_ci	 * We always prefer memory mapped access.
4388c2ecf20Sopenharmony_ci	 */
4398c2ecf20Sopenharmony_ci	if (maddr == NULL) {
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		error = ahc_linux_pci_reserve_io_region(ahc, &base);
4428c2ecf20Sopenharmony_ci		if (error == 0) {
4438c2ecf20Sopenharmony_ci			ahc->tag = BUS_SPACE_PIO;
4448c2ecf20Sopenharmony_ci			ahc->bsh.ioport = (u_long)base;
4458c2ecf20Sopenharmony_ci			command |= PCIM_CMD_PORTEN;
4468c2ecf20Sopenharmony_ci		} else {
4478c2ecf20Sopenharmony_ci			printk("aic7xxx: PCI%d:%d:%d IO region 0x%llx[0..255] "
4488c2ecf20Sopenharmony_ci			       "unavailable. Cannot map device.\n",
4498c2ecf20Sopenharmony_ci			       ahc_get_pci_bus(ahc->dev_softc),
4508c2ecf20Sopenharmony_ci			       ahc_get_pci_slot(ahc->dev_softc),
4518c2ecf20Sopenharmony_ci			       ahc_get_pci_function(ahc->dev_softc),
4528c2ecf20Sopenharmony_ci			       (unsigned long long)base);
4538c2ecf20Sopenharmony_ci		}
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci	ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, 4);
4568c2ecf20Sopenharmony_ci	return (error);
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ciint
4608c2ecf20Sopenharmony_ciahc_pci_map_int(struct ahc_softc *ahc)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	int error;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	error = request_irq(ahc->dev_softc->irq, ahc_linux_isr,
4658c2ecf20Sopenharmony_ci			    IRQF_SHARED, "aic7xxx", ahc);
4668c2ecf20Sopenharmony_ci	if (error == 0)
4678c2ecf20Sopenharmony_ci		ahc->platform_data->irq = ahc->dev_softc->irq;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	return (-error);
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
472