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 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) 45 #include <linux/device.h> 46 #include <linux/eisa.h> 47 48 #define EISA_MFCTR_CHAR0(ID) (char)(((ID>>26) & 0x1F) | '@') /* Bits 26-30 */ 49 #define EISA_MFCTR_CHAR1(ID) (char)(((ID>>21) & 0x1F) | '@') /* Bits 21-25 */ 50 #define EISA_MFCTR_CHAR2(ID) (char)(((ID>>16) & 0x1F) | '@') /* Bits 16-20 */ 51 #define EISA_PRODUCT_ID(ID) (short)((ID>>4) & 0xFFF) /* Bits 4-15 */ 52 #define EISA_REVISION_ID(ID) (uint8_t)(ID & 0x0F) /* Bits 0-3 */ 53 54 static int aic7770_eisa_dev_probe(struct device *dev); 55 static int aic7770_eisa_dev_remove(struct device *dev); 56 static struct eisa_driver aic7770_driver = { 57 .driver = { 58 .name = "aic7xxx", 59 .probe = aic7770_eisa_dev_probe, 60 .remove = aic7770_eisa_dev_remove, 61 } 62 }; 63 64 typedef struct device *aic7770_dev_t; 65 #else 66 #define MINSLOT 1 67 #define NUMSLOTS 16 68 #define IDOFFSET 0x80 69 70 typedef void *aic7770_dev_t; 71 #endif 72 73 static int aic7770_linux_config(struct aic7770_identity *entry, 74 aic7770_dev_t dev, u_int eisaBase); 75 76 int 77 ahc_linux_eisa_init(void) 78 { 79 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) 80 struct eisa_device_id *eid; 81 struct aic7770_identity *id; 82 int i; 83 84 if (aic7xxx_probe_eisa_vl == 0) 85 return -ENODEV; 86 87 /* 88 * Linux requires the EISA IDs to be specified in 89 * the EISA ID string format. Perform the conversion 90 * and setup a table with a NUL terminal entry. 91 */ 92 aic7770_driver.id_table = malloc(sizeof(struct eisa_device_id) * 93 (ahc_num_aic7770_devs + 1), 94 M_DEVBUF, M_NOWAIT); 95 if (aic7770_driver.id_table == NULL) 96 return -ENOMEM; 97 98 for (eid = (struct eisa_device_id *)aic7770_driver.id_table, 99 id = aic7770_ident_table, i = 0; 100 i < ahc_num_aic7770_devs; eid++, id++, i++) { 101 102 sprintf(eid->sig, "%c%c%c%03X%01X", 103 EISA_MFCTR_CHAR0(id->full_id), 104 EISA_MFCTR_CHAR1(id->full_id), 105 EISA_MFCTR_CHAR2(id->full_id), 106 EISA_PRODUCT_ID(id->full_id), 107 EISA_REVISION_ID(id->full_id)); 108 eid->driver_data = i; 109 } 110 eid->sig[0] = 0; 111 112 return eisa_driver_register(&aic7770_driver); 113 #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) */ 114 struct aic7770_identity *entry; 115 u_int slot; 116 u_int eisaBase; 117 u_int i; 118 int ret = -ENODEV; 119 120 if (aic7xxx_probe_eisa_vl == 0) 121 return ret; 122 123 eisaBase = 0x1000 + AHC_EISA_SLOT_OFFSET; 124 for (slot = 1; slot < NUMSLOTS; eisaBase+=0x1000, slot++) { 125 uint32_t eisa_id; 126 size_t id_size; 127 128 if (request_region(eisaBase, AHC_EISA_IOSIZE, "aic7xxx") == 0) 129 continue; 130 131 eisa_id = 0; 132 id_size = sizeof(eisa_id); 133 for (i = 0; i < 4; i++) { 134 /* VLcards require priming*/ 135 outb(0x80 + i, eisaBase + IDOFFSET); 136 eisa_id |= inb(eisaBase + IDOFFSET + i) 137 << ((id_size-i-1) * 8); 138 } 139 release_region(eisaBase, AHC_EISA_IOSIZE); 140 if (eisa_id & 0x80000000) 141 continue; /* no EISA card in slot */ 142 143 entry = aic7770_find_device(eisa_id); 144 if (entry != NULL) { 145 aic7770_linux_config(entry, NULL, eisaBase); 146 ret = 0; 147 } 148 } 149 return ret; 150 #endif 151 } 152 153 void 154 ahc_linux_eisa_exit(void) 155 { 156 if(aic7xxx_probe_eisa_vl != 0 && aic7770_driver.id_table != NULL) { 157 eisa_driver_unregister(&aic7770_driver); 158 free(aic7770_driver.id_table, M_DEVBUF); 159 } 160 } 161 162 static int 163 aic7770_linux_config(struct aic7770_identity *entry, aic7770_dev_t dev, 164 u_int eisaBase) 165 { 166 struct ahc_softc *ahc; 167 char buf[80]; 168 char *name; 169 int error; 170 171 /* 172 * Allocate a softc for this card and 173 * set it up for attachment by our 174 * common detect routine. 175 */ 176 sprintf(buf, "ahc_eisa:%d", eisaBase >> 12); 177 name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT); 178 if (name == NULL) 179 return (ENOMEM); 180 strcpy(name, buf); 181 ahc = ahc_alloc(&aic7xxx_driver_template, name); 182 if (ahc == NULL) 183 return (ENOMEM); 184 error = aic7770_config(ahc, entry, eisaBase); 185 if (error != 0) { 186 ahc->bsh.ioport = 0; 187 ahc_free(ahc); 188 return (error); 189 } 190 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) 191 dev->driver_data = (void *)ahc; 192 if (aic7xxx_detect_complete) 193 error = ahc_linux_register_host(ahc, &aic7xxx_driver_template); 194 #endif 195 return (error); 196 } 197 198 int 199 aic7770_map_registers(struct ahc_softc *ahc, u_int port) 200 { 201 /* 202 * Lock out other contenders for our i/o space. 203 */ 204 if (request_region(port, AHC_EISA_IOSIZE, "aic7xxx") == 0) 205 return (ENOMEM); 206 ahc->tag = BUS_SPACE_PIO; 207 ahc->bsh.ioport = port; 208 return (0); 209 } 210 211 int 212 aic7770_map_int(struct ahc_softc *ahc, u_int irq) 213 { 214 int error; 215 int shared; 216 217 shared = 0; 218 if ((ahc->flags & AHC_EDGE_INTERRUPT) == 0) 219 shared = SA_SHIRQ; 220 221 error = request_irq(irq, ahc_linux_isr, shared, "aic7xxx", ahc); 222 if (error == 0) 223 ahc->platform_data->irq = irq; 224 225 return (-error); 226 } 227 228 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) 229 static int 230 aic7770_eisa_dev_probe(struct device *dev) 231 { 232 struct eisa_device *edev; 233 234 edev = to_eisa_device(dev); 235 return (aic7770_linux_config(aic7770_ident_table + edev->id.driver_data, 236 dev, edev->base_addr+AHC_EISA_SLOT_OFFSET)); 237 } 238 239 static int 240 aic7770_eisa_dev_remove(struct device *dev) 241 { 242 struct ahc_softc *ahc; 243 u_long l; 244 245 /* 246 * We should be able to just perform 247 * the free directly, but check our 248 * list for extra sanity. 249 */ 250 ahc_list_lock(&l); 251 ahc = ahc_find_softc((struct ahc_softc *)dev->driver_data); 252 if (ahc != NULL) { 253 u_long s; 254 255 ahc_lock(ahc, &s); 256 ahc_intr_enable(ahc, FALSE); 257 ahc_unlock(ahc, &s); 258 ahc_free(ahc); 259 } 260 ahc_list_unlock(&l); 261 262 return (0); 263 } 264 #endif 265