brintos

brintos / linux-shallow public Read only

0
0
Text · 8.1 KiB · b0bef83 Raw
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