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 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27#include <sys/types.h>
28#include <sys/conf.h>
29#include <sys/ddi.h>
30#include <sys/sunddi.h>
31#include <sys/autoconf.h>
32#include <sys/ddi_impldefs.h>
33#include <sys/ddi_subrdefs.h>
34#include <sys/cmn_err.h>
35#include <sys/errno.h>
36#include <sys/kmem.h>
37#include <sys/debug.h>
38#include <sys/sysmacros.h>
39#include <sys/spl.h>
40#include <sys/async.h>
41#include <sys/dvma.h>
42#include <sys/upa64s.h>
43#include <sys/machsystm.h>
44
45/*
46 * driver global data:
47 */
48static void *per_upa64s_state;		/* soft state pointer */
49
50/*
51 * function prototypes for bus ops routines:
52 */
53static int
54upa64s_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
55    off_t offset, off_t len, caddr_t *addrp);
56static int
57upa64s_ctlops(dev_info_t *dip, dev_info_t *rdip,
58    ddi_ctl_enum_t op, void *arg, void *result);
59static int
60upa64_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
61    ddi_intr_handle_impl_t *hdlp, void *result);
62static int
63upa64s_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
64    ddi_intr_handle_impl_t *hdlp);
65static int
66upa64s_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
67    ddi_intr_handle_impl_t *hdlp);
68
69/*
70 * function prototypes for dev ops routines:
71 */
72static int upa64s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
73static int upa64s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
74static int upa64s_power(dev_info_t *dip, int component, int level);
75
76/*
77 * bus ops and dev ops structures:
78 */
79static struct bus_ops upa64s_bus_ops = {
80	BUSO_REV,
81	upa64s_map,
82	0,
83	0,
84	0,
85	i_ddi_map_fault,
86	ddi_no_dma_map,
87	ddi_no_dma_allochdl,
88	ddi_no_dma_freehdl,
89	ddi_no_dma_bindhdl,
90	ddi_no_dma_unbindhdl,
91	ddi_no_dma_flush,
92	ddi_no_dma_win,
93	ddi_no_dma_mctl,
94	upa64s_ctlops,
95	ddi_bus_prop_op,
96	0,
97	0,
98	0,
99	0,
100	0,
101	0,
102	0,
103	0,
104	0,
105	0,
106	0,
107	0,
108	upa64_intr_ops
109};
110
111static struct dev_ops upa64s_ops = {
112	DEVO_REV,
113	0,
114	ddi_no_info,
115	nulldev,
116	0,
117	upa64s_attach,
118	upa64s_detach,
119	nodev,
120	(struct cb_ops *)0,
121	&upa64s_bus_ops,
122	upa64s_power,
123	ddi_quiesce_not_supported,	/* devo_quiesce */
124};
125
126/*
127 * module definitions:
128 */
129#include <sys/modctl.h>
130extern struct mod_ops mod_driverops;
131
132static struct modldrv modldrv = {
133	&mod_driverops, 		/* type of module */
134	"UPA64S nexus driver",	/* name of module */
135	&upa64s_ops,			/* driver ops */
136};
137
138static struct modlinkage modlinkage = {
139	MODREV_1, (void *)&modldrv, NULL
140};
141
142int
143_init(void)
144{
145	int e;
146
147	/*
148	 * Initialize per instance bus soft state pointer.
149	 */
150	if (e = ddi_soft_state_init(&per_upa64s_state,
151	    sizeof (upa64s_devstate_t), 2))
152		return (e);
153	/*
154	 * Install the module.
155	 */
156	if (e = mod_install(&modlinkage))
157		ddi_soft_state_fini(&per_upa64s_state);
158	return (e);
159}
160
161int
162_fini(void)
163{
164	int e = mod_remove(&modlinkage);
165	if (e)
166		return (e);
167	ddi_soft_state_fini(&per_upa64s_state);
168	return (e);
169}
170
171int
172_info(struct modinfo *modinfop)
173{
174	return (mod_info(&modlinkage, modinfop));
175}
176
177
178/*
179 * forward declarations:
180 */
181static void upa64s_intrdist(void *arg);
182static int init_child(dev_info_t *child);
183static int report_dev(dev_info_t *dip);
184static int get_properties(upa64s_devstate_t *upa64s_p, dev_info_t *dip);
185static void save_state(upa64s_devstate_t *upa64s_p);
186static void restore_state(upa64s_devstate_t *upa64s_p);
187static int xlate_reg_prop(dev_info_t *dip, upa64s_regspec_t *upa64s_rp,
188    off_t off, off_t len, struct regspec *rp);
189static int get_reg_set(dev_info_t *dip, dev_info_t *child, int rnumber,
190    off_t off, off_t len, struct regspec *rp);
191static off_t get_reg_set_size(dev_info_t *child, int rnumber);
192static uint_t get_nreg_set(dev_info_t *child);
193
194
195/* device driver entry points */
196
197/*
198 * attach entry point:
199 */
200static int
201upa64s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
202{
203	upa64s_devstate_t *upa64s_p;	/* per upa64s state pointer */
204	ddi_device_acc_attr_t attr;
205	int instance;
206	char *pmc[] = { "NAME=Framebuffer Power", "0=Off", "1=On", NULL };
207
208	switch (cmd) {
209	case DDI_ATTACH:
210		/*
211		 * Allocate and get the per instance soft state structure.
212		 */
213		instance = ddi_get_instance(dip);
214		if (alloc_upa64s_soft_state(instance) != DDI_SUCCESS) {
215			cmn_err(CE_WARN, "%s%d: can't allocate upa64s state",
216			    ddi_get_name(dip), instance);
217			return (DDI_FAILURE);
218		}
219		upa64s_p = get_upa64s_soft_state(instance);
220		upa64s_p->dip = dip;
221
222		/*
223		 * Get key properties of the bridge node.
224		 */
225		if (get_properties(upa64s_p, dip) != DDI_SUCCESS)
226			goto fail;
227
228		/*
229		 * Create "pm-components" property for the purpose of
230		 * doing Power Management.
231		 */
232		if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
233		    "pm-components", pmc, ((sizeof (pmc)/sizeof (char *)) - 1))
234		    != DDI_PROP_SUCCESS) {
235			cmn_err(CE_WARN, "%s%d: failed to create pm-components "
236			    "property.", ddi_get_name(dip), instance);
237			goto fail;
238		}
239
240		/* Map in the UPA's registers */
241		attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
242		attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
243		attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
244		if (ddi_regs_map_setup(dip, 0,
245		    (caddr_t *)&upa64s_p->config_base, 0, 0, &attr,
246		    &upa64s_p->config_base_ah) != DDI_SUCCESS) {
247			cmn_err(CE_WARN, "%s%d: failed to map reg1.",
248			    ddi_get_name(dip), instance);
249			goto fail;
250		}
251
252		upa64s_p->upa0_config = (uint64_t *)(upa64s_p->config_base +
253		    UPA64S_UPA0_CONFIG_OFFSET);
254		upa64s_p->upa1_config = (uint64_t *)(upa64s_p->config_base +
255		    UPA64S_UPA1_CONFIG_OFFSET);
256		upa64s_p->if_config = (uint64_t *)(upa64s_p->config_base +
257		    UPA64S_IF_CONFIG_OFFSET);
258		upa64s_p->estar = (uint64_t *)(upa64s_p->config_base +
259		    UPA64S_ESTAR_OFFSET);
260
261		if (ddi_regs_map_setup(dip, 1, (caddr_t *)&upa64s_p->imr[0],
262		    0, 0, &attr, &upa64s_p->imr_ah[0]) != DDI_SUCCESS) {
263			cmn_err(CE_WARN, "%s%d: failed to map reg2.",
264			    ddi_get_name(dip), instance);
265			goto fail1;
266		}
267
268		if (ddi_regs_map_setup(dip, 2, (caddr_t *)&upa64s_p->imr[1],
269		    0, 0, &attr, &upa64s_p->imr_ah[1]) != DDI_SUCCESS) {
270			cmn_err(CE_WARN, "%s%d: failed to map reg3.",
271			    ddi_get_name(dip), instance);
272			goto fail2;
273		}
274
275		/*
276		 * Power level of a component is unknown at attach time.
277		 * Bring the power level to what is needed for normal operation.
278		 */
279		upa64s_p->power_level = UPA64S_PM_UNKNOWN;
280		if (pm_raise_power(dip, UPA64S_PM_COMP, UPA64S_PM_NORMOP) !=
281		    DDI_SUCCESS) {
282			cmn_err(CE_WARN, "%s%d: failed to raise the power.",
283			    ddi_get_name(dip), instance);
284			goto fail3;
285		}
286
287		intr_dist_add(upa64s_intrdist, dip);
288
289		ddi_report_dev(dip);
290		return (DDI_SUCCESS);
291
292	case DDI_RESUME:
293
294		upa64s_p = get_upa64s_soft_state(ddi_get_instance(dip));
295		DBG(D_ATTACH, dip, "DDI_RESUME\n");
296		restore_state(upa64s_p);
297
298		/*
299		 * Power level of a component is unknown at resume time.
300		 * Bring the power level to what it was before suspend.
301		 */
302		upa64s_p->power_level = UPA64S_PM_UNKNOWN;
303		if (pm_raise_power(dip, UPA64S_PM_COMP,
304		    upa64s_p->saved_power_level) != DDI_SUCCESS)
305			cmn_err(CE_WARN, "%s%d: failed to change power level "
306			    "during resume!", ddi_get_name(dip), instance);
307
308		return (DDI_SUCCESS);
309
310	default:
311		return (DDI_FAILURE);
312	}
313
314fail3:
315	ddi_regs_map_free(&upa64s_p->imr_ah[1]);
316fail2:
317	ddi_regs_map_free(&upa64s_p->imr_ah[0]);
318fail1:
319	ddi_regs_map_free(&upa64s_p->config_base_ah);
320fail:
321	free_upa64s_soft_state(instance);
322	return (DDI_FAILURE);
323}
324
325
326/*
327 * detach entry point:
328 */
329static int
330upa64s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
331{
332	int instance = ddi_get_instance(dip);
333	upa64s_devstate_t *upa64s_p = get_upa64s_soft_state(instance);
334
335	switch (cmd) {
336	case DDI_DETACH:
337
338		DBG(D_DETACH, dip, "DDI_DETACH\n");
339
340		/*
341		 * Power down the device.
342		 */
343		if (pm_lower_power(dip, UPA64S_PM_COMP, UPA64S_PM_RESET) !=
344		    DDI_SUCCESS)
345			DBG(D_DETACH, dip, "failed to power off!\n");
346
347		intr_dist_rem(upa64s_intrdist, dip);
348
349		ddi_regs_map_free(&upa64s_p->config_base_ah);
350		ddi_regs_map_free(&upa64s_p->imr_ah[0]);
351		ddi_regs_map_free(&upa64s_p->imr_ah[1]);
352		free_upa64s_soft_state(instance);
353		return (DDI_SUCCESS);
354
355	case DDI_SUSPEND:
356
357		DBG(D_DETACH, dip, "DDI_SUSPEND\n");
358		save_state(upa64s_p);
359		upa64s_p->saved_power_level = upa64s_p->power_level;
360		return (DDI_SUCCESS);
361	}
362
363	return (DDI_FAILURE);
364}
365
366/*
367 * power entry point:
368 *
369 * This entry point is called by Power Management framework to
370 * reset upa bus and slow down/speed up the upa interface of
371 * Schizo chip.
372 */
373static int
374upa64s_power(dev_info_t *dip, int component, int level)
375{
376	int instance = ddi_get_instance(dip);
377	upa64s_devstate_t *upa64s_p = get_upa64s_soft_state(instance);
378	volatile uint64_t uint64_data;
379
380	DBG2(D_POWER, dip, "component=%d, level=%d\n", component, level);
381	if (component != UPA64S_PM_COMP ||
382	    level < UPA64S_PM_RESET || level > UPA64S_PM_NORMOP)
383		return (DDI_FAILURE);
384
385	/*
386	 * We can't set the hardware to the state that it is
387	 * already in.  So if the power state is not known, inquire the
388	 * state of the hardware.  If it is already in that state,
389	 * record and return, otherwise make the state change.
390	 */
391	if (upa64s_p->power_level == UPA64S_PM_UNKNOWN) {
392		uint64_data = ddi_get64(upa64s_p->config_base_ah,
393		    upa64s_p->if_config);
394		if ((level == UPA64S_PM_RESET &&
395		    uint64_data == UPA64S_NOT_POK_RST_L) ||
396		    (level == UPA64S_PM_NORMOP &&
397		    uint64_data == UPA64S_POK_NOT_RST_L)) {
398			upa64s_p->power_level = level;
399			return (DDI_SUCCESS);
400		}
401	}
402
403	if (level == upa64s_p->power_level) {
404		DBG1(D_POWER, dip, "device is already at power level %d\n",
405		    level);
406		return (DDI_SUCCESS);
407	}
408
409
410	if (level == UPA64S_PM_RESET) {
411		/*
412		 * Assert UPA64S_RESET
413		 */
414		ddi_put64(upa64s_p->config_base_ah, upa64s_p->if_config,
415		    UPA64S_POK_RST_L);
416
417		/*
418		 * Deassert UPA64S_POK.  Flush the store buffer.
419		 */
420		ddi_put64(upa64s_p->config_base_ah, upa64s_p->if_config,
421		    UPA64S_NOT_POK_RST_L);
422		uint64_data = ddi_get64(upa64s_p->config_base_ah,
423		    upa64s_p->if_config);
424
425		/*
426		 * Internal UPA clock to 1/2 speed
427		 */
428		ddi_put64(upa64s_p->config_base_ah, upa64s_p->estar,
429		    UPA64S_1_2_SPEED);
430
431		/*
432		 * Internal UPA clock to 1/64 speed.  Flush the store buffer.
433		 */
434		ddi_put64(upa64s_p->config_base_ah, upa64s_p->estar,
435		    UPA64S_1_64_SPEED);
436		uint64_data = ddi_get64(upa64s_p->config_base_ah,
437		    upa64s_p->estar);
438	} else {
439		/*
440		 * Internal UPA clock to 1/2 speed
441		 */
442		ddi_put64(upa64s_p->config_base_ah, upa64s_p->estar,
443		    UPA64S_1_2_SPEED);
444
445		/*
446		 * Internal UPA clock to full speed.  Flush the store buffer.
447		 */
448		ddi_put64(upa64s_p->config_base_ah, upa64s_p->estar,
449		    UPA64S_FULL_SPEED);
450		uint64_data = ddi_get64(upa64s_p->config_base_ah,
451		    upa64s_p->estar);
452
453		/*
454		 * Assert UPA64S_POK.  Flush the store buffer before
455		 * the wait delay.
456		 */
457		ddi_put64(upa64s_p->config_base_ah, upa64s_p->if_config,
458		    UPA64S_POK_RST_L);
459		uint64_data = ddi_get64(upa64s_p->config_base_ah,
460		    upa64s_p->if_config);
461
462		/*
463		 * Delay 20 milliseconds for the signals to settle down.
464		 */
465		delay(drv_usectohz(20*1000));
466
467		/*
468		 * Deassert UPA64S_RESET.  Flush the store buffer.
469		 */
470		ddi_put64(upa64s_p->config_base_ah, upa64s_p->if_config,
471		    UPA64S_POK_NOT_RST_L);
472		uint64_data = ddi_get64(upa64s_p->config_base_ah,
473		    upa64s_p->if_config);
474	}
475	upa64s_p->power_level = level;
476
477	return (DDI_SUCCESS);
478}
479
480/* bus driver entry points */
481
482/*
483 * bus map entry point:
484 *
485 * 	if map request is for an rnumber
486 *		get the corresponding regspec from device node
487 * 	build a new regspec in our parent's format
488 *	build a new map_req with the new regspec
489 *	call up the tree to complete the mapping
490 */
491static int
492upa64s_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
493    off_t off, off_t len, caddr_t *addrp)
494{
495	struct regspec regspec;
496	ddi_map_req_t p_map_request;
497	int rnumber, rval;
498
499	DBG4(D_MAP, dip, "upa64s_map() mp=%x.%x addrp=%x.%08x\n",
500	    HI32(mp), LO32(mp), HI32(addrp), LO32(addrp));
501
502	/*
503	 * User level mappings are not supported yet.
504	 */
505	if (mp->map_flags & DDI_MF_USER_MAPPING) {
506		DBG2(D_MAP, dip, "rdip=%s%d: no user level mappings yet!\n",
507		    ddi_get_name(rdip), ddi_get_instance(rdip));
508		return (DDI_ME_UNIMPLEMENTED);
509	}
510
511	/*
512	 * Now handle the mapping according to its type.
513	 */
514	switch (mp->map_type) {
515	case DDI_MT_REGSPEC:
516
517		/*
518		 * We assume the register specification is in PCI format.
519		 * We must convert it into a regspec of our parent's
520		 * and pass the request to our parent.
521		 */
522		DBG3(D_MAP, dip, "rdip=%s%d: REGSPEC - handlep=%x\n",
523		    ddi_get_name(rdip), ddi_get_instance(rdip),
524		    mp->map_handlep);
525		rval = xlate_reg_prop(dip, (upa64s_regspec_t *)mp->map_obj.rp,
526		    off, len, &regspec);
527		break;
528
529	case DDI_MT_RNUMBER:
530
531		/*
532		 * Get the "reg" property from the device node and convert
533		 * it to our parent's format.
534		 */
535		DBG4(D_MAP, dip, "rdip=%s%d: rnumber=%x handlep=%x\n",
536		    ddi_get_name(rdip), ddi_get_instance(rdip),
537		    mp->map_obj.rnumber, mp->map_handlep);
538		rnumber = mp->map_obj.rnumber;
539		if (rnumber < 0)
540			return (DDI_ME_RNUMBER_RANGE);
541		rval = get_reg_set(dip, rdip,  rnumber, off, len, &regspec);
542		break;
543
544	default:
545		return (DDI_ME_INVAL);
546
547	}
548	if (rval != DDI_SUCCESS) {
549		DBG(D_MAP, dip, "failed on regspec\n\n");
550		return (rval);
551	}
552
553	/*
554	 * Now we have a copy of the upa64s regspec converted to our parent's
555	 * format.  Build a new map request based on this regspec and pass
556	 * it to our parent.
557	 */
558	p_map_request = *mp;
559	p_map_request.map_type = DDI_MT_REGSPEC;
560	p_map_request.map_obj.rp = &regspec;
561	rval = ddi_map(dip, &p_map_request, 0, 0, addrp);
562	DBG3(D_MAP, dip, "ddi_map returns: rval=%x addrp=%x.%08x\n\n",
563	    rval, HI32(*addrp), LO32(*addrp));
564	return (rval);
565}
566
567/*
568 * Translate the UPA devices interrupt property.  This is the only case I
569 * know of where the interrupts property is meaningless.  As a result, we
570 * just use UPA_BASE_INO as our interrupt value and add to it the upa port id.
571 * UPA portid is returned too.
572 */
573#define	UPA_BASE_INO	0x2a
574
575static int
576upa64s_xlate_intr(dev_info_t *rdip, int32_t safariport, uint32_t *intr)
577{
578	uint32_t ino = UPA_BASE_INO;
579	int32_t portid;
580
581	/* Clear the ffb's interrupts property, it's meaningless */
582	*intr = 0;
583
584	if ((portid = ddi_getprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
585	    "upa-portid", -1)) == -1)
586		return (-1);
587
588	ino += portid;
589
590	*intr = UPA64S_MAKE_MONDO(safariport, ino);
591
592	DBG5(D_A_ISPEC, rdip, "upa64s_xlate_intr: rdip=%s%d: upa portid %d "
593	    "ino=%x mondo 0x%x\n", ddi_get_name(rdip), ddi_get_instance(rdip),
594	    portid, ino, *intr);
595
596	return (portid);
597}
598
599/*
600 * bus add intrspec entry point:
601 */
602static int
603upa64s_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
604    ddi_intr_handle_impl_t *hdlp)
605{
606	int upaport, instance = ddi_get_instance(dip);
607	upa64s_devstate_t *upa64s_p = get_upa64s_soft_state(instance);
608#ifdef DEBUG
609	uint_t (*int_handler)(caddr_t, caddr_t) = hdlp->ih_cb_func;
610	caddr_t int_handler_arg1 = hdlp->ih_cb_arg1;
611#endif /* DEBUG */
612	uint_t cpu_id;
613	volatile uint64_t imr_data;
614
615	upaport = upa64s_xlate_intr(rdip, upa64s_p->safari_id,
616	    (uint32_t *)&hdlp->ih_vector);
617
618	if (hdlp->ih_vector == 0)
619		return (DDI_FAILURE);
620
621	DBG3(D_A_ISPEC, dip,
622	    "rdip=%s%d - IDDI_INTR_TYPE_NORMAL, mondo=%x\n",
623	    ddi_driver_name(rdip), ddi_get_instance(rdip), hdlp->ih_vector);
624
625	/*
626	 * Make sure an interrupt handler isn't already installed.
627	 */
628	if (upa64s_p->ino_state[upaport] != INO_FREE) {
629		return (DDI_FAILURE);
630	}
631
632	/*
633	 * Install the handler in the system table.
634	 */
635#ifdef	DEBUG
636	DBG2(D_A_ISPEC, dip, "i_ddi_add_ivintr: hdlr=%p arg=%p\n",
637	    int_handler, int_handler_arg1);
638#endif
639	if (i_ddi_add_ivintr(hdlp) != DDI_SUCCESS)
640		return (DDI_FAILURE);
641
642	cpu_id = intr_dist_cpuid();
643
644	/*
645	 * Enable the interrupt through its interrupt mapping register.
646	 */
647	imr_data = UPA64S_CPUID_TO_IMR(cpu_id);
648	imr_data = UPA64S_GET_MAP_REG(hdlp->ih_vector, imr_data);
649
650	DBG4(D_A_ISPEC, dip, "IMR [upaport=%d mapping reg 0x%p] = %x.%x\n",
651	    upaport, upa64s_p->imr[upaport], HI32(imr_data), LO32(imr_data));
652
653	ddi_put64(upa64s_p->imr_ah[upaport], upa64s_p->imr[upaport], imr_data);
654	/* Read the data back to flush store buffers. */
655	imr_data = ddi_get64(upa64s_p->imr_ah[upaport], upa64s_p->imr[upaport]);
656	upa64s_p->ino_state[upaport] = INO_INUSE;
657
658	DBG(D_A_ISPEC, dip, "add_intr success!\n");
659	return (DDI_SUCCESS);
660}
661
662
663/*
664 * bus remove intrspec entry point
665 */
666static int
667upa64s_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
668    ddi_intr_handle_impl_t *hdlp)
669{
670	upa64s_devstate_t *upa64s_p =
671	    get_upa64s_soft_state(ddi_get_instance(dip));
672	int upaport;
673#ifndef lint
674	volatile uint64_t tmp;
675#endif
676
677	/*
678	 * Make sure the mondo is valid.
679	 */
680	upaport = upa64s_xlate_intr(rdip, upa64s_p->safari_id,
681	    (uint32_t *)&hdlp->ih_vector);
682
683	if (hdlp->ih_vector == 0)
684		return (DDI_FAILURE);
685
686	DBG3(D_R_ISPEC, dip,
687	    "rdip=%s%d - IDDI_INTR_TYPE_NORMAL, mondo=%x\n",
688	    ddi_driver_name(rdip), ddi_get_instance(rdip), hdlp->ih_vector);
689
690	if (upa64s_p->ino_state[upaport] != INO_INUSE) {
691		return (DDI_FAILURE);
692	}
693
694	/* Call up to our parent to handle the removal */
695	i_ddi_rem_ivintr(hdlp);
696
697	ddi_put64(upa64s_p->imr_ah[upaport], upa64s_p->imr[upaport], 0);
698#ifndef lint
699	/* Flush store buffers */
700	tmp = ddi_get64(upa64s_p->imr_ah[upaport], upa64s_p->imr[upaport]);
701#endif
702
703	upa64s_p->ino_state[upaport] = INO_FREE;
704	return (DDI_SUCCESS);
705}
706
707
708/* new intr_ops structure */
709static int
710upa64_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
711    ddi_intr_handle_impl_t *hdlp, void *result)
712{
713	int	ret = DDI_SUCCESS;
714
715	switch (intr_op) {
716	case DDI_INTROP_GETCAP:
717		*(int *)result = DDI_INTR_FLAG_EDGE;
718		break;
719	case DDI_INTROP_ALLOC:
720		*(int *)result = hdlp->ih_scratch1;
721		break;
722	case DDI_INTROP_FREE:
723		break;
724	case DDI_INTROP_GETPRI:
725		/*
726		 * We only have slave UPA devices so force the PIL to 5.
727		 * this is done since all slave UPA devices have historically
728		 * had their PILs set to 5.  Only do it if the PIL is not
729		 * being preset.
730		 */
731		*(int *)result = hdlp->ih_pri ? hdlp->ih_pri : 5;
732		break;
733	case DDI_INTROP_SETPRI:
734		break;
735	case DDI_INTROP_ADDISR:
736		ret = upa64s_add_intr_impl(dip, rdip, hdlp);
737		break;
738	case DDI_INTROP_REMISR:
739		ret = upa64s_remove_intr_impl(dip, rdip, hdlp);
740		break;
741	case DDI_INTROP_ENABLE:
742	case DDI_INTROP_DISABLE:
743		break;
744	case DDI_INTROP_NINTRS:
745	case DDI_INTROP_NAVAIL:
746		*(int *)result = i_ddi_get_intx_nintrs(rdip);
747		break;
748	case DDI_INTROP_SETCAP:
749	case DDI_INTROP_SETMASK:
750	case DDI_INTROP_CLRMASK:
751	case DDI_INTROP_GETPENDING:
752		ret = DDI_ENOTSUP;
753		break;
754	case DDI_INTROP_SUPPORTED_TYPES:
755		/* only support fixed interrupts */
756		*(int *)result = i_ddi_get_intx_nintrs(rdip) ?
757		    DDI_INTR_TYPE_FIXED : 0;
758		break;
759	default:
760		ret = i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result);
761		break;
762	}
763
764	return (ret);
765}
766
767#ifdef DEBUG
768uint_t upa64s_debug_flags = (uint_t)0;
769
770extern void prom_printf(const char *, ...);
771#endif
772
773/*
774 * control ops entry point:
775 *
776 * Requests handled completely:
777 *	DDI_CTLOPS_INITCHILD	see init_child() for details
778 *	DDI_CTLOPS_UNINITCHILD
779 *	DDI_CTLOPS_REPORTDEV	see report_dev() for details
780 *	DDI_CTLOPS_REGSIZE
781 *	DDI_CTLOPS_NREGS
782 *
783 * All others passed to parent.
784 */
785static int
786upa64s_ctlops(dev_info_t *dip, dev_info_t *rdip,
787    ddi_ctl_enum_t op, void *arg, void *result)
788{
789	DBG5(D_CTLOPS, dip, "dip=%x.%x rdip=%x.%x op=%x",
790	    HI32(dip), LO32(dip), HI32(rdip), LO32(rdip), op);
791	DBG4(D_CTLOPS|D_CONT, dip, " arg=%x.%x result=%x.%x\n",
792	    HI32(arg), LO32(arg), HI32(result), LO32(result));
793
794	switch (op) {
795	case DDI_CTLOPS_INITCHILD:
796		DBG2(D_CTLOPS, dip, "DDI_CTLOPS_INITCHILD: rdip=%s%d\n",
797		    ddi_get_name(rdip), ddi_get_instance(rdip));
798		return (init_child((dev_info_t *)arg));
799
800	case DDI_CTLOPS_UNINITCHILD:
801		DBG2(D_CTLOPS, dip, "DDI_CTLOPS_UNINITCHILD: rdip=%s%d\n",
802		    ddi_get_name(rdip), ddi_get_instance(rdip));
803		ddi_set_name_addr((dev_info_t *)arg, NULL);
804		ddi_remove_minor_node((dev_info_t *)arg, NULL);
805		impl_rem_dev_props((dev_info_t *)arg);
806		return (DDI_SUCCESS);
807
808	case DDI_CTLOPS_REPORTDEV:
809		DBG2(D_CTLOPS, dip, "DDI_CTLOPS_REPORTDEV: rdip=%s%d\n",
810		    ddi_get_name(rdip), ddi_get_instance(rdip));
811		return (report_dev(rdip));
812
813	case DDI_CTLOPS_REGSIZE:
814		DBG2(D_CTLOPS, dip, "DDI_CTLOPS_REGSIZE: rdip=%s%d\n",
815		    ddi_get_name(rdip), ddi_get_instance(rdip));
816		*((off_t *)result) = get_reg_set_size(rdip, *((int *)arg));
817		return (*((off_t *)result) == -1 ? DDI_FAILURE : DDI_SUCCESS);
818
819	case DDI_CTLOPS_NREGS:
820		DBG2(D_CTLOPS, dip, "DDI_CTLOPS_NREGS: rdip=%s%d\n",
821		    ddi_get_name(rdip), ddi_get_instance(rdip));
822		*((uint_t *)result) = get_nreg_set(rdip);
823		return (DDI_SUCCESS);
824	}
825
826	/*
827	 * Now pass the request up to our parent.
828	 */
829	DBG3(D_CTLOPS, dip, "passing request to parent: rdip=%s%d op=%x\n\n",
830	    ddi_get_name(rdip), ddi_get_instance(rdip), op);
831	return (ddi_ctlops(dip, rdip, op, arg, result));
832}
833
834
835/* support routines */
836
837/*
838 * get_properties
839 *
840 * This function is called from the attach routine to get the key
841 * properties of the upa64s node.
842 *
843 * used by: upa64s_attach()
844 *
845 * return value: none
846 */
847static int
848get_properties(upa64s_devstate_t *upa64s_p, dev_info_t *dip)
849{
850	int safari_id;
851
852	/*
853	 * Get the device's safari id.
854	 */
855	safari_id = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
856	    "portid", -1);
857	if (safari_id == -1) {
858		int instance = ddi_get_instance(dip);
859		panic("%s%d: no portid property", ddi_get_name(dip), instance);
860	}
861	upa64s_p->safari_id = safari_id;
862
863	return (DDI_SUCCESS);
864}
865
866
867/*
868 * save_state
869 *
870 * This routine saves a copy of the upa64s register state.
871 *
872 * used by: upa64s_detach() on a suspend operation
873 */
874static void
875save_state(upa64s_devstate_t *upa64s_p)
876{
877	upa64s_p->imr_data[0] = ddi_get64(upa64s_p->imr_ah[0],
878	    upa64s_p->imr[0]);
879	upa64s_p->imr_data[1] = ddi_get64(upa64s_p->imr_ah[1],
880	    upa64s_p->imr[1]);
881}
882
883
884/*
885 * restore_state
886 *
887 * This routine restores a copy of the upa64s register state.
888 *
889 * used by: upa64s_attach() on a resume operation
890 */
891static void
892restore_state(upa64s_devstate_t *upa64s_p)
893{
894#ifndef lint
895	volatile uint64_t tmp;
896#endif
897	ddi_put64(upa64s_p->imr_ah[0], upa64s_p->imr[0],
898	    upa64s_p->imr_data[0]);
899	ddi_put64(upa64s_p->imr_ah[1], upa64s_p->imr[1],
900	    upa64s_p->imr_data[1]);
901#ifndef lint
902	/* Flush the store buffer */
903	tmp = ddi_get64(upa64s_p->imr_ah[0], upa64s_p->imr[0]);
904	tmp = ddi_get64(upa64s_p->imr_ah[1], upa64s_p->imr[1]);
905#endif
906}
907
908
909/*
910 * get_reg_set
911 *
912 * This routine will get a upa64s format regspec for a given
913 * device node and register number.
914 *
915 * used by: upa64s_map()
916 *
917 * return value:
918 *
919 *	DDI_SUCCESS		- on success
920 *	DDI_ME_INVAL		- regspec is invalid
921 *	DDI_ME_RNUMBER_RANGE	- rnumber out of range
922 */
923static int
924get_reg_set(dev_info_t *dip, dev_info_t *child, int rnumber,
925    off_t off, off_t len, struct regspec *rp)
926{
927	upa64s_regspec_t *upa64s_rp;
928	int i, n, rval;
929
930	/*
931	 * Get child device "reg" property
932	 */
933	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
934	    (caddr_t)&upa64s_rp, &i) != DDI_SUCCESS)
935		return (DDI_ME_RNUMBER_RANGE);
936
937	n = i / (int)sizeof (upa64s_regspec_t);
938	if (rnumber >= n) {
939		kmem_free(upa64s_rp, i);
940		return (DDI_ME_RNUMBER_RANGE);
941	}
942
943	/*
944	 * Convert each the upa64s format register specification to
945	 * out parent format.
946	 */
947	rval = xlate_reg_prop(dip, &upa64s_rp[rnumber], off, len, rp);
948	kmem_free(upa64s_rp, i);
949	return (rval);
950}
951
952
953/*
954 * xlate_reg_prop
955 *
956 * This routine converts a upa64s format regspec to a standard
957 * regspec containing the corresponding system address.
958 *
959 * used by: upa64s_map()
960 *
961 * return value:
962 *
963 *	DDI_SUCCESS
964 *	DDI_FAILURE	- off + len is beyond device address range
965 *	DDI_ME_INVAL	- regspec is invalid
966 */
967static int
968xlate_reg_prop(dev_info_t *dip, upa64s_regspec_t *child_rp, off_t off,
969    off_t len, struct regspec *rp)
970{
971	int n_ranges, ranges_len, i;
972	uint64_t child_beg, child_end;
973	upa64s_ranges_t *range_p, *rng_p;
974
975	DBG4(D_MAP, dip, "upa64s regspec - ((%x,%x) (%x,%x))\n",
976	    HI32(child_rp->upa64s_phys), LO32(child_rp->upa64s_phys),
977	    HI32(child_rp->upa64s_size), LO32(child_rp->upa64s_size));
978	DBG2(D_MAP, dip, "upa64s xlate_reg_prp - off=%lx len=%lx\n", off, len);
979#if 0
980	/*
981	 * both FFB and AFB have broken "reg" properties, all mapping
982	 * requests are done through reg-0 with very long offsets.
983	 * Hence this safety check is always violated.
984	 */
985	if (off + len > child_rp->upa64s_size) {
986		DBG(D_MAP, dip, "upa64s xlate_reg_prp: bad off + len\n");
987		return (DDI_FAILURE);
988	}
989#endif
990	/*
991	 * current "struct regspec" only supports 32-bit sizes.
992	 */
993	if (child_rp->upa64s_size >= (1ull << 32))
994		panic("upa64s: reg size must be less than 4 Gb");
995
996	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
997	    "ranges", (caddr_t)&range_p, &ranges_len) != DDI_SUCCESS) {
998		ranges_len = 0;
999		cmn_err(CE_WARN, "%s%d: no ranges property",
1000		    ddi_get_name(dip), ddi_get_instance(dip));
1001	}
1002
1003	n_ranges = ranges_len / sizeof (upa64s_regspec_t);
1004	child_beg = child_rp->upa64s_phys;
1005#if 0
1006	/*
1007	 * again, this safety checking can not be performed.
1008	 * Hack by adding a pratical max child reg bank length.
1009	 */
1010	child_end = child_beg + child_rp->upa64s_size;
1011#else
1012#define	UPA64S_MAX_CHILD_LEN	0xe000000
1013	child_end = child_beg + UPA64S_MAX_CHILD_LEN;
1014#endif
1015	for (i = 0, rng_p = range_p; i < n_ranges; i++, rng_p++) {
1016		uint64_t rng_beg = rng_p->upa64s_child;
1017		uint64_t rng_end = rng_beg + rng_p->upa64s_size;
1018		if ((rng_beg <= child_beg) && (rng_end >= child_end)) {
1019			uint64_t addr = child_beg - rng_beg + off;
1020			addr += rng_p->upa64s_parent;
1021			rp->regspec_bustype = HI32(addr);
1022			rp->regspec_addr = LO32(addr);
1023			rp->regspec_size = len ? len : child_rp->upa64s_size;
1024			break;
1025		}
1026	}
1027	if (ranges_len)
1028		kmem_free(range_p, ranges_len);
1029	DBG4(D_MAP, dip, "regspec (%x,%x,%x) i=%x\n",
1030	    rp->regspec_bustype, rp->regspec_addr, rp->regspec_size, i);
1031	return (i < n_ranges? DDI_SUCCESS : DDI_ME_INVAL);
1032}
1033
1034
1035/*
1036 * report_dev
1037 *
1038 * This function is called from our control ops routine on a
1039 * DDI_CTLOPS_REPORTDEV request.
1040 */
1041static int
1042report_dev(dev_info_t *dip)
1043{
1044	if (dip == (dev_info_t *)0)
1045		return (DDI_FAILURE);
1046	cmn_err(CE_CONT, "?UPA64S-device: %s@%s, %s #%d\n",
1047	    ddi_node_name(dip), ddi_get_name_addr(dip),
1048	    ddi_get_name(dip), ddi_get_instance(dip));
1049	return (DDI_SUCCESS);
1050}
1051
1052
1053/*
1054 * init_child
1055 *
1056 * This function is called from our control ops routine on a
1057 * DDI_CTLOPS_INITCHILD request.  It builds and sets the device's
1058 * parent private data area.
1059 *
1060 * used by: upa64s_ctlops()
1061 *
1062 * return value: none
1063 */
1064static int
1065init_child(dev_info_t *child)
1066{
1067	upa64s_regspec_t *child_rp;
1068	int i;
1069	char addr[256];
1070	int32_t portid;
1071
1072	if ((portid = ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
1073	    "upa-portid", -1)) == -1)
1074		return (DDI_FAILURE);
1075
1076	/*
1077	 * Set the address portion of the node name based on
1078	 * the function and device number.
1079	 */
1080	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
1081	    (caddr_t)&child_rp, &i) != DDI_SUCCESS) {
1082		return (DDI_FAILURE);
1083	}
1084
1085	(void) sprintf(addr, "%x,%x", portid, LO32(child_rp->upa64s_phys));
1086	ddi_set_name_addr(child, addr);
1087
1088	ddi_set_parent_data(child, NULL);
1089	kmem_free(child_rp, i);
1090	return (DDI_SUCCESS);
1091}
1092
1093
1094/*
1095 * get_reg_set_size
1096 *
1097 * Given a dev info pointer to a child and a register number, this
1098 * routine returns the size element of that reg set property.
1099 *
1100 * used by: upa64s_ctlops() - DDI_CTLOPS_REGSIZE
1101 *
1102 * return value: size of reg set on success, -1 on error
1103 */
1104static off_t
1105get_reg_set_size(dev_info_t *child, int rnumber)
1106{
1107	upa64s_regspec_t *upa64s_rp;
1108	uint_t size;
1109	int i;
1110
1111	if (rnumber < 0)
1112		return (-1);
1113
1114	/*
1115	 * Get the reg property for the device.
1116	 */
1117	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
1118	    (caddr_t)&upa64s_rp, &i) != DDI_SUCCESS)
1119		return (-1);
1120
1121	if (rnumber >= (i / (int)sizeof (upa64s_regspec_t))) {
1122		kmem_free(upa64s_rp, i);
1123		return (-1);
1124	}
1125
1126	/*  >4G reg size not supported */
1127	size = (uint32_t)upa64s_rp[rnumber].upa64s_size;
1128	kmem_free(upa64s_rp, i);
1129	return (size);
1130}
1131
1132
1133/*
1134 * get_nreg_set
1135 *
1136 * Given a dev info pointer to a child, this routine returns the
1137 * number of sets in its "reg" property.
1138 *
1139 * used by: upa64s_ctlops() - DDI_CTLOPS_NREGS
1140 *
1141 * return value: # of reg sets on success, zero on error
1142 */
1143static uint_t
1144get_nreg_set(dev_info_t *child)
1145{
1146	upa64s_regspec_t *upa64s_rp;
1147	int i, n;
1148
1149	/*
1150	 * Get the reg property for the device.
1151	 */
1152	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
1153	    (caddr_t)&upa64s_rp, &i) != DDI_SUCCESS)
1154		return (0);
1155
1156	n =  i / (int)sizeof (upa64s_regspec_t);
1157	kmem_free(upa64s_rp, i);
1158	return (n);
1159}
1160
1161
1162/*
1163 * upa64s_intrdist
1164 *
1165 * The following routine is the callback function for this nexus driver
1166 * to support interrupt distribution on sun4u systems. When this
1167 * function is called by the interrupt distribution framework, it will
1168 * reprogram all the active the mondo registers.
1169 */
1170static void
1171upa64s_intrdist(void *arg)
1172{
1173	dev_info_t *dip = (dev_info_t *)arg;
1174	int instance = ddi_get_instance(dip);
1175	upa64s_devstate_t *upa64s_p = get_upa64s_soft_state(instance);
1176	uint_t upaport;
1177
1178	for (upaport = 0; upaport < UPA64S_PORTS; upaport++) {
1179		volatile uint64_t *imr;
1180		volatile uint64_t imr_dat;
1181		uint_t mondo;
1182		uint32_t cpuid;
1183
1184		if (upa64s_p->ino_state[upaport] != INO_INUSE)
1185			continue;
1186
1187		imr = upa64s_p->imr[upaport];
1188		mondo = UPA64S_IMR_TO_MONDO(*imr);
1189		cpuid = intr_dist_cpuid();
1190		imr_dat = UPA64S_CPUID_TO_IMR(cpuid);
1191		imr_dat = UPA64S_GET_MAP_REG(mondo, imr_dat);
1192
1193		/* Check and re-program cpu target if necessary */
1194		DBG2(D_INTRDIST, dip, "mondo=%x cpuid=%x\n", mondo, cpuid);
1195		if (UPA64S_IMR_TO_CPUID(*imr) == cpuid) {
1196			DBG(D_INTRDIST, dip, "same cpuid\n");
1197			continue;
1198		}
1199		ddi_put64(upa64s_p->imr_ah[upaport], (uint64_t *)imr, imr_dat);
1200		imr_dat = ddi_get64(upa64s_p->imr_ah[upaport], (uint64_t *)imr);
1201	}
1202}
1203
1204
1205#ifdef DEBUG
1206static void
1207upa64s_debug(uint_t flag, dev_info_t *dip, char *fmt,
1208    uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
1209{
1210	char *s = NULL;
1211	uint_t cont = 0;
1212	if (flag & D_CONT) {
1213		flag &= ~D_CONT;
1214		cont = 1;
1215	}
1216	if (!(upa64s_debug_flags & flag))
1217		return;
1218
1219	switch (flag) {
1220	case D_ATTACH:		s = "attach";		break;
1221	case D_DETACH:		s = "detach";		break;
1222	case D_POWER:		s = "power";		break;
1223	case D_MAP:		s = "map";		break;
1224	case D_CTLOPS:		s = "ctlops";		break;
1225	case D_G_ISPEC:		s = "get_intrspec";	break;
1226	case D_A_ISPEC:		s = "add_intrspec";	break;
1227	case D_R_ISPEC:		s = "remove_intrspec";	break;
1228	case D_INIT_CLD:	s = "init_child";	break;
1229	case D_INTRDIST:	s = "intrdist";		break;
1230	}
1231
1232	if (s && cont == 0) {
1233		prom_printf("%s(%d): %s: ", ddi_get_name(dip),
1234		    ddi_get_instance(dip), s);
1235	}
1236	prom_printf(fmt, a1, a2, a3, a4, a5);
1237}
1238#endif
1239