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 * This is the Beep driver for SMBUS based beep mechanism.
29 * The driver exports the interfaces to set frequency,
30 * turn on beeper and turn off beeper to the generic beep
31 * module. If a beep is in progress, the driver discards a
32 * second beep. This driver uses the 8254 timer to program
33 * the beeper ports.
34 */
35 #include <sys/types.h>
36 #include <sys/conf.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39 #include <sys/modctl.h>
40 #include <sys/ddi_impldefs.h>
41 #include <sys/kmem.h>
42 #include <sys/devops.h>
43 #include <sys/grbeep.h>
44 #include <sys/beep.h>
45
46
47 /* Pointer to the state structure */
48 static void *grbeep_statep;
49
50
51 /*
52 * Debug stuff
53 */
54 #ifdef DEBUG
55 int grbeep_debug = 0;
56 #define GRBEEP_DEBUG(args) if (grbeep_debug) cmn_err args
57 #define GRBEEP_DEBUG1(args) if (grbeep_debug > 1) cmn_err args
58 #else
59 #define GRBEEP_DEBUG(args)
60 #define GRBEEP_DEBUG1(args)
61 #endif
62
63
64 /*
65 * Prototypes
66 */
67 static int grbeep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
68 static int grbeep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
69 static int grbeep_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
70 void **result);
71 static void grbeep_freq(void *arg, int freq);
72 static void grbeep_on(void *arg);
73 static void grbeep_off(void *arg);
74 static void grbeep_cleanup(grbeep_state_t *);
75 static int grbeep_map_regs(dev_info_t *, grbeep_state_t *);
76 static grbeep_state_t *grbeep_obtain_state(dev_info_t *);
77
78
79 struct cb_ops grbeep_cb_ops = {
80 nulldev, /* open */
81 nulldev, /* close */
82 nulldev, /* strategy */
83 nulldev, /* print */
84 nulldev, /* dump */
85 nulldev, /* read */
86 nulldev, /* write */
87 nulldev, /* ioctl */
88 nulldev, /* devmap */
89 nulldev, /* mmap */
90 nulldev, /* segmap */
91 nochpoll, /* poll */
92 ddi_prop_op, /* cb_prop_op */
93 NULL, /* streamtab */
94 D_MP | D_NEW
95 };
96
97
98 static struct dev_ops grbeep_ops = {
99 DEVO_REV, /* Devo_rev */
100 0, /* Refcnt */
101 grbeep_info, /* Info */
102 nulldev, /* Identify */
103 nulldev, /* Probe */
104 grbeep_attach, /* Attach */
105 grbeep_detach, /* Detach */
106 nodev, /* Reset */
107 &grbeep_cb_ops, /* Driver operations */
108 0, /* Bus operations */
109 NULL, /* Power */
110 ddi_quiesce_not_supported, /* devo_quiesce */
111 };
112
113
114 static struct modldrv modldrv = {
115 &mod_driverops, /* This one is a driver */
116 "SMBUS Beep Driver", /* Name of the module. */
117 &grbeep_ops, /* Driver ops */
118 };
119
120
121 static struct modlinkage modlinkage = {
122 MODREV_1, (void *)&modldrv, NULL
123 };
124
125
126 int
_init(void)127 _init(void)
128 {
129 int error;
130
131 /* Initialize the soft state structures */
132 if ((error = ddi_soft_state_init(&grbeep_statep,
133 sizeof (grbeep_state_t), 1)) != 0) {
134
135 return (error);
136 }
137
138 /* Install the loadable module */
139 if ((error = mod_install(&modlinkage)) != 0) {
140 ddi_soft_state_fini(&grbeep_statep);
141 }
142
143 return (error);
144 }
145
146
147 int
_info(struct modinfo * modinfop)148 _info(struct modinfo *modinfop)
149 {
150 return (mod_info(&modlinkage, modinfop));
151 }
152
153
154 int
_fini(void)155 _fini(void)
156 {
157 int error;
158
159 error = mod_remove(&modlinkage);
160
161 if (error == 0) {
162 /* Release per module resources */
163 ddi_soft_state_fini(&grbeep_statep);
164 }
165
166 return (error);
167 }
168
169
170 /*
171 * Beep entry points
172 */
173
174 /*
175 * grbeep_attach:
176 */
177 static int
grbeep_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)178 grbeep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
179 {
180 int instance;
181
182 /* Pointer to soft state */
183 grbeep_state_t *grbeeptr = NULL;
184
185 GRBEEP_DEBUG1((CE_CONT, "grbeep_attach: Start"));
186
187 switch (cmd) {
188 case DDI_ATTACH:
189 break;
190 case DDI_RESUME:
191
192 return (DDI_SUCCESS);
193 default:
194
195 return (DDI_FAILURE);
196 }
197
198 /* Get the instance and create soft state */
199 instance = ddi_get_instance(dip);
200
201 if (ddi_soft_state_zalloc(grbeep_statep, instance) != 0) {
202
203 return (DDI_FAILURE);
204 }
205
206 grbeeptr = ddi_get_soft_state(grbeep_statep, instance);
207
208 if (grbeeptr == NULL) {
209
210 return (DDI_FAILURE);
211 }
212
213 GRBEEP_DEBUG1((CE_CONT, "grbeeptr = 0x%p, instance %x",
214 (void *)grbeeptr, instance));
215
216 /* Save the dip */
217 grbeeptr->grbeep_dip = dip;
218
219 /* Initialize beeper mode */
220 grbeeptr->grbeep_mode = GRBEEP_OFF;
221
222 /* Map the Beep Control and Beep counter Registers */
223 if (grbeep_map_regs(dip, grbeeptr) != DDI_SUCCESS) {
224
225 GRBEEP_DEBUG((CE_WARN,
226 "grbeep_attach: Mapping of beep registers failed."));
227
228 grbeep_cleanup(grbeeptr);
229
230 return (DDI_FAILURE);
231 }
232
233 (void) beep_init((void *)dip, grbeep_on, grbeep_off, grbeep_freq);
234
235 /* Display information in the banner */
236 ddi_report_dev(dip);
237
238 GRBEEP_DEBUG1((CE_CONT, "grbeep_attach: dip = 0x%p done",
239 (void *)dip));
240
241 return (DDI_SUCCESS);
242 }
243
244
245 /*
246 * grbeep_detach:
247 */
248 static int
grbeep_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)249 grbeep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
250 {
251 /* Pointer to soft state */
252 grbeep_state_t *grbeeptr = NULL;
253
254 GRBEEP_DEBUG1((CE_CONT, "grbeep_detach: Start"));
255
256 switch (cmd) {
257 case DDI_SUSPEND:
258 grbeeptr = grbeep_obtain_state(dip);
259
260 if (grbeeptr == NULL) {
261
262 return (DDI_FAILURE);
263 }
264
265 /*
266 * If a beep is in progress; fail suspend
267 */
268 if (grbeeptr->grbeep_mode == GRBEEP_OFF) {
269
270 return (DDI_SUCCESS);
271 } else {
272
273 return (DDI_FAILURE);
274 }
275 default:
276
277 return (DDI_FAILURE);
278 }
279 }
280
281
282 /*
283 * grbeep_info:
284 */
285 /* ARGSUSED */
286 static int
grbeep_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)287 grbeep_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
288 void *arg, void **result)
289 {
290 dev_t dev;
291 grbeep_state_t *grbeeptr;
292 int instance, error;
293
294 switch (infocmd) {
295 case DDI_INFO_DEVT2DEVINFO:
296 dev = (dev_t)arg;
297 instance = GRBEEP_UNIT(dev);
298
299 if ((grbeeptr = ddi_get_soft_state(grbeep_statep,
300 instance)) == NULL) {
301
302 return (DDI_FAILURE);
303 }
304
305 *result = (void *)grbeeptr->grbeep_dip;
306
307 error = DDI_SUCCESS;
308 break;
309 case DDI_INFO_DEVT2INSTANCE:
310 dev = (dev_t)arg;
311 instance = GRBEEP_UNIT(dev);
312
313 *result = (void *)(uintptr_t)instance;
314
315 error = DDI_SUCCESS;
316 break;
317 default:
318 error = DDI_FAILURE;
319
320 }
321
322 return (error);
323 }
324
325
326 /*
327 * grbeep_freq() :
328 * Set beep frequency
329 */
330 static void
grbeep_freq(void * arg,int freq)331 grbeep_freq(void *arg, int freq)
332 {
333 dev_info_t *dip = (dev_info_t *)arg;
334 grbeep_state_t *grbeeptr = grbeep_obtain_state(dip);
335 int divisor = 0;
336
337 ASSERT(freq != 0);
338
339 GRBEEP_DEBUG1((CE_CONT, "grbeep_freq: dip=0x%p freq=%d mode=%d",
340 (void *)dip, freq, grbeeptr->grbeep_mode));
341
342 GRBEEP_WRITE_FREQ_CONTROL_REG(GRBEEP_CONTROL);
343
344 divisor = GRBEEP_INPUT_FREQ / freq;
345
346 if (divisor > GRBEEP_DIVISOR_MAX) {
347 divisor = GRBEEP_DIVISOR_MAX;
348 } else if (divisor < GRBEEP_DIVISOR_MIN) {
349 divisor = GRBEEP_DIVISOR_MIN;
350 }
351
352 GRBEEP_DEBUG1((CE_CONT, "grbeep_freq: first=0x%x second=0x%x",
353 (divisor & 0xff), ((divisor & 0xff00) >> 8)));
354
355 GRBEEP_WRITE_FREQ_DIVISOR_REG(divisor & 0xff);
356 GRBEEP_WRITE_FREQ_DIVISOR_REG((divisor & 0xff00) >> 8);
357 }
358
359
360 /*
361 * grbeep_on() :
362 * Turn the beeper on
363 */
364 static void
grbeep_on(void * arg)365 grbeep_on(void *arg)
366 {
367 dev_info_t *dip = (dev_info_t *)arg;
368 grbeep_state_t *grbeeptr = grbeep_obtain_state(dip);
369
370 GRBEEP_DEBUG1((CE_CONT, "grbeep_on: dip = 0x%p mode=%d",
371 (void *)dip, grbeeptr->grbeep_mode));
372
373 if (grbeeptr->grbeep_mode == GRBEEP_OFF) {
374
375 grbeeptr->grbeep_mode = GRBEEP_ON;
376 GRBEEP_DEBUG1((CE_CONT, "grbeep_on: Starting beep"));
377 GRBEEP_WRITE_START_STOP_REG(GRBEEP_START);
378
379 }
380
381 GRBEEP_DEBUG1((CE_CONT, "grbeep_on: dip = 0x%p done", (void *)dip));
382 }
383
384
385 /*
386 * grbeep_off() :
387 * Turn the beeper off
388 */
389 static void
grbeep_off(void * arg)390 grbeep_off(void *arg)
391 {
392 dev_info_t *dip = (dev_info_t *)arg;
393 grbeep_state_t *grbeeptr = grbeep_obtain_state(dip);
394
395 GRBEEP_DEBUG1((CE_CONT, "grbeep_off: dip = 0x%p mode=%d",
396 (void *)dip, grbeeptr->grbeep_mode));
397
398 if (grbeeptr->grbeep_mode == GRBEEP_ON) {
399
400 grbeeptr->grbeep_mode = GRBEEP_OFF;
401 GRBEEP_DEBUG1((CE_CONT, "grbeep_off: Stopping beep"));
402 GRBEEP_WRITE_START_STOP_REG(GRBEEP_STOP);
403
404 }
405
406 GRBEEP_DEBUG1((CE_CONT, "grbeep_off: dip = 0x%p done", (void *)dip));
407 }
408
409 /*
410 * grbeep_map_regs() :
411 *
412 * The write beep port register and spkr control register
413 * should be mapped into a non-cacheable portion of the system
414 * addressable space.
415 */
416 static int
grbeep_map_regs(dev_info_t * dip,grbeep_state_t * grbeeptr)417 grbeep_map_regs(dev_info_t *dip, grbeep_state_t *grbeeptr)
418 {
419 ddi_device_acc_attr_t attr;
420
421 GRBEEP_DEBUG1((CE_CONT, "grbeep_map_regs: Start"));
422
423 /* The host controller will be little endian */
424 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
425 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
426 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
427
428 /* Map in operational registers */
429 if (ddi_regs_map_setup(dip, 2,
430 (caddr_t *)&grbeeptr->grbeep_freq_regs,
431 0,
432 sizeof (grbeep_freq_regs_t),
433 &attr,
434 &grbeeptr->grbeep_freq_regs_handle)
435 != DDI_SUCCESS) {
436
437 GRBEEP_DEBUG((CE_CONT, "grbeep_map_regs: Failed to map"));
438 return (DDI_FAILURE);
439 }
440
441 /* Map in operational registers */
442 if (ddi_regs_map_setup(dip, 3,
443 (caddr_t *)&grbeeptr->grbeep_start_stop_reg,
444 0,
445 1,
446 &attr,
447 &grbeeptr->grbeep_start_stop_reg_handle)
448 != DDI_SUCCESS) {
449
450 GRBEEP_DEBUG((CE_CONT, "grbeep_map_regs: Failed to map"));
451 ddi_regs_map_free((void *)&grbeeptr->grbeep_freq_regs_handle);
452
453 return (DDI_FAILURE);
454 }
455
456 GRBEEP_DEBUG1((CE_CONT, "grbeep_map_regs: done"));
457
458 return (DDI_SUCCESS);
459 }
460
461
462 /*
463 * grbeep_obtain_state:
464 */
465 static grbeep_state_t *
grbeep_obtain_state(dev_info_t * dip)466 grbeep_obtain_state(dev_info_t *dip)
467 {
468 int instance = ddi_get_instance(dip);
469
470 grbeep_state_t *state = ddi_get_soft_state(grbeep_statep, instance);
471
472 ASSERT(state != NULL);
473
474 GRBEEP_DEBUG1((CE_CONT, "grbeep_obtain_state: done"));
475
476 return (state);
477 }
478
479
480 /*
481 * grbeep_cleanup :
482 * Cleanup soft state
483 */
484 static void
grbeep_cleanup(grbeep_state_t * grbeeptr)485 grbeep_cleanup(grbeep_state_t *grbeeptr)
486 {
487 int instance = ddi_get_instance(grbeeptr->grbeep_dip);
488
489 ddi_soft_state_free(grbeep_statep, instance);
490
491 GRBEEP_DEBUG1((CE_CONT, "grbeep_cleanup: done"));
492 }
493