Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  linux/drivers/acorn/scsi/cumana_2.c
0004  *
0005  *  Copyright (C) 1997-2005 Russell King
0006  *
0007  *  Changelog:
0008  *   30-08-1997 RMK 0.0.0   Created, READONLY version.
0009  *   22-01-1998 RMK 0.0.1   Updated to 2.1.80.
0010  *   15-04-1998 RMK 0.0.1   Only do PIO if FAS216 will allow it.
0011  *   02-05-1998 RMK 0.0.2   Updated & added DMA support.
0012  *   27-06-1998 RMK     Changed asm/delay.h to linux/delay.h
0013  *   18-08-1998 RMK 0.0.3   Fixed synchronous transfer depth.
0014  *   02-04-2000 RMK 0.0.4   Updated for new error handling code.
0015  */
0016 #include <linux/module.h>
0017 #include <linux/blkdev.h>
0018 #include <linux/kernel.h>
0019 #include <linux/string.h>
0020 #include <linux/ioport.h>
0021 #include <linux/proc_fs.h>
0022 #include <linux/delay.h>
0023 #include <linux/interrupt.h>
0024 #include <linux/init.h>
0025 #include <linux/dma-mapping.h>
0026 #include <linux/pgtable.h>
0027 
0028 #include <asm/dma.h>
0029 #include <asm/ecard.h>
0030 #include <asm/io.h>
0031 
0032 #include <scsi/scsi.h>
0033 #include <scsi/scsi_cmnd.h>
0034 #include <scsi/scsi_device.h>
0035 #include <scsi/scsi_eh.h>
0036 #include <scsi/scsi_host.h>
0037 #include <scsi/scsi_tcq.h>
0038 #include "fas216.h"
0039 #include "arm_scsi.h"
0040 
0041 #include <scsi/scsicam.h>
0042 
0043 #define CUMANASCSI2_STATUS      (0x0000)
0044 #define STATUS_INT          (1 << 0)
0045 #define STATUS_DRQ          (1 << 1)
0046 #define STATUS_LATCHED          (1 << 3)
0047 
0048 #define CUMANASCSI2_ALATCH      (0x0014)
0049 #define ALATCH_ENA_INT          (3)
0050 #define ALATCH_DIS_INT          (2)
0051 #define ALATCH_ENA_TERM         (5)
0052 #define ALATCH_DIS_TERM         (4)
0053 #define ALATCH_ENA_BIT32        (11)
0054 #define ALATCH_DIS_BIT32        (10)
0055 #define ALATCH_ENA_DMA          (13)
0056 #define ALATCH_DIS_DMA          (12)
0057 #define ALATCH_DMA_OUT          (15)
0058 #define ALATCH_DMA_IN           (14)
0059 
0060 #define CUMANASCSI2_PSEUDODMA       (0x0200)
0061 
0062 #define CUMANASCSI2_FAS216_OFFSET   (0x0300)
0063 #define CUMANASCSI2_FAS216_SHIFT    2
0064 
0065 /*
0066  * Version
0067  */
0068 #define VERSION "1.00 (13/11/2002 2.5.47)"
0069 
0070 /*
0071  * Use term=0,1,0,0,0 to turn terminators on/off
0072  */
0073 static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
0074 
0075 #define NR_SG   256
0076 
0077 struct cumanascsi2_info {
0078     FAS216_Info     info;
0079     struct expansion_card   *ec;
0080     void __iomem        *base;
0081     unsigned int        terms;      /* Terminator state */
0082     struct scatterlist  sg[NR_SG];  /* Scatter DMA list */
0083 };
0084 
0085 #define CSTATUS_IRQ (1 << 0)
0086 #define CSTATUS_DRQ (1 << 1)
0087 
0088 /* Prototype: void cumanascsi_2_irqenable(ec, irqnr)
0089  * Purpose  : Enable interrupts on Cumana SCSI 2 card
0090  * Params   : ec    - expansion card structure
0091  *          : irqnr - interrupt number
0092  */
0093 static void
0094 cumanascsi_2_irqenable(struct expansion_card *ec, int irqnr)
0095 {
0096     struct cumanascsi2_info *info = ec->irq_data;
0097     writeb(ALATCH_ENA_INT, info->base + CUMANASCSI2_ALATCH);
0098 }
0099 
0100 /* Prototype: void cumanascsi_2_irqdisable(ec, irqnr)
0101  * Purpose  : Disable interrupts on Cumana SCSI 2 card
0102  * Params   : ec    - expansion card structure
0103  *          : irqnr - interrupt number
0104  */
0105 static void
0106 cumanascsi_2_irqdisable(struct expansion_card *ec, int irqnr)
0107 {
0108     struct cumanascsi2_info *info = ec->irq_data;
0109     writeb(ALATCH_DIS_INT, info->base + CUMANASCSI2_ALATCH);
0110 }
0111 
0112 static const expansioncard_ops_t cumanascsi_2_ops = {
0113     .irqenable  = cumanascsi_2_irqenable,
0114     .irqdisable = cumanascsi_2_irqdisable,
0115 };
0116 
0117 /* Prototype: void cumanascsi_2_terminator_ctl(host, on_off)
0118  * Purpose  : Turn the Cumana SCSI 2 terminators on or off
0119  * Params   : host   - card to turn on/off
0120  *          : on_off - !0 to turn on, 0 to turn off
0121  */
0122 static void
0123 cumanascsi_2_terminator_ctl(struct Scsi_Host *host, int on_off)
0124 {
0125     struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
0126 
0127     if (on_off) {
0128         info->terms = 1;
0129         writeb(ALATCH_ENA_TERM, info->base + CUMANASCSI2_ALATCH);
0130     } else {
0131         info->terms = 0;
0132         writeb(ALATCH_DIS_TERM, info->base + CUMANASCSI2_ALATCH);
0133     }
0134 }
0135 
0136 /* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs)
0137  * Purpose  : handle interrupts from Cumana SCSI 2 card
0138  * Params   : irq    - interrupt number
0139  *        dev_id - user-defined (Scsi_Host structure)
0140  */
0141 static irqreturn_t
0142 cumanascsi_2_intr(int irq, void *dev_id)
0143 {
0144     struct cumanascsi2_info *info = dev_id;
0145 
0146     return fas216_intr(&info->info);
0147 }
0148 
0149 /* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type)
0150  * Purpose  : initialises DMA/PIO
0151  * Params   : host      - host
0152  *        SCpnt     - command
0153  *        direction - DMA on to/off of card
0154  *        min_type  - minimum DMA support that we must have for this transfer
0155  * Returns  : type of transfer to be performed
0156  */
0157 static fasdmatype_t
0158 cumanascsi_2_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp,
0159                fasdmadir_t direction, fasdmatype_t min_type)
0160 {
0161     struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
0162     struct device *dev = scsi_get_device(host);
0163     int dmach = info->info.scsi.dma;
0164 
0165     writeb(ALATCH_DIS_DMA, info->base + CUMANASCSI2_ALATCH);
0166 
0167     if (dmach != NO_DMA &&
0168         (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
0169         int bufs, map_dir, dma_dir, alatch_dir;
0170 
0171         bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
0172 
0173         if (direction == DMA_OUT) {
0174             map_dir = DMA_TO_DEVICE;
0175             dma_dir = DMA_MODE_WRITE;
0176             alatch_dir = ALATCH_DMA_OUT;
0177         } else {
0178             map_dir = DMA_FROM_DEVICE;
0179             dma_dir = DMA_MODE_READ;
0180             alatch_dir = ALATCH_DMA_IN;
0181         }
0182 
0183         dma_map_sg(dev, info->sg, bufs, map_dir);
0184 
0185         disable_dma(dmach);
0186         set_dma_sg(dmach, info->sg, bufs);
0187         writeb(alatch_dir, info->base + CUMANASCSI2_ALATCH);
0188         set_dma_mode(dmach, dma_dir);
0189         enable_dma(dmach);
0190         writeb(ALATCH_ENA_DMA, info->base + CUMANASCSI2_ALATCH);
0191         writeb(ALATCH_DIS_BIT32, info->base + CUMANASCSI2_ALATCH);
0192         return fasdma_real_all;
0193     }
0194 
0195     /*
0196      * If we're not doing DMA,
0197      *  we'll do pseudo DMA
0198      */
0199     return fasdma_pio;
0200 }
0201 
0202 /*
0203  * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer)
0204  * Purpose  : handles pseudo DMA
0205  * Params   : host      - host
0206  *        SCpnt     - command
0207  *        direction - DMA on to/off of card
0208  *        transfer  - minimum number of bytes we expect to transfer
0209  */
0210 static void
0211 cumanascsi_2_dma_pseudo(struct Scsi_Host *host, struct scsi_pointer *SCp,
0212             fasdmadir_t direction, int transfer)
0213 {
0214     struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
0215     unsigned int length;
0216     unsigned char *addr;
0217 
0218     length = SCp->this_residual;
0219     addr = SCp->ptr;
0220 
0221     if (direction == DMA_OUT)
0222 #if 0
0223         while (length > 1) {
0224             unsigned long word;
0225             unsigned int status = readb(info->base + CUMANASCSI2_STATUS);
0226 
0227             if (status & STATUS_INT)
0228                 goto end;
0229 
0230             if (!(status & STATUS_DRQ))
0231                 continue;
0232 
0233             word = *addr | *(addr + 1) << 8;
0234             writew(word, info->base + CUMANASCSI2_PSEUDODMA);
0235             addr += 2;
0236             length -= 2;
0237         }
0238 #else
0239         printk ("PSEUDO_OUT???\n");
0240 #endif
0241     else {
0242         if (transfer && (transfer & 255)) {
0243             while (length >= 256) {
0244                 unsigned int status = readb(info->base + CUMANASCSI2_STATUS);
0245 
0246                 if (status & STATUS_INT)
0247                     return;
0248         
0249                 if (!(status & STATUS_DRQ))
0250                     continue;
0251 
0252                 readsw(info->base + CUMANASCSI2_PSEUDODMA,
0253                        addr, 256 >> 1);
0254                 addr += 256;
0255                 length -= 256;
0256             }
0257         }
0258 
0259         while (length > 0) {
0260             unsigned long word;
0261             unsigned int status = readb(info->base + CUMANASCSI2_STATUS);
0262 
0263             if (status & STATUS_INT)
0264                 return;
0265 
0266             if (!(status & STATUS_DRQ))
0267                 continue;
0268 
0269             word = readw(info->base + CUMANASCSI2_PSEUDODMA);
0270             *addr++ = word;
0271             if (--length > 0) {
0272                 *addr++ = word >> 8;
0273                 length --;
0274             }
0275         }
0276     }
0277 }
0278 
0279 /* Prototype: int cumanascsi_2_dma_stop(host, SCpnt)
0280  * Purpose  : stops DMA/PIO
0281  * Params   : host  - host
0282  *        SCpnt - command
0283  */
0284 static void
0285 cumanascsi_2_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp)
0286 {
0287     struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
0288     if (info->info.scsi.dma != NO_DMA) {
0289         writeb(ALATCH_DIS_DMA, info->base + CUMANASCSI2_ALATCH);
0290         disable_dma(info->info.scsi.dma);
0291     }
0292 }
0293 
0294 /* Prototype: const char *cumanascsi_2_info(struct Scsi_Host * host)
0295  * Purpose  : returns a descriptive string about this interface,
0296  * Params   : host - driver host structure to return info for.
0297  * Returns  : pointer to a static buffer containing null terminated string.
0298  */
0299 const char *cumanascsi_2_info(struct Scsi_Host *host)
0300 {
0301     struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
0302     static char string[150];
0303 
0304     sprintf(string, "%s (%s) in slot %d v%s terminators o%s",
0305         host->hostt->name, info->info.scsi.type, info->ec->slot_no,
0306         VERSION, info->terms ? "n" : "ff");
0307 
0308     return string;
0309 }
0310 
0311 /* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
0312  * Purpose  : Set a driver specific function
0313  * Params   : host   - host to setup
0314  *          : buffer - buffer containing string describing operation
0315  *          : length - length of string
0316  * Returns  : -EINVAL, or 0
0317  */
0318 static int
0319 cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
0320 {
0321     int ret = length;
0322 
0323     if (length >= 11 && strncmp(buffer, "CUMANASCSI2", 11) == 0) {
0324         buffer += 11;
0325         length -= 11;
0326 
0327         if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
0328             if (buffer[5] == '1')
0329                 cumanascsi_2_terminator_ctl(host, 1);
0330             else if (buffer[5] == '0')
0331                 cumanascsi_2_terminator_ctl(host, 0);
0332             else
0333                 ret = -EINVAL;
0334         } else {
0335             ret = -EINVAL;
0336         }
0337     } else {
0338         ret = -EINVAL;
0339     }
0340 
0341     return ret;
0342 }
0343 
0344 static int cumanascsi_2_show_info(struct seq_file *m, struct Scsi_Host *host)
0345 {
0346     struct cumanascsi2_info *info;
0347     info = (struct cumanascsi2_info *)host->hostdata;
0348 
0349     seq_printf(m, "Cumana SCSI II driver v%s\n", VERSION);
0350     fas216_print_host(&info->info, m);
0351     seq_printf(m, "Term    : o%s\n",
0352             info->terms ? "n" : "ff");
0353 
0354     fas216_print_stats(&info->info, m);
0355     fas216_print_devices(&info->info, m);
0356     return 0;
0357 }
0358 
0359 static struct scsi_host_template cumanascsi2_template = {
0360     .module             = THIS_MODULE,
0361     .show_info          = cumanascsi_2_show_info,
0362     .write_info         = cumanascsi_2_set_proc_info,
0363     .name               = "Cumana SCSI II",
0364     .info               = cumanascsi_2_info,
0365     .queuecommand           = fas216_queue_command,
0366     .eh_host_reset_handler      = fas216_eh_host_reset,
0367     .eh_bus_reset_handler       = fas216_eh_bus_reset,
0368     .eh_device_reset_handler    = fas216_eh_device_reset,
0369     .eh_abort_handler       = fas216_eh_abort,
0370     .cmd_size           = sizeof(struct fas216_cmd_priv),
0371     .can_queue          = 1,
0372     .this_id            = 7,
0373     .sg_tablesize           = SG_MAX_SEGMENTS,
0374     .dma_boundary           = IOMD_DMA_BOUNDARY,
0375     .proc_name          = "cumanascsi2",
0376 };
0377 
0378 static int cumanascsi2_probe(struct expansion_card *ec,
0379                  const struct ecard_id *id)
0380 {
0381     struct Scsi_Host *host;
0382     struct cumanascsi2_info *info;
0383     void __iomem *base;
0384     int ret;
0385 
0386     ret = ecard_request_resources(ec);
0387     if (ret)
0388         goto out;
0389 
0390     base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);
0391     if (!base) {
0392         ret = -ENOMEM;
0393         goto out_region;
0394     }
0395 
0396     host = scsi_host_alloc(&cumanascsi2_template,
0397                    sizeof(struct cumanascsi2_info));
0398     if (!host) {
0399         ret = -ENOMEM;
0400         goto out_region;
0401     }
0402 
0403     ecard_set_drvdata(ec, host);
0404 
0405     info = (struct cumanascsi2_info *)host->hostdata;
0406     info->ec    = ec;
0407     info->base  = base;
0408 
0409     cumanascsi_2_terminator_ctl(host, term[ec->slot_no]);
0410 
0411     info->info.scsi.io_base     = base + CUMANASCSI2_FAS216_OFFSET;
0412     info->info.scsi.io_shift    = CUMANASCSI2_FAS216_SHIFT;
0413     info->info.scsi.irq     = ec->irq;
0414     info->info.scsi.dma     = ec->dma;
0415     info->info.ifcfg.clockrate  = 40; /* MHz */
0416     info->info.ifcfg.select_timeout = 255;
0417     info->info.ifcfg.asyncperiod    = 200; /* ns */
0418     info->info.ifcfg.sync_max_depth = 7;
0419     info->info.ifcfg.cntl3      = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
0420     info->info.ifcfg.disconnect_ok  = 1;
0421     info->info.ifcfg.wide_max_size  = 0;
0422     info->info.ifcfg.capabilities   = FASCAP_PSEUDODMA;
0423     info->info.dma.setup        = cumanascsi_2_dma_setup;
0424     info->info.dma.pseudo       = cumanascsi_2_dma_pseudo;
0425     info->info.dma.stop     = cumanascsi_2_dma_stop;
0426 
0427     ec->irqaddr = info->base + CUMANASCSI2_STATUS;
0428     ec->irqmask = STATUS_INT;
0429 
0430     ecard_setirq(ec, &cumanascsi_2_ops, info);
0431 
0432     ret = fas216_init(host);
0433     if (ret)
0434         goto out_free;
0435 
0436     ret = request_irq(ec->irq, cumanascsi_2_intr,
0437               0, "cumanascsi2", info);
0438     if (ret) {
0439         printk("scsi%d: IRQ%d not free: %d\n",
0440                host->host_no, ec->irq, ret);
0441         goto out_release;
0442     }
0443 
0444     if (info->info.scsi.dma != NO_DMA) {
0445         if (request_dma(info->info.scsi.dma, "cumanascsi2")) {
0446             printk("scsi%d: DMA%d not free, using PIO\n",
0447                    host->host_no, info->info.scsi.dma);
0448             info->info.scsi.dma = NO_DMA;
0449         } else {
0450             set_dma_speed(info->info.scsi.dma, 180);
0451             info->info.ifcfg.capabilities |= FASCAP_DMA;
0452         }
0453     }
0454 
0455     ret = fas216_add(host, &ec->dev);
0456     if (ret == 0)
0457         goto out;
0458 
0459     if (info->info.scsi.dma != NO_DMA)
0460         free_dma(info->info.scsi.dma);
0461     free_irq(ec->irq, info);
0462 
0463  out_release:
0464     fas216_release(host);
0465 
0466  out_free:
0467     scsi_host_put(host);
0468 
0469  out_region:
0470     ecard_release_resources(ec);
0471 
0472  out:
0473     return ret;
0474 }
0475 
0476 static void cumanascsi2_remove(struct expansion_card *ec)
0477 {
0478     struct Scsi_Host *host = ecard_get_drvdata(ec);
0479     struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
0480 
0481     ecard_set_drvdata(ec, NULL);
0482     fas216_remove(host);
0483 
0484     if (info->info.scsi.dma != NO_DMA)
0485         free_dma(info->info.scsi.dma);
0486     free_irq(ec->irq, info);
0487 
0488     fas216_release(host);
0489     scsi_host_put(host);
0490     ecard_release_resources(ec);
0491 }
0492 
0493 static const struct ecard_id cumanascsi2_cids[] = {
0494     { MANU_CUMANA, PROD_CUMANA_SCSI_2 },
0495     { 0xffff, 0xffff },
0496 };
0497 
0498 static struct ecard_driver cumanascsi2_driver = {
0499     .probe      = cumanascsi2_probe,
0500     .remove     = cumanascsi2_remove,
0501     .id_table   = cumanascsi2_cids,
0502     .drv = {
0503         .name       = "cumanascsi2",
0504     },
0505 };
0506 
0507 static int __init cumanascsi2_init(void)
0508 {
0509     return ecard_register_driver(&cumanascsi2_driver);
0510 }
0511 
0512 static void __exit cumanascsi2_exit(void)
0513 {
0514     ecard_remove_driver(&cumanascsi2_driver);
0515 }
0516 
0517 module_init(cumanascsi2_init);
0518 module_exit(cumanascsi2_exit);
0519 
0520 MODULE_AUTHOR("Russell King");
0521 MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines");
0522 module_param_array(term, int, NULL, 0);
0523 MODULE_PARM_DESC(term, "SCSI bus termination");
0524 MODULE_LICENSE("GPL");