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 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 * This file contains ddi functions common to intel architectures
30 */
31
32#include <sys/archsystm.h>
33#include <sys/types.h>
34#include <sys/dditypes.h>
35#include <sys/ddi_impldefs.h>
36#include <sys/sunddi.h>
37#include <sys/cpu.h>
38
39/*
40 * DDI Mapping
41 */
42
43/*
44 * i_ddi_bus_map:
45 * Generic bus_map entry point, for byte addressable devices
46 * conforming to the reg/range addressing model with no HAT layer
47 * to be programmed at this level.
48 */
49
50int
51i_ddi_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
52	off_t offset, off_t len, caddr_t *vaddrp)
53{
54	struct regspec tmp_reg, *rp;
55	ddi_map_req_t mr = *mp;		/* Get private copy of request */
56	int error;
57
58	mp = &mr;
59
60	/*
61	 * First, if given an rnumber, convert it to a regspec...
62	 */
63
64	if (mp->map_type == DDI_MT_RNUMBER)  {
65
66		int rnumber = mp->map_obj.rnumber;
67#ifdef	DDI_MAP_DEBUG
68		static char *out_of_range =
69		    "i_ddi_bus_map: Out of range rnumber <%d>, device <%s>";
70#endif	/* DDI_MAP_DEBUG */
71
72		rp = i_ddi_rnumber_to_regspec(rdip, rnumber);
73		if (rp == (struct regspec *)0)  {
74#ifdef	DDI_MAP_DEBUG
75			cmn_err(CE_WARN, out_of_range, rnumber,
76			    ddi_get_name(rdip));
77#endif	/* DDI_MAP_DEBUG */
78			return (DDI_ME_RNUMBER_RANGE);
79		}
80
81		/*
82		 * Convert the given ddi_map_req_t from rnumber to regspec...
83		 */
84
85		mp->map_type = DDI_MT_REGSPEC;
86		mp->map_obj.rp = rp;
87	}
88
89	/*
90	 * Adjust offset and length correspnding to called values...
91	 * XXX: A non-zero length means override the one in the regspec.
92	 * XXX: (Regardless of what's in the parent's range)
93	 */
94
95	tmp_reg = *(mp->map_obj.rp);		/* Preserve underlying data */
96	rp = mp->map_obj.rp = &tmp_reg;		/* Use tmp_reg in request */
97
98#ifdef	DDI_MAP_DEBUG
99	cmn_err(CE_CONT,
100	    "i_ddi_bus_map: <%s,%s> <0x%x, 0x%x, 0x%d> "
101	    "offset %d len %d handle 0x%x\n",
102	    ddi_get_name(dip), ddi_get_name(rdip),
103	    rp->regspec_bustype, rp->regspec_addr, rp->regspec_size,
104	    offset, len, mp->map_handlep);
105#endif	/* DDI_MAP_DEBUG */
106
107	/*
108	 * I/O or memory mapping
109	 *
110	 *	<bustype=0, addr=x, len=x>: memory
111	 *	<bustype=1, addr=x, len=x>: i/o
112	 *	<bustype>1, addr=0, len=x>: x86-compatibility i/o
113	 */
114
115	if (rp->regspec_bustype > 1 && rp->regspec_addr != 0) {
116		cmn_err(CE_WARN, "<%s,%s>: invalid register spec"
117		    " <0x%x, 0x%x, 0x%x>\n", ddi_get_name(dip),
118		    ddi_get_name(rdip), rp->regspec_bustype,
119		    rp->regspec_addr, rp->regspec_size);
120		return (DDI_ME_INVAL);
121	}
122
123	if (rp->regspec_bustype > 1 && rp->regspec_addr == 0) {
124		/*
125		 * compatibility i/o mapping
126		 */
127		rp->regspec_bustype += (uint_t)offset;
128	} else {
129		/*
130		 * Normal memory or i/o mapping
131		 */
132		rp->regspec_addr += (uint_t)offset;
133	}
134
135	if (len != 0)
136		rp->regspec_size = (uint_t)len;
137
138#ifdef	DDI_MAP_DEBUG
139	cmn_err(CE_CONT,
140	    "               <%s,%s> <0x%x, 0x%x, 0x%d> "
141	    "offset %d len %d\n",
142	    ddi_get_name(dip), ddi_get_name(rdip),
143	    rp->regspec_bustype, rp->regspec_addr, rp->regspec_size,
144	    offset, len);
145#endif	/* DDI_MAP_DEBUG */
146
147	/*
148	 * If we had an MMU, this is where you'd program the MMU and hat layer.
149	 * Since we're using the default function here, we do not have an MMU
150	 * to program.
151	 */
152
153	/*
154	 * Apply any parent ranges at this level, if applicable.
155	 * (This is where nexus specific regspec translation takes place.
156	 * Use of this function is implicit agreement that translation is
157	 * provided via ddi_apply_range.)  Note that we assume that
158	 * the request is within the parents limits.
159	 */
160
161#ifdef	DDI_MAP_DEBUG
162	ddi_map_debug("applying range of parent <%s> to child <%s>...\n",
163	    ddi_get_name(dip), ddi_get_name(rdip));
164#endif	/* DDI_MAP_DEBUG */
165
166	if ((error = i_ddi_apply_range(dip, rdip, mp->map_obj.rp)) != 0)
167		return (error);
168
169	/*
170	 * Call my parents bus_map function with modified values...
171	 */
172
173	return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp));
174}
175
176/*
177 * Creating register mappings and handling interrupts:
178 */
179
180struct regspec *
181i_ddi_rnumber_to_regspec(dev_info_t *dip, int rnumber)
182{
183	if (rnumber >= sparc_pd_getnreg(DEVI(dip)))
184		return ((struct regspec *)0);
185
186	return (sparc_pd_getreg(DEVI(dip), rnumber));
187}
188
189/*
190 * Static function to determine if a reg prop is enclosed within
191 * a given a range spec.  (For readability: only used by i_ddi_aply_range.).
192 */
193static int
194reg_is_enclosed_in_range(struct regspec *rp, struct rangespec *rangep)
195{
196	if (rp->regspec_bustype != rangep->rng_cbustype)
197		return (0);
198
199	if (rp->regspec_addr < rangep->rng_coffset)
200		return (0);
201
202	if (rangep->rng_size == 0)
203		return (1);	/* size is really 2**(bits_per_word) */
204
205	if ((rp->regspec_addr + rp->regspec_size - 1) <=
206	    (rangep->rng_coffset + rangep->rng_size - 1))
207		return (1);
208
209	return (0);
210}
211
212/*
213 * i_ddi_apply_range:
214 * Apply range of dp to struct regspec *rp, if applicable.
215 * If there's any range defined, it gets applied.
216 */
217
218int
219i_ddi_apply_range(dev_info_t *dp, dev_info_t *rdip, struct regspec *rp)
220{
221	int nrange, b;
222	struct rangespec *rangep;
223	static char *out_of_range =
224	    "Out of range register specification from device node <%s>\n";
225
226	nrange = sparc_pd_getnrng(dp);
227	if (nrange == 0)  {
228#ifdef	DDI_MAP_DEBUG
229		ddi_map_debug("    No range.\n");
230#endif	/* DDI_MAP_DEBUG */
231		return (0);
232	}
233
234	/*
235	 * Find a match, making sure the regspec is within the range
236	 * of the parent, noting that a size of zero in a range spec
237	 * really means a size of 2**(bitsperword).
238	 */
239
240	for (b = 0, rangep = sparc_pd_getrng(dp, 0); b < nrange; ++b, ++rangep)
241		if (reg_is_enclosed_in_range(rp, rangep))
242			break;		/* found a match */
243
244	if (b == nrange)  {
245		cmn_err(CE_WARN, out_of_range, ddi_get_name(rdip));
246		return (DDI_ME_REGSPEC_RANGE);
247	}
248
249#ifdef	DDI_MAP_DEBUG
250	ddi_map_debug("    Input:  %x.%x.%x\n", rp->regspec_bustype,
251	    rp->regspec_addr, rp->regspec_size);
252	ddi_map_debug("    Range:  %x.%x %x.%x %x\n",
253	    rangep->rng_cbustype, rangep->rng_coffset,
254	    rangep->rng_bustype, rangep->rng_offset, rangep->rng_size);
255#endif	/* DDI_MAP_DEBUG */
256
257	rp->regspec_bustype = rangep->rng_bustype;
258	rp->regspec_addr += rangep->rng_offset - rangep->rng_coffset;
259
260#ifdef	DDI_MAP_DEBUG
261	ddi_map_debug("    Return: %x.%x.%x\n", rp->regspec_bustype,
262	    rp->regspec_addr, rp->regspec_size);
263#endif	/* DDI_MAP_DEBUG */
264
265	return (0);
266}
267
268/*
269 * i_ddi_map_fault: wrapper for bus_map_fault.
270 */
271int
272i_ddi_map_fault(dev_info_t *dip, dev_info_t *rdip,
273	struct hat *hat, struct seg *seg, caddr_t addr,
274	struct devpage *dp, pfn_t pfn, uint_t prot, uint_t lock)
275{
276	dev_info_t *pdip;
277
278	if (dip == NULL)
279		return (DDI_FAILURE);
280
281	pdip = (dev_info_t *)DEVI(dip)->devi_bus_map_fault;
282
283	/* request appropriate parent to map fault */
284	return ((*(DEVI(pdip)->devi_ops->devo_bus_ops->bus_map_fault))(pdip,
285	    rdip, hat, seg, addr, dp, pfn, prot, lock));
286}
287
288/*
289 * Return an integer in native machine format from an OBP 1275 integer
290 * representation, which is big-endian, with no particular alignment
291 * guarantees.  intp points to the OBP data, and n the number of bytes.
292 *
293 * Byte-swapping is needed on intel.
294 */
295int
296impl_ddi_prop_int_from_prom(uchar_t *intp, int n)
297{
298	int	i = 0;
299
300	ASSERT(n > 0 && n <= 4);
301
302	intp += n;
303	while (n-- > 0) {
304		i = (i << 8) | *(--intp);
305	}
306
307	return (i);
308}
309
310
311int drv_usec_coarse_timing = 0;
312
313/*
314 * Time delay function called by drivers
315 */
316void
317drv_usecwait(clock_t count)
318{
319	int tens = 0;
320	extern int gethrtime_hires;
321
322	if (gethrtime_hires) {
323		hrtime_t start, end;
324		hrtime_t waittime;
325
326		if (drv_usec_coarse_timing) {
327			/* revert to the wait time as before using tsc */
328			/* in case there are callers depending on the */
329			/* old behaviour */
330			waittime = ((count > 10) ?
331			    (((hrtime_t)count / 10) + 1) : 1) *
332			    10 * (NANOSEC / MICROSEC);
333		} else  {
334			waittime = (hrtime_t)count * (NANOSEC / MICROSEC);
335		}
336		start = end =  gethrtime();
337		while ((end - start) < waittime) {
338			SMT_PAUSE();
339			end = gethrtime();
340		}
341		return;
342
343	}
344
345	if (count > 10)
346		tens = count/10;
347	tens++;			/* roundup; wait at least 10 microseconds */
348	while (tens > 0) {
349		tenmicrosec();
350		tens--;
351	}
352}
353