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

a3000.c (7009B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2#include <linux/types.h>
      3#include <linux/mm.h>
      4#include <linux/ioport.h>
      5#include <linux/init.h>
      6#include <linux/slab.h>
      7#include <linux/spinlock.h>
      8#include <linux/interrupt.h>
      9#include <linux/platform_device.h>
     10#include <linux/module.h>
     11
     12#include <asm/page.h>
     13#include <asm/amigaints.h>
     14#include <asm/amigahw.h>
     15
     16#include <scsi/scsi.h>
     17#include <scsi/scsi_cmnd.h>
     18#include <scsi/scsi_device.h>
     19#include <scsi/scsi_eh.h>
     20#include <scsi/scsi_tcq.h>
     21#include "wd33c93.h"
     22#include "a3000.h"
     23
     24
     25struct a3000_hostdata {
     26	struct WD33C93_hostdata wh;
     27	struct a3000_scsiregs *regs;
     28};
     29
     30static irqreturn_t a3000_intr(int irq, void *data)
     31{
     32	struct Scsi_Host *instance = data;
     33	struct a3000_hostdata *hdata = shost_priv(instance);
     34	unsigned int status = hdata->regs->ISTR;
     35	unsigned long flags;
     36
     37	if (!(status & ISTR_INT_P))
     38		return IRQ_NONE;
     39	if (status & ISTR_INTS) {
     40		spin_lock_irqsave(instance->host_lock, flags);
     41		wd33c93_intr(instance);
     42		spin_unlock_irqrestore(instance->host_lock, flags);
     43		return IRQ_HANDLED;
     44	}
     45	pr_warn("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status);
     46	return IRQ_NONE;
     47}
     48
     49static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
     50{
     51	struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd);
     52	struct Scsi_Host *instance = cmd->device->host;
     53	struct a3000_hostdata *hdata = shost_priv(instance);
     54	struct WD33C93_hostdata *wh = &hdata->wh;
     55	struct a3000_scsiregs *regs = hdata->regs;
     56	unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
     57	unsigned long addr = virt_to_bus(scsi_pointer->ptr);
     58
     59	/*
     60	 * if the physical address has the wrong alignment, or if
     61	 * physical address is bad, or if it is a write and at the
     62	 * end of a physical memory chunk, then allocate a bounce
     63	 * buffer
     64	 */
     65	if (addr & A3000_XFER_MASK) {
     66		wh->dma_bounce_len = (scsi_pointer->this_residual + 511) & ~0x1ff;
     67		wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len,
     68						GFP_KERNEL);
     69
     70		/* can't allocate memory; use PIO */
     71		if (!wh->dma_bounce_buffer) {
     72			wh->dma_bounce_len = 0;
     73			return 1;
     74		}
     75
     76		if (!dir_in) {
     77			/* copy to bounce buffer for a write */
     78			memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr,
     79			       scsi_pointer->this_residual);
     80		}
     81
     82		addr = virt_to_bus(wh->dma_bounce_buffer);
     83	}
     84
     85	/* setup dma direction */
     86	if (!dir_in)
     87		cntr |= CNTR_DDIR;
     88
     89	/* remember direction */
     90	wh->dma_dir = dir_in;
     91
     92	regs->CNTR = cntr;
     93
     94	/* setup DMA *physical* address */
     95	regs->ACR = addr;
     96
     97	if (dir_in) {
     98		/* invalidate any cache */
     99		cache_clear(addr, scsi_pointer->this_residual);
    100	} else {
    101		/* push any dirty cache */
    102		cache_push(addr, scsi_pointer->this_residual);
    103	}
    104
    105	/* start DMA */
    106	mb();			/* make sure setup is completed */
    107	regs->ST_DMA = 1;
    108	mb();			/* make sure DMA has started before next IO */
    109
    110	/* return success */
    111	return 0;
    112}
    113
    114static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
    115		     int status)
    116{
    117	struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(SCpnt);
    118	struct a3000_hostdata *hdata = shost_priv(instance);
    119	struct WD33C93_hostdata *wh = &hdata->wh;
    120	struct a3000_scsiregs *regs = hdata->regs;
    121
    122	/* disable SCSI interrupts */
    123	unsigned short cntr = CNTR_PDMD;
    124
    125	if (!wh->dma_dir)
    126		cntr |= CNTR_DDIR;
    127
    128	regs->CNTR = cntr;
    129	mb();			/* make sure CNTR is updated before next IO */
    130
    131	/* flush if we were reading */
    132	if (wh->dma_dir) {
    133		regs->FLUSH = 1;
    134		mb();		/* don't allow prefetch */
    135		while (!(regs->ISTR & ISTR_FE_FLG))
    136			barrier();
    137		mb();		/* no IO until FLUSH is done */
    138	}
    139
    140	/* clear a possible interrupt */
    141	/* I think that this CINT is only necessary if you are
    142	 * using the terminal count features.   HM 7 Mar 1994
    143	 */
    144	regs->CINT = 1;
    145
    146	/* stop DMA */
    147	regs->SP_DMA = 1;
    148	mb();			/* make sure DMA is stopped before next IO */
    149
    150	/* restore the CONTROL bits (minus the direction flag) */
    151	regs->CNTR = CNTR_PDMD | CNTR_INTEN;
    152	mb();			/* make sure CNTR is updated before next IO */
    153
    154	/* copy from a bounce buffer, if necessary */
    155	if (status && wh->dma_bounce_buffer) {
    156		if (SCpnt) {
    157			if (wh->dma_dir && SCpnt)
    158				memcpy(scsi_pointer->ptr, wh->dma_bounce_buffer,
    159				       scsi_pointer->this_residual);
    160			kfree(wh->dma_bounce_buffer);
    161			wh->dma_bounce_buffer = NULL;
    162			wh->dma_bounce_len = 0;
    163		} else {
    164			kfree(wh->dma_bounce_buffer);
    165			wh->dma_bounce_buffer = NULL;
    166			wh->dma_bounce_len = 0;
    167		}
    168	}
    169}
    170
    171static struct scsi_host_template amiga_a3000_scsi_template = {
    172	.module			= THIS_MODULE,
    173	.name			= "Amiga 3000 built-in SCSI",
    174	.show_info		= wd33c93_show_info,
    175	.write_info		= wd33c93_write_info,
    176	.proc_name		= "A3000",
    177	.queuecommand		= wd33c93_queuecommand,
    178	.eh_abort_handler	= wd33c93_abort,
    179	.eh_host_reset_handler	= wd33c93_host_reset,
    180	.can_queue		= CAN_QUEUE,
    181	.this_id		= 7,
    182	.sg_tablesize		= SG_ALL,
    183	.cmd_per_lun		= CMD_PER_LUN,
    184	.cmd_size		= sizeof(struct scsi_pointer),
    185};
    186
    187static int __init amiga_a3000_scsi_probe(struct platform_device *pdev)
    188{
    189	struct resource *res;
    190	struct Scsi_Host *instance;
    191	int error;
    192	struct a3000_scsiregs *regs;
    193	wd33c93_regs wdregs;
    194	struct a3000_hostdata *hdata;
    195
    196	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    197	if (!res)
    198		return -ENODEV;
    199
    200	if (!request_mem_region(res->start, resource_size(res), "wd33c93"))
    201		return -EBUSY;
    202
    203	instance = scsi_host_alloc(&amiga_a3000_scsi_template,
    204				   sizeof(struct a3000_hostdata));
    205	if (!instance) {
    206		error = -ENOMEM;
    207		goto fail_alloc;
    208	}
    209
    210	instance->irq = IRQ_AMIGA_PORTS;
    211
    212	regs = ZTWO_VADDR(res->start);
    213	regs->DAWR = DAWR_A3000;
    214
    215	wdregs.SASR = &regs->SASR;
    216	wdregs.SCMD = &regs->SCMD;
    217
    218	hdata = shost_priv(instance);
    219	hdata->wh.no_sync = 0xff;
    220	hdata->wh.fast = 0;
    221	hdata->wh.dma_mode = CTRL_DMA;
    222	hdata->regs = regs;
    223
    224	wd33c93_init(instance, wdregs, dma_setup, dma_stop, WD33C93_FS_12_15);
    225	error = request_irq(IRQ_AMIGA_PORTS, a3000_intr, IRQF_SHARED,
    226			    "A3000 SCSI", instance);
    227	if (error)
    228		goto fail_irq;
    229
    230	regs->CNTR = CNTR_PDMD | CNTR_INTEN;
    231
    232	error = scsi_add_host(instance, NULL);
    233	if (error)
    234		goto fail_host;
    235
    236	platform_set_drvdata(pdev, instance);
    237
    238	scsi_scan_host(instance);
    239	return 0;
    240
    241fail_host:
    242	free_irq(IRQ_AMIGA_PORTS, instance);
    243fail_irq:
    244	scsi_host_put(instance);
    245fail_alloc:
    246	release_mem_region(res->start, resource_size(res));
    247	return error;
    248}
    249
    250static int __exit amiga_a3000_scsi_remove(struct platform_device *pdev)
    251{
    252	struct Scsi_Host *instance = platform_get_drvdata(pdev);
    253	struct a3000_hostdata *hdata = shost_priv(instance);
    254	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    255
    256	hdata->regs->CNTR = 0;
    257	scsi_remove_host(instance);
    258	free_irq(IRQ_AMIGA_PORTS, instance);
    259	scsi_host_put(instance);
    260	release_mem_region(res->start, resource_size(res));
    261	return 0;
    262}
    263
    264static struct platform_driver amiga_a3000_scsi_driver = {
    265	.remove = __exit_p(amiga_a3000_scsi_remove),
    266	.driver   = {
    267		.name	= "amiga-a3000-scsi",
    268	},
    269};
    270
    271module_platform_driver_probe(amiga_a3000_scsi_driver, amiga_a3000_scsi_probe);
    272
    273MODULE_DESCRIPTION("Amiga 3000 built-in SCSI");
    274MODULE_LICENSE("GPL");
    275MODULE_ALIAS("platform:amiga-a3000-scsi");