cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sgiwd93.c (8330B)


      1/*
      2 * This file is subject to the terms and conditions of the GNU General Public
      3 * License.  See the file "COPYING" in the main directory of this archive
      4 * 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 DEBUG
     15
     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_SIZE
     54
     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 inline
     70void 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, without
     86		 * magic only 8192 works correctly
     87		 */
     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 single
     98	 * byte, we tag on an extra zero length dma descriptor at the end of
     99	 * 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:-(  The
    121	 * other wd33c93 drivers deal with it the same way (which isn't that
    122	 * obvious).  IMHO a better fix would be, not to do these dma setups
    123	 * 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	else
    137		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 int
    204 * arguments not with pointers.  So this is going to blow up beautyfully
    205 * on 64-bit systems with memory outside the compat address spaces.
    206 */
    207static 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 int 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	return 0;
    306}
    307
    308static struct platform_driver sgiwd93_driver = {
    309	.probe  = sgiwd93_probe,
    310	.remove = sgiwd93_remove,
    311	.driver = {
    312		.name   = "sgiwd93",
    313	}
    314};
    315
    316static int __init sgiwd93_module_init(void)
    317{
    318	return platform_driver_register(&sgiwd93_driver);
    319}
    320
    321static void __exit sgiwd93_module_exit(void)
    322{
    323	return platform_driver_unregister(&sgiwd93_driver);
    324}
    325
    326module_init(sgiwd93_module_init);
    327module_exit(sgiwd93_module_exit);
    328
    329MODULE_DESCRIPTION("SGI WD33C93 driver");
    330MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
    331MODULE_LICENSE("GPL");
    332MODULE_ALIAS("platform:sgiwd93");