xref: /illumos-gate/usr/src/uts/sun4u/grover/io/grfans.c (revision 90f7985f020eb82d06bd0d75396ff794105f7528)
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 #include <sys/stat.h>
28 #include <sys/file.h>
29 #include <sys/uio.h>
30 #include <sys/modctl.h>
31 #include <sys/open.h>
32 #include <sys/types.h>
33 #include <sys/kmem.h>
34 #include <sys/systm.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/conf.h>
38 #include <sys/mode.h>
39 #include <sys/policy.h>
40 
41 #include <sys/grfans.h>
42 
43 /*
44  * cb ops
45  */
46 static int grfans_open(dev_t *, int, int, cred_t *);
47 static int grfans_close(dev_t, int, int, cred_t *);
48 static int grfans_read(dev_t dev, struct uio *uiop, cred_t *cred_p);
49 static int grfans_write(dev_t dev, struct uio *uiop, cred_t *cred_p);
50 static int grfans_io(dev_t dev, struct uio *uiop, int rw);
51 /*
52  * dev ops
53  */
54 static int grfans_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
55 		void **result);
56 static int grfans_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
57 static int grfans_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
58 
59 static struct cb_ops grfans_cbops = {
60 	grfans_open,		/* open */
61 	grfans_close,		/* close */
62 	nodev,			/* strategy */
63 	nodev,			/* print */
64 	nodev,			/* dump */
65 	grfans_read,		/* read */
66 	grfans_write,		/* write */
67 	nodev,			/* ioctl */
68 	nodev,			/* devmap */
69 	nodev,			/* mmap */
70 	nodev,			/* segmap */
71 	nochpoll,		/* poll */
72 	ddi_prop_op,		/* cb_prop_op */
73 	NULL,			/* streamtab */
74 	D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
75 	CB_REV,			/* rev */
76 	nodev,			/* int (*cb_aread)() */
77 	nodev			/* int (*cb_awrite)() */
78 };
79 
80 static struct dev_ops grfans_ops = {
81 	DEVO_REV,
82 	0,
83 	grfans_info,
84 	nulldev,
85 	nulldev,
86 	grfans_attach,
87 	grfans_detach,
88 	nodev,
89 	&grfans_cbops,
90 	NULL,			/* bus_ops */
91 	NULL,			/* power */
92 	ddi_quiesce_not_needed,		/* quiesce */
93 };
94 
95 static struct modldrv grfans_modldrv = {
96 	&mod_driverops,		/* type of module - driver */
97 	"grfans device driver",
98 	&grfans_ops,
99 };
100 
101 static struct modlinkage grfans_modlinkage = {
102 	MODREV_1,
103 	&grfans_modldrv,
104 	0
105 };
106 
107 static void *grfans_soft_statep;
108 static int grfans_debug = 0;
109 
110 int
111 _init(void)
112 {
113 	int    error;
114 
115 	error = mod_install(&grfans_modlinkage);
116 	if (error == 0) {
117 		(void) ddi_soft_state_init(&grfans_soft_statep,
118 		    sizeof (struct grfans_unit), 1);
119 	}
120 
121 	return (error);
122 }
123 
124 int
125 _fini(void)
126 {
127 	int    error;
128 
129 	error = mod_remove(&grfans_modlinkage);
130 	if (error == 0) {
131 		ddi_soft_state_fini(&grfans_soft_statep);
132 	}
133 
134 	return (error);
135 }
136 
137 int
138 _info(struct modinfo *modinfop)
139 {
140 	return (mod_info(&grfans_modlinkage, modinfop));
141 }
142 
143 /* ARGSUSED */
144 static int
145 grfans_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
146 {
147 	dev_t	dev;
148 	int	instance;
149 
150 	if (infocmd == DDI_INFO_DEVT2INSTANCE) {
151 		dev = (dev_t)arg;
152 		instance = MINOR_TO_DEVINST(dev);
153 		*result = (void *)(uintptr_t)instance;
154 		return (DDI_SUCCESS);
155 	}
156 	return (DDI_FAILURE);
157 }
158 
159 static int
160 grfans_do_attach(dev_info_t *dip)
161 {
162 	struct grfans_unit *unitp;
163 	int instance;
164 	ddi_device_acc_attr_t attr;
165 	int nregs;
166 	char name[32];
167 
168 	instance = ddi_get_instance(dip);
169 
170 	if (ddi_soft_state_zalloc(grfans_soft_statep, instance) != 0) {
171 		cmn_err(CE_WARN, "%s%d failed to zalloc softstate",
172 		    ddi_get_name(dip), instance);
173 
174 		return (DDI_FAILURE);
175 	}
176 
177 	if (grfans_debug) {
178 		printf("attached instance number %d\n", instance);
179 	}
180 
181 	unitp = ddi_get_soft_state(grfans_soft_statep, instance);
182 	if (unitp == NULL)
183 		return (DDI_FAILURE);
184 
185 	(void) snprintf(name, sizeof (name), "%s%d", ddi_driver_name(dip),
186 	    instance);
187 
188 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
189 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
190 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
191 
192 	if (grfans_debug) {
193 		printf("number of registers is %d\n",
194 		    ddi_dev_nregs(dip, &nregs));
195 	}
196 
197 	if (ddi_regs_map_setup(dip, 0,
198 	    (caddr_t *)&unitp->cpufan_reg,
199 	    3, 1, &attr, &unitp->cpufan_rhandle) != DDI_SUCCESS) {
200 		cmn_err(CE_WARN, "%s ddi_regs_map_setup failed for regset "
201 		    "0", name);
202 		ddi_soft_state_free(grfans_soft_statep, instance);
203 		return (DDI_FAILURE);
204 	}
205 
206 	if (ddi_regs_map_setup(dip, 1,
207 	    (caddr_t *)&unitp->sysfan_reg,
208 	    0, 1, &attr, &unitp->sysfan_rhandle) != DDI_SUCCESS) {
209 		cmn_err(CE_WARN, "%s ddi_regs_map_setup failed for regset "
210 		    "1", name);
211 		ddi_regs_map_free(&unitp->cpufan_rhandle);
212 		ddi_soft_state_free(grfans_soft_statep, instance);
213 		return (DDI_FAILURE);
214 	}
215 
216 	if (ddi_create_minor_node(dip, "cpu_fan", S_IFCHR,
217 	    DEVINST_TO_MINOR(instance) | CHANNEL_TO_MINOR(CPU_FAN_CHANNEL),
218 	    FANS_NODE_TYPE, 0) == DDI_FAILURE) {
219 		cmn_err(CE_WARN, "%s ddi_create_minor_node failed"
220 		    " for cpu fan", name);
221 		ddi_regs_map_free(&unitp->cpufan_rhandle);
222 		ddi_regs_map_free(&unitp->sysfan_rhandle);
223 		ddi_soft_state_free(grfans_soft_statep, instance);
224 		ddi_remove_minor_node(dip, NULL);
225 
226 		return (DDI_FAILURE);
227 	}
228 
229 	if (ddi_create_minor_node(dip, "sys_fan", S_IFCHR,
230 	    DEVINST_TO_MINOR(instance) | CHANNEL_TO_MINOR(SYSTEM_FAN_CHANNEL),
231 	    FANS_NODE_TYPE, 0) == DDI_FAILURE) {
232 		cmn_err(CE_WARN, "%s ddi_create_minor_node failed"
233 		    " for system fan", name);
234 		ddi_regs_map_free(&unitp->cpufan_rhandle);
235 		ddi_regs_map_free(&unitp->sysfan_rhandle);
236 		ddi_soft_state_free(grfans_soft_statep, instance);
237 		ddi_remove_minor_node(dip, NULL);
238 
239 		return (DDI_FAILURE);
240 	}
241 
242 	mutex_init(&unitp->mutex, NULL, MUTEX_DRIVER, NULL);
243 
244 	return (DDI_SUCCESS);
245 }
246 
247 static int
248 grfans_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
249 {
250 	switch (cmd) {
251 	case DDI_ATTACH:
252 		return (grfans_do_attach(dip));
253 
254 	case DDI_RESUME:
255 		return (DDI_SUCCESS);
256 
257 	default:
258 		return (DDI_FAILURE);
259 	}
260 }
261 
262 static int
263 grfans_do_detach(dev_info_t *dip)
264 {
265 	struct grfans_unit *unitp;
266 	int instance;
267 
268 	instance = ddi_get_instance(dip);
269 	unitp = ddi_get_soft_state(grfans_soft_statep, instance);
270 	ddi_remove_minor_node(dip, NULL);
271 
272 	ddi_regs_map_free(&unitp->cpufan_rhandle);
273 	ddi_regs_map_free(&unitp->sysfan_rhandle);
274 
275 	mutex_destroy(&unitp->mutex);
276 
277 	ddi_soft_state_free(grfans_soft_statep, instance);
278 
279 	return (DDI_SUCCESS);
280 }
281 
282 static int
283 grfans_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
284 {
285 	switch (cmd) {
286 	case DDI_DETACH:
287 		return (grfans_do_detach(dip));
288 
289 	case DDI_SUSPEND:
290 		return (DDI_SUCCESS);
291 
292 	default:
293 		return (DDI_FAILURE);
294 	}
295 }
296 
297 /*ARGSUSED*/
298 static int
299 grfans_open(dev_t *devp, int flags, int otyp, cred_t *credp)
300 {
301 	struct grfans_unit *unitp;
302 	int err = 0;
303 	int instance = MINOR_TO_DEVINST(*devp);
304 	int channel;
305 
306 	/*
307 	 * must be privileged to access this device
308 	 */
309 	if (secpolicy_sys_config(credp, B_FALSE) != 0)
310 		return (EPERM);
311 
312 	if (instance < 0) {
313 		cmn_err(CE_WARN, "grfan: instance less than 0:  %d\n",
314 		    instance);
315 
316 		return (ENXIO);
317 	}
318 
319 	unitp = ddi_get_soft_state(grfans_soft_statep, instance);
320 	if (unitp == NULL) {
321 		cmn_err(CE_WARN, "grfan: no soft state for instance %d\n",
322 		    instance);
323 
324 		return (ENXIO);
325 	}
326 
327 	if (otyp != OTYP_CHR)
328 		return (EINVAL);
329 
330 	channel = MINOR_TO_CHANNEL(getminor(*devp));
331 
332 	mutex_enter(&unitp->mutex);
333 
334 	if (flags & FEXCL) {
335 		if (unitp->oflag[channel] != 0)
336 			err = EBUSY;
337 		else
338 			unitp->oflag[channel] = FEXCL;
339 	} else {
340 		if (unitp->oflag[channel] == FEXCL)
341 			err = EBUSY;
342 		else
343 			unitp->oflag[channel] = (uint16_t)FOPEN;
344 	}
345 
346 	mutex_exit(&unitp->mutex);
347 
348 	return (err);
349 }
350 
351 /*ARGSUSED*/
352 static int
353 grfans_close(dev_t dev, int flags, int otyp, cred_t *credp)
354 {
355 	struct grfans_unit *unitp;
356 	int instance = MINOR_TO_DEVINST(dev);
357 	int channel;
358 
359 	if (instance < 0)
360 		return (ENXIO);
361 
362 	unitp = ddi_get_soft_state(grfans_soft_statep, instance);
363 	if (unitp == NULL)
364 		return (ENXIO);
365 
366 	channel = MINOR_TO_CHANNEL(getminor(dev));
367 
368 	unitp->oflag[channel] = 0;
369 
370 	return (DDI_SUCCESS);
371 }
372 
373 /*ARGSUSED*/
374 static int
375 grfans_read(dev_t dev, struct uio *uiop, cred_t *cred_p)
376 {
377 	return (grfans_io(dev, uiop, B_READ));
378 }
379 
380 /*ARGSUSED*/
381 static int
382 grfans_write(dev_t dev, struct uio *uiop, cred_t *cred_p)
383 {
384 	return (grfans_io(dev, uiop, B_WRITE));
385 }
386 
387 static int
388 grfans_io(dev_t dev, struct uio *uiop, int rw)
389 {
390 	struct grfans_unit *unitp;
391 	int instance = MINOR_TO_DEVINST(getminor(dev));
392 	int ret = 0;
393 	size_t len = uiop->uio_resid;
394 	int8_t out_value, req_value, reg_value;
395 	caddr_t outputaddr;
396 
397 	if (instance < 0)
398 		return (ENXIO);
399 
400 	if (len == 0)
401 		return (0);
402 
403 	unitp = ddi_get_soft_state(grfans_soft_statep, instance);
404 
405 	if (unitp == NULL)
406 		return (ENXIO);
407 
408 	if (MINOR_TO_CHANNEL(getminor(dev)) == CPU_FAN_CHANNEL)
409 		outputaddr = &unitp->cpufan_output;
410 	else
411 		outputaddr = &unitp->sysfan_output;
412 
413 	if (rw == B_READ) {
414 		if (*outputaddr == UNKNOWN_OUT)
415 			return (EIO);
416 		return (uiomove(outputaddr, 1, UIO_READ, uiop));
417 	}
418 
419 	/*
420 	 * rw == B_WRITE.
421 	 */
422 	if ((ret = uiomove(&req_value, sizeof (req_value), UIO_WRITE,
423 	    uiop)) == 0) {
424 		if (MINOR_TO_CHANNEL(dev) == CPU_FAN_CHANNEL) {
425 			/*
426 			 * Check bounds for cpu fan
427 			 */
428 			if (req_value == 0) {
429 				reg_value = CPU_FAN_0;
430 				out_value = 0;
431 			} else if (req_value <= 25) {
432 				reg_value = CPU_FAN_25;
433 				out_value = 25;
434 			} else if (req_value <= 50) {
435 				reg_value = CPU_FAN_50;
436 				out_value = 50;
437 			} else if (req_value <= 75) {
438 				reg_value = CPU_FAN_75;
439 				out_value = 75;
440 			} else if (req_value <= 100) {
441 				reg_value = CPU_FAN_100;
442 				out_value = 100;
443 			} else
444 				ret = EINVAL;
445 
446 			if (ret != EINVAL) {
447 				uint8_t reg;
448 
449 				*outputaddr = out_value;
450 
451 				reg = ddi_get8(unitp->cpufan_rhandle,
452 				    unitp->cpufan_reg);
453 				reg = (reg & ~CPU_FAN_MASK) | reg_value;
454 				ddi_put8(unitp->cpufan_rhandle,
455 				    unitp->cpufan_reg, reg);
456 				(void) ddi_get8(unitp->cpufan_rhandle,
457 				    unitp->cpufan_reg);
458 
459 				if (grfans_debug) {
460 					printf("set output to %d at addr %p\n",
461 					    out_value,
462 					    (void *)unitp->cpufan_reg);
463 				}
464 			}
465 		} else {
466 			if (req_value == 0) {
467 				reg_value = SYS_FAN_OFF;
468 				out_value = 0;
469 			} else if (req_value > 0) {
470 				reg_value = SYS_FAN_ON;
471 				out_value = 100;
472 			} else {
473 				ret = EINVAL;
474 			}
475 
476 			if (ret != EINVAL) {
477 				*outputaddr = out_value;
478 
479 				ddi_put8(unitp->sysfan_rhandle,
480 				    unitp->sysfan_reg,
481 				    reg_value);
482 				(void) ddi_get8(unitp->sysfan_rhandle,
483 				    unitp->sysfan_reg);
484 				if (grfans_debug) {
485 					printf("set SYSFAN output to %d at "
486 					    "addr %p\n", out_value,
487 					    (void *)unitp->sysfan_reg);
488 				}
489 			}
490 		}
491 	} else {
492 		ret = EFAULT;
493 	}
494 
495 	return (ret);
496 }
497