xref: /illumos-gate/usr/src/uts/sun4u/io/pmugpio.c (revision 1bde0be88b435e0d708ac4f377f1a521530f2ebd)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/conf.h>
31 #include <sys/ddi.h>
32 #include <sys/sunddi.h>
33 #include <sys/modctl.h>
34 #include <sys/ddi_impldefs.h>
35 #include <sys/kmem.h>
36 #include <sys/devops.h>
37 
38 /*
39  * The pmugpio driver supports ALOM GPIO bits for resetSC and
40  * watchdog heartbeat on all relevant platforms.  Historically,
41  * pmugpio is a leaf off the Chalupa pmubus.  In addition to
42  * this support the pmugpio driver has been modified to support
43  * Minneapolis/Boston Controller (MBC) FPGA GPIO and Seattle CPLD
44  * GPIO.
45  */
46 
47 typedef enum {
48 	PMUGPIO_MBC,		/* Boston MBC FPGA GPIO - 8-bit */
49 	PMUGPIO_CPLD,		/* Seattle CPLD GPIO - 8-bit */
50 	PMUGPIO_OTHER		/* Chalupa - 8-bit */
51 } pmugpio_access_type_t;
52 
53 /*
54  * FWARC 2005/686: gpio device compatible property
55  */
56 #define	PMUGPIO_DEVICE_TYPE "gpio-device-type"
57 
58 /*
59  * CPLD GPIO Register defines.
60  */
61 #define	CPLD_RESET_SC		0x01	/* Reset SC */
62 #define	CPLD_WATCHDOG		0x02	/* Watchdog */
63 
64 #define	CPLD_RESET_DELAY	3	/* microsecond delay */
65 
66 /*
67  * MBC FPGA CSR defines.
68  */
69 #define	MBC_PPC_RESET		0x10	/* Reset ALOM */
70 #define	MBC_WATCHDOG		0x40	/* Watchdog heartbeat bit */
71 
72 /*
73  * Time periods, in nanoseconds
74  */
75 #define	PMUGPIO_TWO_SEC		2000000000LL
76 
77 static	dev_info_t	*pmugpio_dip;
78 
79 typedef struct pmugpio_state {
80 	uint8_t			*pmugpio_reset_reg;
81 	ddi_acc_handle_t	pmugpio_reset_reg_handle;
82 	uint8_t			*pmugpio_watchdog_reg;
83 	ddi_acc_handle_t	pmugpio_watchdog_reg_handle;
84 	hrtime_t		hw_last_pat;
85 	pmugpio_access_type_t	access_type;
86 } pmugpio_state_t;
87 
88 static void *pmugpio_statep;
89 
90 static int pmugpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
91 static int pmugpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
92 static int pmugpio_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
93 		void **result);
94 static int pmugpio_map_regs(dev_info_t *, pmugpio_state_t *);
95 
96 struct cb_ops pmugpio_cb_ops = {
97 	nulldev,	/* open  */
98 	nulldev,	/* close */
99 	nulldev,	/* strategy */
100 	nulldev,	/* print */
101 	nulldev,	/* dump */
102 	nulldev,	/* read */
103 	nulldev,	/* write */
104 	nulldev,	/* ioctl */
105 	nulldev,	/* devmap */
106 	nulldev,	/* mmap */
107 	nulldev,	/* segmap */
108 	nochpoll,	/* poll */
109 	ddi_prop_op,	/* cb_prop_op */
110 	NULL,		/* streamtab  */
111 	D_MP | D_NEW
112 };
113 
114 static struct dev_ops pmugpio_ops = {
115 	DEVO_REV,		/* Devo_rev */
116 	0,			/* Refcnt */
117 	pmugpio_info,		/* Info */
118 	nulldev,		/* Identify */
119 	nulldev,		/* Probe */
120 	pmugpio_attach,		/* Attach */
121 	pmugpio_detach,		/* Detach */
122 	nodev,			/* Reset */
123 	&pmugpio_cb_ops,		/* Driver operations */
124 	0,			/* Bus operations */
125 	NULL			/* Power */
126 };
127 
128 static struct modldrv modldrv = {
129 	&mod_driverops, 		/* This one is a driver */
130 	"Pmugpio Driver %I%", 		/* Name of the module. */
131 	&pmugpio_ops,			/* Driver ops */
132 };
133 
134 static struct modlinkage modlinkage = {
135 	MODREV_1, (void *)&modldrv, NULL
136 };
137 
138 int
139 _init(void)
140 {
141 	int error;
142 
143 	/* Initialize the soft state structures */
144 	if ((error = ddi_soft_state_init(&pmugpio_statep,
145 	    sizeof (pmugpio_state_t), 1)) != 0) {
146 		return (error);
147 	}
148 
149 	/* Install the loadable module */
150 	if ((error = mod_install(&modlinkage)) != 0) {
151 		ddi_soft_state_fini(&pmugpio_statep);
152 	}
153 	return (error);
154 }
155 
156 int
157 _info(struct modinfo *modinfop)
158 {
159 	return (mod_info(&modlinkage, modinfop));
160 }
161 
162 int
163 _fini(void)
164 {
165 	int error;
166 
167 	error = mod_remove(&modlinkage);
168 	if (error == 0) {
169 		/* Release per module resources */
170 		ddi_soft_state_fini(&pmugpio_statep);
171 	}
172 	return (error);
173 }
174 
175 static int
176 pmugpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
177 {
178 	int		instance;
179 	pmugpio_state_t	*pmugpio_ptr = NULL;
180 
181 	switch (cmd) {
182 	case DDI_ATTACH:
183 		break;
184 	case DDI_RESUME:
185 		return (DDI_SUCCESS);
186 	default:
187 		return (DDI_FAILURE);
188 	}
189 
190 	/* Get the instance and create soft state */
191 	instance = ddi_get_instance(dip);
192 	if (ddi_soft_state_zalloc(pmugpio_statep, instance) != 0) {
193 		return (DDI_FAILURE);
194 	}
195 	pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance);
196 	if (pmugpio_ptr == NULL) {
197 		return (DDI_FAILURE);
198 	}
199 
200 	if (pmugpio_map_regs(dip, pmugpio_ptr) != DDI_SUCCESS) {
201 		ddi_soft_state_free(pmugpio_statep, instance);
202 		return (DDI_FAILURE);
203 	}
204 
205 	/* Display information in the banner */
206 	ddi_report_dev(dip);
207 
208 	/* Save the dip */
209 	pmugpio_dip = dip;
210 
211 	return (DDI_SUCCESS);
212 }
213 
214 /* ARGSUSED */
215 static int
216 pmugpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
217 {
218 	/* Pointer to soft state */
219 	switch (cmd) {
220 	case DDI_SUSPEND:
221 		return (DDI_SUCCESS);
222 	default:
223 		return (DDI_FAILURE);
224 	}
225 }
226 
227 /* ARGSUSED */
228 static int
229 pmugpio_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
230 		void *arg, void **result)
231 {
232 	dev_t dev;
233 	int instance, error;
234 
235 	switch (infocmd) {
236 	case DDI_INFO_DEVT2DEVINFO:
237 		*result = (void *)pmugpio_dip;
238 		error = DDI_SUCCESS;
239 		break;
240 	case DDI_INFO_DEVT2INSTANCE:
241 		dev = (dev_t)arg;
242 		instance = getminor(dev);
243 		*result = (void *)(uintptr_t)instance;
244 		error = DDI_SUCCESS;
245 		break;
246 	default:
247 		error = DDI_FAILURE;
248 	}
249 	return (error);
250 }
251 
252 void
253 pmugpio_watchdog_pat(void)
254 {
255 	dev_info_t *dip = pmugpio_dip;
256 	int instance;
257 	pmugpio_state_t *pmugpio_ptr;
258 	hrtime_t now;
259 	uint8_t value;
260 
261 	if (dip == NULL) {
262 		return;
263 	}
264 	instance = ddi_get_instance(dip);
265 	pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance);
266 	if (pmugpio_ptr == NULL) {
267 		return;
268 	}
269 	/*
270 	 * The RMC can read interrupts either high to low OR low to high. As
271 	 * a result all that needs to happen is that when we hit the time to
272 	 * send an signal we simply need to change the state.
273 	 */
274 	now = gethrtime();
275 	if ((now - pmugpio_ptr->hw_last_pat) >= PMUGPIO_TWO_SEC) {
276 		/*
277 		 * fetch current reg value and invert it
278 		 */
279 		switch (pmugpio_ptr->access_type) {
280 		case PMUGPIO_CPLD:
281 			value = (CPLD_WATCHDOG ^
282 			    ddi_get8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
283 				pmugpio_ptr->pmugpio_watchdog_reg));
284 
285 			ddi_put8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
286 			    pmugpio_ptr->pmugpio_watchdog_reg, value);
287 			break;
288 
289 		case PMUGPIO_MBC:
290 			value = (uint8_t)(MBC_WATCHDOG ^
291 			    ddi_get8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
292 			    pmugpio_ptr->pmugpio_watchdog_reg));
293 
294 			ddi_put8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
295 			    pmugpio_ptr->pmugpio_watchdog_reg, value);
296 			break;
297 
298 		case PMUGPIO_OTHER:
299 			value = (uint8_t)(0xff ^
300 			    ddi_get8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
301 			    pmugpio_ptr->pmugpio_watchdog_reg));
302 
303 			ddi_put8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
304 			    pmugpio_ptr->pmugpio_watchdog_reg, value);
305 			break;
306 
307 		default:
308 			cmn_err(CE_WARN, "pmugpio_watchdog_pat: Invalid type");
309 		}
310 		pmugpio_ptr->hw_last_pat = now;
311 	}
312 }
313 
314 void
315 pmugpio_reset(void)
316 {
317 	dev_info_t *dip = pmugpio_dip;
318 	int instance;
319 	pmugpio_state_t *pmugpio_ptr;
320 	uint8_t value;
321 
322 	if (dip == NULL) {
323 		return;
324 	}
325 	instance = ddi_get_instance(dip);
326 	pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance);
327 	if (pmugpio_ptr == NULL) {
328 		return;
329 	}
330 
331 	/*
332 	 * For Chalupa, turn all bits on then off again - pmubus nexus
333 	 * will ensure that only unmasked bit is affected.
334 	 * For CPLD and MBC, turn just reset bit on, then off.
335 	 */
336 	switch (pmugpio_ptr->access_type) {
337 	case PMUGPIO_CPLD:
338 		value = ddi_get8(pmugpio_ptr->pmugpio_reset_reg_handle,
339 		    pmugpio_ptr->pmugpio_reset_reg);
340 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
341 		    pmugpio_ptr->pmugpio_reset_reg, (value | CPLD_RESET_SC));
342 
343 		drv_usecwait(CPLD_RESET_DELAY);
344 
345 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
346 		    pmugpio_ptr->pmugpio_reset_reg, (value & ~CPLD_RESET_SC));
347 		break;
348 
349 	case PMUGPIO_MBC:
350 		value = ddi_get8(pmugpio_ptr->pmugpio_reset_reg_handle,
351 		    pmugpio_ptr->pmugpio_reset_reg);
352 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
353 		    pmugpio_ptr->pmugpio_reset_reg,
354 			(value | MBC_PPC_RESET));
355 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
356 		    pmugpio_ptr->pmugpio_reset_reg,
357 			(value & ~MBC_PPC_RESET));
358 		break;
359 
360 	case PMUGPIO_OTHER:
361 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
362 		    pmugpio_ptr->pmugpio_reset_reg, ~0);
363 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
364 		    pmugpio_ptr->pmugpio_reset_reg, 0);
365 		break;
366 
367 	default:
368 		cmn_err(CE_WARN, "pmugpio_reset: Invalid type");
369 	}
370 }
371 
372 static int
373 pmugpio_map_regs(dev_info_t *dip, pmugpio_state_t *pmugpio_ptr)
374 {
375 	ddi_device_acc_attr_t attr;
376 	char *pmugpio_type;
377 
378 	/* The host controller will be little endian */
379 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
380 	attr.devacc_attr_endian_flags  = DDI_STRUCTURE_LE_ACC;
381 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
382 
383 	/*
384 	 * Determine access type per FWARC 2005/686.
385 	 * For Boston and Seattle, the OBP gpio device contains a property
386 	 * named "gpio-device-type".
387 	 *
388 	 * Boston:  gpio-device-type = SUNW,mbc
389 	 * Seattle: gpio-device-type = SUNW,cpld
390 	 *
391 	 * If this property does not exist, we are a legacy Chalupa.
392 	 */
393 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
394 	    PMUGPIO_DEVICE_TYPE, &pmugpio_type) == DDI_PROP_SUCCESS) {
395 		if (strcmp(pmugpio_type, "SUNW,mbc") == 0)
396 			pmugpio_ptr->access_type = PMUGPIO_MBC;
397 		else if (strcmp(pmugpio_type, "SUNW,cpld") == 0)
398 			pmugpio_ptr->access_type = PMUGPIO_CPLD;
399 		else {
400 			cmn_err(CE_WARN, "unexpected gpio-device-type: %s\n",
401 			    pmugpio_type);
402 			ddi_prop_free(pmugpio_type);
403 			return (DDI_FAILURE);
404 		}
405 		ddi_prop_free(pmugpio_type);
406 	}
407 	else
408 		pmugpio_ptr->access_type = PMUGPIO_OTHER;
409 
410 	switch (pmugpio_ptr->access_type) {
411 	case PMUGPIO_CPLD:
412 	case PMUGPIO_MBC:
413 		if (ddi_regs_map_setup(dip, 0,
414 		    (caddr_t *)&pmugpio_ptr->pmugpio_reset_reg, 0, 1, &attr,
415 		    &pmugpio_ptr->pmugpio_reset_reg_handle) != DDI_SUCCESS)
416 			return (DDI_FAILURE);
417 		/* MBC and CPLD have reset and watchdog bits in same reg. */
418 		pmugpio_ptr->pmugpio_watchdog_reg_handle =
419 			pmugpio_ptr->pmugpio_reset_reg_handle;
420 		pmugpio_ptr->pmugpio_watchdog_reg =
421 			pmugpio_ptr->pmugpio_reset_reg;
422 		break;
423 
424 	case PMUGPIO_OTHER:
425 		if (ddi_regs_map_setup(dip, 1,
426 		    (caddr_t *)&pmugpio_ptr->pmugpio_watchdog_reg, 0, 1, &attr,
427 		    &pmugpio_ptr->pmugpio_watchdog_reg_handle) != DDI_SUCCESS) {
428 			return (DDI_FAILURE);
429 		}
430 		if (ddi_regs_map_setup(dip, 0,
431 		    (caddr_t *)&pmugpio_ptr->pmugpio_reset_reg, 0, 1, &attr,
432 		    &pmugpio_ptr->pmugpio_reset_reg_handle) != DDI_SUCCESS) {
433 			ddi_regs_map_free(
434 				&pmugpio_ptr->pmugpio_watchdog_reg_handle);
435 			return (DDI_FAILURE);
436 		}
437 		break;
438 
439 	default:
440 		cmn_err(CE_WARN, "pmugpio_map_regs: Invalid type");
441 		return (DDI_FAILURE);
442 	}
443 
444 	return (DDI_SUCCESS);
445 }
446