xref: /illumos-gate/usr/src/uts/intel/io/dktp/controller/ata/ata_dma.c (revision 2d6eb4a5e0a47d30189497241345dc5466bb68ab)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/debug.h>
29 
30 #include "ata_common.h"
31 #include "ata_disk.h"
32 #include "atapi.h"
33 #include "pciide.h"
34 
35 /*
36  * grap the PCI-IDE status byte
37  */
38 #define	PCIIDE_STATUS_GET(hdl, addr)	\
39 	ddi_get8((hdl), ((uchar_t *)(addr) + PCIIDE_BMISX_REG))
40 
41 /*
42  * DMA attributes for device I/O
43  */
44 
45 ddi_dma_attr_t ata_pciide_dma_attr = {
46 	DMA_ATTR_V0,		/* dma_attr_version */
47 	0,			/* dma_attr_addr_lo */
48 	0xffffffffU,		/* dma_attr_addr_hi */
49 	0xffff,			/* dma_attr_count_max */
50 	sizeof (int),		/* dma_attr_align */
51 	1,			/* dma_attr_burstsizes */
52 	1,			/* dma_attr_minxfer */
53 	0x100 << SCTRSHFT,	/* dma_attr_maxxfer */
54 				/* note that this value can change */
55 				/* based on max_transfer property */
56 	0xffff,			/* dma_attr_seg */
57 	ATA_DMA_NSEGS,		/* dma_attr_sgllen */
58 	512,			/* dma_attr_granular */
59 	0			/* dma_attr_flags */
60 };
61 
62 /*
63  * DMA attributes for the Bus Mastering PRD table
64  *
65  * PRD table Must not cross 4k boundary.
66  *
67  * NOTE: the SFF-8038i spec says don't cross a 64k boundary but
68  * some chip specs seem to think the spec says 4k boundary, Intel
69  * 82371AB, section 5.2.3. I don't know whether the 4k restriction
70  * is for real or just a typo. I've specified 4k just to be safe.
71  * The same Intel spec says the buffer must be 64K aligned, I don't
72  * believe that and have specified 4 byte alignment.
73  *
74  */
75 
76 #define	PCIIDE_BOUNDARY	(0x1000)
77 
78 ddi_dma_attr_t ata_prd_dma_attr = {
79 	DMA_ATTR_V0,		/* dma_attr_version */
80 	0,			/* dma_attr_addr_lo */
81 	0xffffffffU,		/* dma_attr_addr_hi */
82 	PCIIDE_BOUNDARY - 1,	/* dma_attr_count_max */
83 	sizeof (int),		/* dma_attr_align */
84 	1,			/* dma_attr_burstsizes */
85 	1,			/* dma_attr_minxfer */
86 	PCIIDE_BOUNDARY,	/* dma_attr_maxxfer */
87 	PCIIDE_BOUNDARY - 1,	/* dma_attr_seg */
88 	1,			/* dma_attr_sgllen */
89 	1,			/* dma_attr_granular */
90 	0			/* dma_attr_flags */
91 };
92 
93 
94 
95 size_t	prd_size = sizeof (prde_t) * ATA_DMA_NSEGS;
96 
97 int
ata_pciide_alloc(dev_info_t * dip,ata_ctl_t * ata_ctlp)98 ata_pciide_alloc(
99 	dev_info_t *dip,
100 	ata_ctl_t *ata_ctlp)
101 {
102 	ddi_device_acc_attr_t	dev_attr;
103 	ddi_dma_cookie_t	cookie;
104 	size_t			buf_size;
105 	uint_t			count;
106 	int			rc;
107 
108 	dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
109 	dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
110 	dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
111 
112 
113 	rc = ddi_dma_alloc_handle(dip, &ata_prd_dma_attr, DDI_DMA_SLEEP, NULL,
114 		&ata_ctlp->ac_sg_handle);
115 	if (rc != DDI_SUCCESS) {
116 		ADBG_ERROR(("ata_pciide_alloc 0x%p handle %d\n",
117 		    (void *)ata_ctlp, rc));
118 		goto err3;
119 	}
120 
121 	rc = ddi_dma_mem_alloc(ata_ctlp->ac_sg_handle, prd_size, &dev_attr,
122 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
123 	    &ata_ctlp->ac_sg_list, &buf_size, &ata_ctlp->ac_sg_acc_handle);
124 	if (rc != DDI_SUCCESS) {
125 		ADBG_ERROR(("ata_pciide_alloc 0x%p mem %d\n",
126 		    (void *)ata_ctlp, rc));
127 		goto err2;
128 	}
129 
130 	rc = ddi_dma_addr_bind_handle(ata_ctlp->ac_sg_handle, NULL,
131 	    ata_ctlp->ac_sg_list, buf_size,
132 	    DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
133 	    DDI_DMA_SLEEP, NULL, &cookie, &count);
134 	if (rc != DDI_DMA_MAPPED) {
135 		ADBG_ERROR(("ata_pciide_alloc 0x%p bind %d\n",
136 		    (void *)ata_ctlp, rc));
137 		goto err1;
138 	}
139 
140 	ASSERT(count == 1);
141 	ASSERT((cookie.dmac_address & (sizeof (int) - 1)) == 0);
142 #define	Mask4K	0xfffff000
143 	ASSERT((cookie.dmac_address & Mask4K)
144 		== ((cookie.dmac_address + cookie.dmac_size - 1) & Mask4K));
145 
146 	ata_ctlp->ac_sg_paddr = cookie.dmac_address;
147 	return (TRUE);
148 err1:
149 	ddi_dma_mem_free(&ata_ctlp->ac_sg_acc_handle);
150 	ata_ctlp->ac_sg_acc_handle = NULL;
151 err2:
152 	ddi_dma_free_handle(&ata_ctlp->ac_sg_handle);
153 	ata_ctlp->ac_sg_handle = NULL;
154 err3:
155 	return (FALSE);
156 }
157 
158 
159 void
ata_pciide_free(ata_ctl_t * ata_ctlp)160 ata_pciide_free(ata_ctl_t *ata_ctlp)
161 {
162 	if (ata_ctlp->ac_sg_handle == NULL)
163 		return;
164 
165 	(void) ddi_dma_unbind_handle(ata_ctlp->ac_sg_handle);
166 	ddi_dma_mem_free(&ata_ctlp->ac_sg_acc_handle);
167 	ddi_dma_free_handle(&ata_ctlp->ac_sg_handle);
168 	ata_ctlp->ac_sg_handle = NULL;
169 	ata_ctlp->ac_sg_acc_handle = NULL;
170 }
171 
172 
173 
174 void
ata_pciide_dma_setup(ata_ctl_t * ata_ctlp,prde_t * srcp,int sg_cnt)175 ata_pciide_dma_setup(
176 	ata_ctl_t *ata_ctlp,
177 	prde_t	  *srcp,
178 	int	   sg_cnt)
179 {
180 	ddi_acc_handle_t bmhandle = ata_ctlp->ac_bmhandle;
181 	caddr_t		 bmaddr = ata_ctlp->ac_bmaddr;
182 	ddi_acc_handle_t sg_acc_handle = ata_ctlp->ac_sg_acc_handle;
183 	uint_t		*dstp = (uint_t *)ata_ctlp->ac_sg_list;
184 	int		 idx;
185 
186 	ASSERT(dstp != 0);
187 	ASSERT(sg_cnt != 0);
188 
189 	ADBG_DMA(("ata dma_setup 0x%p 0x%p %d\n", ata_ctlp, srcp, sg_cnt));
190 	/*
191 	 * Copy the PRD list to controller's phys buffer.
192 	 * Copying to a fixed location avoids having to check
193 	 * every ata_pkt for alignment and page boundaries.
194 	 */
195 	for (idx = 0; idx < sg_cnt - 1; idx++, srcp++) {
196 		ddi_put32(sg_acc_handle, dstp++, srcp->p_address);
197 		ddi_put32(sg_acc_handle, dstp++, srcp->p_count);
198 	}
199 
200 	/*
201 	 * set the end of table flag in the last entry
202 	 */
203 	srcp->p_count |= PCIIDE_PRDE_EOT;
204 	ddi_put32(sg_acc_handle, dstp++, srcp->p_address);
205 	ddi_put32(sg_acc_handle, dstp++, srcp->p_count);
206 
207 	/*
208 	 * give the pciide chip the physical address of the PRDE table
209 	 */
210 	ddi_put32(bmhandle, (uint_t *)(bmaddr + PCIIDE_BMIDTPX_REG),
211 		ata_ctlp->ac_sg_paddr);
212 
213 	ADBG_DMA(("ata dma_setup 0x%p 0x%llx\n",
214 		bmaddr, (unsigned long long)ata_ctlp->ac_sg_paddr));
215 }
216 
217 
218 
219 void
ata_pciide_dma_start(ata_ctl_t * ata_ctlp,uchar_t direction)220 ata_pciide_dma_start(
221 	ata_ctl_t *ata_ctlp,
222 	uchar_t direction)
223 {
224 	ddi_acc_handle_t bmhandle = ata_ctlp->ac_bmhandle;
225 	caddr_t		 bmaddr = ata_ctlp->ac_bmaddr;
226 	uchar_t		 tmp;
227 
228 	ASSERT((ata_ctlp->ac_sg_paddr & PCIIDE_BMIDTPX_MASK) == 0);
229 	ASSERT((direction == PCIIDE_BMICX_RWCON_WRITE_TO_MEMORY) ||
230 		(direction == PCIIDE_BMICX_RWCON_READ_FROM_MEMORY));
231 
232 	/*
233 	 * Set the direction control and start the PCIIDE DMA controller
234 	 */
235 	tmp = ddi_get8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMICX_REG);
236 	tmp &= PCIIDE_BMICX_MASK;
237 	ddi_put8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMICX_REG,
238 		(tmp |  direction));
239 
240 	ddi_put8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMICX_REG,
241 		(tmp | PCIIDE_BMICX_SSBM_E | direction));
242 
243 	return;
244 
245 }
246 
247 
248 void
ata_pciide_dma_stop(ata_ctl_t * ata_ctlp)249 ata_pciide_dma_stop(
250 	ata_ctl_t *ata_ctlp)
251 {
252 	ddi_acc_handle_t bmhandle = ata_ctlp->ac_bmhandle;
253 	caddr_t		 bmaddr = ata_ctlp->ac_bmaddr;
254 	uchar_t		 tmp;
255 
256 	/*
257 	 * Stop the PCIIDE DMA controller
258 	 */
259 	tmp = ddi_get8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMICX_REG);
260 	tmp &= (PCIIDE_BMICX_MASK & (~PCIIDE_BMICX_SSBM));
261 
262 	ADBG_DMA(("ata_pciide_dma_stop 0x%p 0x%x\n", bmaddr, tmp));
263 
264 	ddi_put8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMICX_REG, tmp);
265 }
266 
267 /* ARGSUSED */
268 void
ata_pciide_dma_sg_func(gcmd_t * gcmdp,ddi_dma_cookie_t * dmackp,int single_segment,int seg_index)269 ata_pciide_dma_sg_func(
270 	gcmd_t	*gcmdp,
271 	ddi_dma_cookie_t *dmackp,
272 	int	 single_segment,
273 	int	 seg_index)
274 {
275 	ata_pkt_t *ata_pktp = GCMD2APKT(gcmdp);
276 	prde_t	  *dmap;
277 
278 	ASSERT(seg_index < ATA_DMA_NSEGS);
279 	ASSERT(((uint_t)dmackp->dmac_address & PCIIDE_PRDE_ADDR_MASK) == 0);
280 	ASSERT((dmackp->dmac_size & PCIIDE_PRDE_CNT_MASK) == 0);
281 	ASSERT(dmackp->dmac_size <= PCIIDE_PRDE_CNT_MAX);
282 
283 	ADBG_TRACE(("adp_dma_sg_func: gcmdp 0x%p dmackp 0x%p s %d idx %d\n",
284 		    gcmdp, dmackp, single_segment, seg_index));
285 
286 	/* set address of current entry in scatter/gather list */
287 	dmap = ata_pktp->ap_sg_list + seg_index;
288 
289 	/* store the phys addr and count from the cookie */
290 	dmap->p_address = (uint_t)dmackp->dmac_address;
291 	dmap->p_count = (uint_t)dmackp->dmac_size;
292 
293 	/* save the count of scatter/gather segments */
294 	ata_pktp->ap_sg_cnt = seg_index + 1;
295 
296 	/* compute the total bytes in this request */
297 	if (seg_index == 0)
298 		ata_pktp->ap_bcount = 0;
299 	ata_pktp->ap_bcount += dmackp->dmac_size;
300 }
301 
302 
303 
304 int
ata_pciide_status_clear(ata_ctl_t * ata_ctlp)305 ata_pciide_status_clear(
306 	ata_ctl_t *ata_ctlp)
307 {
308 	ddi_acc_handle_t bmhandle = ata_ctlp->ac_bmhandle;
309 	caddr_t		 bmaddr = ata_ctlp->ac_bmaddr;
310 	uchar_t		 status;
311 	uchar_t		 tmp;
312 
313 	/*
314 	 * Get the current PCIIDE status
315 	 */
316 	status = PCIIDE_STATUS_GET(ata_ctlp->ac_bmhandle, ata_ctlp->ac_bmaddr);
317 	tmp = status & PCIIDE_BMISX_MASK;
318 	tmp |= (PCIIDE_BMISX_IDERR | PCIIDE_BMISX_IDEINTS);
319 
320 	ADBG_DMA(("ata_pciide_status_clear 0x%p 0x%x\n",
321 		bmaddr, status));
322 
323 	/*
324 	 * Clear the latches (and preserve the other bits)
325 	 */
326 	ddi_put8(bmhandle, (uchar_t *)bmaddr + PCIIDE_BMISX_REG, tmp);
327 
328 #ifdef NAT_SEMI_PC87415_BUG
329 	/* ??? chip errata ??? */
330 	if (ata_ctlp->ac_nat_semi_bug) {
331 		tmp = ddi_get8(bmhandle, bmaddr + PCIIDE_BMICX_REG);
332 		tmp &= PCIIDE_BMICX_MASK;
333 		ddi_put8(bmhandle, bmaddr + PCIIDE_BMICX_REG,
334 			(tmp | PCIIDE_BMISX_IDERR | PCIIDE_BMISX_IDEINTS));
335 	}
336 #endif
337 	return (status);
338 }
339 
340 int
ata_pciide_status_dmacheck_clear(ata_ctl_t * ata_ctlp)341 ata_pciide_status_dmacheck_clear(
342 	ata_ctl_t *ata_ctlp)
343 {
344 	uchar_t		 status;
345 
346 	/*
347 	 * Get the PCIIDE DMA controller's current status
348 	 */
349 	status = ata_pciide_status_clear(ata_ctlp);
350 
351 	ADBG_DMA(("ata_pciide_status_dmacheck_clear 0x%p 0x%x\n",
352 		ata_ctlp->ac_bmaddr, status));
353 	/*
354 	 * check for errors
355 	 */
356 	if (status & PCIIDE_BMISX_IDERR) {
357 		ADBG_WARN(("ata_pciide_status: 0x%x\n", status));
358 		return (TRUE);
359 	}
360 	return (FALSE);
361 }
362 
363 
364 
365 /*
366  * Check for a pending PCI-IDE interrupt
367  */
368 
369 int
ata_pciide_status_pending(ata_ctl_t * ata_ctlp)370 ata_pciide_status_pending(
371 	ata_ctl_t *ata_ctlp)
372 {
373 	uchar_t status;
374 
375 	status = PCIIDE_STATUS_GET(ata_ctlp->ac_bmhandle, ata_ctlp->ac_bmaddr);
376 	ADBG_DMA(("ata_pciide_status_pending 0x%p 0x%x\n",
377 		ata_ctlp->ac_bmaddr, status));
378 	if (status & PCIIDE_BMISX_IDEINTS)
379 		return (TRUE);
380 	return (FALSE);
381 }
382