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