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 1999-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 bbc based beep mechanism. 31 * 32 */ 33 #include <sys/types.h> 34 #include <sys/conf.h> 35 #include <sys/ddi.h> 36 #include <sys/sunddi.h> 37 #include <sys/modctl.h> 38 #include <sys/ddi_impldefs.h> 39 #include <sys/kmem.h> 40 #include <sys/devops.h> 41 #include <sys/bbc_beep.h> 42 #include <sys/beep_driver.h> 43 44 45 /* Pointer to the state structure */ 46 static void *bbc_beep_statep; 47 48 49 /* 50 * Debug stuff 51 */ 52 #ifdef DEBUG 53 int bbc_beep_debug = 0; 54 #define BBC_BEEP_DEBUG(args) if (bbc_beep_debug) cmn_err args 55 #define BBC_BEEP_DEBUG1(args) if (bbc_beep_debug > 1) cmn_err args 56 #else 57 #define BBC_BEEP_DEBUG(args) 58 #define BBC_BEEP_DEBUG1(args) 59 #endif 60 61 62 /* 63 * Prototypes 64 */ 65 static int bbc_beep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 66 static int bbc_beep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 67 static int bbc_beep_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 68 void **result); 69 static void bbc_beep_freq(dev_info_t *, int); 70 static void bbc_beep_on(dev_info_t *); 71 static void bbc_beep_off(dev_info_t *); 72 static void bbc_beep_cleanup(bbc_beep_state_t *); 73 static int bbc_beep_map_regs(dev_info_t *, bbc_beep_state_t *); 74 static bbc_beep_state_t *bbc_beep_obtain_state(dev_info_t *); 75 static unsigned long bbc_beep_hztocounter(int); 76 77 78 struct cb_ops bbc_beep_cb_ops = { 79 nulldev, /* open */ 80 nulldev, /* close */ 81 nulldev, /* strategy */ 82 nulldev, /* print */ 83 nulldev, /* dump */ 84 nulldev, /* read */ 85 nulldev, /* write */ 86 nulldev, /* ioctl */ 87 nulldev, /* devmap */ 88 nulldev, /* mmap */ 89 nulldev, /* segmap */ 90 nochpoll, /* poll */ 91 ddi_prop_op, /* cb_prop_op */ 92 NULL, /* streamtab */ 93 D_64BIT | D_MP | D_NEW| D_HOTPLUG 94 }; 95 96 97 static struct dev_ops bbc_beep_ops = { 98 DEVO_REV, /* Devo_rev */ 99 0, /* Refcnt */ 100 bbc_beep_info, /* Info */ 101 nulldev, /* Identify */ 102 nulldev, /* Probe */ 103 bbc_beep_attach, /* Attach */ 104 bbc_beep_detach, /* Detach */ 105 nodev, /* Reset */ 106 &bbc_beep_cb_ops, /* Driver operations */ 107 0, /* Bus operations */ 108 ddi_power /* Power */ 109 }; 110 111 112 static struct modldrv modldrv = { 113 &mod_driverops, /* This one is a driver */ 114 "BBC Beep Driver %I%", /* Name of the module. */ 115 &bbc_beep_ops, /* Driver ops */ 116 }; 117 118 119 static struct modlinkage modlinkage = { 120 MODREV_1, (void *)&modldrv, NULL 121 }; 122 123 124 int 125 _init(void) 126 { 127 int error; 128 129 /* Initialize the soft state structures */ 130 if ((error = ddi_soft_state_init(&bbc_beep_statep, 131 sizeof (bbc_beep_state_t), 1)) != 0) { 132 133 return (error); 134 } 135 136 /* Install the loadable module */ 137 if ((error = mod_install(&modlinkage)) != 0) { 138 ddi_soft_state_fini(&bbc_beep_statep); 139 } 140 141 return (error); 142 } 143 144 145 int 146 _info(struct modinfo *modinfop) 147 { 148 return (mod_info(&modlinkage, modinfop)); 149 } 150 151 152 int 153 _fini(void) 154 { 155 int error; 156 157 error = mod_remove(&modlinkage); 158 159 if (error == 0) { 160 /* Release per module resources */ 161 ddi_soft_state_fini(&bbc_beep_statep); 162 } 163 164 return (error); 165 } 166 167 168 /* 169 * Beep entry points 170 */ 171 172 /* 173 * bbc_beep_attach: 174 */ 175 static int 176 bbc_beep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 177 { 178 int instance; /* Instance number */ 179 180 /* Pointer to soft state */ 181 bbc_beep_state_t *bbc_beeptr = NULL; 182 183 BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_attach: Start")); 184 185 switch (cmd) { 186 case DDI_ATTACH: 187 break; 188 case DDI_RESUME: 189 return (DDI_SUCCESS); 190 default: 191 return (DDI_FAILURE); 192 } 193 194 /* Get the instance and create soft state */ 195 instance = ddi_get_instance(dip); 196 197 if (ddi_soft_state_zalloc(bbc_beep_statep, instance) != 0) { 198 199 return (DDI_FAILURE); 200 } 201 202 bbc_beeptr = ddi_get_soft_state(bbc_beep_statep, instance); 203 204 if (bbc_beeptr == NULL) { 205 206 return (DDI_FAILURE); 207 } 208 209 BBC_BEEP_DEBUG1((CE_CONT, "bbc_beeptr = 0x%p, instance %x", 210 (void *)bbc_beeptr, instance)); 211 212 /* Save the dip */ 213 bbc_beeptr->bbc_beep_dip = dip; 214 215 /* Initialize beeper mode */ 216 bbc_beeptr->bbc_beep_mode = BBC_BEEP_OFF; 217 218 /* Map the Beep Control and Beep counter Registers */ 219 if (bbc_beep_map_regs(dip, bbc_beeptr) != DDI_SUCCESS) { 220 221 BBC_BEEP_DEBUG((CE_WARN, \ 222 "bbc_beep_attach: Mapping of bbc registers failed.")); 223 224 bbc_beep_cleanup(bbc_beeptr); 225 226 return (DDI_FAILURE); 227 } 228 229 (void) beep_init(dip, bbc_beep_on, bbc_beep_off, bbc_beep_freq); 230 231 /* Display information in the banner */ 232 ddi_report_dev(dip); 233 234 BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_attach: dip = 0x%p done", 235 (void *)dip)); 236 237 return (DDI_SUCCESS); 238 } 239 240 241 /* 242 * bbc_beep_detach: 243 */ 244 /* ARGSUSED */ 245 static int 246 bbc_beep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 247 { 248 /* Pointer to soft state */ 249 bbc_beep_state_t *bbc_beeptr = NULL; 250 251 BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_detach: Start")); 252 253 switch (cmd) { 254 case DDI_SUSPEND: 255 bbc_beeptr = bbc_beep_obtain_state(dip); 256 257 if (bbc_beeptr == NULL) { 258 259 return (DDI_FAILURE); 260 } 261 262 /* 263 * If a beep is in progress; fail suspend 264 */ 265 if (bbc_beeptr->bbc_beep_mode == BBC_BEEP_OFF) { 266 return (DDI_SUCCESS); 267 } else { 268 return (DDI_FAILURE); 269 } 270 default: 271 272 return (DDI_FAILURE); 273 } 274 } 275 276 277 /* 278 * bbc_beep_info: 279 */ 280 /* ARGSUSED */ 281 static int 282 bbc_beep_info(dev_info_t *dip, ddi_info_cmd_t infocmd, 283 void *arg, void **result) 284 { 285 dev_t dev; 286 bbc_beep_state_t *bbc_beeptr; 287 int instance, error; 288 289 switch (infocmd) { 290 291 case DDI_INFO_DEVT2DEVINFO: 292 dev = (dev_t)arg; 293 instance = BEEP_UNIT(dev); 294 295 if ((bbc_beeptr = ddi_get_soft_state(bbc_beep_statep, 296 instance)) == NULL) { 297 298 return (DDI_FAILURE); 299 } 300 301 *result = (void *)bbc_beeptr->bbc_beep_dip; 302 303 error = DDI_SUCCESS; 304 break; 305 case DDI_INFO_DEVT2INSTANCE: 306 dev = (dev_t)arg; 307 instance = BEEP_UNIT(dev); 308 309 *result = (void *)(uintptr_t)instance; 310 311 error = DDI_SUCCESS; 312 break; 313 default: 314 error = DDI_FAILURE; 315 316 } 317 318 return (error); 319 } 320 321 322 /* 323 * bbc_beep_freq() : 324 * Set the frequency 325 */ 326 static void 327 bbc_beep_freq(dev_info_t *dip, int freq) 328 { 329 unsigned long counter; 330 int8_t beep_c2 = 0; 331 int8_t beep_c3 = 0; 332 333 bbc_beep_state_t *bbc_beeptr = bbc_beep_obtain_state(dip); 334 335 /* Convert the frequency in hz to the bbc counter value */ 336 counter = bbc_beep_hztocounter(freq); 337 338 /* Extract relevant second and third byte of counter value */ 339 beep_c2 = (counter & 0xff00) >> 8; 340 beep_c3 = (counter & 0xff0000) >> 16; 341 342 /* 343 * We need to write individual bytes instead of writing 344 * all of 32 bits to take care of allignment problem. 345 * Write 0 to LS 8 bits and MS 8 bits 346 * Write beep_c3 to bit 8..15 and beep_c2 to bit 16..24 347 * Little Endian format 348 */ 349 BEEP_WRITE_COUNTER_REG(0, 0); 350 BEEP_WRITE_COUNTER_REG(1, beep_c3); 351 BEEP_WRITE_COUNTER_REG(2, beep_c2); 352 BEEP_WRITE_COUNTER_REG(3, 0); 353 354 BBC_BEEP_DEBUG1((CE_CONT, 355 "bbc_beep_freq: dip = 0x%p, freq = %d, counter = 0x%x : Done", 356 (void *)dip, freq, (int)counter)); 357 } 358 359 360 /* 361 * bbc_beep_on() : 362 * Turn the beeper on 363 */ 364 static void 365 bbc_beep_on(dev_info_t *dip) 366 { 367 bbc_beep_state_t *bbc_beeptr = bbc_beep_obtain_state(dip); 368 369 BEEP_WRITE_CTRL_REG(BBC_BEEP_ON); 370 371 bbc_beeptr->bbc_beep_mode = BBC_BEEP_ON; 372 373 BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_on: dip = 0x%p done", 374 (void *)dip)); 375 } 376 377 378 /* 379 * bbc_beep_off() : 380 * Turn the beeper off 381 */ 382 static void 383 bbc_beep_off(dev_info_t *dip) 384 { 385 bbc_beep_state_t *bbc_beeptr = bbc_beep_obtain_state(dip); 386 387 BEEP_WRITE_CTRL_REG(BBC_BEEP_OFF); 388 389 bbc_beeptr->bbc_beep_mode = BBC_BEEP_OFF; 390 391 BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_off: dip = 0x%p done", 392 (void *)dip)); 393 } 394 395 396 /* 397 * bbc_beep_map_regs() : 398 * 399 * The Keyboard Beep Control Register and Keyboard Beep Counter Register 400 * should be mapped into a non-cacheable portion of the system 401 * addressable space. 402 */ 403 static int 404 bbc_beep_map_regs(dev_info_t *dip, bbc_beep_state_t *bbc_beeptr) 405 { 406 ddi_device_acc_attr_t attr; 407 408 BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_map_regs: Start\n")); 409 410 /* The host controller will be little endian */ 411 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 412 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 413 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 414 415 /* Map in operational registers */ 416 if (ddi_regs_map_setup(dip, 0, 417 (caddr_t *)&bbc_beeptr->bbc_beep_regsp, 418 0, 419 sizeof (bbc_beep_regs_t), 420 &attr, 421 &bbc_beeptr->bbc_beep_regs_handle) != DDI_SUCCESS) { 422 423 return (DDI_FAILURE); 424 } 425 426 BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_map_regs: done\n")); 427 428 return (DDI_SUCCESS); 429 } 430 431 432 /* 433 * bbc_beep_obtain_state: 434 */ 435 static bbc_beep_state_t * 436 bbc_beep_obtain_state(dev_info_t *dip) 437 { 438 int instance = ddi_get_instance(dip); 439 440 bbc_beep_state_t *state = ddi_get_soft_state(bbc_beep_statep, instance); 441 442 ASSERT(state != NULL); 443 444 BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_obtain_state: done")); 445 446 return (state); 447 } 448 449 450 /* 451 * bbc_beep_cleanup : 452 * Cleanup soft state 453 */ 454 static void 455 bbc_beep_cleanup(bbc_beep_state_t *bbc_beeptr) 456 { 457 int instance = ddi_get_instance(bbc_beeptr->bbc_beep_dip); 458 459 ddi_soft_state_free(bbc_beep_statep, instance); 460 461 BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_cleanup: done")); 462 } 463 464 465 /* 466 * bbc_beep_hztocounter() : 467 * Given a frequency in hz, find out the value to 468 * be set in the Keyboard Beep Counter register 469 * BBC beeper uses the following formula to calculate 470 * frequency. The formulae is : 471 * frequency generated = system freq /2^(n+2) 472 * Where n = position of the bit of counter register 473 * that is turned on and can range between 10 to 18. 474 * So in this function, the inputs are frequency generated 475 * and system frequency and we need to find out n, i.e, which 476 * bit to turn on.(Ref. to Section 4.2.22 of the BBC programming 477 * manual). 478 */ 479 unsigned long 480 bbc_beep_hztocounter(int freq) 481 { 482 int i; 483 unsigned long counter; 484 int newfreq, oldfreq; 485 486 int system_freq; 487 488 /* 489 * Get system frequency for the root dev_info properties 490 */ 491 system_freq = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(), 492 0, "clock-frequency", 0); 493 494 oldfreq = 0; 495 496 /* 497 * Calculate frequency by turning on ith bit and 498 * matching it with the passed frequency and we do this 499 * in a loop for all the relevant bits 500 */ 501 for (i = BBC_BEEP_MIN_SHIFT, counter = 1 << BBC_BEEP_MSBIT; 502 i >= BBC_BEEP_MAX_SHIFT; i--, counter >>= 1) { 503 504 /* 505 * Calculate the frequency by dividing the system 506 * frequency by 2^i 507 */ 508 newfreq = system_freq >> i; 509 510 /* 511 * Check if we turn on the ith bit, the 512 * frequency matches exactly or not 513 */ 514 if (newfreq == freq) { 515 /* 516 * Exact match of passed frequency with the 517 * counter value 518 */ 519 520 return (counter); 521 } 522 523 /* 524 * If calculated frequency is bigger 525 * return the passed frequency 526 */ 527 if (newfreq > freq) { 528 529 if (i == BBC_BEEP_MIN_SHIFT) { 530 /* Input freq is less than the possible min */ 531 532 return (counter); 533 } 534 535 /* 536 * Find out the nearest frequency to the passed 537 * frequency by comparing the difference between 538 * the calculated frequency and the passed frequency 539 */ 540 if ((freq - oldfreq) > (newfreq - freq)) { 541 /* Return new counter corres. to newfreq */ 542 543 return (counter); 544 } 545 546 /* Return old counter corresponding to oldfreq */ 547 548 return (counter << 1); 549 } 550 551 oldfreq = newfreq; 552 } 553 554 /* 555 * Input freq is greater than the possible max; 556 * Back off the counter value and return max counter 557 * value possible in the register 558 */ 559 return (counter << 1); 560 } 561