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