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