332 lines · c
1/*2 * This file is subject to the terms and conditions of the GNU General Public3 * License. See the file "COPYING" in the main directory of this archive4 * for more details.5 *6 * Copyright (C) 1996 David S. Miller (davem@davemloft.net)7 * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu)8 * Copyright (C) 2001 Florian Lohoff (flo@rfc822.org)9 * Copyright (C) 2003, 07 Ralf Baechle (ralf@linux-mips.org)10 * 11 * (In all truth, Jed Schimmel wrote all this code.)12 */13 14#undef DEBUG15 16#include <linux/delay.h>17#include <linux/dma-mapping.h>18#include <linux/gfp.h>19#include <linux/interrupt.h>20#include <linux/init.h>21#include <linux/kernel.h>22#include <linux/types.h>23#include <linux/module.h>24#include <linux/platform_device.h>25#include <linux/spinlock.h>26 27#include <asm/sgi/hpc3.h>28#include <asm/sgi/ip22.h>29#include <asm/sgi/wd.h>30 31#include <scsi/scsi.h>32#include <scsi/scsi_cmnd.h>33#include <scsi/scsi_device.h>34#include <scsi/scsi_eh.h>35#include <scsi/scsi_tcq.h>36#include "wd33c93.h"37 38struct ip22_hostdata {39 struct WD33C93_hostdata wh;40 dma_addr_t dma;41 void *cpu;42 struct device *dev;43};44 45#define host_to_hostdata(host) ((struct ip22_hostdata *)((host)->hostdata))46 47struct hpc_chunk {48 struct hpc_dma_desc desc;49 u32 _padding; /* align to quadword boundary */50};51 52/* space for hpc dma descriptors */53#define HPC_DMA_SIZE PAGE_SIZE54 55#define DMA_DIR(d) ((d == DATA_OUT_DIR) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)56 57static irqreturn_t sgiwd93_intr(int irq, void *dev_id)58{59 struct Scsi_Host * host = dev_id;60 unsigned long flags;61 62 spin_lock_irqsave(host->host_lock, flags);63 wd33c93_intr(host);64 spin_unlock_irqrestore(host->host_lock, flags);65 66 return IRQ_HANDLED;67}68 69static inline70void fill_hpc_entries(struct ip22_hostdata *hd, struct scsi_cmnd *cmd, int din)71{72 struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd);73 unsigned long len = scsi_pointer->this_residual;74 void *addr = scsi_pointer->ptr;75 dma_addr_t physaddr;76 unsigned long count;77 struct hpc_chunk *hcp;78 79 physaddr = dma_map_single(hd->dev, addr, len, DMA_DIR(din));80 scsi_pointer->dma_handle = physaddr;81 hcp = hd->cpu;82 83 while (len) {84 /*85 * even cntinfo could be up to 16383, without86 * magic only 8192 works correctly87 */88 count = len > 8192 ? 8192 : len;89 hcp->desc.pbuf = physaddr;90 hcp->desc.cntinfo = count;91 hcp++;92 len -= count;93 physaddr += count;94 }95 96 /*97 * To make sure, if we trip an HPC bug, that we transfer every single98 * byte, we tag on an extra zero length dma descriptor at the end of99 * the chain.100 */101 hcp->desc.pbuf = 0;102 hcp->desc.cntinfo = HPCDMA_EOX;103 dma_sync_single_for_device(hd->dev, hd->dma,104 (unsigned long)(hcp + 1) - (unsigned long)hd->cpu,105 DMA_TO_DEVICE);106}107 108static int dma_setup(struct scsi_cmnd *cmd, int datainp)109{110 struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd);111 struct ip22_hostdata *hdata = host_to_hostdata(cmd->device->host);112 struct hpc3_scsiregs *hregs =113 (struct hpc3_scsiregs *) cmd->device->host->base;114 115 pr_debug("dma_setup: datainp<%d> hcp<%p> ", datainp, hdata->cpu);116 117 hdata->wh.dma_dir = datainp;118 119 /*120 * wd33c93 shouldn't pass us bogus dma_setups, but it does:-( The121 * other wd33c93 drivers deal with it the same way (which isn't that122 * obvious). IMHO a better fix would be, not to do these dma setups123 * in the first place.124 */125 if (scsi_pointer->ptr == NULL || scsi_pointer->this_residual == 0)126 return 1;127 128 fill_hpc_entries(hdata, cmd, datainp);129 130 pr_debug(" HPCGO\n");131 132 /* Start up the HPC. */133 hregs->ndptr = hdata->dma;134 if (datainp)135 hregs->ctrl = HPC3_SCTRL_ACTIVE;136 else137 hregs->ctrl = HPC3_SCTRL_ACTIVE | HPC3_SCTRL_DIR;138 139 return 0;140}141 142static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,143 int status)144{145 struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(SCpnt);146 struct ip22_hostdata *hdata = host_to_hostdata(instance);147 struct hpc3_scsiregs *hregs;148 149 if (!SCpnt)150 return;151 152 if (scsi_pointer->ptr == NULL || scsi_pointer->this_residual == 0)153 return;154 155 hregs = (struct hpc3_scsiregs *) SCpnt->device->host->base;156 157 pr_debug("dma_stop: status<%d> ", status);158 159 /* First stop the HPC and flush it's FIFO. */160 if (hdata->wh.dma_dir) {161 hregs->ctrl |= HPC3_SCTRL_FLUSH;162 while (hregs->ctrl & HPC3_SCTRL_ACTIVE)163 barrier();164 }165 hregs->ctrl = 0;166 dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,167 scsi_pointer->this_residual,168 DMA_DIR(hdata->wh.dma_dir));169 170 pr_debug("\n");171}172 173void sgiwd93_reset(unsigned long base)174{175 struct hpc3_scsiregs *hregs = (struct hpc3_scsiregs *) base;176 177 hregs->ctrl = HPC3_SCTRL_CRESET;178 udelay(50);179 hregs->ctrl = 0;180}181EXPORT_SYMBOL_GPL(sgiwd93_reset);182 183static inline void init_hpc_chain(struct ip22_hostdata *hdata)184{185 struct hpc_chunk *hcp = (struct hpc_chunk *)hdata->cpu;186 dma_addr_t dma = hdata->dma;187 unsigned long start, end;188 189 start = (unsigned long) hcp;190 end = start + HPC_DMA_SIZE;191 while (start < end) {192 hcp->desc.pnext = (u32) (dma + sizeof(struct hpc_chunk));193 hcp->desc.cntinfo = HPCDMA_EOX;194 hcp++;195 dma += sizeof(struct hpc_chunk);196 start += sizeof(struct hpc_chunk);197 }198 hcp--;199 hcp->desc.pnext = hdata->dma;200}201 202/*203 * Kludge alert - the SCSI code calls the abort and reset method with int204 * arguments not with pointers. So this is going to blow up beautyfully205 * on 64-bit systems with memory outside the compat address spaces.206 */207static const struct scsi_host_template sgiwd93_template = {208 .module = THIS_MODULE,209 .proc_name = "SGIWD93",210 .name = "SGI WD93",211 .queuecommand = wd33c93_queuecommand,212 .eh_abort_handler = wd33c93_abort,213 .eh_host_reset_handler = wd33c93_host_reset,214 .can_queue = 16,215 .this_id = 7,216 .sg_tablesize = SG_ALL,217 .cmd_per_lun = 8,218 .dma_boundary = PAGE_SIZE - 1,219 .cmd_size = sizeof(struct scsi_pointer),220};221 222static int sgiwd93_probe(struct platform_device *pdev)223{224 struct sgiwd93_platform_data *pd = pdev->dev.platform_data;225 unsigned char *wdregs = pd->wdregs;226 struct hpc3_scsiregs *hregs = pd->hregs;227 struct ip22_hostdata *hdata;228 struct Scsi_Host *host;229 wd33c93_regs regs;230 unsigned int unit = pd->unit;231 unsigned int irq = pd->irq;232 int err;233 234 host = scsi_host_alloc(&sgiwd93_template, sizeof(struct ip22_hostdata));235 if (!host) {236 err = -ENOMEM;237 goto out;238 }239 240 host->base = (unsigned long) hregs;241 host->irq = irq;242 243 hdata = host_to_hostdata(host);244 hdata->dev = &pdev->dev;245 hdata->cpu = dma_alloc_noncoherent(&pdev->dev, HPC_DMA_SIZE,246 &hdata->dma, DMA_TO_DEVICE, GFP_KERNEL);247 if (!hdata->cpu) {248 printk(KERN_WARNING "sgiwd93: Could not allocate memory for "249 "host %d buffer.\n", unit);250 err = -ENOMEM;251 goto out_put;252 }253 254 init_hpc_chain(hdata);255 256 regs.SASR = wdregs + 3;257 regs.SCMD = wdregs + 7;258 259 hdata->wh.no_sync = 0;260 hdata->wh.fast = 1;261 hdata->wh.dma_mode = CTRL_BURST;262 263 wd33c93_init(host, regs, dma_setup, dma_stop, WD33C93_FS_MHZ(20));264 265 err = request_irq(irq, sgiwd93_intr, 0, "SGI WD93", host);266 if (err) {267 printk(KERN_WARNING "sgiwd93: Could not register irq %d "268 "for host %d.\n", irq, unit);269 goto out_free;270 }271 272 platform_set_drvdata(pdev, host);273 274 err = scsi_add_host(host, NULL);275 if (err)276 goto out_irq;277 278 scsi_scan_host(host);279 280 return 0;281 282out_irq:283 free_irq(irq, host);284out_free:285 dma_free_noncoherent(&pdev->dev, HPC_DMA_SIZE, hdata->cpu, hdata->dma,286 DMA_TO_DEVICE);287out_put:288 scsi_host_put(host);289out:290 291 return err;292}293 294static void sgiwd93_remove(struct platform_device *pdev)295{296 struct Scsi_Host *host = platform_get_drvdata(pdev);297 struct ip22_hostdata *hdata = (struct ip22_hostdata *) host->hostdata;298 struct sgiwd93_platform_data *pd = pdev->dev.platform_data;299 300 scsi_remove_host(host);301 free_irq(pd->irq, host);302 dma_free_noncoherent(&pdev->dev, HPC_DMA_SIZE, hdata->cpu, hdata->dma,303 DMA_TO_DEVICE);304 scsi_host_put(host);305}306 307static struct platform_driver sgiwd93_driver = {308 .probe = sgiwd93_probe,309 .remove_new = sgiwd93_remove,310 .driver = {311 .name = "sgiwd93",312 }313};314 315static int __init sgiwd93_module_init(void)316{317 return platform_driver_register(&sgiwd93_driver);318}319 320static void __exit sgiwd93_module_exit(void)321{322 return platform_driver_unregister(&sgiwd93_driver);323}324 325module_init(sgiwd93_module_init);326module_exit(sgiwd93_module_exit);327 328MODULE_DESCRIPTION("SGI WD33C93 driver");329MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");330MODULE_LICENSE("GPL");331MODULE_ALIAS("platform:sgiwd93");332