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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 * This file contains ddi functions common to intel architectures
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>
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.
51 i_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
)
54 struct regspec tmp_reg
, *rp
;
55 ddi_map_req_t mr
= *mp
; /* Get private copy of request */
61 * First, if given an rnumber, convert it to a regspec...
64 if (mp
->map_type
== DDI_MT_RNUMBER
) {
66 int rnumber
= mp
->map_obj
.rnumber
;
68 static char *out_of_range
=
69 "i_ddi_bus_map: Out of range rnumber <%d>, device <%s>";
70 #endif /* DDI_MAP_DEBUG */
72 rp
= i_ddi_rnumber_to_regspec(rdip
, rnumber
);
75 cmn_err(CE_WARN
, out_of_range
, rnumber
,
77 #endif /* DDI_MAP_DEBUG */
78 return (DDI_ME_RNUMBER_RANGE
);
82 * Convert the given ddi_map_req_t from rnumber to regspec...
85 mp
->map_type
= DDI_MT_REGSPEC
;
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)
95 tmp_reg
= *(mp
->map_obj
.rp
); /* Preserve underlying data */
96 rp
= mp
->map_obj
.rp
= &tmp_reg
; /* Use tmp_reg in request */
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 */
108 * I/O or memory mapping
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
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
);
123 if (rp
->regspec_bustype
> 1 && rp
->regspec_addr
== 0) {
125 * compatibility i/o mapping
127 rp
->regspec_bustype
+= (uint_t
)offset
;
130 * Normal memory or i/o mapping
132 rp
->regspec_addr
+= (uint_t
)offset
;
136 rp
->regspec_size
= (uint_t
)len
;
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
,
145 #endif /* DDI_MAP_DEBUG */
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
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.
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 */
166 if ((error
= i_ddi_apply_range(dip
, rdip
, mp
->map_obj
.rp
)) != 0)
170 * Call my parents bus_map function with modified values...
173 return (ddi_map(dip
, mp
, (off_t
)0, (off_t
)0, vaddrp
));
177 * Creating register mappings and handling interrupts:
181 i_ddi_rnumber_to_regspec(dev_info_t
*dip
, int rnumber
)
183 if (rnumber
>= sparc_pd_getnreg(DEVI(dip
)))
186 return (sparc_pd_getreg(DEVI(dip
), rnumber
));
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.).
194 reg_is_enclosed_in_range(struct regspec
*rp
, struct rangespec
*rangep
)
196 if (rp
->regspec_bustype
!= rangep
->rng_cbustype
)
199 if (rp
->regspec_addr
< rangep
->rng_coffset
)
202 if (rangep
->rng_size
== 0)
203 return (1); /* size is really 2**(bits_per_word) */
205 if ((rp
->regspec_addr
+ rp
->regspec_size
- 1) <=
206 (rangep
->rng_coffset
+ rangep
->rng_size
- 1))
214 * Apply range of dp to struct regspec *rp, if applicable.
215 * If there's any range defined, it gets applied.
219 i_ddi_apply_range(dev_info_t
*dp
, dev_info_t
*rdip
, struct regspec
*rp
)
222 struct rangespec
*rangep
;
223 static char *out_of_range
=
224 "Out of range register specification from device node <%s>\n";
226 nrange
= sparc_pd_getnrng(dp
);
229 ddi_map_debug(" No range.\n");
230 #endif /* DDI_MAP_DEBUG */
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).
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 */
245 cmn_err(CE_WARN
, out_of_range
, ddi_get_name(rdip
));
246 return (DDI_ME_REGSPEC_RANGE
);
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 */
257 rp
->regspec_bustype
= rangep
->rng_bustype
;
258 rp
->regspec_addr
+= rangep
->rng_offset
- rangep
->rng_coffset
;
261 ddi_map_debug(" Return: %x.%x.%x\n", rp
->regspec_bustype
,
262 rp
->regspec_addr
, rp
->regspec_size
);
263 #endif /* DDI_MAP_DEBUG */
269 * i_ddi_map_fault: wrapper for bus_map_fault.
272 i_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
)
279 return (DDI_FAILURE
);
281 pdip
= (dev_info_t
*)DEVI(dip
)->devi_bus_map_fault
;
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
));
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.
293 * Byte-swapping is needed on intel.
296 impl_ddi_prop_int_from_prom(uchar_t
*intp
, int n
)
300 ASSERT(n
> 0 && n
<= 4);
304 i
= (i
<< 8) | *(--intp
);
311 int drv_usec_coarse_timing
= 0;
314 * Time delay function called by drivers
317 drv_usecwait(clock_t count
)
320 extern int gethrtime_hires
;
322 if (gethrtime_hires
) {
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 */
330 waittime
= ((count
> 10) ?
331 (((hrtime_t
)count
/ 10) + 1) : 1) *
332 10 * (NANOSEC
/ MICROSEC
);
334 waittime
= (hrtime_t
)count
* (NANOSEC
/ MICROSEC
);
336 start
= end
= gethrtime();
337 while ((end
- start
) < waittime
) {
347 tens
++; /* roundup; wait at least 10 microseconds */