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
47ddi_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
80ddi_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
97size_t	prd_size = sizeof (prde_t) * ATA_DMA_NSEGS;
98
99int
100ata_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);
150err1:
151	ddi_dma_mem_free(&ata_ctlp->ac_sg_acc_handle);
152	ata_ctlp->ac_sg_acc_handle = NULL;
153err2:
154	ddi_dma_free_handle(&ata_ctlp->ac_sg_handle);
155	ata_ctlp->ac_sg_handle = NULL;
156err3:
157	return (FALSE);
158}
159
160
161void
162ata_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
176void
177ata_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
221void
222ata_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
250void
251ata_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 */
270void
271ata_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
306int
307ata_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
342int
343ata_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
371int
372ata_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