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
57 static 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
71 struct tuple {
72 	uint8_t reg;
73 	uint8_t val;
74 };
75 
76 struct connect_command {
77 	boolean_t set_bit;
78 	uint8_t value;
79 };
80 
81 struct 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 
90 struct tuple cpu_sequence [] =
91 {
92 	{HPC3130_INTERRUPT,
93 		HPC3130_PRSNT1 | HPC3130_DETECT0},
94 	{HPC3130_EVENT_STATUS, 0xff},
95 	{HPC3130_NO_REGISTER, 0},
96 };
97 
98 struct 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 
112 struct 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  */
121 static 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 
139 static int control_slot_control = HPC3130_SLOT_CONTROL_ENABLE;
140 
141 hpc3130_unit_t *hpc3130soft_statep;
142 
143 static int hpc3130_atoi(const char *);
144 int hpc3130_lookup_slot(char *, int);
145 
146 static int hpc3130_init(dev_info_t *, struct tuple *);
147 static uint_t hpc3130_hard_intr(caddr_t);
148 
149 static int hpc3130_cpu_init(hpc3130_unit_t *, int, i2c_client_hdl_t);
150 static int hpc3130_debounce_status(i2c_client_hdl_t, int, uint8_t *);
151 static int hpc3130_read(i2c_client_hdl_t, uint8_t, uint8_t, uint8_t *);
152 static int hpc3130_write(i2c_client_hdl_t, uint8_t, uint8_t, uint8_t);
153 static int hpc3130_rw(i2c_client_hdl_t, uint8_t, boolean_t, uint8_t *);
154 
155 static int hpc3130_do_attach(dev_info_t *);
156 static int hpc3130_do_detach(dev_info_t *);
157 static int hpc3130_do_resume(void);
158 static int hpc3130_do_suspend();
159 static int hpc3130_get(intptr_t, int, hpc3130_unit_t *, int);
160 static int hpc3130_set(intptr_t, int, hpc3130_unit_t *, int);
161 
162 static int hpc3130_slot_connect(caddr_t, hpc_slot_t, void *, uint_t);
163 static int hpc3130_slot_disconnect(caddr_t, hpc_slot_t, void *, uint_t);
164 static int hpc3130_verify_slot_power(hpc3130_unit_t *, i2c_client_hdl_t,
165 					uint8_t, char *, boolean_t);
166 static int hpc3130_slot_insert(caddr_t, hpc_slot_t, void *, uint_t);
167 static int hpc3130_slot_remove(caddr_t, hpc_slot_t, void *, uint_t);
168 static int hpc3130_slot_control(caddr_t, hpc_slot_t, int, caddr_t);
169 /*
170  * cb ops
171  */
172 static int hpc3130_open(dev_t *, int, int, cred_t *);
173 static int hpc3130_close(dev_t, int, int, cred_t *);
174 static int hpc3130_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
175 static int hpc3130_poll(dev_t dev, short events, int anyyet,  short
176 			*reventsp, struct pollhead **phpp);
177 
178 static 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  */
202 static int hpc3130_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
203 		void **result);
204 static int hpc3130_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
205 static int hpc3130_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
206 
207 static 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 
222 extern struct mod_ops mod_driverops;
223 
224 static struct modldrv hpc3130_modldrv = {
225 	&mod_driverops,			/* type of module - driver */
226 	"Hotplug controller driver",
227 	&hpc3130_ops
228 };
229 
230 static struct modlinkage hpc3130_modlinkage = {
231 	MODREV_1,
232 	&hpc3130_modldrv,
233 	0
234 };
235 
236 int
_init(void)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 
249 int
_fini(void)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 
261 int
_info(struct modinfo * modinfop)262 _info(struct modinfo *modinfop)
263 {
264 	return (mod_info(&hpc3130_modlinkage, modinfop));
265 }
266 
267 static int
hpc3130_open(dev_t * devp,int flags,int otyp,cred_t * credp)268 hpc3130_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 
309 static int
hpc3130_close(dev_t dev,int flags,int otyp,cred_t * credp)310 hpc3130_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 
333 static int
hpc3130_get(intptr_t arg,int reg,hpc3130_unit_t * unitp,int mode)334 hpc3130_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 
376 static int
hpc3130_set(intptr_t arg,int reg,hpc3130_unit_t * unitp,int mode)377 hpc3130_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 
414 static int
hpc3130_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)415 hpc3130_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 
687 static int
hpc3130_poll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)688 hpc3130_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 */
715 static int
hpc3130_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)716 hpc3130_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 
730 static int
hpc3130_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)731 hpc3130_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 
743 static int
hpc3130_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)744 hpc3130_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 
756 static int
hpc3130_do_attach(dev_info_t * dip)757 hpc3130_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 
997 failout4:
998 	hpc_free_slot_ops(hpc3130_p->hpc3130_slot_ops);
999 failout3:
1000 	i2c_client_unregister(hpc3130_p->hpc3130_hdl);
1001 failout2:
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));
1006 failout1:
1007 	kmem_free(hpc3130_p->hpc3130_slot_table_data,
1008 	    hpc3130_p->hpc3130_slot_table_size);
1009 failout0:
1010 	ddi_soft_state_free(hpc3130soft_statep, instance);
1011 
1012 	return (DDI_FAILURE);
1013 }
1014 
1015 static int
hpc3130_do_resume()1016 hpc3130_do_resume()
1017 {
1018 	return (DDI_SUCCESS);
1019 }
1020 
1021 static int
hpc3130_do_suspend()1022 hpc3130_do_suspend()
1023 {
1024 	return (DDI_SUCCESS);
1025 }
1026 
1027 static int
hpc3130_do_detach(dev_info_t * dip)1028 hpc3130_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 
1066 int
hpc3130_set_led(hpc3130_unit_t * unitp,int slot,int led,uint8_t value)1067 hpc3130_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 
1113 int
hpc3130_get_led(i2c_client_hdl_t handle,int slot,int led,uint8_t * value)1114 hpc3130_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 
1127 static int
hpc3130_write(i2c_client_hdl_t handle,uint8_t offset,uint8_t port,uint8_t data)1128 hpc3130_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 
1138 static int
hpc3130_read(i2c_client_hdl_t handle,uint8_t offset,uint8_t port,uint8_t * data)1139 hpc3130_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 
1149 static int
hpc3130_rw(i2c_client_hdl_t handle,uint8_t reg,boolean_t write,uint8_t * data)1150 hpc3130_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  */
1202 static int
hpc3130_init(dev_info_t * dip,struct tuple * init_sequence)1203 hpc3130_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;
1244 out:
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  */
1254 static uint_t
hpc3130_hard_intr(caddr_t arg)1255 hpc3130_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 
1395 static int
hpc3130_cpu_init(hpc3130_unit_t * hpc3130_p,int slot,i2c_client_hdl_t handle)1396 hpc3130_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;
1441 out:
1442 
1443 	return (result);
1444 }
1445 
1446 static int
hpc3130_debounce_status(i2c_client_hdl_t handle,int slot,uint8_t * status)1447 hpc3130_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 
1485 static int
hpc3130_slot_connect(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)1486 hpc3130_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 
1649 out:
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 
1674 static int
hpc3130_slot_disconnect(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)1675 hpc3130_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;
1795 out:
1796 	mutex_exit(&hpc3130_p->hpc3130_mutex);
1797 
1798 	return (result);
1799 }
1800 
1801 static int
hpc3130_verify_slot_power(hpc3130_unit_t * hpc3130_p,i2c_client_hdl_t handle,uint8_t offset,char * phys_slot,boolean_t slot_target_state)1802 hpc3130_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 
1871 static int
hpc3130_slot_insert(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)1872 hpc3130_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 
1879 static int
hpc3130_slot_remove(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)1880 hpc3130_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 
1887 static int
hpc3130_slot_control(caddr_t ops_arg,hpc_slot_t slot_hdl,int request,caddr_t arg)1888 hpc3130_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 
2047 int
hpc3130_lookup_slot(char * nexus,int pcidev)2048 hpc3130_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 
2065 static int
isdigit(int ch)2066 isdigit(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 
2074 static int
hpc3130_atoi(const char * p)2075 hpc3130_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