aic7xxx_osm_pci.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:11k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  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. 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/aic7xxx_osm_pci.c#32 $
  40.  */
  41. #include "aic7xxx_osm.h"
  42. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
  43. struct pci_device_id
  44. {
  45. };
  46. #endif
  47. static int ahc_linux_pci_dev_probe(struct pci_dev *pdev,
  48. const struct pci_device_id *ent);
  49. static int ahc_linux_pci_reserve_io_region(struct ahc_softc *ahc,
  50. u_long *base);
  51. #ifdef MMAPIO
  52. static int ahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
  53.  u_long *bus_addr,
  54.  uint8_t **maddr);
  55. #endif /* MMAPIO */
  56. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
  57. static void ahc_linux_pci_dev_remove(struct pci_dev *pdev);
  58. /* We do our own ID filtering.  So, grab all SCSI storage class devices. */
  59. static struct pci_device_id ahc_linux_pci_id_table[] = {
  60. {
  61. 0x9004, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
  62. PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
  63. },
  64. {
  65. 0x9005, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
  66. PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
  67. },
  68. { 0 }
  69. };
  70. MODULE_DEVICE_TABLE(pci, ahc_linux_pci_id_table);
  71. struct pci_driver aic7xxx_pci_driver = {
  72. name: "aic7xxx",
  73. probe: ahc_linux_pci_dev_probe,
  74. remove: ahc_linux_pci_dev_remove,
  75. id_table: ahc_linux_pci_id_table
  76. };
  77. static void
  78. ahc_linux_pci_dev_remove(struct pci_dev *pdev)
  79. {
  80. struct ahc_softc *ahc;
  81. u_long l;
  82. /*
  83.  * We should be able to just perform
  84.  * the free directly, but check our
  85.  * list for extra sanity.
  86.  */
  87. ahc_list_lock(&l);
  88. ahc = ahc_find_softc((struct ahc_softc *)pdev->driver_data);
  89. if (ahc != NULL) {
  90. u_long s;
  91. ahc_lock(ahc, &s);
  92. ahc_intr_enable(ahc, FALSE);
  93. ahc_unlock(ahc, &s);
  94. ahc_free(ahc);
  95. }
  96. ahc_list_unlock(&l);
  97. }
  98. #endif /* !LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) */
  99. static int
  100. ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
  101. {
  102. char  buf[80];
  103. struct  ahc_softc *ahc;
  104. ahc_dev_softc_t  pci;
  105. struct  ahc_pci_identity *entry;
  106. char *name;
  107. int  error;
  108. /*
  109.  * Some BIOSen report the same device multiple times.
  110.  */
  111. TAILQ_FOREACH(ahc, &ahc_tailq, links) {
  112. struct pci_dev *probed_pdev;
  113. probed_pdev = ahc->dev_softc;
  114. if (probed_pdev->bus->number == pdev->bus->number
  115.  && probed_pdev->devfn == pdev->devfn)
  116. break;
  117. }
  118. if (ahc != NULL) {
  119. /* Skip duplicate. */
  120. return (-ENODEV);
  121. }
  122. pci = pdev;
  123. entry = ahc_find_pci_device(pci);
  124. if (entry == NULL)
  125. return (-ENODEV);
  126. /*
  127.  * Allocate a softc for this card and
  128.  * set it up for attachment by our
  129.  * common detect routine.
  130.  */
  131. sprintf(buf, "ahc_pci:%d:%d:%d",
  132. ahc_get_pci_bus(pci),
  133. ahc_get_pci_slot(pci),
  134. ahc_get_pci_function(pci));
  135. name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
  136. if (name == NULL)
  137. return (-ENOMEM);
  138. strcpy(name, buf);
  139. ahc = ahc_alloc(NULL, name);
  140. if (ahc == NULL)
  141. return (-ENOMEM);
  142. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
  143. if (pci_enable_device(pdev)) {
  144. ahc_free(ahc);
  145. return (-ENODEV);
  146. }
  147. pci_set_master(pdev);
  148. if (sizeof(bus_addr_t) > 4
  149. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
  150.  && ahc_linux_get_memsize() > 0x80000000
  151.  && pci_set_dma_mask(pdev, 0x7FFFFFFFFFULL) == 0) {
  152. #else
  153.  && ahc_linux_get_memsize() > 0x80000000) {
  154. ahc->dev_softc->dma_mask = 
  155.     (bus_addr_t)(0x7FFFFFFFFFULL & (bus_addr_t)~0);
  156. #endif
  157. ahc->flags |= AHC_39BIT_ADDRESSING;
  158. ahc->platform_data->hw_dma_mask =
  159.     (bus_addr_t)(0x7FFFFFFFFFULL & (bus_addr_t)~0);
  160. }
  161. #endif
  162. ahc->dev_softc = pci;
  163. error = ahc_pci_config(ahc, entry);
  164. if (error != 0) {
  165. ahc_free(ahc);
  166. return (-error);
  167. }
  168. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
  169. pci_set_drvdata(pdev, ahc);
  170. if (aic7xxx_detect_complete)
  171. ahc_linux_register_host(ahc, aic7xxx_driver_template);
  172. #endif
  173. return (0);
  174. }
  175. int
  176. ahc_linux_pci_probe(Scsi_Host_Template *template)
  177. {
  178. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
  179. return (pci_module_init(&aic7xxx_pci_driver));
  180. #else
  181. struct pci_dev *pdev;
  182. u_int class;
  183. int found;
  184. /* If we don't have a PCI bus, we can't find any adapters. */
  185. if (pci_present() == 0)
  186. return (0);
  187. found = 0;
  188. pdev = NULL;
  189. class = PCI_CLASS_STORAGE_SCSI << 8;
  190. while ((pdev = pci_find_class(class, pdev)) != NULL) {
  191. ahc_dev_softc_t pci;
  192. int error;
  193. pci = pdev;
  194. error = ahc_linux_pci_dev_probe(pdev, /*pci_devid*/NULL);
  195. if (error == 0)
  196. found++;
  197. }
  198. return (found);
  199. #endif
  200. }
  201. static int
  202. ahc_linux_pci_reserve_io_region(struct ahc_softc *ahc, u_long *base)
  203. {
  204. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
  205. *base = pci_resource_start(ahc->dev_softc, 0);
  206. #else
  207. *base = ahc_pci_read_config(ahc->dev_softc, PCIR_MAPS, 4);
  208. *base &= PCI_BASE_ADDRESS_IO_MASK;
  209. #endif
  210. if (*base == 0)
  211. return (ENOMEM);
  212. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
  213. if (check_region(*base, 256) != 0)
  214. return (ENOMEM);
  215. else
  216. request_region(*base, 256, "aic7xxx");
  217. #else
  218. if (request_region(*base, 256, "aic7xxx") == 0)
  219. return (ENOMEM);
  220. #endif
  221. return (0);
  222. }
  223. #ifdef MMAPIO
  224. static int
  225. ahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
  226.  u_long *bus_addr,
  227.  uint8_t **maddr)
  228. {
  229. u_long start;
  230. u_long base_page;
  231. u_long base_offset;
  232. int error;
  233. error = 0;
  234. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
  235. start = pci_resource_start(ahc->dev_softc, 1);
  236. base_page = start & PAGE_MASK;
  237. base_offset = start - base_page;
  238. #else
  239. start = ahc_pci_read_config(ahc->dev_softc, PCIR_MAPS+4, 4);
  240. base_offset = start & PCI_BASE_ADDRESS_MEM_MASK;
  241. base_page = base_offset & PAGE_MASK;
  242. base_offset -= base_page;
  243. #endif
  244. if (start != 0) {
  245. *bus_addr = start;
  246. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
  247. if (request_mem_region(start, 0x1000, "aic7xxx") == 0)
  248. error = ENOMEM;
  249. #endif
  250. if (error == 0) {
  251. *maddr = ioremap_nocache(base_page, base_offset + 256);
  252. if (*maddr == NULL) {
  253. error = ENOMEM;
  254. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
  255. release_mem_region(start, 0x1000);
  256. #endif
  257. } else
  258. *maddr += base_offset;
  259. }
  260. } else
  261. error = ENOMEM;
  262. return (error);
  263. }
  264. #endif /* MMAPIO */
  265. int
  266. ahc_pci_map_registers(struct ahc_softc *ahc)
  267. {
  268. uint32_t command;
  269. u_long  base;
  270. uint8_t *maddr;
  271. int  error;
  272. /*
  273.  * If its allowed, we prefer memory mapped access.
  274.  */
  275. command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, 4);
  276. command &= ~(PCIM_CMD_PORTEN|PCIM_CMD_MEMEN);
  277. base = 0;
  278. maddr = NULL;
  279. #ifdef MMAPIO
  280. error = ahc_linux_pci_reserve_mem_region(ahc, &base, &maddr);
  281. if (error == 0) {
  282. ahc->platform_data->mem_busaddr = base;
  283. ahc->tag = BUS_SPACE_MEMIO;
  284. ahc->bsh.maddr = maddr;
  285. ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND,
  286.      command | PCIM_CMD_MEMEN, 4);
  287. /*
  288.  * Do a quick test to see if memory mapped
  289.  * I/O is functioning correctly.
  290.  */
  291. if (ahc_inb(ahc, HCNTRL) == 0xFF) {
  292. printf("aic7xxx: PCI Device %d:%d:%d "
  293.        "failed memory mapped testn",
  294.        ahc_get_pci_bus(ahc->dev_softc),
  295.        ahc_get_pci_slot(ahc->dev_softc),
  296.        ahc_get_pci_function(ahc->dev_softc));
  297. iounmap((void *)((u_long)maddr & PAGE_MASK));
  298. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
  299. release_mem_region(ahc->platform_data->mem_busaddr,
  300.    0x1000);
  301. #endif
  302. ahc->bsh.maddr = NULL;
  303. maddr = NULL;
  304. } else
  305. command |= PCIM_CMD_MEMEN;
  306. } else {
  307. printf("aic7xxx: PCI%d:%d:%d MEM region 0x%lx "
  308.        "unavailable. Cannot memory map device.n",
  309.        ahc_get_pci_bus(ahc->dev_softc),
  310.        ahc_get_pci_slot(ahc->dev_softc),
  311.        ahc_get_pci_function(ahc->dev_softc),
  312.        base);
  313. }
  314. #endif /* MMAPIO */
  315. /*
  316.  * We always prefer memory mapped access.
  317.  */
  318. if (maddr == NULL) {
  319. error = ahc_linux_pci_reserve_io_region(ahc, &base);
  320. if (error == 0) {
  321. ahc->tag = BUS_SPACE_PIO;
  322. ahc->bsh.ioport = base;
  323. command |= PCIM_CMD_PORTEN;
  324. } else {
  325. printf("aic7xxx: PCI%d:%d:%d IO region 0x%lx[0..255] "
  326.        "unavailable. Cannot map device.n",
  327.        ahc_get_pci_bus(ahc->dev_softc),
  328.        ahc_get_pci_slot(ahc->dev_softc),
  329.        ahc_get_pci_function(ahc->dev_softc),
  330.        base);
  331. }
  332. }
  333. ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, 4);
  334. return (error);
  335. }
  336. int
  337. ahc_pci_map_int(struct ahc_softc *ahc)
  338. {
  339. int error;
  340. error = request_irq(ahc->dev_softc->irq, ahc_linux_isr,
  341.     SA_SHIRQ, "aic7xxx", ahc);
  342. if (error == 0)
  343. ahc->platform_data->irq = ahc->dev_softc->irq;
  344. return (-error);
  345. }
  346. void
  347. ahc_power_state_change(struct ahc_softc *ahc, ahc_power_state new_state)
  348. {
  349. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
  350. pci_set_power_state(ahc->dev_softc, new_state);
  351. #else
  352. uint32_t cap;
  353. u_int cap_offset;
  354. /*
  355.  * Traverse the capability list looking for
  356.  * the power management capability.
  357.  */
  358. cap = 0;
  359. cap_offset = ahc_pci_read_config(ahc->dev_softc,
  360.  PCIR_CAP_PTR, /*bytes*/1);
  361. while (cap_offset != 0) {
  362. cap = ahc_pci_read_config(ahc->dev_softc,
  363.   cap_offset, /*bytes*/4);
  364. if ((cap & 0xFF) == 1
  365.  && ((cap >> 16) & 0x3) > 0) {
  366. uint32_t pm_control;
  367. pm_control = ahc_pci_read_config(ahc->dev_softc,
  368.  cap_offset + 4,
  369.  /*bytes*/4);
  370. pm_control &= ~0x3;
  371. pm_control |= new_state;
  372. ahc_pci_write_config(ahc->dev_softc,
  373.      cap_offset + 4,
  374.      pm_control, /*bytes*/2);
  375. break;
  376. }
  377. cap_offset = (cap >> 8) & 0xFF;
  378. }
  379. #endif 
  380. }