18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/scsi/arm/arxescsi.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1997-2000 Russell King, Stefan Hanske 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This driver is based on experimentation. Hence, it may have made 88c2ecf20Sopenharmony_ci * assumptions about the particular card that I have available, and 98c2ecf20Sopenharmony_ci * may not be reliable! 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Changelog: 128c2ecf20Sopenharmony_ci * 30-08-1997 RMK 0.0.0 Created, READONLY version as cumana_2.c 138c2ecf20Sopenharmony_ci * 22-01-1998 RMK 0.0.1 Updated to 2.1.80 148c2ecf20Sopenharmony_ci * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. 158c2ecf20Sopenharmony_ci * 11-06-1998 SH 0.0.2 Changed to support ARXE 16-bit SCSI card 168c2ecf20Sopenharmony_ci * enabled writing 178c2ecf20Sopenharmony_ci * 01-01-2000 SH 0.1.0 Added *real* pseudo dma writing 188c2ecf20Sopenharmony_ci * (arxescsi_pseudo_dma_write) 198c2ecf20Sopenharmony_ci * 02-04-2000 RMK 0.1.1 Updated for new error handling code. 208c2ecf20Sopenharmony_ci * 22-10-2000 SH Updated for new registering scheme. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 248c2ecf20Sopenharmony_ci#include <linux/kernel.h> 258c2ecf20Sopenharmony_ci#include <linux/string.h> 268c2ecf20Sopenharmony_ci#include <linux/ioport.h> 278c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 288c2ecf20Sopenharmony_ci#include <linux/unistd.h> 298c2ecf20Sopenharmony_ci#include <linux/stat.h> 308c2ecf20Sopenharmony_ci#include <linux/delay.h> 318c2ecf20Sopenharmony_ci#include <linux/init.h> 328c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <asm/dma.h> 358c2ecf20Sopenharmony_ci#include <asm/io.h> 368c2ecf20Sopenharmony_ci#include <asm/ecard.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include "../scsi.h" 398c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 408c2ecf20Sopenharmony_ci#include "fas216.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct arxescsi_info { 438c2ecf20Sopenharmony_ci FAS216_Info info; 448c2ecf20Sopenharmony_ci struct expansion_card *ec; 458c2ecf20Sopenharmony_ci void __iomem *base; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define DMADATA_OFFSET (0x200) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define DMASTAT_OFFSET (0x600) 518c2ecf20Sopenharmony_ci#define DMASTAT_DRQ (1 << 0) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define CSTATUS_IRQ (1 << 0) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define VERSION "1.10 (23/01/2003 2.5.57)" 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type) 598c2ecf20Sopenharmony_ci * Purpose : initialises DMA/PIO 608c2ecf20Sopenharmony_ci * Params : host - host 618c2ecf20Sopenharmony_ci * SCpnt - command 628c2ecf20Sopenharmony_ci * direction - DMA on to/off of card 638c2ecf20Sopenharmony_ci * min_type - minimum DMA support that we must have for this transfer 648c2ecf20Sopenharmony_ci * Returns : 0 if we should not set CMD_WITHDMA for transfer info command 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic fasdmatype_t 678c2ecf20Sopenharmony_ciarxescsi_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp, 688c2ecf20Sopenharmony_ci fasdmadir_t direction, fasdmatype_t min_type) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci /* 718c2ecf20Sopenharmony_ci * We don't do real DMA 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci return fasdma_pseudo; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void arxescsi_pseudo_dma_write(unsigned char *addr, void __iomem *base) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci __asm__ __volatile__( 798c2ecf20Sopenharmony_ci " stmdb sp!, {r0-r12}\n" 808c2ecf20Sopenharmony_ci " mov r3, %0\n" 818c2ecf20Sopenharmony_ci " mov r1, %1\n" 828c2ecf20Sopenharmony_ci " add r2, r1, #512\n" 838c2ecf20Sopenharmony_ci " mov r4, #256\n" 848c2ecf20Sopenharmony_ci ".loop_1: ldmia r3!, {r6, r8, r10, r12}\n" 858c2ecf20Sopenharmony_ci " mov r5, r6, lsl #16\n" 868c2ecf20Sopenharmony_ci " mov r7, r8, lsl #16\n" 878c2ecf20Sopenharmony_ci ".loop_2: ldrb r0, [r1, #1536]\n" 888c2ecf20Sopenharmony_ci " tst r0, #1\n" 898c2ecf20Sopenharmony_ci " beq .loop_2\n" 908c2ecf20Sopenharmony_ci " stmia r2, {r5-r8}\n\t" 918c2ecf20Sopenharmony_ci " mov r9, r10, lsl #16\n" 928c2ecf20Sopenharmony_ci " mov r11, r12, lsl #16\n" 938c2ecf20Sopenharmony_ci ".loop_3: ldrb r0, [r1, #1536]\n" 948c2ecf20Sopenharmony_ci " tst r0, #1\n" 958c2ecf20Sopenharmony_ci " beq .loop_3\n" 968c2ecf20Sopenharmony_ci " stmia r2, {r9-r12}\n" 978c2ecf20Sopenharmony_ci " subs r4, r4, #16\n" 988c2ecf20Sopenharmony_ci " bne .loop_1\n" 998c2ecf20Sopenharmony_ci " ldmia sp!, {r0-r12}\n" 1008c2ecf20Sopenharmony_ci : 1018c2ecf20Sopenharmony_ci : "r" (addr), "r" (base)); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer) 1068c2ecf20Sopenharmony_ci * Purpose : handles pseudo DMA 1078c2ecf20Sopenharmony_ci * Params : host - host 1088c2ecf20Sopenharmony_ci * SCpnt - command 1098c2ecf20Sopenharmony_ci * direction - DMA on to/off of card 1108c2ecf20Sopenharmony_ci * transfer - minimum number of bytes we expect to transfer 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_cistatic void 1138c2ecf20Sopenharmony_ciarxescsi_dma_pseudo(struct Scsi_Host *host, struct scsi_pointer *SCp, 1148c2ecf20Sopenharmony_ci fasdmadir_t direction, int transfer) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; 1178c2ecf20Sopenharmony_ci unsigned int length, error = 0; 1188c2ecf20Sopenharmony_ci void __iomem *base = info->info.scsi.io_base; 1198c2ecf20Sopenharmony_ci unsigned char *addr; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci length = SCp->this_residual; 1228c2ecf20Sopenharmony_ci addr = SCp->ptr; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (direction == DMA_OUT) { 1258c2ecf20Sopenharmony_ci unsigned int word; 1268c2ecf20Sopenharmony_ci while (length > 256) { 1278c2ecf20Sopenharmony_ci if (readb(base + 0x80) & STAT_INT) { 1288c2ecf20Sopenharmony_ci error = 1; 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci arxescsi_pseudo_dma_write(addr, base); 1328c2ecf20Sopenharmony_ci addr += 256; 1338c2ecf20Sopenharmony_ci length -= 256; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (!error) 1378c2ecf20Sopenharmony_ci while (length > 0) { 1388c2ecf20Sopenharmony_ci if (readb(base + 0x80) & STAT_INT) 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) 1428c2ecf20Sopenharmony_ci continue; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci word = *addr | *(addr + 1) << 8; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci writew(word, base + DMADATA_OFFSET); 1478c2ecf20Sopenharmony_ci if (length > 1) { 1488c2ecf20Sopenharmony_ci addr += 2; 1498c2ecf20Sopenharmony_ci length -= 2; 1508c2ecf20Sopenharmony_ci } else { 1518c2ecf20Sopenharmony_ci addr += 1; 1528c2ecf20Sopenharmony_ci length -= 1; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci else { 1578c2ecf20Sopenharmony_ci if (transfer && (transfer & 255)) { 1588c2ecf20Sopenharmony_ci while (length >= 256) { 1598c2ecf20Sopenharmony_ci if (readb(base + 0x80) & STAT_INT) { 1608c2ecf20Sopenharmony_ci error = 1; 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) 1658c2ecf20Sopenharmony_ci continue; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci readsw(base + DMADATA_OFFSET, addr, 256 >> 1); 1688c2ecf20Sopenharmony_ci addr += 256; 1698c2ecf20Sopenharmony_ci length -= 256; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (!(error)) 1748c2ecf20Sopenharmony_ci while (length > 0) { 1758c2ecf20Sopenharmony_ci unsigned long word; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (readb(base + 0x80) & STAT_INT) 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) 1818c2ecf20Sopenharmony_ci continue; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci word = readw(base + DMADATA_OFFSET); 1848c2ecf20Sopenharmony_ci *addr++ = word; 1858c2ecf20Sopenharmony_ci if (--length > 0) { 1868c2ecf20Sopenharmony_ci *addr++ = word >> 8; 1878c2ecf20Sopenharmony_ci length --; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* 1948c2ecf20Sopenharmony_ci * Function: int arxescsi_dma_stop(host, SCpnt) 1958c2ecf20Sopenharmony_ci * Purpose : stops DMA/PIO 1968c2ecf20Sopenharmony_ci * Params : host - host 1978c2ecf20Sopenharmony_ci * SCpnt - command 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic void arxescsi_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * no DMA to stop 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* 2078c2ecf20Sopenharmony_ci * Function: const char *arxescsi_info(struct Scsi_Host * host) 2088c2ecf20Sopenharmony_ci * Purpose : returns a descriptive string about this interface, 2098c2ecf20Sopenharmony_ci * Params : host - driver host structure to return info for. 2108c2ecf20Sopenharmony_ci * Returns : pointer to a static buffer containing null terminated string. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_cistatic const char *arxescsi_info(struct Scsi_Host *host) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; 2158c2ecf20Sopenharmony_ci static char string[150]; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci sprintf(string, "%s (%s) in slot %d v%s", 2188c2ecf20Sopenharmony_ci host->hostt->name, info->info.scsi.type, info->ec->slot_no, 2198c2ecf20Sopenharmony_ci VERSION); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return string; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int 2258c2ecf20Sopenharmony_ciarxescsi_show_info(struct seq_file *m, struct Scsi_Host *host) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct arxescsi_info *info; 2288c2ecf20Sopenharmony_ci info = (struct arxescsi_info *)host->hostdata; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci seq_printf(m, "ARXE 16-bit SCSI driver v%s\n", VERSION); 2318c2ecf20Sopenharmony_ci fas216_print_host(&info->info, m); 2328c2ecf20Sopenharmony_ci fas216_print_stats(&info->info, m); 2338c2ecf20Sopenharmony_ci fas216_print_devices(&info->info, m); 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic struct scsi_host_template arxescsi_template = { 2388c2ecf20Sopenharmony_ci .show_info = arxescsi_show_info, 2398c2ecf20Sopenharmony_ci .name = "ARXE SCSI card", 2408c2ecf20Sopenharmony_ci .info = arxescsi_info, 2418c2ecf20Sopenharmony_ci .queuecommand = fas216_noqueue_command, 2428c2ecf20Sopenharmony_ci .eh_host_reset_handler = fas216_eh_host_reset, 2438c2ecf20Sopenharmony_ci .eh_bus_reset_handler = fas216_eh_bus_reset, 2448c2ecf20Sopenharmony_ci .eh_device_reset_handler = fas216_eh_device_reset, 2458c2ecf20Sopenharmony_ci .eh_abort_handler = fas216_eh_abort, 2468c2ecf20Sopenharmony_ci .can_queue = 0, 2478c2ecf20Sopenharmony_ci .this_id = 7, 2488c2ecf20Sopenharmony_ci .sg_tablesize = SG_ALL, 2498c2ecf20Sopenharmony_ci .dma_boundary = PAGE_SIZE - 1, 2508c2ecf20Sopenharmony_ci .proc_name = "arxescsi", 2518c2ecf20Sopenharmony_ci}; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct Scsi_Host *host; 2568c2ecf20Sopenharmony_ci struct arxescsi_info *info; 2578c2ecf20Sopenharmony_ci void __iomem *base; 2588c2ecf20Sopenharmony_ci int ret; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ret = ecard_request_resources(ec); 2618c2ecf20Sopenharmony_ci if (ret) 2628c2ecf20Sopenharmony_ci goto out; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); 2658c2ecf20Sopenharmony_ci if (!base) { 2668c2ecf20Sopenharmony_ci ret = -ENOMEM; 2678c2ecf20Sopenharmony_ci goto out_region; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci host = scsi_host_alloc(&arxescsi_template, sizeof(struct arxescsi_info)); 2718c2ecf20Sopenharmony_ci if (!host) { 2728c2ecf20Sopenharmony_ci ret = -ENOMEM; 2738c2ecf20Sopenharmony_ci goto out_region; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci info = (struct arxescsi_info *)host->hostdata; 2778c2ecf20Sopenharmony_ci info->ec = ec; 2788c2ecf20Sopenharmony_ci info->base = base; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci info->info.scsi.io_base = base + 0x2000; 2818c2ecf20Sopenharmony_ci info->info.scsi.irq = 0; 2828c2ecf20Sopenharmony_ci info->info.scsi.dma = NO_DMA; 2838c2ecf20Sopenharmony_ci info->info.scsi.io_shift = 5; 2848c2ecf20Sopenharmony_ci info->info.ifcfg.clockrate = 24; /* MHz */ 2858c2ecf20Sopenharmony_ci info->info.ifcfg.select_timeout = 255; 2868c2ecf20Sopenharmony_ci info->info.ifcfg.asyncperiod = 200; /* ns */ 2878c2ecf20Sopenharmony_ci info->info.ifcfg.sync_max_depth = 0; 2888c2ecf20Sopenharmony_ci info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; 2898c2ecf20Sopenharmony_ci info->info.ifcfg.disconnect_ok = 0; 2908c2ecf20Sopenharmony_ci info->info.ifcfg.wide_max_size = 0; 2918c2ecf20Sopenharmony_ci info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; 2928c2ecf20Sopenharmony_ci info->info.dma.setup = arxescsi_dma_setup; 2938c2ecf20Sopenharmony_ci info->info.dma.pseudo = arxescsi_dma_pseudo; 2948c2ecf20Sopenharmony_ci info->info.dma.stop = arxescsi_dma_stop; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci ec->irqaddr = base; 2978c2ecf20Sopenharmony_ci ec->irqmask = CSTATUS_IRQ; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci ret = fas216_init(host); 3008c2ecf20Sopenharmony_ci if (ret) 3018c2ecf20Sopenharmony_ci goto out_unregister; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ret = fas216_add(host, &ec->dev); 3048c2ecf20Sopenharmony_ci if (ret == 0) 3058c2ecf20Sopenharmony_ci goto out; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci fas216_release(host); 3088c2ecf20Sopenharmony_ci out_unregister: 3098c2ecf20Sopenharmony_ci scsi_host_put(host); 3108c2ecf20Sopenharmony_ci out_region: 3118c2ecf20Sopenharmony_ci ecard_release_resources(ec); 3128c2ecf20Sopenharmony_ci out: 3138c2ecf20Sopenharmony_ci return ret; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic void arxescsi_remove(struct expansion_card *ec) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct Scsi_Host *host = ecard_get_drvdata(ec); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ecard_set_drvdata(ec, NULL); 3218c2ecf20Sopenharmony_ci fas216_remove(host); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci fas216_release(host); 3248c2ecf20Sopenharmony_ci scsi_host_put(host); 3258c2ecf20Sopenharmony_ci ecard_release_resources(ec); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic const struct ecard_id arxescsi_cids[] = { 3298c2ecf20Sopenharmony_ci { MANU_ARXE, PROD_ARXE_SCSI }, 3308c2ecf20Sopenharmony_ci { 0xffff, 0xffff }, 3318c2ecf20Sopenharmony_ci}; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic struct ecard_driver arxescsi_driver = { 3348c2ecf20Sopenharmony_ci .probe = arxescsi_probe, 3358c2ecf20Sopenharmony_ci .remove = arxescsi_remove, 3368c2ecf20Sopenharmony_ci .id_table = arxescsi_cids, 3378c2ecf20Sopenharmony_ci .drv = { 3388c2ecf20Sopenharmony_ci .name = "arxescsi", 3398c2ecf20Sopenharmony_ci }, 3408c2ecf20Sopenharmony_ci}; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int __init init_arxe_scsi_driver(void) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci return ecard_register_driver(&arxescsi_driver); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void __exit exit_arxe_scsi_driver(void) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci ecard_remove_driver(&arxescsi_driver); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cimodule_init(init_arxe_scsi_driver); 3538c2ecf20Sopenharmony_cimodule_exit(exit_arxe_scsi_driver); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stefan Hanske"); 3568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ARXESCSI driver for Acorn machines"); 3578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3588c2ecf20Sopenharmony_ci 359