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 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 148 _info(struct modinfo *modinfop) 149 { 150 return (mod_info(&modlinkage, modinfop)); 151 } 152 153 154 int 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 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 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 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 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 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 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 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 * 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 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