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 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * Daktari platform specific hotplug controller. This
28 * driver exports the same interfaces to user space
29 * as the generic hpc3130 driver.  It adds specific
30 * functionality found on Daktari, such as slot button
31 * and platform specific LED displays.  Placed in
32 * the daktari specific platform directory, it will
33 * be loaded instead of the generic module.
34 */
35
36
37#include <sys/types.h>
38#include <sys/cmn_err.h>
39#include <sys/kmem.h>
40#include <sys/errno.h>
41#include <sys/cpuvar.h>
42#include <sys/open.h>
43#include <sys/stat.h>
44#include <sys/conf.h>
45#include <sys/ddi.h>
46#include <sys/sunddi.h>
47#include <sys/modctl.h>
48#include <sys/note.h>
49#include <sys/hotplug/hpctrl.h>
50#include <sys/hotplug/hpcsvc.h>
51#include <sys/i2c/clients/hpc3130.h>
52#include <sys/hpc3130_events.h>
53#include <sys/daktari.h>
54#include <sys/hpc3130_dak.h>
55
56#ifdef DEBUG
57static int hpc3130debug = 0;
58
59#define	D1CMN_ERR(ARGS) if (hpc3130debug & 0x1) cmn_err ARGS;
60#define	D2CMN_ERR(ARGS) if (hpc3130debug & 0x2) cmn_err ARGS;
61
62#else
63
64#define	D1CMN_ERR(ARGS)
65#define	D2CMN_ERR(ARGS)
66
67#endif /* DEBUG */
68
69#define	HPC3130_REG(offset, slot) ((offset) + ((slot)*8))
70#define	HPC3130_PIL	1
71struct tuple {
72	uint8_t reg;
73	uint8_t val;
74};
75
76struct connect_command {
77	boolean_t set_bit;
78	uint8_t value;
79};
80
81struct tuple pci_sequence [] =
82{
83	{HPC3130_GCR, HPC3130_AUTO2_SEQ},
84	{HPC3130_INTERRUPT, HPC3130_PWRGOOD |
85		HPC3130_DETECT0 | HPC3130_PRSNT1 | HPC3130_PRSNT2},
86	{HPC3130_EVENT_STATUS, 0xff},
87	{HPC3130_NO_REGISTER, 0},
88};
89
90struct tuple cpu_sequence [] =
91{
92	{HPC3130_INTERRUPT,
93		HPC3130_PRSNT1 | HPC3130_DETECT0},
94	{HPC3130_EVENT_STATUS, 0xff},
95	{HPC3130_NO_REGISTER, 0},
96};
97
98struct connect_command connect_sequence [] =
99{
100	{B_TRUE,  HPC3130_SLOTREQ64},
101	{B_FALSE, HPC3130_SLOTRST},
102	{B_FALSE, HPC3130_CLKON},
103	{B_FALSE, HPC3130_REQ64},
104	{B_FALSE, HPC3130_SLOTREQ64},
105	{B_TRUE,  HPC3130_SLOTRST},
106	{B_FALSE, HPC3130_BUS_CTL},
107};
108
109#define	HPC3130_CONNECT_SEQ_COUNT (sizeof (connect_sequence)/ \
110	sizeof (struct connect_command))
111
112struct xlate_entry {
113	char	*nexus;
114	int	pcidev;
115};
116/*
117 * The order here is significant.  Its the order
118 * of appearance of slots from bottom to top
119 * on a Sun-Fire-880
120 */
121static struct xlate_entry slot_translate[] =
122{
123	{"/pci@8,700000", 5},	/* PCI0 */
124	{"/pci@8,700000", 4},	/* PCI1 */
125	{"/pci@8,700000", 3},	/* PCI2 */
126	{"/pci@8,700000", 2},	/* PCI3 */
127
128	{"/pci@9,700000", 4},	/* PCI4 */
129	{"/pci@9,700000", 3},	/* PCI5 */
130	{"/pci@9,700000", 2},	/* PCI6 */
131
132	{"/pci@9,600000", 2},	/* PCI7 */
133	{"/pci@9,600000", 1}	/* PCI8 */
134};
135
136#define	HPC3130_LOOKUP_SLOTS (sizeof (slot_translate)/ \
137	sizeof (struct xlate_entry))
138
139static int control_slot_control = HPC3130_SLOT_CONTROL_ENABLE;
140
141hpc3130_unit_t *hpc3130soft_statep;
142
143static int hpc3130_atoi(const char *);
144int hpc3130_lookup_slot(char *, int);
145
146static int hpc3130_init(dev_info_t *, struct tuple *);
147static uint_t hpc3130_hard_intr(caddr_t);
148
149static int hpc3130_cpu_init(hpc3130_unit_t *, int, i2c_client_hdl_t);
150static int hpc3130_debounce_status(i2c_client_hdl_t, int, uint8_t *);
151static int hpc3130_read(i2c_client_hdl_t, uint8_t, uint8_t, uint8_t *);
152static int hpc3130_write(i2c_client_hdl_t, uint8_t, uint8_t, uint8_t);
153static int hpc3130_rw(i2c_client_hdl_t, uint8_t, boolean_t, uint8_t *);
154
155static int hpc3130_do_attach(dev_info_t *);
156static int hpc3130_do_detach(dev_info_t *);
157static int hpc3130_do_resume(void);
158static int hpc3130_do_suspend();
159static int hpc3130_get(intptr_t, int, hpc3130_unit_t *, int);
160static int hpc3130_set(intptr_t, int, hpc3130_unit_t *, int);
161
162static int hpc3130_slot_connect(caddr_t, hpc_slot_t, void *, uint_t);
163static int hpc3130_slot_disconnect(caddr_t, hpc_slot_t, void *, uint_t);
164static int hpc3130_verify_slot_power(hpc3130_unit_t *, i2c_client_hdl_t,
165					uint8_t, char *, boolean_t);
166static int hpc3130_slot_insert(caddr_t, hpc_slot_t, void *, uint_t);
167static int hpc3130_slot_remove(caddr_t, hpc_slot_t, void *, uint_t);
168static int hpc3130_slot_control(caddr_t, hpc_slot_t, int, caddr_t);
169/*
170 * cb ops
171 */
172static int hpc3130_open(dev_t *, int, int, cred_t *);
173static int hpc3130_close(dev_t, int, int, cred_t *);
174static int hpc3130_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
175static int hpc3130_poll(dev_t dev, short events, int anyyet,  short
176			*reventsp, struct pollhead **phpp);
177
178static struct cb_ops hpc3130_cbops = {
179	hpc3130_open,			/* open  */
180	hpc3130_close,			/* close */
181	nodev,				/* strategy */
182	nodev,				/* print */
183	nodev,				/* dump */
184	nodev,				/* read */
185	nodev,				/* write */
186	hpc3130_ioctl,			/* ioctl */
187	nodev,				/* devmap */
188	nodev,				/* mmap */
189	nodev,				/* segmap */
190	hpc3130_poll,			/* poll */
191	ddi_prop_op,			/* cb_prop_op */
192	NULL,				/* streamtab */
193	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
194	CB_REV,				/* rev */
195	nodev,				/* int (*cb_aread)() */
196	nodev				/* int (*cb_awrite)() */
197};
198
199/*
200 * dev ops
201 */
202static int hpc3130_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
203		void **result);
204static int hpc3130_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
205static int hpc3130_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
206
207static struct dev_ops hpc3130_ops = {
208	DEVO_REV,
209	0,
210	hpc3130_info,
211	nulldev,
212	nulldev,
213	hpc3130_attach,
214	hpc3130_detach,
215	nodev,
216	&hpc3130_cbops,
217	NULL,			/* bus_ops */
218	NULL,			/* power */
219	ddi_quiesce_not_needed,		/* quiesce */
220};
221
222extern struct mod_ops mod_driverops;
223
224static struct modldrv hpc3130_modldrv = {
225	&mod_driverops,			/* type of module - driver */
226	"Hotplug controller driver",
227	&hpc3130_ops
228};
229
230static struct modlinkage hpc3130_modlinkage = {
231	MODREV_1,
232	&hpc3130_modldrv,
233	0
234};
235
236int
237_init(void)
238{
239	int error;
240
241	error = mod_install(&hpc3130_modlinkage);
242
243	if (!error)
244		(void) ddi_soft_state_init((void *)&hpc3130soft_statep,
245		    sizeof (hpc3130_unit_t), 4);
246	return (error);
247}
248
249int
250_fini(void)
251{
252	int error;
253
254	error = mod_remove(&hpc3130_modlinkage);
255	if (!error)
256		ddi_soft_state_fini((void *)&hpc3130soft_statep);
257
258	return (error);
259}
260
261int
262_info(struct modinfo *modinfop)
263{
264	return (mod_info(&hpc3130_modlinkage, modinfop));
265}
266
267static int
268hpc3130_open(dev_t *devp, int flags, int otyp, cred_t *credp)
269{
270	_NOTE(ARGUNUSED(credp))
271	hpc3130_unit_t *unitp;
272	int instance;
273	int error = 0;
274
275	if (otyp != OTYP_CHR) {
276		return (EINVAL);
277	}
278
279	instance = MINOR_TO_INST(getminor(*devp));
280
281	unitp = (hpc3130_unit_t *)
282	    ddi_get_soft_state(hpc3130soft_statep, instance);
283
284	if (unitp == NULL) {
285		return (ENXIO);
286	}
287
288	mutex_enter(&unitp->hpc3130_mutex);
289
290	if (flags & FEXCL) {
291		if (unitp->hpc3130_oflag != 0) {
292			error = EBUSY;
293		} else {
294			unitp->hpc3130_oflag = FEXCL;
295		}
296	} else {
297		if (unitp->hpc3130_oflag == FEXCL) {
298			error = EBUSY;
299		} else {
300			unitp->hpc3130_oflag = FOPEN;
301		}
302	}
303
304	mutex_exit(&unitp->hpc3130_mutex);
305
306	return (error);
307}
308
309static int
310hpc3130_close(dev_t dev, int flags, int otyp, cred_t *credp)
311{
312	_NOTE(ARGUNUSED(flags, otyp, credp))
313	hpc3130_unit_t *unitp;
314	int instance;
315
316	instance = MINOR_TO_INST(getminor(dev));
317
318	unitp = (hpc3130_unit_t *)
319	    ddi_get_soft_state(hpc3130soft_statep, instance);
320
321	if (unitp == NULL) {
322		return (ENXIO);
323	}
324
325	mutex_enter(&unitp->hpc3130_mutex);
326
327	unitp->hpc3130_oflag = 0;
328
329	mutex_exit(&unitp->hpc3130_mutex);
330	return (DDI_SUCCESS);
331}
332
333static int
334hpc3130_get(intptr_t arg, int reg, hpc3130_unit_t *unitp, int mode)
335{
336	i2c_transfer_t		*i2c_tran_pointer;
337	int err = DDI_SUCCESS;
338
339	if (arg == (intptr_t)NULL) {
340		D2CMN_ERR((CE_WARN, "ioctl: arg passed in to "
341		    "ioctl = NULL"));
342		return (EINVAL);
343	}
344	(void) i2c_transfer_alloc(unitp->hpc3130_hdl, &i2c_tran_pointer,
345	    1, 1, I2C_SLEEP);
346	if (i2c_tran_pointer == NULL) {
347		D2CMN_ERR((CE_WARN, "Failed in HPC3130_GET_STATUS"
348		    " i2c_tran_pointer not allocated"));
349		return (ENOMEM);
350	}
351
352	i2c_tran_pointer->i2c_flags = I2C_WR_RD;
353	i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg;
354
355	err = i2c_transfer(unitp->hpc3130_hdl, i2c_tran_pointer);
356	if (err) {
357		D2CMN_ERR((CE_WARN, "Failed in HPC3130_GET_STATUS"
358		    " i2c_trasfer routine"));
359		i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
360		return (err);
361	}
362	D1CMN_ERR((CE_NOTE, "The i2c_rbuf contains %x",
363	    i2c_tran_pointer->i2c_rbuf[0]));
364
365	if (ddi_copyout((caddr_t)i2c_tran_pointer->i2c_rbuf,
366	    (caddr_t)arg,
367	    sizeof (uint8_t), mode) != DDI_SUCCESS) {
368		D2CMN_ERR((CE_WARN, "Failed in HPC3130_GET_STATUS"
369		    " ddi_copyout routine"));
370		err = EFAULT;
371	}
372	i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
373	return (err);
374}
375
376static int
377hpc3130_set(intptr_t arg, int reg, hpc3130_unit_t *unitp, int mode)
378{
379	i2c_transfer_t		*i2c_tran_pointer;
380	int err = DDI_SUCCESS;
381	uint8_t passin_byte;
382
383	if (arg == (intptr_t)NULL) {
384		D2CMN_ERR((CE_WARN, "ioctl: arg passed in to "
385		    "ioctl = NULL"));
386		return (EINVAL);
387	}
388	if (ddi_copyin((caddr_t)arg, (caddr_t)&passin_byte,
389	    sizeof (uint8_t), mode) != DDI_SUCCESS) {
390		D2CMN_ERR((CE_WARN, "Failed in HPC3130_SET_CONTROL "
391		    "ddi_copyin routine"));
392
393		return (EFAULT);
394	}
395	(void) i2c_transfer_alloc(unitp->hpc3130_hdl, &i2c_tran_pointer,
396	    2, 0, I2C_SLEEP);
397	if (i2c_tran_pointer == NULL) {
398		D2CMN_ERR((CE_WARN, "Failed in "
399		    "HPC3130_SET_CONTROL i2c_tran_pointer not allocated"));
400
401		return (ENOMEM);
402	}
403
404	i2c_tran_pointer->i2c_flags = I2C_WR;
405	i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg;
406	i2c_tran_pointer->i2c_wbuf[1] = passin_byte;
407
408	err = i2c_transfer(unitp->hpc3130_hdl, i2c_tran_pointer);
409	i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
410
411	return (err);
412}
413
414static int
415hpc3130_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
416    int *rvalp)
417{
418	_NOTE(ARGUNUSED(credp, rvalp))
419	hpc3130_unit_t		*unitp;
420	int			err = DDI_SUCCESS;
421	i2c_transfer_t		*i2c_tran_pointer;
422	i2c_reg_t		ioctl_reg;
423	int port = MINOR_TO_PORT(getminor(dev));
424	int instance = MINOR_TO_INST(getminor(dev));
425	hpc3130_slot_table_entry_t *ste;
426
427	unitp = (hpc3130_unit_t *)
428	    ddi_get_soft_state(hpc3130soft_statep, instance);
429
430	if (unitp == NULL) {
431		D1CMN_ERR((CE_WARN, "unitp not filled"));
432		return (ENOMEM);
433	}
434
435	/*
436	 * It should be the case that the port number is a valid
437	 * index in the per instance slot table. If it is not
438	 * then we should fail out.
439	 */
440	if (!(port >= 0 && port < unitp->hpc3130_slot_table_length)) {
441		return (EINVAL);
442	}
443
444	mutex_enter(&unitp->hpc3130_mutex);
445
446	ste = &unitp->hpc3130_slot_table[port];
447
448	D2CMN_ERR((CE_NOTE, "ioctl: port = %d  instance = %d",
449	    port, instance));
450
451	switch (cmd) {
452	case HPC3130_GET_STATUS:
453		err = hpc3130_get(arg, HPC3130_HP_STATUS_REG(port), unitp,
454		    mode);
455		break;
456
457	case HPC3130_GET_CONTROL:
458		err = hpc3130_get(arg, HPC3130_HP_CONTROL_REG(port), unitp,
459		    mode);
460		break;
461
462	case HPC3130_SET_CONTROL:
463		if (control_slot_control == HPC3130_SLOT_CONTROL_DISABLE) {
464			cmn_err(CE_WARN, "Cannot change control register.");
465			err = EINVAL;
466			break;
467		}
468		err = hpc3130_set(arg, HPC3130_HP_CONTROL_REG(port), unitp,
469		    mode);
470		break;
471
472	case HPC3130_GET_EVENT_STATUS:
473		err = hpc3130_get(arg, HPC3130_INTERRUPT_STATUS_REG(port),
474		    unitp, mode);
475		break;
476
477	case HPC3130_SET_EVENT_STATUS:
478		err = hpc3130_set(arg, HPC3130_INTERRUPT_STATUS_REG(port),
479		    unitp, mode);
480		break;
481
482	case HPC3130_GET_GENERAL_CONFIG:
483		err = hpc3130_get(arg, HPC3130_GENERAL_CONFIG_REG(port),
484		    unitp, mode);
485		break;
486
487	case HPC3130_SET_GENERAL_CONFIG:
488		err = hpc3130_set(arg, HPC3130_GENERAL_CONFIG_REG(port),
489		    unitp, mode);
490		break;
491
492	case HPC3130_GET_INDICATOR_CONTROL:
493		err = hpc3130_get(arg, HPC3130_ATTENTION_INDICATOR(port),
494		    unitp, mode);
495		break;
496
497	case HPC3130_SET_INDICATOR_CONTROL:
498		err = hpc3130_set(arg, HPC3130_ATTENTION_INDICATOR(port),
499		    unitp, mode);
500		break;
501
502	case HPC3130_GET_EVENT_ENABLE:
503		err = hpc3130_get(arg, HPC3130_INTERRUPT_ENABLE_REG(port),
504		    unitp, mode);
505		break;
506
507	case HPC3130_SET_EVENT_ENABLE:
508		err = hpc3130_set(arg, HPC3130_INTERRUPT_ENABLE_REG(port),
509		    unitp, mode);
510		break;
511
512	case HPC3130_ENABLE_SLOT_CONTROL:
513		control_slot_control = HPC3130_SLOT_CONTROL_ENABLE;
514		D2CMN_ERR((CE_NOTE, "Set the control_slot_control variable to"
515		    "HPC3130_SLOT_CONTROL_ENABLE"));
516		break;
517
518	case HPC3130_DISABLE_SLOT_CONTROL:
519		control_slot_control = HPC3130_SLOT_CONTROL_DISABLE;
520		D2CMN_ERR((CE_NOTE, "Set the control_slot_control variable to"
521		    "HPC3130_SLOT_CONTROL_DISABLE"));
522		break;
523
524	case I2C_GET_REG:
525		if (arg == (intptr_t)NULL) {
526			D2CMN_ERR((CE_WARN, "ioctl: arg passed in to "
527			    "ioctl = NULL"));
528			err = EINVAL;
529			break;
530		}
531		if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_reg,
532		    sizeof (i2c_reg_t), mode) != DDI_SUCCESS) {
533			D2CMN_ERR((CE_WARN, "Failed in I2C_GET_REG "
534			    "ddi_copyin routine"));
535			err = EFAULT;
536			break;
537		}
538		(void) i2c_transfer_alloc(unitp->hpc3130_hdl, &i2c_tran_pointer,
539		    1, 1, I2C_SLEEP);
540		if (i2c_tran_pointer == NULL) {
541			D2CMN_ERR((CE_WARN, "Failed in I2C_GET_REG "
542			    "i2c_tran_pointer not allocated"));
543			err = ENOMEM;
544			break;
545		}
546
547		i2c_tran_pointer->i2c_flags = I2C_WR_RD;
548		i2c_tran_pointer->i2c_wbuf[0] = ioctl_reg.reg_num;
549
550		err = i2c_transfer(unitp->hpc3130_hdl, i2c_tran_pointer);
551		if (err) {
552			D2CMN_ERR((CE_WARN, "Failed in I2C_GET_REG "
553			    "i2c_transfer routine"));
554			i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
555			break;
556		}
557		ioctl_reg.reg_value = i2c_tran_pointer->i2c_rbuf[0];
558		if (ddi_copyout((caddr_t)&ioctl_reg, (caddr_t)arg,
559		    sizeof (i2c_reg_t), mode) != DDI_SUCCESS) {
560			D2CMN_ERR((CE_WARN, "Failed in I2C_GET_REG "
561			    "ddi_copyout routine"));
562			err = EFAULT;
563		}
564
565		i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
566		break;
567
568	case I2C_SET_REG:
569		if (arg == (intptr_t)NULL) {
570			D2CMN_ERR((CE_WARN, "ioctl: arg passed in to "
571			    "ioctl = NULL"));
572			err = EINVAL;
573			break;
574		}
575		if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_reg,
576		    sizeof (i2c_reg_t), mode) != DDI_SUCCESS) {
577			D2CMN_ERR((CE_WARN, "Failed in I2C_SET_REG "
578			    "ddi_copyin routine"));
579			err = EFAULT;
580			break;
581		}
582		(void) i2c_transfer_alloc(unitp->hpc3130_hdl, &i2c_tran_pointer,
583		    2, 0, I2C_SLEEP);
584		if (i2c_tran_pointer == NULL) {
585			D2CMN_ERR((CE_WARN, "Failed in I2C_GET_REG "
586			    "i2c_tran_pointer not allocated"));
587			err = ENOMEM;
588			break;
589		}
590
591		i2c_tran_pointer->i2c_flags = I2C_WR;
592		i2c_tran_pointer->i2c_wbuf[0] = ioctl_reg.reg_num;
593		i2c_tran_pointer->i2c_wbuf[1] = (uchar_t)ioctl_reg.reg_value;
594
595		err = i2c_transfer(unitp->hpc3130_hdl, i2c_tran_pointer);
596		if (err) {
597			D2CMN_ERR((CE_WARN, "Failed in I2C_SET_REG "
598			    "i2c_transfer routine"));
599			i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
600			break;
601		}
602
603		i2c_transfer_free(unitp->hpc3130_hdl, i2c_tran_pointer);
604		break;
605
606	case HPC3130_GET_EVENT: {
607		struct hpc3130_event ev;
608
609		bzero(&ev, sizeof (struct hpc3130_event));
610
611		if (unitp->slots_are == HPC3130_SLOT_TYPE_SBD) {
612			DAK_GET_SBD_APID(ev.name, sizeof (ev.name), port);
613		} else {
614			(void) snprintf(ev.name, HPC3130_NAME_MAX,
615			    "/devices%s:", ste->nexus);
616			ASSERT(strlen(ev.name) < HPC3130_NAME_MAX - 1);
617			DAK_GET_PCI_APID(ev.name + strlen(ev.name),
618			    HPC3130_NAME_MAX - strlen(ev.name),
619			    hpc3130_lookup_slot(ste->nexus,
620			    ste->hpc3130_slot_info.pci_dev_num));
621		}
622
623		if (unitp->events[port] & HPC3130_IEVENT_OCCUPANCY) {
624			unitp->events[port] &= ~HPC3130_IEVENT_OCCUPANCY;
625			ev.id = (unitp->present[port] == B_FALSE ?
626			    HPC3130_EVENT_REMOVAL :
627			    HPC3130_EVENT_INSERTION);
628		} else if (unitp->events[port] & HPC3130_IEVENT_POWER) {
629			unitp->events[port] &= ~HPC3130_IEVENT_POWER;
630			ev.id = (unitp->power[port] == B_TRUE ?
631			    HPC3130_EVENT_POWERON :
632			    HPC3130_EVENT_POWEROFF);
633		} else if (unitp->events[port] & HPC3130_IEVENT_BUTTON) {
634			unitp->events[port] &= ~HPC3130_IEVENT_BUTTON;
635			ev.id = HPC3130_EVENT_BUTTON;
636		} else if (unitp->events[port] & HPC3130_IEVENT_FAULT) {
637			unitp->events[port] &= ~HPC3130_IEVENT_FAULT;
638			ev.id = (unitp->fault_led[port] == HPC3130_ATTN_ON ?
639			    HPC3130_LED_FAULT_ON :
640			    HPC3130_LED_FAULT_OFF);
641		} else if (unitp->events[port] & HPC3130_IEVENT_OK2REM) {
642			unitp->events[port] &= ~HPC3130_IEVENT_OK2REM;
643			ev.id = (unitp->ok2rem_led[port] == HPC3130_ATTN_ON ?
644			    HPC3130_LED_REMOVABLE_ON :
645			    HPC3130_LED_REMOVABLE_OFF);
646		}
647
648		D1CMN_ERR((CE_NOTE,
649		    "sending EVENT: ap_id=%s, event=%d", ev.name, ev.id));
650
651		if (ddi_copyout((caddr_t)&ev, (caddr_t)arg,
652			sizeof (struct hpc3130_event), mode) != DDI_SUCCESS) {
653			D1CMN_ERR((CE_WARN, "Failed in hpc3130_ioctl"
654			    " ddi_copyout routine"));
655			err = EFAULT;
656		}
657		break;
658	}
659	case HPC3130_CONF_DR: {
660		uint8_t offset;
661		int dr_conf;
662
663		if (ddi_copyin((caddr_t)arg, (caddr_t)&dr_conf,
664		    sizeof (int), mode) != DDI_SUCCESS) {
665			D2CMN_ERR((CE_WARN, "Failed in HPC3130_CONF_DR "
666			    "ddi_copyin routine"))
667			err = EFAULT;
668			break;
669		}
670
671		offset = ste->callback_info.offset;
672
673		unitp->enabled[offset] =
674		    (dr_conf == HPC3130_DR_DISABLE ? B_FALSE : B_TRUE);
675
676		break;
677	}
678	default:
679		D2CMN_ERR((CE_WARN, "Invalid IOCTL cmd: %x", cmd));
680		err = EINVAL;
681	}
682
683	mutex_exit(&unitp->hpc3130_mutex);
684	return (err);
685}
686
687static int
688hpc3130_poll(dev_t dev, short events, int anyyet, short *reventsp,
689    struct pollhead **phpp)
690{
691	_NOTE(ARGUNUSED(events))
692	hpc3130_unit_t *unitp;
693	int port = MINOR_TO_PORT(getminor(dev));
694	int instance = MINOR_TO_INST(getminor(dev));
695
696	if (!(port >= 0 && port < HPC3130_MAX_SLOT)) {
697		return (EINVAL);
698	}
699	unitp = (hpc3130_unit_t *)
700	    ddi_get_soft_state(hpc3130soft_statep, instance);
701
702	mutex_enter(&unitp->hpc3130_mutex);
703	if (unitp->events[port]) {
704		*reventsp = POLLIN;
705	} else {
706		*reventsp = 0;
707		if (!anyyet)
708			*phpp = &unitp->pollhead[port];
709	}
710	mutex_exit(&unitp->hpc3130_mutex);
711	return (0);
712}
713
714/* ARGSUSED */
715static int
716hpc3130_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
717{
718	dev_t	dev;
719	int	instance;
720
721	if (infocmd == DDI_INFO_DEVT2INSTANCE) {
722		dev = (dev_t)arg;
723		instance = MINOR_TO_INST(getminor(dev));
724		*result = (void *)(uintptr_t)instance;
725		return (DDI_SUCCESS);
726	}
727	return (DDI_FAILURE);
728}
729
730static int
731hpc3130_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
732{
733	switch (cmd) {
734	case DDI_ATTACH:
735		return (hpc3130_do_attach(dip));
736	case DDI_RESUME:
737		return (hpc3130_do_resume());
738	default:
739		return (DDI_FAILURE);
740	}
741}
742
743static int
744hpc3130_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
745{
746	switch (cmd) {
747	case DDI_DETACH:
748		return (hpc3130_do_detach(dip));
749	case DDI_SUSPEND:
750		return (hpc3130_do_suspend());
751	default:
752		return (DDI_FAILURE);
753	}
754}
755
756static int
757hpc3130_do_attach(dev_info_t *dip)
758{
759	hpc3130_unit_t	*hpc3130_p;
760	char		*s;
761	char		*nexus;
762	char		*pcidev;
763	char		*reg_offset;
764	int		r, i, n, j;
765	char		name[MAXNAMELEN];
766	minor_t		minor_number;
767	int		hpc3130_pil = HPC3130_PIL;
768	int		instance = ddi_get_instance(dip);
769
770	/*
771	 * Allocate the soft state structure for this instance.
772	 */
773	r = ddi_soft_state_zalloc(hpc3130soft_statep, instance);
774	if (r != DDI_SUCCESS) {
775		return (DDI_FAILURE);
776	}
777
778	hpc3130_p =
779	    (hpc3130_unit_t *)ddi_get_soft_state(hpc3130soft_statep, instance);
780	ASSERT(hpc3130_p);
781
782	if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
783	    "interrupt-priorities", (caddr_t)&hpc3130_pil,
784	    sizeof (hpc3130_pil)) != DDI_PROP_SUCCESS) {
785		goto failout0;
786	}
787
788	if (ddi_intr_hilevel(dip, 0)) {
789		cmn_err(CE_WARN, "High level interrupt not supported");
790		goto failout0;
791	}
792
793	/*
794	 * Get the "slot-table" property which defines the list of
795	 * hot-pluggable slots for this controller along with the
796	 * corresponding bus nexus node and device identification
797	 * for each slot.
798	 */
799	r = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
800	    "slot-table", (caddr_t)&hpc3130_p->hpc3130_slot_table_data,
801	    &hpc3130_p->hpc3130_slot_table_size);
802
803	switch (r) {
804	case DDI_PROP_SUCCESS:
805		break;
806	case DDI_PROP_NOT_FOUND:
807		cmn_err(CE_WARN,
808		    "couldn't find slot-table property");
809		return (DDI_FAILURE);
810	case DDI_PROP_UNDEFINED:
811		cmn_err(CE_WARN,
812		    "slot-table undefined");
813		return (DDI_FAILURE);
814	case DDI_PROP_NO_MEMORY:
815		cmn_err(CE_WARN,
816		    "can't allocate memory for slot-table");
817		return (DDI_FAILURE);
818	}
819
820	/*
821	 * Determine the size of the slot table from the OBP property and
822	 * allocate the slot table arrary..
823	 */
824	for (i = 0, n = 0; i < hpc3130_p->hpc3130_slot_table_size; i++) {
825		if (hpc3130_p->hpc3130_slot_table_data[i] == 0) {
826			n++;
827		}
828	}
829
830	D1CMN_ERR((CE_NOTE, "hpc3130_attach(): slot table has %d entries", n));
831
832	/*
833	 * There should be HPC3130_TABLE_COLUMNS elements per entry
834	 */
835	if (n % HPC3130_TABLE_COLUMNS) {
836		cmn_err(CE_WARN, "bad format in slot-table");
837		goto failout1;
838	}
839
840	hpc3130_p->dip = dip;
841	hpc3130_p->hpc3130_slot_table_length = n / HPC3130_TABLE_COLUMNS;
842
843	if (ddi_get_iblock_cookie(dip, 0, &hpc3130_p->ic_trap_cookie) !=
844	    DDI_SUCCESS)  {
845		cmn_err(CE_WARN, "ddi_get_iblock_cookie FAILED");
846		goto failout1;
847	}
848
849	mutex_init(&hpc3130_p->hpc3130_mutex, NULL, MUTEX_DRIVER,
850	    (void *)hpc3130_p->ic_trap_cookie);
851	/*
852	 * Create enough space for each slot table entry
853	 * based on how many entries in the property
854	 */
855	hpc3130_p->hpc3130_slot_table = (hpc3130_slot_table_entry_t *)
856	    kmem_zalloc(hpc3130_p->hpc3130_slot_table_length *
857	    sizeof (hpc3130_slot_table_entry_t), KM_SLEEP);
858
859	/*
860	 * Setup to talk to the i2c nexus
861	 */
862	if (i2c_client_register(dip, &hpc3130_p->hpc3130_hdl) != I2C_SUCCESS) {
863		cmn_err(CE_WARN, "failed to register as i2c client");
864		goto failout2;
865	}
866
867	s = hpc3130_p->hpc3130_slot_table_data;
868	for (i = 0; i < hpc3130_p->hpc3130_slot_table_length; i++) {
869		hpc3130_slot_table_entry_t *ste;
870
871		/* Pick off pointer to nexus path */
872		nexus = s;
873		s = s + strlen(s) + 1;
874
875		/* Pick off pointer to 3130 register offset */
876		reg_offset = s;
877		s = s + strlen(s) + 1;
878
879		/* Pick off pointer to the device number */
880		pcidev = s;
881
882		s = s + strlen(s) + 1;
883
884		j = hpc3130_atoi(reg_offset);
885
886		if (j < 0 || j >= HPC3130_MAX_SLOT) {
887			cmn_err(CE_WARN,
888			    "invalid register offset value");
889			goto failout3;
890		}
891
892		ste = &hpc3130_p->hpc3130_slot_table[j];
893
894		(void) strcpy(ste->nexus, nexus);
895
896		if (strncmp(ste->nexus, "/pci", 4) == 0) {
897
898			ste->hpc3130_slot_info.pci_dev_num =
899			    hpc3130_atoi(pcidev);
900
901			DAK_GET_PCI_APID(ste->hpc3130_slot_info.pci_slot_name,
902			    PCI_SLOT_NAME_LEN,
903			    hpc3130_lookup_slot(ste->nexus,
904			    hpc3130_atoi(pcidev)));
905
906			ste->hpc3130_slot_info.slot_type = HPC_SLOT_TYPE_PCI;
907			ste->hpc3130_slot_info.slot_flags =
908			    HPC_SLOT_CREATE_DEVLINK;
909			hpc3130_p->slots_are = HPC3130_SLOT_TYPE_PCI;
910
911		} else {
912
913			ste->hpc3130_slot_info.sbd_slot_num =
914			    hpc3130_atoi(reg_offset);
915
916			ste->hpc3130_slot_info.slot_type = HPC_SLOT_TYPE_SBD;
917
918			hpc3130_p->slots_are = HPC3130_SLOT_TYPE_SBD;
919		}
920
921		hpc3130_p->present[j] = B_FALSE;
922		hpc3130_p->enabled[j] = B_TRUE;
923
924		/*
925		 * The "callback_info" structure of the slot_table is what gets
926		 * passed back in the callback routines.  All that is needed
927		 * at that point is the device handle  and the register offset
928		 * within it the chip it represents.
929		 */
930		ste->callback_info.handle = (caddr_t)hpc3130_p->hpc3130_hdl;
931
932		ste->callback_info.offset = hpc3130_atoi(reg_offset);
933
934		ste->callback_info.statep = (caddr_t)hpc3130_p;
935	}
936
937	hpc3130_p->hpc3130_slot_ops = hpc_alloc_slot_ops(KM_SLEEP);
938	hpc3130_p->hpc3130_slot_ops->hpc_version = 0;
939
940	hpc3130_p->hpc3130_slot_ops->hpc_op_connect = hpc3130_slot_connect;
941	hpc3130_p->hpc3130_slot_ops->hpc_op_disconnect =
942	    hpc3130_slot_disconnect;
943	hpc3130_p->hpc3130_slot_ops->hpc_op_insert = hpc3130_slot_insert;
944	hpc3130_p->hpc3130_slot_ops->hpc_op_remove = hpc3130_slot_remove;
945	hpc3130_p->hpc3130_slot_ops->hpc_op_control = hpc3130_slot_control;
946
947	cv_init(&hpc3130_p->hpc3130_cond, NULL, CV_DEFAULT, NULL);
948
949	if (hpc3130_init(dip, (hpc3130_p->slots_are == HPC3130_SLOT_TYPE_SBD) ?
950	    cpu_sequence : pci_sequence) != DDI_SUCCESS) {
951			goto failout4;
952	}
953
954	if (ddi_add_intr(dip, 0, &hpc3130_p->ic_trap_cookie,
955	    NULL, hpc3130_hard_intr,
956	    (caddr_t)hpc3130_p) != DDI_SUCCESS) {
957		cmn_err(CE_WARN, "failed to add interrupt");
958		goto failout4;
959	}
960
961	/*
962	 * Register with the "services" module
963	 */
964	for (i = 0; i < hpc3130_p->hpc3130_slot_table_length; i++) {
965		hpc3130_slot_table_entry_t *ste =
966		    &hpc3130_p->hpc3130_slot_table[i];
967		hpc3130_p->power[i] = B_TRUE;
968		if (ste->callback_info.handle != NULL) {
969			(void) hpc_slot_register(dip, ste->nexus,
970			    &ste->hpc3130_slot_info,
971			    &ste->hpc3130_slot_handle,
972			    hpc3130_p->hpc3130_slot_ops,
973			    (caddr_t)&ste->callback_info, 0);
974		}
975	}
976
977	(void) snprintf(hpc3130_p->hpc3130_name,
978	    sizeof (hpc3130_p->hpc3130_name),
979	    "%s%d", ddi_node_name(dip), instance);
980
981	for (i = 0; i < HPC3130_MAX_SLOT; i++) {
982		(void) snprintf(name, MAXNAMELEN, "port_%d", i);
983		minor_number = INST_TO_MINOR(instance) |
984		    PORT_TO_MINOR(I2C_PORT(i));
985		if (ddi_create_minor_node(dip, name, S_IFCHR, minor_number,
986		    "ddi_i2c:controller", 0) == DDI_FAILURE) {
987			D1CMN_ERR((CE_WARN, "ddi_create_minor_node failed "
988			    "for %s", name));
989			ddi_remove_intr(dip, 0u,
990			    hpc3130_p->ic_trap_cookie);
991			goto failout4;
992		}
993	}
994
995	return (DDI_SUCCESS);
996
997failout4:
998	hpc_free_slot_ops(hpc3130_p->hpc3130_slot_ops);
999failout3:
1000	i2c_client_unregister(hpc3130_p->hpc3130_hdl);
1001failout2:
1002	mutex_destroy(&hpc3130_p->hpc3130_mutex);
1003	kmem_free(hpc3130_p->hpc3130_slot_table,
1004	    hpc3130_p->hpc3130_slot_table_length *
1005	    sizeof (hpc3130_slot_table_entry_t));
1006failout1:
1007	kmem_free(hpc3130_p->hpc3130_slot_table_data,
1008	    hpc3130_p->hpc3130_slot_table_size);
1009failout0:
1010	ddi_soft_state_free(hpc3130soft_statep, instance);
1011
1012	return (DDI_FAILURE);
1013}
1014
1015static int
1016hpc3130_do_resume()
1017{
1018	return (DDI_SUCCESS);
1019}
1020
1021static int
1022hpc3130_do_suspend()
1023{
1024	return (DDI_SUCCESS);
1025}
1026
1027static int
1028hpc3130_do_detach(dev_info_t *dip)
1029{
1030	int i;
1031	int instance = ddi_get_instance(dip);
1032	hpc3130_unit_t *hpc3130_p;
1033
1034	hpc3130_p = (hpc3130_unit_t *)ddi_get_soft_state(hpc3130soft_statep,
1035	    instance);
1036	if (hpc3130_p == NULL)
1037		return (ENXIO);
1038
1039	i2c_client_unregister(hpc3130_p->hpc3130_hdl);
1040
1041	ddi_remove_intr(dip, 0u, hpc3130_p->ic_trap_cookie);
1042
1043	cv_destroy(&hpc3130_p->hpc3130_cond);
1044
1045	for (i = 0; i < hpc3130_p->hpc3130_slot_table_length; i++) {
1046		(void) hpc_slot_unregister(
1047		    &hpc3130_p->hpc3130_slot_table[i].hpc3130_slot_handle);
1048	}
1049
1050	kmem_free(hpc3130_p->hpc3130_slot_table,
1051	    hpc3130_p->hpc3130_slot_table_length *
1052	    sizeof (hpc3130_slot_table_entry_t));
1053
1054	kmem_free(hpc3130_p->hpc3130_slot_table_data,
1055	    hpc3130_p->hpc3130_slot_table_size);
1056
1057	hpc_free_slot_ops(hpc3130_p->hpc3130_slot_ops);
1058
1059	mutex_destroy(&hpc3130_p->hpc3130_mutex);
1060
1061	ddi_soft_state_free(hpc3130soft_statep, instance);
1062
1063	return (DDI_SUCCESS);
1064}
1065
1066int
1067hpc3130_set_led(hpc3130_unit_t *unitp, int slot, int led, uint8_t value)
1068{
1069	i2c_client_hdl_t handle = unitp->hpc3130_hdl;
1070	uint8_t old;
1071	uint8_t	new;
1072
1073	if (hpc3130_read(handle, HPC3130_ATTEN, slot, &old) != DDI_SUCCESS) {
1074		return (DDI_FAILURE);
1075	}
1076	new = (old & ~HPC3130_ATTN_MASK(led)) |
1077	    value << HPC3130_ATTN_SHIFT(led);
1078
1079	D1CMN_ERR((CE_NOTE, "setting led %d to %x", led, value));
1080
1081	if (hpc3130_write(handle, HPC3130_ATTEN, slot, new) != DDI_SUCCESS) {
1082		return (DDI_FAILURE);
1083	}
1084
1085	if ((value == HPC3130_ATTN_OFF || value == HPC3130_ATTN_ON) &&
1086	    ((old & HPC3130_ATTN_MASK(led)) !=
1087	    (new & HPC3130_ATTN_MASK(led)))) {
1088		/*
1089		 * We're turning a LED on or off (i.e., not blinking), and
1090		 * the value actually did change.
1091		 */
1092		if (led == HPC3130_LED_OK2REM) {
1093			unitp->events[slot] |= HPC3130_IEVENT_OK2REM;
1094			unitp->ok2rem_led[slot] = value;
1095			D1CMN_ERR((CE_NOTE,
1096			    "recording IEVENT_OK2REM slot=%d, val=%d",
1097			    slot, value));
1098		} else {
1099			unitp->events[slot] |= HPC3130_IEVENT_FAULT;
1100			unitp->fault_led[slot] = value;
1101			D1CMN_ERR((CE_NOTE,
1102			    "recording IEVENT_FAULT slot=%d, val=%d",
1103			    slot, value));
1104		}
1105		ASSERT(MUTEX_HELD(&unitp->hpc3130_mutex));
1106		mutex_exit(&unitp->hpc3130_mutex);
1107		pollwakeup(&unitp->pollhead[slot], POLLIN);
1108		mutex_enter(&unitp->hpc3130_mutex);
1109	}
1110	return (DDI_SUCCESS);
1111}
1112
1113int
1114hpc3130_get_led(i2c_client_hdl_t handle, int slot,
1115    int led, uint8_t *value)
1116{
1117	uint8_t	temp;
1118
1119	if (hpc3130_read(handle, HPC3130_ATTEN, slot, &temp) != DDI_SUCCESS) {
1120		return (DDI_FAILURE);
1121	}
1122
1123	*value = (temp & HPC3130_ATTN_MASK(led)) >> HPC3130_ATTN_SHIFT(led);
1124	return (DDI_SUCCESS);
1125}
1126
1127static int
1128hpc3130_write(i2c_client_hdl_t handle, uint8_t offset,
1129    uint8_t port, uint8_t data)
1130{
1131	ASSERT(port < HPC3130_MAX_SLOT);
1132	ASSERT(handle);
1133
1134	return (hpc3130_rw(handle,
1135	    HPC3130_REG(offset, port), B_TRUE, &data));
1136}
1137
1138static int
1139hpc3130_read(i2c_client_hdl_t handle, uint8_t offset,
1140    uint8_t port, uint8_t *data)
1141{
1142	ASSERT(port < HPC3130_MAX_SLOT);
1143	ASSERT(handle);
1144
1145	return (hpc3130_rw(handle,
1146	    HPC3130_REG(offset, port), B_FALSE, data));
1147}
1148
1149static int
1150hpc3130_rw(i2c_client_hdl_t handle, uint8_t reg,
1151    boolean_t write, uint8_t *data)
1152{
1153	i2c_transfer_t	*i2c_tran_pointer;
1154	int		err;
1155	int		rlen;
1156	int		wlen;
1157
1158	if (write == B_TRUE) {
1159		wlen = 2;
1160		rlen = 0;
1161	} else {
1162		wlen = 1;
1163		rlen = 1;
1164	}
1165
1166	(void) i2c_transfer_alloc(handle,
1167	    &i2c_tran_pointer, wlen, rlen, I2C_SLEEP);
1168
1169	if (i2c_tran_pointer == NULL) {
1170		D1CMN_ERR((CE_WARN, "Failed in hpc3130_rw: "
1171		    "no transfer structure 0x%x", reg));
1172		return (DDI_FAILURE);
1173	}
1174	i2c_tran_pointer->i2c_wbuf[0] = reg;
1175	if (write == B_TRUE) {
1176		i2c_tran_pointer->i2c_flags = I2C_WR;
1177		i2c_tran_pointer->i2c_wbuf[1] = *data;
1178	} else {
1179		i2c_tran_pointer->i2c_flags = I2C_WR_RD;
1180	}
1181
1182	err = i2c_transfer(handle, i2c_tran_pointer);
1183	if (err) {
1184		D1CMN_ERR((CE_WARN, "Failed in hpc3130_rw: "
1185		    "no I2C data transfered 0x%x", reg));
1186		(void) i2c_transfer_free(handle, i2c_tran_pointer);
1187		return (DDI_FAILURE);
1188	}
1189
1190	if (write == B_FALSE)
1191		*data = i2c_tran_pointer->i2c_rbuf[0];
1192
1193	(void) i2c_transfer_free(handle, i2c_tran_pointer);
1194
1195	return (DDI_SUCCESS);
1196}
1197
1198/*
1199 * Put the hot plug controller(s) in proper mode for further
1200 * operations.
1201 */
1202static int
1203hpc3130_init(dev_info_t *dip,
1204    struct tuple *init_sequence)
1205{
1206
1207	int			slot;
1208	i2c_client_hdl_t	handle;
1209	hpc3130_unit_t		*hpc3130_p;
1210	int			instance = ddi_get_instance(dip);
1211	int			error = DDI_FAILURE;
1212	struct tuple		*tp;
1213
1214	hpc3130_p =
1215	    (hpc3130_unit_t *)ddi_get_soft_state(hpc3130soft_statep,
1216	    instance);
1217	ASSERT(hpc3130_p);
1218
1219	mutex_enter(&hpc3130_p->hpc3130_mutex);
1220
1221	handle = hpc3130_p->hpc3130_hdl;
1222
1223	for (slot = 0; slot < HPC3130_MAX_SLOT; slot++) {
1224		tp = init_sequence;
1225		while (tp->reg != HPC3130_NO_REGISTER) {
1226			if (hpc3130_write(handle, tp->reg, slot,
1227			    tp->val) != DDI_SUCCESS) {
1228				goto out;
1229			}
1230			tp++;
1231		}
1232		/*
1233		 * CPU slots need some special initialization
1234		 * attention.
1235		 */
1236		if (hpc3130_p->slots_are == HPC3130_SLOT_TYPE_SBD) {
1237			if (hpc3130_cpu_init(hpc3130_p, slot, handle)
1238			    != DDI_SUCCESS) {
1239				goto out;
1240			}
1241		}
1242	}
1243	error = DDI_SUCCESS;
1244out:
1245	mutex_exit(&hpc3130_p->hpc3130_mutex);
1246
1247	return (error);
1248}
1249
1250/*
1251 * When the TI 3130 produces an interrupt,
1252 * this routine is called to sort it out.
1253 */
1254static uint_t
1255hpc3130_hard_intr(caddr_t arg)
1256{
1257	uint8_t			interrupt;
1258	uint8_t			status;
1259	uint8_t			slot;
1260	i2c_client_hdl_t	handle;
1261	hpc3130_slot_type_t	slot_type;
1262	uint_t			rc = DDI_INTR_UNCLAIMED;
1263
1264	hpc3130_unit_t		*hpc3130_p = (hpc3130_unit_t *)arg;
1265	ASSERT(hpc3130_p);
1266
1267	mutex_enter(&hpc3130_p->hpc3130_mutex);
1268
1269	slot_type = hpc3130_p->slots_are;
1270	handle = hpc3130_p->hpc3130_hdl;
1271
1272	for (slot = 0; slot < HPC3130_MAX_SLOT; slot++) {
1273
1274		/*
1275		 * Read the interrupt event register - see
1276		 * which event(s) took place.
1277		 */
1278		if (hpc3130_read(handle, HPC3130_EVENT_STATUS, slot,
1279		    &interrupt)) {
1280			continue;
1281		}
1282
1283		if (interrupt == 0)
1284			continue;
1285
1286		rc = DDI_INTR_CLAIMED;
1287
1288		if (hpc3130_debounce_status(handle,
1289		    slot, &status) != DDI_SUCCESS) {
1290			continue;
1291		}
1292
1293		if (interrupt & HPC3130_PWRGOOD) {
1294			hpc3130_p->power[slot] = B_FALSE;
1295			if (!(status & HPC3130_PWRGOOD)) {
1296				hpc3130_p->power[slot] = B_TRUE;
1297			}
1298			cv_signal(&hpc3130_p->hpc3130_cond);
1299			hpc3130_p->events[slot] |= HPC3130_IEVENT_POWER;
1300		}
1301
1302		if (interrupt & HPC3130_DETECT0) {
1303			if (slot_type == HPC3130_SLOT_TYPE_SBD) {
1304				boolean_t present = !(status&HPC3130_DETECT0);
1305
1306				/* Turn ON/OFF OK-to-remove LED */
1307				(void) hpc3130_set_led(hpc3130_p,
1308				    slot,
1309				    HPC3130_LED_OK2REM,
1310				    (present ? HPC3130_ATTN_ON :
1311				    HPC3130_ATTN_OFF));
1312				if (!present) {
1313					/* Clear the FAULT LED on removal */
1314					(void) hpc3130_set_led(hpc3130_p,
1315					    slot,
1316					    HPC3130_LED_FAULT,
1317					    HPC3130_ATTN_OFF);
1318				}
1319
1320				hpc3130_p->present[slot] = present;
1321				hpc3130_p->events[slot] |=
1322				    HPC3130_IEVENT_OCCUPANCY;
1323			} else {
1324				ASSERT(slot_type == HPC3130_SLOT_TYPE_PCI);
1325
1326				if (!(status & HPC3130_DETECT0)) {
1327					/*
1328					 * Event on the downward
1329					 * stroke of the button.
1330					 */
1331					hpc3130_p->events[slot] |=
1332					    HPC3130_IEVENT_BUTTON;
1333				}
1334			}
1335		}
1336
1337		if (interrupt & (HPC3130_PRSNT1 | HPC3130_PRSNT2)) {
1338			if (slot_type == HPC3130_SLOT_TYPE_SBD) {
1339				if (!(status & HPC3130_PRSNT1)) {
1340					/*
1341					 * Event only on the downward
1342					 * stroke of the button.
1343					 */
1344					hpc3130_p->events[slot] |=
1345					    HPC3130_IEVENT_BUTTON;
1346				}
1347			} else {
1348				ASSERT(slot_type == HPC3130_SLOT_TYPE_PCI);
1349				if ((status & (HPC3130_PRSNT1 |
1350				    HPC3130_PRSNT2)) ==
1351				    (HPC3130_PRSNT1 | HPC3130_PRSNT2)) {
1352
1353					hpc3130_p->present[slot] = B_FALSE;
1354
1355					/* Turn OFF Fault LED */
1356					(void) hpc3130_set_led(hpc3130_p,
1357					    slot,
1358					    HPC3130_LED_FAULT,
1359					    HPC3130_ATTN_OFF);
1360					/* Turn OFF OK-to-remove LED */
1361					(void) hpc3130_set_led(hpc3130_p,
1362					    slot,
1363					    HPC3130_LED_OK2REM,
1364					    HPC3130_ATTN_OFF);
1365				} else {
1366
1367					hpc3130_p->present[slot] = B_TRUE;
1368
1369					/* Turn ON OK-to-remove LED */
1370					(void) hpc3130_set_led(hpc3130_p,
1371					    slot,
1372					    HPC3130_LED_OK2REM,
1373					    HPC3130_ATTN_ON);
1374				}
1375
1376				hpc3130_p->events[slot] |=
1377				    HPC3130_IEVENT_OCCUPANCY;
1378			}
1379		}
1380		if (hpc3130_p->events[slot] &&
1381		    (hpc3130_p->present[slot] == B_TRUE)) {
1382			mutex_exit(&hpc3130_p->hpc3130_mutex);
1383			pollwakeup(&hpc3130_p->pollhead[slot], POLLIN);
1384			mutex_enter(&hpc3130_p->hpc3130_mutex);
1385		}
1386		(void) hpc3130_write(handle, HPC3130_EVENT_STATUS,
1387		    slot, interrupt);
1388	}
1389
1390	mutex_exit(&hpc3130_p->hpc3130_mutex);
1391
1392	return (rc);
1393}
1394
1395static int
1396hpc3130_cpu_init(hpc3130_unit_t *hpc3130_p, int slot, i2c_client_hdl_t handle)
1397{
1398	uint8_t	slot_status;
1399	uint8_t	control_reg;
1400
1401	int	result = HPC_ERR_FAILED;
1402
1403	if (hpc3130_read(handle, HPC3130_STATUS, slot,
1404	    &slot_status)) {
1405		goto out;
1406	}
1407
1408	if (hpc3130_read(handle, HPC3130_CONTROL, slot,
1409	    &control_reg)) {
1410		goto out;
1411	}
1412
1413	/*
1414	 * For the CPU slots, the DETECT[0] pin on the HPC3130
1415	 * goes low when a CPU module is in the slot. Pulled
1416	 * high otherwise.
1417	 */
1418	if (slot_status & HPC3130_DETECT0) {
1419		D1CMN_ERR((CE_NOTE, "hpc3130_cpu_init(): "
1420		    "[0x%x]Power off....[%d]",
1421		    slot_status, slot));
1422		control_reg = control_reg & ~HPC3130_SLTPWRCTL;
1423	} else {
1424		D1CMN_ERR((CE_NOTE, "hpc3130_cpu_init(): "
1425		    "[0x%x]Power LEFT on!!!....[%d]",
1426		    slot_status, slot));
1427		hpc3130_p->present[slot] = B_TRUE;
1428		control_reg = control_reg | HPC3130_SLTPWRCTL;
1429
1430	}
1431
1432	/*
1433	 * Set the control register accordingly
1434	 */
1435	if (hpc3130_write(handle, HPC3130_CONTROL,
1436	    slot, control_reg) != DDI_SUCCESS) {
1437		goto out;
1438	}
1439
1440	result = DDI_SUCCESS;
1441out:
1442
1443	return (result);
1444}
1445
1446static int
1447hpc3130_debounce_status(i2c_client_hdl_t handle,
1448    int slot, uint8_t *status)
1449{
1450	int	count, limit;
1451	uint8_t	old;
1452
1453	ASSERT(status);
1454
1455	/*
1456	 * Get HPC3130_DEBOUNCE_COUNT consecutive equal
1457	 * readings from the status register
1458	 */
1459
1460	count = 0; limit = 0; old = 0xff;
1461	do {
1462		if (hpc3130_read(handle, HPC3130_STATUS,
1463		    slot, status)) {
1464			return (DDI_FAILURE);
1465		}
1466		if (old != *status) {
1467			count = 0;
1468		} else {
1469			count += 1;
1470		}
1471
1472		limit += 1;
1473		old = *status;
1474
1475	} while (count < HPC3130_DEBOUNCE_COUNT &&
1476	    limit < HPC3130_DEBOUNCE_LIMIT);
1477
1478	if (limit == HPC3130_DEBOUNCE_LIMIT) {
1479		return (DDI_FAILURE);
1480	}
1481
1482	return (DDI_SUCCESS);
1483}
1484
1485static int
1486hpc3130_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
1487    void *data, uint_t flags)
1488{
1489	_NOTE(ARGUNUSED(slot_hdl, data, flags))
1490	uint8_t			control;
1491	uint8_t			offset;
1492	uint8_t			config;
1493	uint8_t			status;
1494	hpc3130_unit_t		*hpc3130_p;
1495	i2c_client_hdl_t	handle;
1496	int			i;
1497	int			result = HPC_ERR_FAILED;
1498	hpc3130_slot_type_t	slot_type;
1499	hpc3130_slot_table_entry_t *ste;
1500	char			phys_slot[MAXPATHLEN];
1501	boolean_t		needs_to_be_powered_off = B_FALSE;
1502
1503	hpc3130_callback_arg_t	*info_p = (hpc3130_callback_arg_t *)ops_arg;
1504
1505	/*
1506	 * Callback parameter has specific device handle and offset
1507	 * information in it.
1508	 */
1509
1510	hpc3130_p = (hpc3130_unit_t *)info_p->statep;
1511	ASSERT(hpc3130_p);
1512
1513	mutex_enter(&hpc3130_p->hpc3130_mutex);
1514
1515	handle = (i2c_client_hdl_t)info_p->handle;
1516	offset = info_p->offset;
1517
1518	ste = &hpc3130_p->hpc3130_slot_table[offset];
1519
1520	if (hpc3130_p->slots_are == HPC3130_SLOT_TYPE_SBD) {
1521		DAK_GET_SBD_APID(phys_slot, MAXPATHLEN, offset);
1522	} else {
1523		DAK_GET_PCI_APID(phys_slot, MAXPATHLEN,
1524		    hpc3130_lookup_slot(ste->nexus,
1525		    ste->hpc3130_slot_info.pci_dev_num));
1526	}
1527
1528	ASSERT(ste->hpc3130_slot_handle != NULL);
1529
1530	slot_type = hpc3130_p->slots_are;
1531
1532	if (hpc3130_p->enabled[offset] == B_FALSE) {
1533		cmn_err(CE_WARN, "hot-plug disabled on %s", phys_slot);
1534		goto out;
1535	}
1536
1537	/* Return (do nothing) if power already applied */
1538	if (hpc3130_p->power[offset] == B_TRUE) {
1539		D1CMN_ERR((CE_NOTE, "Slot power already on %s", phys_slot));
1540		mutex_exit(&hpc3130_p->hpc3130_mutex);
1541		return (HPC_SUCCESS);
1542	}
1543
1544	if (hpc3130_read(handle, HPC3130_STATUS, offset,
1545	    &status)) {
1546		goto out;
1547	}
1548
1549	/* Read the slot control register to get current value */
1550	if (hpc3130_read(handle, HPC3130_CONTROL, offset,
1551	    &control)) {
1552		goto out;
1553	}
1554
1555	if (slot_type == HPC3130_SLOT_TYPE_SBD) {
1556
1557		D1CMN_ERR((CE_NOTE, "CPU connect %d control=%x status=%x",
1558		    offset, control, status));
1559
1560		control = control | HPC3130_SLTPWRCTL;
1561		if (hpc3130_write(handle, HPC3130_CONTROL, offset,
1562		    control) != DDI_SUCCESS) {
1563			goto out;
1564		}
1565
1566	} else {
1567
1568		D1CMN_ERR((CE_NOTE, "PCI connect %d", offset));
1569
1570		/*
1571		 * PCI needs special sequencing of the control signals.
1572		 */
1573
1574		if (hpc3130_read(handle, HPC3130_GCR, offset,
1575		    &config)) {
1576			goto out;
1577		}
1578
1579		/* Assert RST to comply with PCI spec. */
1580		control &= ~HPC3130_SLOTRST;
1581		if (hpc3130_write(handle, HPC3130_CONTROL, offset,
1582		    control) != DDI_SUCCESS) {
1583			goto out;
1584		}
1585		drv_usecwait(HPC3130_ADEQUATE_PAUSE);
1586
1587		/* Send the power on signal and verify the result */
1588		control = control | HPC3130_SLTPWRCTL;
1589		if ((hpc3130_write(handle, HPC3130_CONTROL, offset,
1590		    control) != DDI_SUCCESS) ||
1591		    (hpc3130_verify_slot_power(hpc3130_p, handle, offset,
1592		    phys_slot, B_TRUE) == HPC_ERR_FAILED)) {
1593			goto out;
1594		}
1595
1596		/* The slot is now powered on. */
1597
1598		drv_usecwait(HPC3130_ADEQUATE_PAUSE);
1599
1600		/* Extinguish the "OK-to-remove" indicator */
1601		(void) hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_OK2REM,
1602		    HPC3130_ATTN_OFF);
1603
1604		/*
1605		 * Perform bus/card speed check functions.
1606		 */
1607		if (hpc3130_read(handle, HPC3130_STATUS, offset, &status)) {
1608			goto out;
1609		}
1610		if ((config & HPC3130_SYSM66STAT) &&
1611		    !(status & HPC3130_M66EN)) {
1612			cmn_err(CE_WARN, "66Mhz bus can't accept "
1613			    "33Mhz card in %s", phys_slot);
1614			needs_to_be_powered_off = B_TRUE;
1615			goto out;
1616		}
1617		if (!(config & HPC3130_SYSM66STAT) &&
1618		    (status & HPC3130_M66EN)) {
1619			cmn_err(CE_NOTE, "66Mhz capable card throttled "
1620			    "back to 33Mhz in %s", phys_slot);
1621		}
1622
1623		/*
1624		 * Send the connect sequence (see struct connect_sequence)
1625		 */
1626		for (i = 0; i < HPC3130_CONNECT_SEQ_COUNT; i++) {
1627			if (connect_sequence[i].set_bit == B_TRUE) {
1628				control |= connect_sequence[i].value;
1629			} else {
1630				control &= ~connect_sequence[i].value;
1631			}
1632			if (hpc3130_write(handle, HPC3130_CONTROL, offset,
1633			    control) != DDI_SUCCESS) {
1634				goto out;
1635			}
1636			drv_usecwait(HPC3130_ADEQUATE_PAUSE);
1637		}
1638	}
1639
1640	(void) hpc_slot_event_notify(ste->hpc3130_slot_handle,
1641	    HPC_EVENT_SLOT_POWER_ON, 0);
1642
1643	/* Flash the "fault" indicator */
1644	(void) hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_FAULT,
1645	    HPC3130_ATTN_SLO);
1646
1647	result = HPC_SUCCESS;
1648
1649out:
1650	if (needs_to_be_powered_off == B_TRUE) {
1651		/*
1652		 * We are in an error state where the slot is powered on, and
1653		 * it must be powered off.
1654		 */
1655
1656		/* Send the power off signal and verify the result */
1657		control = control & ~HPC3130_SLTPWRCTL;
1658		if ((hpc3130_write(handle, HPC3130_CONTROL, offset,
1659		    control) == DDI_SUCCESS) &&
1660		    (hpc3130_verify_slot_power(hpc3130_p, handle, offset,
1661		    phys_slot, B_FALSE) == HPC_SUCCESS)) {
1662			/* Re-light "OK-to-remove" LED */
1663			(void) hpc3130_set_led(hpc3130_p, offset,
1664			    HPC3130_LED_OK2REM, HPC3130_ATTN_ON);
1665		}
1666	}
1667
1668	mutex_exit(&hpc3130_p->hpc3130_mutex);
1669
1670	return (result);
1671}
1672
1673
1674static int
1675hpc3130_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
1676    void *data, uint_t flags)
1677{
1678	_NOTE(ARGUNUSED(slot_hdl, data, flags))
1679	uint8_t			control;
1680	uint8_t			offset;
1681	i2c_client_hdl_t	handle;
1682	hpc3130_unit_t		*hpc3130_p;
1683	int			result = HPC_ERR_FAILED;
1684	hpc3130_slot_type_t	slot_type;
1685	hpc3130_slot_table_entry_t *ste;
1686	char			phys_slot[MAXPATHLEN];
1687
1688	hpc3130_callback_arg_t	*info_p = (hpc3130_callback_arg_t *)ops_arg;
1689
1690	/*
1691	 * Callback parameter has specific device handle and offset
1692	 * information in it.
1693	 */
1694	hpc3130_p = (hpc3130_unit_t *)info_p->statep;
1695	ASSERT(hpc3130_p);
1696
1697	mutex_enter(&hpc3130_p->hpc3130_mutex);
1698
1699	handle = (i2c_client_hdl_t)info_p->handle;
1700	offset = info_p->offset;
1701
1702	ASSERT(handle == hpc3130_p->hpc3130_hdl);
1703
1704	ste = &hpc3130_p->hpc3130_slot_table[offset];
1705
1706	if (hpc3130_p->slots_are == HPC3130_SLOT_TYPE_SBD) {
1707		DAK_GET_SBD_APID(phys_slot, MAXPATHLEN, offset);
1708	} else {
1709		DAK_GET_PCI_APID(phys_slot, MAXPATHLEN,
1710		    hpc3130_lookup_slot(ste->nexus,
1711		    ste->hpc3130_slot_info.pci_dev_num));
1712	}
1713
1714	ASSERT(ste->hpc3130_slot_handle != NULL);
1715
1716	slot_type = hpc3130_p->slots_are;
1717
1718	/*
1719	 * Read the slot control register to get current value
1720	 */
1721	if (hpc3130_read(handle, HPC3130_CONTROL, offset,
1722	    &control)) {
1723		goto out;
1724	}
1725
1726	if (slot_type == HPC3130_SLOT_TYPE_SBD) {
1727
1728		D1CMN_ERR((CE_NOTE, "CPU disconnect %d", offset));
1729
1730		control = control & ~HPC3130_SLTPWRCTL;
1731		/*
1732		 * Write out the modified control register
1733		 */
1734		if (hpc3130_write(handle, HPC3130_CONTROL, offset,
1735		    control) != DDI_SUCCESS) {
1736			goto out;
1737		}
1738	} else {
1739
1740		D1CMN_ERR((CE_NOTE, "PCI disconnect %d", offset));
1741
1742		control &= ~HPC3130_SLOTRST;
1743		if (hpc3130_write(handle, HPC3130_CONTROL, offset,
1744		    control) != DDI_SUCCESS) {
1745			goto out;
1746		}
1747
1748		control |= HPC3130_BUS_CTL;
1749		if (hpc3130_write(handle, HPC3130_CONTROL, offset,
1750		    control) != DDI_SUCCESS) {
1751			goto out;
1752		}
1753	}
1754
1755	D1CMN_ERR((CE_WARN, "disconnect present[%d]==%d",
1756	    offset, hpc3130_p->present[offset]));
1757
1758	if (hpc3130_verify_slot_power(hpc3130_p, handle, offset,
1759	    phys_slot, B_FALSE) == HPC_ERR_FAILED) {
1760		goto out;
1761	}
1762
1763	(void) hpc_slot_event_notify(ste->hpc3130_slot_handle,
1764	    HPC_EVENT_SLOT_POWER_OFF, 0);
1765
1766	if (hpc3130_p->present[offset] == B_TRUE) {
1767		/*
1768		 * Illuminate the "OK-to-remove" indicator
1769		 * if there is a card in the slot.
1770		 */
1771
1772		(void) hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_OK2REM,
1773		    HPC3130_ATTN_ON);
1774
1775		/*
1776		 * Turn off the "fault" indicator
1777		 */
1778		(void) hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_FAULT,
1779		    HPC3130_ATTN_OFF);
1780	} else {
1781		/*
1782		 * If the slot is being powered off with
1783		 * no cards in there, its at "boot time",
1784		 * put the LEDs in a sane state
1785		 */
1786		if (slot_type == HPC3130_SLOT_TYPE_PCI) {
1787			(void) hpc3130_set_led(hpc3130_p, offset,
1788			    HPC3130_LED_FAULT, HPC3130_ATTN_OFF);
1789			(void) hpc3130_set_led(hpc3130_p, offset,
1790			    HPC3130_LED_OK2REM, HPC3130_ATTN_OFF);
1791		}
1792	}
1793
1794	result = HPC_SUCCESS;
1795out:
1796	mutex_exit(&hpc3130_p->hpc3130_mutex);
1797
1798	return (result);
1799}
1800
1801static int
1802hpc3130_verify_slot_power(hpc3130_unit_t *hpc3130_p, i2c_client_hdl_t handle,
1803    uint8_t offset, char *phys_slot, boolean_t slot_target_state)
1804{
1805	uint8_t			tries = 0;
1806	uint8_t			status;
1807	int			result = HPC_SUCCESS;
1808	clock_t			timeleft;
1809	clock_t			tm = drv_usectohz(300000);
1810	boolean_t		slot_actual_state;
1811	boolean_t		failure = B_FALSE;
1812	hpc3130_slot_table_entry_t *ste;
1813
1814	/* This function is called while holding the hpc3130 mutex. */
1815
1816	/*
1817	 * For slot_target_state and slot_actual_state:
1818	 *    B_TRUE  == the slot is powered on
1819	 *    B_FALSE == the slot is powered off
1820	 */
1821
1822	ste = &hpc3130_p->hpc3130_slot_table[offset];
1823	slot_actual_state = hpc3130_p->power[offset];
1824
1825	while ((slot_actual_state != slot_target_state) &&
1826	    (failure != B_TRUE)) {
1827		timeleft = cv_reltimedwait(&hpc3130_p->hpc3130_cond,
1828		    &hpc3130_p->hpc3130_mutex, tm, TR_CLOCK_TICK);
1829		if (timeleft == -1) {
1830			if (tries++ < HPC3130_POWER_TRIES) {
1831				/*
1832				 * The interrupt was missed - explicitly
1833				 * check the status.
1834				 */
1835				if (hpc3130_read(handle,
1836				    HPC3130_STATUS, offset, &status)) {
1837					failure = B_TRUE;
1838					continue;
1839				}
1840				if (status & HPC3130_PWRGOOD) {
1841					slot_actual_state = B_FALSE;
1842				} else {
1843					slot_actual_state = B_TRUE;
1844				}
1845				hpc3130_p->power[offset] = slot_actual_state;
1846			} else {
1847				/* Too many tries.  We failed. */
1848				failure = B_TRUE;
1849			}
1850		}
1851	}
1852
1853	if (failure == B_TRUE) {
1854		result = HPC_ERR_FAILED;
1855		if (slot_target_state == B_TRUE) {
1856			cmn_err(CE_WARN,
1857			    "Could not power on slot %s", phys_slot);
1858		} else {
1859			cmn_err(CE_WARN,
1860			    "Could not power off slot %s", phys_slot);
1861		}
1862		(void) hpc3130_set_led(hpc3130_p, offset, HPC3130_LED_FAULT,
1863		    HPC3130_ATTN_ON);
1864		(void) hpc_slot_event_notify(ste->hpc3130_slot_handle,
1865		    HPC_EVENT_SLOT_NOT_HEALTHY, 0);
1866	}
1867
1868	return (result);
1869}
1870
1871static int
1872hpc3130_slot_insert(caddr_t ops_arg, hpc_slot_t slot_hdl,
1873    void *data, uint_t flags)
1874{
1875	_NOTE(ARGUNUSED(ops_arg, slot_hdl, data, flags))
1876	return (HPC_ERR_NOTSUPPORTED);
1877}
1878
1879static int
1880hpc3130_slot_remove(caddr_t ops_arg, hpc_slot_t slot_hdl,
1881    void *data, uint_t flags)
1882{
1883	_NOTE(ARGUNUSED(ops_arg, slot_hdl, data, flags))
1884	return (HPC_ERR_NOTSUPPORTED);
1885}
1886
1887static int
1888hpc3130_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
1889    int request, caddr_t arg)
1890{
1891	_NOTE(ARGUNUSED(slot_hdl))
1892	i2c_client_hdl_t	handle;
1893	uint8_t			offset;
1894	uint8_t			state;
1895	hpc_led_info_t		*led_info;
1896	hpc3130_unit_t		*hpc3130_p;
1897	hpc3130_slot_type_t	slot_type;
1898
1899	hpc3130_callback_arg_t	*info_p = (hpc3130_callback_arg_t *)ops_arg;
1900
1901	/*
1902	 * Callback parameter has specific device handle and offset
1903	 * information in it.
1904	 */
1905
1906	hpc3130_p = (hpc3130_unit_t *)info_p->statep;
1907	ASSERT(hpc3130_p);
1908
1909	mutex_enter(&hpc3130_p->hpc3130_mutex);
1910
1911	handle = (i2c_client_hdl_t)info_p->handle;
1912	offset = info_p->offset;
1913
1914	ASSERT(handle == hpc3130_p->hpc3130_hdl);
1915
1916	slot_type = hpc3130_p->slots_are;
1917
1918	switch (request) {
1919		case HPC_CTRL_GET_LED_STATE: {
1920			int led;
1921
1922			led_info = (hpc_led_info_t *)arg;
1923			if (led_info->led != HPC_FAULT_LED &&
1924			    led_info->led != HPC_ATTN_LED) {
1925				D1CMN_ERR((CE_WARN,
1926				    "Only FAULT and ATTN leds allowed"));
1927				mutex_exit(&hpc3130_p->hpc3130_mutex);
1928				return (HPC_ERR_INVALID);
1929			}
1930
1931			if (led_info->led == HPC_FAULT_LED)
1932				led = HPC3130_LED_FAULT;
1933			else
1934				led = HPC3130_LED_OK2REM;
1935
1936			if (hpc3130_get_led(handle, offset, led, &state) !=
1937			    DDI_SUCCESS) {
1938				mutex_exit(&hpc3130_p->hpc3130_mutex);
1939				return (HPC_ERR_FAILED);
1940			}
1941
1942			/* Make sure that no one broke the conversion macros */
1943			ASSERT(state < sizeof (hpc3130_to_hpc_led_map));
1944			ASSERT(state ==
1945			    HPC3130_FROM_HPC_LED(HPC3130_TO_HPC_LED(state)));
1946
1947			led_info->state = HPC3130_TO_HPC_LED(state);
1948		}
1949		break;
1950		case HPC_CTRL_SET_LED_STATE: {
1951			int led;
1952
1953			/*
1954			 * The HPC3130 support modifications to the Fault and
1955			 * Ok-to-remove LEDs.
1956			 */
1957			led_info = (hpc_led_info_t *)arg;
1958			if (led_info->led != HPC_FAULT_LED &&
1959			    led_info->led != HPC_ATTN_LED) {
1960				D1CMN_ERR((CE_WARN,
1961				    "Only FAULT and ATTN leds allowed"));
1962				mutex_exit(&hpc3130_p->hpc3130_mutex);
1963				return (HPC_ERR_INVALID);
1964			}
1965
1966			if (led_info->led == HPC_FAULT_LED)
1967				led = HPC3130_LED_FAULT;
1968			else
1969				led = HPC3130_LED_OK2REM;
1970
1971			state = led_info->state;
1972			if (state >= sizeof (hpc3130_from_hpc_led_map) ||
1973			    (state != HPC3130_TO_HPC_LED(
1974			    HPC3130_FROM_HPC_LED(state)))) {
1975				D1CMN_ERR((CE_WARN,
1976				    "Improper LED value: %d %d", state,
1977				    HPC3130_TO_HPC_LED(
1978				    HPC3130_FROM_HPC_LED(state))));
1979				mutex_exit(&hpc3130_p->hpc3130_mutex);
1980				return (HPC_ERR_INVALID);
1981			}
1982
1983			(void) hpc3130_set_led(hpc3130_p, offset, led,
1984			    HPC3130_FROM_HPC_LED(state));
1985		}
1986		break;
1987		case HPC_CTRL_GET_SLOT_STATE: {
1988			if (hpc3130_p->power[offset] == B_FALSE) {
1989				if (hpc3130_p->present[offset] == B_FALSE) {
1990					*(ap_rstate_t *)arg =
1991					    AP_RSTATE_EMPTY;
1992				} else {
1993					*(ap_rstate_t *)arg =
1994					    AP_RSTATE_DISCONNECTED;
1995				}
1996			} else {
1997				*(ap_rstate_t *)arg =
1998				    AP_RSTATE_CONNECTED;
1999			}
2000		}
2001		break;
2002		case HPC_CTRL_GET_BOARD_TYPE: {
2003			*(hpc_board_type_t *)arg =
2004			    (slot_type == HPC3130_SLOT_TYPE_SBD ?
2005			    HPC_BOARD_UNKNOWN : HPC_BOARD_PCI_HOTPLUG);
2006		}
2007		break;
2008		case HPC_CTRL_DEV_CONFIG_START:
2009		case HPC_CTRL_DEV_UNCONFIG_START:
2010			(void) hpc3130_set_led(hpc3130_p, offset,
2011			    HPC3130_LED_FAULT, HPC3130_ATTN_SLO);
2012		break;
2013		case HPC_CTRL_DEV_CONFIG_FAILURE:
2014			(void) hpc3130_set_led(hpc3130_p, offset,
2015			    HPC3130_LED_FAULT, HPC3130_ATTN_ON);
2016		break;
2017		case HPC_CTRL_DEV_CONFIGURED:
2018			(void) hpc3130_set_led(hpc3130_p, offset,
2019			    HPC3130_LED_FAULT, HPC3130_ATTN_OFF);
2020			hpc3130_p->present[offset] = B_TRUE;
2021		break;
2022		case HPC_CTRL_DEV_UNCONFIGURED:
2023			if (hpc3130_p->power[offset] == B_TRUE) {
2024				(void) hpc3130_set_led(hpc3130_p, offset,
2025				    HPC3130_LED_FAULT, HPC3130_ATTN_SLO);
2026			} else {
2027				(void) hpc3130_set_led(hpc3130_p, offset,
2028				    HPC3130_LED_FAULT, HPC3130_ATTN_OFF);
2029			}
2030		break;
2031		case HPC_CTRL_DISABLE_SLOT: {
2032			hpc3130_p->enabled[offset] = B_FALSE;
2033		}
2034		break;
2035		case HPC_CTRL_ENABLE_SLOT: {
2036			hpc3130_p->enabled[offset] = B_TRUE;
2037		}
2038		break;
2039		default:
2040			mutex_exit(&hpc3130_p->hpc3130_mutex);
2041			return (HPC_ERR_FAILED);
2042	}
2043	mutex_exit(&hpc3130_p->hpc3130_mutex);
2044	return (HPC_SUCCESS);
2045}
2046
2047int
2048hpc3130_lookup_slot(char *nexus, int pcidev)
2049{
2050	int	i = 0;
2051
2052	while (i < HPC3130_LOOKUP_SLOTS &&
2053	    (slot_translate[i].pcidev != pcidev ||
2054	    strcmp(nexus, slot_translate[i].nexus) != 0))
2055		i++;
2056	ASSERT(i != HPC3130_LOOKUP_SLOTS);
2057	return (i);
2058}
2059
2060/*
2061 * A routine to convert a number (represented as a string) to
2062 * the integer value it represents.
2063 */
2064
2065static int
2066isdigit(int ch)
2067{
2068	return (ch >= '0' && ch <= '9');
2069}
2070
2071#define	isspace(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
2072#define	bad(val)	(val == NULL || !isdigit(*val))
2073
2074static int
2075hpc3130_atoi(const char *p)
2076{
2077	int n;
2078	int c, neg = 0;
2079
2080	if (!isdigit(c = *p)) {
2081		while (isspace(c))
2082			c = *++p;
2083		switch (c) {
2084		case '-':
2085			neg++;
2086			/* FALLTHROUGH */
2087		case '+':
2088			c = *++p;
2089		}
2090		if (!isdigit(c))
2091			return (0);
2092	}
2093	for (n = '0' - c; isdigit(c = *++p); ) {
2094		n *= 10; /* two steps to avoid unnecessary overflow */
2095		n += '0' - c; /* accum neg to avoid surprises at MAX */
2096	}
2097	return (neg ? n : -n);
2098}
2099