1 /* 2 * Linux driver attachment glue for aic7770 based controllers. 3 * 4 * Copyright (c) 2000-2003 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. Redistributions in binary form must reproduce at minimum a disclaimer 14 * substantially similar to the "NO WARRANTY" disclaimer below 15 * ("Disclaimer") and any redistribution must be conditioned upon 16 * including a substantially similar Disclaimer requirement for further 17 * binary redistribution. 18 * 3. Neither the names of the above-listed copyright holders nor the names 19 * of any contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * Alternatively, this software may be distributed under the terms of the 23 * GNU General Public License ("GPL") version 2 as published by the Free 24 * Software Foundation. 25 * 26 * NO WARRANTY 27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 28 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 30 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 31 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 36 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGES. 38 * 39 * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7770_osm.c#14 $ 40 */ 41 42 #include "aic7xxx_osm.h" 43 44 #include <linux/device.h> 45 #include <linux/eisa.h> 46 47 #define EISA_MFCTR_CHAR0(ID) (char)(((ID>>26) & 0x1F) | '@') /* Bits 26-30 */ 48 #define EISA_MFCTR_CHAR1(ID) (char)(((ID>>21) & 0x1F) | '@') /* Bits 21-25 */ 49 #define EISA_MFCTR_CHAR2(ID) (char)(((ID>>16) & 0x1F) | '@') /* Bits 16-20 */ 50 #define EISA_PRODUCT_ID(ID) (short)((ID>>4) & 0xFFF) /* Bits 4-15 */ 51 #define EISA_REVISION_ID(ID) (uint8_t)(ID & 0x0F) /* Bits 0-3 */ 52 53 static int aic7770_eisa_dev_probe(struct device *dev); 54 static int aic7770_eisa_dev_remove(struct device *dev); 55 static struct eisa_driver aic7770_driver = { 56 .driver = { 57 .name = "aic7xxx", 58 .probe = aic7770_eisa_dev_probe, 59 .remove = aic7770_eisa_dev_remove, 60 } 61 }; 62 63 typedef struct device *aic7770_dev_t; 64 65 static int aic7770_linux_config(struct aic7770_identity *entry, 66 aic7770_dev_t dev, u_int eisaBase); 67 68 int 69 ahc_linux_eisa_init(void) 70 { 71 struct eisa_device_id *eid; 72 struct aic7770_identity *id; 73 int i; 74 75 if (aic7xxx_probe_eisa_vl == 0) 76 return -ENODEV; 77 78 /* 79 * Linux requires the EISA IDs to be specified in 80 * the EISA ID string format. Perform the conversion 81 * and setup a table with a NUL terminal entry. 82 */ 83 aic7770_driver.id_table = malloc(sizeof(struct eisa_device_id) * 84 (ahc_num_aic7770_devs + 1), 85 M_DEVBUF, M_NOWAIT); 86 if (aic7770_driver.id_table == NULL) 87 return -ENOMEM; 88 89 for (eid = (struct eisa_device_id *)aic7770_driver.id_table, 90 id = aic7770_ident_table, i = 0; 91 i < ahc_num_aic7770_devs; eid++, id++, i++) { 92 93 sprintf(eid->sig, "%c%c%c%03X%01X", 94 EISA_MFCTR_CHAR0(id->full_id), 95 EISA_MFCTR_CHAR1(id->full_id), 96 EISA_MFCTR_CHAR2(id->full_id), 97 EISA_PRODUCT_ID(id->full_id), 98 EISA_REVISION_ID(id->full_id)); 99 eid->driver_data = i; 100 } 101 eid->sig[0] = 0; 102 103 return eisa_driver_register(&aic7770_driver); 104 } 105 106 void 107 ahc_linux_eisa_exit(void) 108 { 109 if(aic7xxx_probe_eisa_vl != 0 && aic7770_driver.id_table != NULL) { 110 eisa_driver_unregister(&aic7770_driver); 111 free(aic7770_driver.id_table, M_DEVBUF); 112 } 113 } 114 115 static int 116 aic7770_linux_config(struct aic7770_identity *entry, aic7770_dev_t dev, 117 u_int eisaBase) 118 { 119 struct ahc_softc *ahc; 120 char buf[80]; 121 char *name; 122 int error; 123 124 /* 125 * Allocate a softc for this card and 126 * set it up for attachment by our 127 * common detect routine. 128 */ 129 sprintf(buf, "ahc_eisa:%d", eisaBase >> 12); 130 name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT); 131 if (name == NULL) 132 return (ENOMEM); 133 strcpy(name, buf); 134 ahc = ahc_alloc(&aic7xxx_driver_template, name); 135 if (ahc == NULL) 136 return (ENOMEM); 137 error = aic7770_config(ahc, entry, eisaBase); 138 if (error != 0) { 139 ahc->bsh.ioport = 0; 140 ahc_free(ahc); 141 return (error); 142 } 143 144 dev->driver_data = (void *)ahc; 145 if (aic7xxx_detect_complete) 146 error = ahc_linux_register_host(ahc, &aic7xxx_driver_template); 147 return (error); 148 } 149 150 int 151 aic7770_map_registers(struct ahc_softc *ahc, u_int port) 152 { 153 /* 154 * Lock out other contenders for our i/o space. 155 */ 156 if (request_region(port, AHC_EISA_IOSIZE, "aic7xxx") == 0) 157 return (ENOMEM); 158 ahc->tag = BUS_SPACE_PIO; 159 ahc->bsh.ioport = port; 160 return (0); 161 } 162 163 int 164 aic7770_map_int(struct ahc_softc *ahc, u_int irq) 165 { 166 int error; 167 int shared; 168 169 shared = 0; 170 if ((ahc->flags & AHC_EDGE_INTERRUPT) == 0) 171 shared = SA_SHIRQ; 172 173 error = request_irq(irq, ahc_linux_isr, shared, "aic7xxx", ahc); 174 if (error == 0) 175 ahc->platform_data->irq = irq; 176 177 return (-error); 178 } 179 180 static int 181 aic7770_eisa_dev_probe(struct device *dev) 182 { 183 struct eisa_device *edev; 184 185 edev = to_eisa_device(dev); 186 return (aic7770_linux_config(aic7770_ident_table + edev->id.driver_data, 187 dev, edev->base_addr+AHC_EISA_SLOT_OFFSET)); 188 } 189 190 static int 191 aic7770_eisa_dev_remove(struct device *dev) 192 { 193 struct ahc_softc *ahc = dev_get_drvata(dev); 194 u_long s; 195 196 ahc_lock(ahc, &s); 197 ahc_intr_enable(ahc, FALSE); 198 ahc_unlock(ahc, &s); 199 ahc_free(ahc); 200 201 return (0); 202 } 203