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