diff options
Diffstat (limited to 'drivers/scsi/a2091.c')
-rw-r--r-- | drivers/scsi/a2091.c | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c new file mode 100644 index 000000000000..9928a2fbce0c --- /dev/null +++ b/drivers/scsi/a2091.c | |||
@@ -0,0 +1,260 @@ | |||
1 | #include <linux/types.h> | ||
2 | #include <linux/mm.h> | ||
3 | #include <linux/blkdev.h> | ||
4 | #include <linux/sched.h> | ||
5 | #include <linux/version.h> | ||
6 | #include <linux/init.h> | ||
7 | #include <linux/interrupt.h> | ||
8 | |||
9 | #include <asm/setup.h> | ||
10 | #include <asm/page.h> | ||
11 | #include <asm/pgtable.h> | ||
12 | #include <asm/amigaints.h> | ||
13 | #include <asm/amigahw.h> | ||
14 | #include <linux/zorro.h> | ||
15 | #include <asm/irq.h> | ||
16 | #include <linux/spinlock.h> | ||
17 | |||
18 | #include "scsi.h" | ||
19 | #include <scsi/scsi_host.h> | ||
20 | #include "wd33c93.h" | ||
21 | #include "a2091.h" | ||
22 | |||
23 | #include<linux/stat.h> | ||
24 | |||
25 | #define DMA(ptr) ((a2091_scsiregs *)((ptr)->base)) | ||
26 | #define HDATA(ptr) ((struct WD33C93_hostdata *)((ptr)->hostdata)) | ||
27 | |||
28 | static irqreturn_t a2091_intr (int irq, void *_instance, struct pt_regs *fp) | ||
29 | { | ||
30 | unsigned long flags; | ||
31 | unsigned int status; | ||
32 | struct Scsi_Host *instance = (struct Scsi_Host *)_instance; | ||
33 | |||
34 | status = DMA(instance)->ISTR; | ||
35 | if (!(status & (ISTR_INT_F|ISTR_INT_P)) || !(status & ISTR_INTS)) | ||
36 | return IRQ_NONE; | ||
37 | |||
38 | spin_lock_irqsave(instance->host_lock, flags); | ||
39 | wd33c93_intr(instance); | ||
40 | spin_unlock_irqrestore(instance->host_lock, flags); | ||
41 | return IRQ_HANDLED; | ||
42 | } | ||
43 | |||
44 | static int dma_setup (Scsi_Cmnd *cmd, int dir_in) | ||
45 | { | ||
46 | unsigned short cntr = CNTR_PDMD | CNTR_INTEN; | ||
47 | unsigned long addr = virt_to_bus(cmd->SCp.ptr); | ||
48 | struct Scsi_Host *instance = cmd->device->host; | ||
49 | |||
50 | /* don't allow DMA if the physical address is bad */ | ||
51 | if (addr & A2091_XFER_MASK || | ||
52 | (!dir_in && mm_end_of_chunk (addr, cmd->SCp.this_residual))) | ||
53 | { | ||
54 | HDATA(instance)->dma_bounce_len = (cmd->SCp.this_residual + 511) | ||
55 | & ~0x1ff; | ||
56 | HDATA(instance)->dma_bounce_buffer = | ||
57 | kmalloc (HDATA(instance)->dma_bounce_len, GFP_KERNEL); | ||
58 | |||
59 | /* can't allocate memory; use PIO */ | ||
60 | if (!HDATA(instance)->dma_bounce_buffer) { | ||
61 | HDATA(instance)->dma_bounce_len = 0; | ||
62 | return 1; | ||
63 | } | ||
64 | |||
65 | /* get the physical address of the bounce buffer */ | ||
66 | addr = virt_to_bus(HDATA(instance)->dma_bounce_buffer); | ||
67 | |||
68 | /* the bounce buffer may not be in the first 16M of physmem */ | ||
69 | if (addr & A2091_XFER_MASK) { | ||
70 | /* we could use chipmem... maybe later */ | ||
71 | kfree (HDATA(instance)->dma_bounce_buffer); | ||
72 | HDATA(instance)->dma_bounce_buffer = NULL; | ||
73 | HDATA(instance)->dma_bounce_len = 0; | ||
74 | return 1; | ||
75 | } | ||
76 | |||
77 | if (!dir_in) { | ||
78 | /* copy to bounce buffer for a write */ | ||
79 | if (cmd->use_sg) | ||
80 | #if 0 | ||
81 | panic ("scsi%ddma: incomplete s/g support", | ||
82 | instance->host_no); | ||
83 | #else | ||
84 | memcpy (HDATA(instance)->dma_bounce_buffer, | ||
85 | cmd->SCp.ptr, cmd->SCp.this_residual); | ||
86 | #endif | ||
87 | else | ||
88 | memcpy (HDATA(instance)->dma_bounce_buffer, | ||
89 | cmd->request_buffer, cmd->request_bufflen); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | /* setup dma direction */ | ||
94 | if (!dir_in) | ||
95 | cntr |= CNTR_DDIR; | ||
96 | |||
97 | /* remember direction */ | ||
98 | HDATA(cmd->device->host)->dma_dir = dir_in; | ||
99 | |||
100 | DMA(cmd->device->host)->CNTR = cntr; | ||
101 | |||
102 | /* setup DMA *physical* address */ | ||
103 | DMA(cmd->device->host)->ACR = addr; | ||
104 | |||
105 | if (dir_in){ | ||
106 | /* invalidate any cache */ | ||
107 | cache_clear (addr, cmd->SCp.this_residual); | ||
108 | }else{ | ||
109 | /* push any dirty cache */ | ||
110 | cache_push (addr, cmd->SCp.this_residual); | ||
111 | } | ||
112 | /* start DMA */ | ||
113 | DMA(cmd->device->host)->ST_DMA = 1; | ||
114 | |||
115 | /* return success */ | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt, | ||
120 | int status) | ||
121 | { | ||
122 | /* disable SCSI interrupts */ | ||
123 | unsigned short cntr = CNTR_PDMD; | ||
124 | |||
125 | if (!HDATA(instance)->dma_dir) | ||
126 | cntr |= CNTR_DDIR; | ||
127 | |||
128 | /* disable SCSI interrupts */ | ||
129 | DMA(instance)->CNTR = cntr; | ||
130 | |||
131 | /* flush if we were reading */ | ||
132 | if (HDATA(instance)->dma_dir) { | ||
133 | DMA(instance)->FLUSH = 1; | ||
134 | while (!(DMA(instance)->ISTR & ISTR_FE_FLG)) | ||
135 | ; | ||
136 | } | ||
137 | |||
138 | /* clear a possible interrupt */ | ||
139 | DMA(instance)->CINT = 1; | ||
140 | |||
141 | /* stop DMA */ | ||
142 | DMA(instance)->SP_DMA = 1; | ||
143 | |||
144 | /* restore the CONTROL bits (minus the direction flag) */ | ||
145 | DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN; | ||
146 | |||
147 | /* copy from a bounce buffer, if necessary */ | ||
148 | if (status && HDATA(instance)->dma_bounce_buffer) { | ||
149 | if (SCpnt && SCpnt->use_sg) { | ||
150 | #if 0 | ||
151 | panic ("scsi%d: incomplete s/g support", | ||
152 | instance->host_no); | ||
153 | #else | ||
154 | if( HDATA(instance)->dma_dir ) | ||
155 | memcpy (SCpnt->SCp.ptr, | ||
156 | HDATA(instance)->dma_bounce_buffer, | ||
157 | SCpnt->SCp.this_residual); | ||
158 | kfree (HDATA(instance)->dma_bounce_buffer); | ||
159 | HDATA(instance)->dma_bounce_buffer = NULL; | ||
160 | HDATA(instance)->dma_bounce_len = 0; | ||
161 | |||
162 | #endif | ||
163 | } else { | ||
164 | if (HDATA(instance)->dma_dir && SCpnt) | ||
165 | memcpy (SCpnt->request_buffer, | ||
166 | HDATA(instance)->dma_bounce_buffer, | ||
167 | SCpnt->request_bufflen); | ||
168 | |||
169 | kfree (HDATA(instance)->dma_bounce_buffer); | ||
170 | HDATA(instance)->dma_bounce_buffer = NULL; | ||
171 | HDATA(instance)->dma_bounce_len = 0; | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | int __init a2091_detect(Scsi_Host_Template *tpnt) | ||
177 | { | ||
178 | static unsigned char called = 0; | ||
179 | struct Scsi_Host *instance; | ||
180 | unsigned long address; | ||
181 | struct zorro_dev *z = NULL; | ||
182 | wd33c93_regs regs; | ||
183 | int num_a2091 = 0; | ||
184 | |||
185 | if (!MACH_IS_AMIGA || called) | ||
186 | return 0; | ||
187 | called = 1; | ||
188 | |||
189 | tpnt->proc_name = "A2091"; | ||
190 | tpnt->proc_info = &wd33c93_proc_info; | ||
191 | |||
192 | while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { | ||
193 | if (z->id != ZORRO_PROD_CBM_A590_A2091_1 && | ||
194 | z->id != ZORRO_PROD_CBM_A590_A2091_2) | ||
195 | continue; | ||
196 | address = z->resource.start; | ||
197 | if (!request_mem_region(address, 256, "wd33c93")) | ||
198 | continue; | ||
199 | |||
200 | instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata)); | ||
201 | if (instance == NULL) { | ||
202 | release_mem_region(address, 256); | ||
203 | continue; | ||
204 | } | ||
205 | instance->base = ZTWO_VADDR(address); | ||
206 | instance->irq = IRQ_AMIGA_PORTS; | ||
207 | instance->unique_id = z->slotaddr; | ||
208 | DMA(instance)->DAWR = DAWR_A2091; | ||
209 | regs.SASR = &(DMA(instance)->SASR); | ||
210 | regs.SCMD = &(DMA(instance)->SCMD); | ||
211 | wd33c93_init(instance, regs, dma_setup, dma_stop, WD33C93_FS_8_10); | ||
212 | request_irq(IRQ_AMIGA_PORTS, a2091_intr, SA_SHIRQ, "A2091 SCSI", | ||
213 | instance); | ||
214 | DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN; | ||
215 | num_a2091++; | ||
216 | } | ||
217 | |||
218 | return num_a2091; | ||
219 | } | ||
220 | |||
221 | static int a2091_bus_reset(Scsi_Cmnd *cmd) | ||
222 | { | ||
223 | /* FIXME perform bus-specific reset */ | ||
224 | wd33c93_host_reset(cmd); | ||
225 | return SUCCESS; | ||
226 | } | ||
227 | |||
228 | #define HOSTS_C | ||
229 | |||
230 | static Scsi_Host_Template driver_template = { | ||
231 | .proc_name = "A2901", | ||
232 | .name = "Commodore A2091/A590 SCSI", | ||
233 | .detect = a2091_detect, | ||
234 | .release = a2091_release, | ||
235 | .queuecommand = wd33c93_queuecommand, | ||
236 | .eh_abort_handler = wd33c93_abort, | ||
237 | .eh_bus_reset_handler = a2091_bus_reset, | ||
238 | .eh_host_reset_handler = wd33c93_host_reset, | ||
239 | .can_queue = CAN_QUEUE, | ||
240 | .this_id = 7, | ||
241 | .sg_tablesize = SG_ALL, | ||
242 | .cmd_per_lun = CMD_PER_LUN, | ||
243 | .use_clustering = DISABLE_CLUSTERING | ||
244 | }; | ||
245 | |||
246 | |||
247 | #include "scsi_module.c" | ||
248 | |||
249 | int a2091_release(struct Scsi_Host *instance) | ||
250 | { | ||
251 | #ifdef MODULE | ||
252 | DMA(instance)->CNTR = 0; | ||
253 | release_mem_region(ZTWO_PADDR(instance->base), 256); | ||
254 | free_irq(IRQ_AMIGA_PORTS, instance); | ||
255 | wd33c93_release(); | ||
256 | #endif | ||
257 | return 1; | ||
258 | } | ||
259 | |||
260 | MODULE_LICENSE("GPL"); | ||