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