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