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