xref: /illumos-gate/usr/src/uts/sun4u/io/pmugpio.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 2004 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  * Time periods, in nanoseconds
40  */
41 #define	PMUGPIO_TWO_SEC		2000000000LL
42 
43 static	dev_info_t	*pmugpio_dip;
44 
45 typedef struct pmugpio_state {
46 	uint8_t			*pmugpio_reset_reg;
47 	ddi_acc_handle_t	pmugpio_reset_reg_handle;
48 	uint8_t			*pmugpio_watchdog_reg;
49 	ddi_acc_handle_t	pmugpio_watchdog_reg_handle;
50 	hrtime_t		hw_last_pat;
51 } pmugpio_state_t;
52 
53 static void *pmugpio_statep;
54 
55 static int pmugpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
56 static int pmugpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
57 static int pmugpio_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
58 		void **result);
59 static int pmugpio_map_regs(dev_info_t *, pmugpio_state_t *);
60 
61 struct cb_ops pmugpio_cb_ops = {
62 	nulldev,	/* open  */
63 	nulldev,	/* close */
64 	nulldev,	/* strategy */
65 	nulldev,	/* print */
66 	nulldev,	/* dump */
67 	nulldev,	/* read */
68 	nulldev,	/* write */
69 	nulldev,	/* ioctl */
70 	nulldev,	/* devmap */
71 	nulldev,	/* mmap */
72 	nulldev,	/* segmap */
73 	nochpoll,	/* poll */
74 	ddi_prop_op,	/* cb_prop_op */
75 	NULL,		/* streamtab  */
76 	D_MP | D_NEW
77 };
78 
79 static struct dev_ops pmugpio_ops = {
80 	DEVO_REV,		/* Devo_rev */
81 	0,			/* Refcnt */
82 	pmugpio_info,		/* Info */
83 	nulldev,		/* Identify */
84 	nulldev,		/* Probe */
85 	pmugpio_attach,		/* Attach */
86 	pmugpio_detach,		/* Detach */
87 	nodev,			/* Reset */
88 	&pmugpio_cb_ops,		/* Driver operations */
89 	0,			/* Bus operations */
90 	NULL			/* Power */
91 };
92 
93 static struct modldrv modldrv = {
94 	&mod_driverops, 		/* This one is a driver */
95 	"Pmugpio Driver %I%", 		/* Name of the module. */
96 	&pmugpio_ops,			/* Driver ops */
97 };
98 
99 static struct modlinkage modlinkage = {
100 	MODREV_1, (void *)&modldrv, NULL
101 };
102 
103 int
104 _init(void)
105 {
106 	int error;
107 
108 	/* Initialize the soft state structures */
109 	if ((error = ddi_soft_state_init(&pmugpio_statep,
110 	    sizeof (pmugpio_state_t), 1)) != 0) {
111 		return (error);
112 	}
113 
114 	/* Install the loadable module */
115 	if ((error = mod_install(&modlinkage)) != 0) {
116 		ddi_soft_state_fini(&pmugpio_statep);
117 	}
118 	return (error);
119 }
120 
121 int
122 _info(struct modinfo *modinfop)
123 {
124 	return (mod_info(&modlinkage, modinfop));
125 }
126 
127 int
128 _fini(void)
129 {
130 	int error;
131 
132 	error = mod_remove(&modlinkage);
133 	if (error == 0) {
134 		/* Release per module resources */
135 		ddi_soft_state_fini(&pmugpio_statep);
136 	}
137 	return (error);
138 }
139 
140 static int
141 pmugpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
142 {
143 	int		instance;
144 	pmugpio_state_t	*pmugpio_ptr = NULL;
145 
146 	switch (cmd) {
147 	case DDI_ATTACH:
148 		break;
149 	case DDI_RESUME:
150 		return (DDI_SUCCESS);
151 	default:
152 		return (DDI_FAILURE);
153 	}
154 
155 	/* Get the instance and create soft state */
156 	instance = ddi_get_instance(dip);
157 	if (ddi_soft_state_zalloc(pmugpio_statep, instance) != 0) {
158 		return (DDI_FAILURE);
159 	}
160 	pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance);
161 	if (pmugpio_ptr == NULL) {
162 		return (DDI_FAILURE);
163 	}
164 
165 	if (pmugpio_map_regs(dip, pmugpio_ptr) != DDI_SUCCESS) {
166 		ddi_soft_state_free(pmugpio_statep, instance);
167 		return (DDI_FAILURE);
168 	}
169 
170 	/* Display information in the banner */
171 	ddi_report_dev(dip);
172 
173 	/* Save the dip */
174 	pmugpio_dip = dip;
175 
176 	return (DDI_SUCCESS);
177 }
178 
179 /* ARGSUSED */
180 static int
181 pmugpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
182 {
183 	/* Pointer to soft state */
184 	switch (cmd) {
185 	case DDI_SUSPEND:
186 		return (DDI_SUCCESS);
187 	default:
188 		return (DDI_FAILURE);
189 	}
190 }
191 
192 /* ARGSUSED */
193 static int
194 pmugpio_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
195 		void *arg, void **result)
196 {
197 	dev_t dev;
198 	int instance, error;
199 
200 	switch (infocmd) {
201 	case DDI_INFO_DEVT2DEVINFO:
202 		*result = (void *)pmugpio_dip;
203 		error = DDI_SUCCESS;
204 		break;
205 	case DDI_INFO_DEVT2INSTANCE:
206 		dev = (dev_t)arg;
207 		instance = getminor(dev);
208 		*result = (void *)(uintptr_t)instance;
209 		error = DDI_SUCCESS;
210 		break;
211 	default:
212 		error = DDI_FAILURE;
213 	}
214 	return (error);
215 }
216 
217 void
218 pmugpio_watchdog_pat(void)
219 {
220 	dev_info_t *dip = pmugpio_dip;
221 	int instance;
222 	pmugpio_state_t *pmugpio_ptr;
223 	hrtime_t now;
224 	uint8_t value;
225 
226 	if (dip == NULL) {
227 		return;
228 	}
229 	instance = ddi_get_instance(dip);
230 	pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance);
231 	if (pmugpio_ptr == NULL) {
232 		return;
233 	}
234 	/*
235 	 * The RMC can read interrupts either high to low OR low to high. As
236 	 * a result all that needs to happen is that when we hit the time to
237 	 * send an signal we simply need to change the state.
238 	 */
239 	now = gethrtime();
240 	if ((now - pmugpio_ptr->hw_last_pat) >= PMUGPIO_TWO_SEC) {
241 		/*
242 		 * fetch current reg value and invert it
243 		 */
244 		value = (uint8_t)(0xff ^
245 		    ddi_get8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
246 		    pmugpio_ptr->pmugpio_watchdog_reg));
247 
248 		ddi_put8(pmugpio_ptr->pmugpio_watchdog_reg_handle,
249 		    pmugpio_ptr->pmugpio_watchdog_reg, value);
250 		pmugpio_ptr->hw_last_pat = now;
251 	}
252 }
253 
254 void
255 pmugpio_reset(void)
256 {
257 	dev_info_t *dip = pmugpio_dip;
258 	int instance;
259 	pmugpio_state_t *pmugpio_ptr;
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 	/*
271 	 * turn all bits on then off again - pmubus nexus will ensure
272 	 * that only unmasked bit is affected
273 	 */
274 	ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
275 	    pmugpio_ptr->pmugpio_reset_reg, ~0);
276 	ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle,
277 	    pmugpio_ptr->pmugpio_reset_reg, 0);
278 }
279 
280 static int
281 pmugpio_map_regs(dev_info_t *dip, pmugpio_state_t *pmugpio_ptr)
282 {
283 	ddi_device_acc_attr_t attr;
284 
285 	/* The host controller will be little endian */
286 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
287 	attr.devacc_attr_endian_flags  = DDI_STRUCTURE_LE_ACC;
288 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
289 
290 	if (ddi_regs_map_setup(dip, 1,
291 	    (caddr_t *)&pmugpio_ptr->pmugpio_watchdog_reg, 0, 1, &attr,
292 	    &pmugpio_ptr->pmugpio_watchdog_reg_handle) != DDI_SUCCESS) {
293 		return (DDI_FAILURE);
294 	}
295 	if (ddi_regs_map_setup(dip, 0,
296 	    (caddr_t *)&pmugpio_ptr->pmugpio_reset_reg, 0, 1, &attr,
297 	    &pmugpio_ptr->pmugpio_reset_reg_handle) != DDI_SUCCESS) {
298 		ddi_regs_map_free(&pmugpio_ptr->pmugpio_watchdog_reg_handle);
299 		return (DDI_FAILURE);
300 	}
301 	return (DDI_SUCCESS);
302 }
303