xref: /titanic_44/usr/src/uts/sun4u/io/pmugpio.c (revision f56c1286e5113aa46bd6e723da14d30c123153f2)
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  * CPLD GPIO Register defines.
55  */
56 #define	CPLD_RESET_SC		0x01	/* Reset SC */
57 #define	CPLD_WATCHDOG		0x02	/* Watchdog */
58 
59 #define	CPLD_RESET_DELAY	3	/* microsecond delay */
60 
61 /*
62  * MBC FPGA CSR defines.
63  */
64 #define	MBC_PPC_RESET		0x10	/* Reset ALOM */
65 #define	MBC_WATCHDOG		0x40	/* Watchdog heartbeat bit */
66 
67 /*
68  * Time periods, in nanoseconds
69  */
70 #define	PMUGPIO_TWO_SEC		2000000000LL
71 
72 static	dev_info_t	*pmugpio_dip;
73 
74 typedef struct pmugpio_state {
75 	uint8_t			*pmugpio_reset_reg;
76 	ddi_acc_handle_t	pmugpio_reset_reg_handle;
77 	uint8_t			*pmugpio_watchdog_reg;
78 	ddi_acc_handle_t	pmugpio_watchdog_reg_handle;
79 	hrtime_t		hw_last_pat;
80 	pmugpio_access_type_t	access_type;
81 } pmugpio_state_t;
82 
83 static void *pmugpio_statep;
84 
85 static int pmugpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
86 static int pmugpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
87 static int pmugpio_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
88 		void **result);
89 static int pmugpio_map_regs(dev_info_t *, pmugpio_state_t *);
90 
91 struct cb_ops pmugpio_cb_ops = {
92 	nulldev,	/* open  */
93 	nulldev,	/* close */
94 	nulldev,	/* strategy */
95 	nulldev,	/* print */
96 	nulldev,	/* dump */
97 	nulldev,	/* read */
98 	nulldev,	/* write */
99 	nulldev,	/* ioctl */
100 	nulldev,	/* devmap */
101 	nulldev,	/* mmap */
102 	nulldev,	/* segmap */
103 	nochpoll,	/* poll */
104 	ddi_prop_op,	/* cb_prop_op */
105 	NULL,		/* streamtab  */
106 	D_MP | D_NEW
107 };
108 
109 static struct dev_ops pmugpio_ops = {
110 	DEVO_REV,		/* Devo_rev */
111 	0,			/* Refcnt */
112 	pmugpio_info,		/* Info */
113 	nulldev,		/* Identify */
114 	nulldev,		/* Probe */
115 	pmugpio_attach,		/* Attach */
116 	pmugpio_detach,		/* Detach */
117 	nodev,			/* Reset */
118 	&pmugpio_cb_ops,		/* Driver operations */
119 	0,			/* Bus operations */
120 	NULL			/* Power */
121 };
122 
123 static struct modldrv modldrv = {
124 	&mod_driverops, 		/* This one is a driver */
125 	"Pmugpio Driver %I%", 		/* Name of the module. */
126 	&pmugpio_ops,			/* Driver ops */
127 };
128 
129 static struct modlinkage modlinkage = {
130 	MODREV_1, (void *)&modldrv, NULL
131 };
132 
133 int
134 _init(void)
135 {
136 	int error;
137 
138 	/* Initialize the soft state structures */
139 	if ((error = ddi_soft_state_init(&pmugpio_statep,
140 	    sizeof (pmugpio_state_t), 1)) != 0) {
141 		return (error);
142 	}
143 
144 	/* Install the loadable module */
145 	if ((error = mod_install(&modlinkage)) != 0) {
146 		ddi_soft_state_fini(&pmugpio_statep);
147 	}
148 	return (error);
149 }
150 
151 int
152 _info(struct modinfo *modinfop)
153 {
154 	return (mod_info(&modlinkage, modinfop));
155 }
156 
157 int
158 _fini(void)
159 {
160 	int error;
161 
162 	error = mod_remove(&modlinkage);
163 	if (error == 0) {
164 		/* Release per module resources */
165 		ddi_soft_state_fini(&pmugpio_statep);
166 	}
167 	return (error);
168 }
169 
170 static int
171 pmugpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
172 {
173 	int		instance;
174 	pmugpio_state_t	*pmugpio_ptr = NULL;
175 
176 	switch (cmd) {
177 	case DDI_ATTACH:
178 		break;
179 	case DDI_RESUME:
180 		return (DDI_SUCCESS);
181 	default:
182 		return (DDI_FAILURE);
183 	}
184 
185 	/* Get the instance and create soft state */
186 	instance = ddi_get_instance(dip);
187 	if (ddi_soft_state_zalloc(pmugpio_statep, instance) != 0) {
188 		return (DDI_FAILURE);
189 	}
190 	pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance);
191 	if (pmugpio_ptr == NULL) {
192 		return (DDI_FAILURE);
193 	}
194 
195 	if (pmugpio_map_regs(dip, pmugpio_ptr) != DDI_SUCCESS) {
196 		ddi_soft_state_free(pmugpio_statep, instance);
197 		return (DDI_FAILURE);
198 	}
199 
200 	/* Display information in the banner */
201 	ddi_report_dev(dip);
202 
203 	/* Save the dip */
204 	pmugpio_dip = dip;
205 
206 	return (DDI_SUCCESS);
207 }
208 
209 /* ARGSUSED */
210 static int
211 pmugpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
212 {
213 	/* Pointer to soft state */
214 	switch (cmd) {
215 	case DDI_SUSPEND:
216 		return (DDI_SUCCESS);
217 	default:
218 		return (DDI_FAILURE);
219 	}
220 }
221 
222 /* ARGSUSED */
223 static int
224 pmugpio_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
225 		void *arg, void **result)
226 {
227 	dev_t dev;
228 	int instance, error;
229 
230 	switch (infocmd) {
231 	case DDI_INFO_DEVT2DEVINFO:
232 		*result = (void *)pmugpio_dip;
233 		error = DDI_SUCCESS;
234 		break;
235 	case DDI_INFO_DEVT2INSTANCE:
236 		dev = (dev_t)arg;
237 		instance = getminor(dev);
238 		*result = (void *)(uintptr_t)instance;
239 		error = DDI_SUCCESS;
240 		break;
241 	default:
242 		error = DDI_FAILURE;
243 	}
244 	return (error);
245 }
246 
247 void
248 pmugpio_watchdog_pat(void)
249 {
250 	dev_info_t *dip = pmugpio_dip;
251 	int instance;
252 	pmugpio_state_t *pmugpio_ptr;
253 	hrtime_t now;
254 	uint8_t value;
255 
256 	if (dip == NULL) {
257 		return;
258 	}
259 	instance = ddi_get_instance(dip);
260 	pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance);
261 	if (pmugpio_ptr == NULL) {
262 		return;
263 	}
264 	/*
265 	 * The RMC can read interrupts either high to low OR low to high. As
266 	 * a result all that needs to happen is that when we hit the time to
267 	 * send an signal we simply need to change the state.
268 	 */
269 	now = gethrtime();
270 	if ((now - pmugpio_ptr->hw_last_pat) >= PMUGPIO_TWO_SEC) {
271 		/*
272 		 * fetch current reg value and invert it
273 		 */
274 		switch (pmugpio_ptr->access_type) {
275 		case PMUGPIO_CPLD:
276 			value = (CPLD_WATCHDOG ^
277 			    ddi_get8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
278 				pmugpio_ptr->pmugpio_watchdog_reg));
279 
280 			ddi_put8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
281 			    pmugpio_ptr->pmugpio_watchdog_reg, value);
282 			break;
283 
284 		case PMUGPIO_MBC:
285 			value = (uint8_t)(MBC_WATCHDOG ^
286 			    ddi_get8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
287 			    pmugpio_ptr->pmugpio_watchdog_reg));
288 
289 			ddi_put8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
290 			    pmugpio_ptr->pmugpio_watchdog_reg, value);
291 			break;
292 
293 		case PMUGPIO_OTHER:
294 			value = (uint8_t)(0xff ^
295 			    ddi_get8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
296 			    pmugpio_ptr->pmugpio_watchdog_reg));
297 
298 			ddi_put8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
299 			    pmugpio_ptr->pmugpio_watchdog_reg, value);
300 			break;
301 
302 		default:
303 			cmn_err(CE_WARN, "pmugpio_watchdog_pat: Invalid type");
304 		}
305 		pmugpio_ptr->hw_last_pat = now;
306 	}
307 }
308 
309 void
310 pmugpio_reset(void)
311 {
312 	dev_info_t *dip = pmugpio_dip;
313 	int instance;
314 	pmugpio_state_t *pmugpio_ptr;
315 	uint8_t value;
316 
317 	if (dip == NULL) {
318 		return;
319 	}
320 	instance = ddi_get_instance(dip);
321 	pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance);
322 	if (pmugpio_ptr == NULL) {
323 		return;
324 	}
325 
326 	/*
327 	 * For Chalupa, turn all bits on then off again - pmubus nexus
328 	 * will ensure that only unmasked bit is affected.
329 	 * For CPLD and MBC, turn just reset bit on, then off.
330 	 */
331 	switch (pmugpio_ptr->access_type) {
332 	case PMUGPIO_CPLD:
333 		value = ddi_get8(pmugpio_ptr->pmugpio_reset_reg_handle,
334 		    pmugpio_ptr->pmugpio_reset_reg);
335 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
336 		    pmugpio_ptr->pmugpio_reset_reg, (value | CPLD_RESET_SC));
337 
338 		drv_usecwait(CPLD_RESET_DELAY);
339 
340 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
341 		    pmugpio_ptr->pmugpio_reset_reg, (value & ~CPLD_RESET_SC));
342 		break;
343 
344 	case PMUGPIO_MBC:
345 		value = ddi_get8(pmugpio_ptr->pmugpio_reset_reg_handle,
346 		    pmugpio_ptr->pmugpio_reset_reg);
347 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
348 		    pmugpio_ptr->pmugpio_reset_reg,
349 			(value | MBC_PPC_RESET));
350 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
351 		    pmugpio_ptr->pmugpio_reset_reg,
352 			(value & ~MBC_PPC_RESET));
353 		break;
354 
355 	case PMUGPIO_OTHER:
356 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
357 		    pmugpio_ptr->pmugpio_reset_reg, ~0);
358 		ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
359 		    pmugpio_ptr->pmugpio_reset_reg, 0);
360 		break;
361 
362 	default:
363 		cmn_err(CE_WARN, "pmugpio_reset: Invalid type");
364 	}
365 }
366 
367 static int
368 pmugpio_map_regs(dev_info_t *dip, pmugpio_state_t *pmugpio_ptr)
369 {
370 	ddi_device_acc_attr_t attr;
371 	char *binding_name;
372 
373 	/* The host controller will be little endian */
374 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
375 	attr.devacc_attr_endian_flags  = DDI_STRUCTURE_LE_ACC;
376 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
377 
378 	binding_name = ddi_binding_name(dip);
379 
380 	/*
381 	 * Determine access type.
382 	 */
383 	if (strcmp(binding_name, "mbcgpio") == 0)
384 		pmugpio_ptr->access_type = PMUGPIO_MBC;
385 	else if (strcmp(binding_name, "cpldgpio") == 0)
386 		pmugpio_ptr->access_type = PMUGPIO_CPLD;
387 	else
388 		pmugpio_ptr->access_type = PMUGPIO_OTHER;
389 
390 	switch (pmugpio_ptr->access_type) {
391 	case PMUGPIO_CPLD:
392 	case PMUGPIO_MBC:
393 		if (ddi_regs_map_setup(dip, 0,
394 		    (caddr_t *)&pmugpio_ptr->pmugpio_reset_reg, 0, 1, &attr,
395 		    &pmugpio_ptr->pmugpio_reset_reg_handle) != DDI_SUCCESS)
396 			return (DDI_FAILURE);
397 		/* MBC and CPLD have reset and watchdog bits in same reg. */
398 		pmugpio_ptr->pmugpio_watchdog_reg_handle =
399 			pmugpio_ptr->pmugpio_reset_reg_handle;
400 		pmugpio_ptr->pmugpio_watchdog_reg =
401 			pmugpio_ptr->pmugpio_reset_reg;
402 		break;
403 
404 	case PMUGPIO_OTHER:
405 		if (ddi_regs_map_setup(dip, 1,
406 		    (caddr_t *)&pmugpio_ptr->pmugpio_watchdog_reg, 0, 1, &attr,
407 		    &pmugpio_ptr->pmugpio_watchdog_reg_handle) != DDI_SUCCESS) {
408 			return (DDI_FAILURE);
409 		}
410 		if (ddi_regs_map_setup(dip, 0,
411 		    (caddr_t *)&pmugpio_ptr->pmugpio_reset_reg, 0, 1, &attr,
412 		    &pmugpio_ptr->pmugpio_reset_reg_handle) != DDI_SUCCESS) {
413 			ddi_regs_map_free(
414 				&pmugpio_ptr->pmugpio_watchdog_reg_handle);
415 			return (DDI_FAILURE);
416 		}
417 		break;
418 
419 	default:
420 		cmn_err(CE_WARN, "pmugpio_map_regs: Invalid type");
421 		return (DDI_FAILURE);
422 	}
423 
424 	return (DDI_SUCCESS);
425 }
426