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 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 /*
28 * Driver to map the PIC for the chicago platform.
29 */
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/errno.h>
33 #include <sys/cmn_err.h>
34 #include <sys/param.h>
35 #include <sys/modctl.h>
36 #include <sys/conf.h>
37 #include <sys/open.h>
38 #include <sys/stat.h>
39 #include <sys/clock.h>
40 #include <sys/pic.h>
41 #include <sys/pic16f747.h>
42 #include <sys/ddi.h>
43 #include <sys/sunddi.h>
44 #include <sys/file.h>
45
46 /* dev_ops and cb_ops entry point function declarations */
47 static int pic_attach(dev_info_t *, ddi_attach_cmd_t);
48 static int pic_detach(dev_info_t *, ddi_detach_cmd_t);
49 static int pic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
50 static int pic_open(dev_t *, int, int, cred_t *);
51 static int pic_close(dev_t, int, int, cred_t *);
52 static int pic_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
53
54 struct cb_ops pic_cb_ops = {
55 pic_open,
56 pic_close,
57 nodev,
58 nodev,
59 nodev, /* dump */
60 nodev,
61 nodev,
62 pic_ioctl,
63 nodev, /* devmap */
64 nodev,
65 ddi_segmap, /* segmap */
66 nochpoll,
67 ddi_prop_op,
68 NULL, /* for STREAMS drivers */
69 D_NEW | D_MP /* driver compatibility flag */
70 };
71
72 static struct dev_ops pic_dev_ops = {
73 DEVO_REV, /* driver build version */
74 0, /* device reference count */
75 pic_getinfo,
76 nulldev,
77 nulldev, /* probe */
78 pic_attach,
79 pic_detach,
80 nulldev, /* reset */
81 &pic_cb_ops,
82 (struct bus_ops *)NULL,
83 nulldev, /* power */
84 ddi_quiesce_not_supported, /* devo_quiesce */
85 };
86
87 /*
88 * Fans' and sensors' node names and register offsets
89 */
90 static struct minor_node_info pic_nodes[N_PIC_NODES] = {
91 {NULL, 0, 0}, /* Reserved */
92 {"fan_0", RF_FAN0_PERIOD, F0_FLT_BIT}, /* System Fan 0 */
93 {"fan_1", RF_FAN1_PERIOD, F1_FLT_BIT}, /* System Fan 1 */
94 {"fan_2", RF_FAN2_PERIOD, F2_FLT_BIT}, /* System Fan 2 */
95 {"fan_3", RF_FAN3_PERIOD, F3_FLT_BIT}, /* System Fan 3 */
96 {"fan_4", RF_FAN4_PERIOD, F4_FLT_BIT}, /* System Fan 4 */
97 {"adt7462", RF_LOCAL_TEMP, 0}, /* ADT7462 Local Temperature */
98 {"cpu_0", RF_REMOTE1_TEMP, 0}, /* CPU 0 temp */
99 {"cpu_1", RF_REMOTE2_TEMP, 0}, /* CPU 1 temp */
100 {"mb", RF_REMOTE3_TEMP, 0}, /* Motherboard temp */
101 {"lm95221", RF_LM95221_TEMP, 0}, /* LM95221 Local Temperature */
102 {"fire", RF_FIRE_TEMP, 0}, /* FIRE Temp */
103 {"lsi1064", RF_LSI1064_TEMP, 0}, /* LSI1064 Temp */
104 {"front_panel", RF_FRONT_TEMP, 0}, /* Front Panel Temperature */
105 {"psu", RF_PSU_TEMP, PSUF_FLT_BIT} /* PSU Temp (and ffault) */
106 };
107
108 /*
109 * Soft state
110 */
111 struct pic_softc {
112 dev_info_t *dip;
113 kmutex_t mutex;
114 uint8_t *cmd_reg;
115 ddi_acc_handle_t cmd_handle;
116 };
117 #define getsoftc(inst) ((struct pic_softc *)ddi_get_soft_state(statep, (inst)))
118
119 /* module configuration stuff */
120 static void *statep;
121 extern struct mod_ops mod_driverops;
122
123 static struct modldrv modldrv = {
124 &mod_driverops,
125 "pic_client driver",
126 &pic_dev_ops
127 };
128
129 static struct modlinkage modlinkage = {
130 MODREV_1,
131 &modldrv,
132 0
133 };
134
135 int
_init(void)136 _init(void)
137 {
138 int e;
139
140 if (e = ddi_soft_state_init(&statep, sizeof (struct pic_softc),
141 MAX_PIC_INSTANCES)) {
142 return (e);
143 }
144
145 if ((e = mod_install(&modlinkage)) != 0)
146 ddi_soft_state_fini(&statep);
147
148 return (e);
149 }
150
151 int
_fini(void)152 _fini(void)
153 {
154 int e;
155
156 if ((e = mod_remove(&modlinkage)) != 0)
157 return (e);
158
159 ddi_soft_state_fini(&statep);
160
161 return (DDI_SUCCESS);
162 }
163
164 int
_info(struct modinfo * modinfop)165 _info(struct modinfo *modinfop)
166 {
167 return (mod_info(&modlinkage, modinfop));
168 }
169
170 /*ARGSUSED*/
171 static int
pic_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)172 pic_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
173 {
174 int inst;
175 int retval = DDI_SUCCESS;
176 struct pic_softc *softc;
177
178 inst = PIC_MINOR_TO_INST(getminor((dev_t)arg));
179
180 switch (cmd) {
181 case DDI_INFO_DEVT2DEVINFO:
182 if ((softc = getsoftc(inst)) == NULL) {
183 *result = (void *)NULL;
184 retval = DDI_FAILURE;
185 } else
186 *result = (void *)softc->dip;
187 break;
188
189 case DDI_INFO_DEVT2INSTANCE:
190 *result = (void *)((uintptr_t)inst);
191 break;
192
193 default:
194 retval = DDI_FAILURE;
195 }
196
197 return (retval);
198 }
199
200 static int
pic_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)201 pic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
202 {
203 int inst;
204 int i;
205 struct pic_softc *softc = NULL;
206 char *minor_name;
207 int minor;
208 char name[80];
209 ddi_device_acc_attr_t dev_attr;
210 int res;
211
212 switch (cmd) {
213 case DDI_ATTACH:
214 inst = ddi_get_instance(dip);
215 if (inst >= MAX_PIC_INSTANCES) {
216 cmn_err(CE_WARN, "attach failed, too many instances\n");
217 return (DDI_FAILURE);
218 }
219
220 (void) sprintf(name, "env-monitor%d", inst);
221 minor = PIC_INST_TO_MINOR(inst) | PIC_UNIT_TO_MINOR(0);
222 if (ddi_create_minor_node(dip, name, S_IFCHR, minor,
223 DDI_PSEUDO, NULL) == DDI_FAILURE) {
224 cmn_err(CE_WARN,
225 "ddi_create_minor_node() failed for inst %d\n",
226 inst);
227 return (DDI_FAILURE);
228 }
229
230 /* Allocate a soft state structure for this instance */
231 if (ddi_soft_state_zalloc(statep, inst) != DDI_SUCCESS) {
232 cmn_err(CE_WARN, " ddi_soft_state_zalloc() failed "
233 "for inst %d\n", inst);
234 goto attach_failed;
235 }
236
237 /* Setup soft state */
238 softc = getsoftc(inst);
239 softc->dip = dip;
240 mutex_init(&softc->mutex, NULL, MUTEX_DRIVER, NULL);
241
242 /* Setup device attributes */
243 dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
244 dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
245 dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
246
247 /*
248 * The RF_COMMAND/RF_STATUS and RF_IND_DATA/RF_IND_ADDR
249 * register pairs are mapped as one register set starting
250 * from 0x0 and length 0x42.
251 */
252 res = ddi_regs_map_setup(dip, 0, (caddr_t *)&softc->cmd_reg,
253 0, 0x42, &dev_attr, &softc->cmd_handle);
254 if (res != DDI_SUCCESS) {
255 cmn_err(CE_WARN, "ddi_regs_map_setup() failed\n");
256 goto attach_failed;
257 }
258
259 /* Set up fans' and sensors' device minor nodes */
260 for (i = 1; i < N_PIC_NODES; i++) {
261 minor_name = pic_nodes[i].minor_name;
262 minor = PIC_INST_TO_MINOR(inst) | PIC_UNIT_TO_MINOR(i);
263 if (ddi_create_minor_node(dip, minor_name, S_IFCHR,
264 minor, PICDEV_NODE_TYPE, NULL) == DDI_FAILURE) {
265 cmn_err(CE_WARN,
266 "%s:%d ddi_create_minor_node failed",
267 ddi_driver_name(dip), inst);
268 (void) pic_detach(dip, DDI_DETACH);
269 return (DDI_FAILURE);
270 }
271 }
272
273 /* Create main environmental node */
274 ddi_report_dev(dip);
275
276 return (DDI_SUCCESS);
277
278 case DDI_RESUME:
279 return (DDI_SUCCESS);
280
281 default:
282 return (DDI_FAILURE);
283 }
284
285 attach_failed:
286
287 /* Free soft state, if allocated. remove minor node if added earlier */
288 if (softc)
289 ddi_soft_state_free(statep, inst);
290
291 ddi_remove_minor_node(dip, NULL);
292
293 return (DDI_FAILURE);
294 }
295
296 static int
pic_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)297 pic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
298 {
299 int inst;
300 struct pic_softc *softc;
301
302 switch (cmd) {
303 case DDI_DETACH:
304 inst = ddi_get_instance(dip);
305 if ((softc = getsoftc(inst)) == NULL)
306 return (ENXIO);
307
308 (void) ddi_regs_map_free(&softc->cmd_handle);
309 /* Free the soft state and remove minor node added earlier */
310 mutex_destroy(&softc->mutex);
311 ddi_soft_state_free(statep, inst);
312 ddi_remove_minor_node(dip, NULL);
313 return (DDI_SUCCESS);
314
315 case DDI_SUSPEND:
316 return (DDI_SUCCESS);
317
318 default:
319 return (DDI_FAILURE);
320 }
321 }
322
323 /*ARGSUSED*/
324 static int
pic_open(dev_t * devp,int flag,int otyp,cred_t * credp)325 pic_open(dev_t *devp, int flag, int otyp, cred_t *credp)
326 {
327 int inst = PIC_MINOR_TO_INST(getminor(*devp));
328
329 return (getsoftc(inst) == NULL ? ENXIO : 0);
330 }
331
332 /*ARGSUSED*/
333 static int
pic_close(dev_t dev,int flag,int otyp,cred_t * credp)334 pic_close(dev_t dev, int flag, int otyp, cred_t *credp)
335 {
336 int inst = PIC_MINOR_TO_INST(getminor(dev));
337
338 return (getsoftc(inst) == NULL ? ENXIO : 0);
339 }
340
341 /*ARGSUSED*/
342 static int
pic_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)343 pic_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
344 {
345 int inst;
346 int node;
347 struct pic_softc *softc;
348 uint8_t in_command;
349 int16_t tempr;
350
351 inst = PIC_MINOR_TO_INST(getminor(dev));
352 if ((softc = getsoftc(inst)) == NULL)
353 return (ENXIO);
354
355 mutex_enter(&softc->mutex);
356
357 if (ddi_copyin((caddr_t)arg, &in_command, sizeof (in_command),
358 mode) != DDI_SUCCESS) {
359 mutex_exit(&softc->mutex);
360 return (EFAULT);
361 }
362
363 node = PIC_MINOR_TO_UNIT(getminor(dev));
364 if ((node >= N_PIC_NODES) || (node < 1)) {
365 mutex_exit(&softc->mutex);
366 return (ENXIO);
367 }
368
369 switch (cmd) {
370 case PIC_GET_TEMPERATURE:
371 drv_usecwait(10);
372
373 /* select the temp sensor */
374 (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg +
375 RF_IND_ADDR, pic_nodes[node].reg_offset);
376
377 /* retrieve temperature data */
378 tempr = (int16_t)ddi_get8(softc->cmd_handle,
379 (uint8_t *)softc->cmd_reg + RF_IND_DATA);
380 mutex_exit(&softc->mutex);
381
382 if (tempr == 0xff)
383 return (EIO);
384
385 /*
386 * The temp is passed in as a uint8 value, we need to convert
387 * it to a signed 16 bit value to be able to handle the range
388 * of -64 to 190 degrees.
389 */
390 tempr -= 64;
391 (void) ddi_copyout(&tempr, (caddr_t)arg, sizeof (tempr), mode);
392 return (0);
393
394 case PIC_GET_FAN_SPEED:
395 drv_usecwait(10);
396
397 /* select fan */
398 (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg +
399 RF_IND_ADDR, pic_nodes[node].reg_offset);
400
401 /* retrieve fan data */
402 in_command = ddi_get8(softc->cmd_handle,
403 (uint8_t *)softc->cmd_reg + RF_IND_DATA);
404 mutex_exit(&softc->mutex);
405
406 if (in_command == 0xff)
407 return (EIO);
408
409 (void) ddi_copyout(&in_command, (caddr_t)arg, 1, mode);
410 return (0);
411
412 case PIC_SET_FAN_SPEED:
413 /* select fan */
414 (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg +
415 RF_IND_ADDR, pic_nodes[node].reg_offset);
416
417 /* send the fan data */
418 (void) ddi_put8(softc->cmd_handle,
419 (uint8_t *)softc->cmd_reg + RF_IND_DATA, in_command);
420
421 mutex_exit(&softc->mutex);
422 return (0);
423
424 case PIC_GET_STATUS:
425 mutex_exit(&softc->mutex);
426
427 /* we don't read the status reg anymore */
428 in_command = 0;
429 (void) ddi_copyout(&in_command, (caddr_t)arg, 1, mode);
430
431 return (0);
432
433 case PIC_GET_FAN_STATUS:
434 drv_usecwait(10);
435
436 /* read ffault register */
437 (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg +
438 RF_IND_ADDR, RF_FAN_STATUS);
439
440 /* retrieve fan failure status */
441 in_command = ddi_get8(softc->cmd_handle,
442 (uint8_t *)softc->cmd_reg + RF_IND_DATA);
443 mutex_exit(&softc->mutex);
444
445 if (in_command == 0xff)
446 return (EIO);
447
448 in_command = (in_command >> pic_nodes[node].ff_shift) & 0x1;
449 (void) ddi_copyout(&in_command, (caddr_t)arg, 1, mode);
450 return (0);
451
452 case PIC_SET_ESTAR_MODE:
453 (void) ddi_put8(softc->cmd_handle,
454 (uint8_t *)softc->cmd_reg + RF_COMMAND, CMD_TO_ESTAR);
455 mutex_exit(&softc->mutex);
456 return (0);
457
458 default:
459 mutex_exit(&softc->mutex);
460 cmn_err(CE_NOTE, "cmd %d isnt valid", cmd);
461 return (EINVAL);
462 }
463 }
464