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 #include <sys/types.h> 28 #include <sys/conf.h> 29 #include <sys/ddi.h> 30 #include <sys/sunddi.h> 31 #include <sys/modctl.h> 32 #include <sys/ddi_impldefs.h> 33 #include <sys/kmem.h> 34 #include <sys/devops.h> 35 36 /* 37 * The pmugpio driver supports ALOM GPIO bits for resetSC and 38 * watchdog heartbeat on all relevant platforms. Historically, 39 * pmugpio is a leaf off the Chalupa pmubus. In addition to 40 * this support the pmugpio driver has been modified to support 41 * Minneapolis/Boston Controller (MBC) FPGA GPIO and Seattle CPLD 42 * GPIO. 43 */ 44 45 typedef enum { 46 PMUGPIO_MBC, /* Boston MBC FPGA GPIO - 8-bit */ 47 PMUGPIO_CPLD, /* Seattle CPLD GPIO - 8-bit */ 48 PMUGPIO_OTHER /* Chalupa - 8-bit */ 49 } pmugpio_access_type_t; 50 51 /* 52 * FWARC 2005/686: gpio device compatible property 53 */ 54 #define PMUGPIO_DEVICE_TYPE "gpio-device-type" 55 56 /* 57 * CPLD GPIO Register defines. 58 */ 59 #define CPLD_RESET_SC 0x01 /* Reset SC */ 60 #define CPLD_WATCHDOG 0x02 /* Watchdog */ 61 62 #define CPLD_RESET_DELAY 3 /* microsecond delay */ 63 64 /* 65 * MBC FPGA CSR defines. 66 */ 67 #define MBC_PPC_RESET 0x10 /* Reset ALOM */ 68 #define MBC_WATCHDOG 0x40 /* Watchdog heartbeat bit */ 69 70 /* 71 * Time periods, in nanoseconds 72 */ 73 #define PMUGPIO_TWO_SEC 2000000000LL 74 75 static dev_info_t *pmugpio_dip; 76 77 typedef struct pmugpio_state { 78 uint8_t *pmugpio_reset_reg; 79 ddi_acc_handle_t pmugpio_reset_reg_handle; 80 uint8_t *pmugpio_watchdog_reg; 81 ddi_acc_handle_t pmugpio_watchdog_reg_handle; 82 hrtime_t hw_last_pat; 83 pmugpio_access_type_t access_type; 84 } pmugpio_state_t; 85 86 static void *pmugpio_statep; 87 88 static int pmugpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 89 static int pmugpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 90 static int pmugpio_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 91 void **result); 92 static int pmugpio_map_regs(dev_info_t *, pmugpio_state_t *); 93 94 struct cb_ops pmugpio_cb_ops = { 95 nulldev, /* open */ 96 nulldev, /* close */ 97 nulldev, /* strategy */ 98 nulldev, /* print */ 99 nulldev, /* dump */ 100 nulldev, /* read */ 101 nulldev, /* write */ 102 nulldev, /* ioctl */ 103 nulldev, /* devmap */ 104 nulldev, /* mmap */ 105 nulldev, /* segmap */ 106 nochpoll, /* poll */ 107 ddi_prop_op, /* cb_prop_op */ 108 NULL, /* streamtab */ 109 D_MP | D_NEW 110 }; 111 112 static struct dev_ops pmugpio_ops = { 113 DEVO_REV, /* Devo_rev */ 114 0, /* Refcnt */ 115 pmugpio_info, /* Info */ 116 nulldev, /* Identify */ 117 nulldev, /* Probe */ 118 pmugpio_attach, /* Attach */ 119 pmugpio_detach, /* Detach */ 120 nodev, /* Reset */ 121 &pmugpio_cb_ops, /* Driver operations */ 122 0, /* Bus operations */ 123 NULL, /* Power */ 124 ddi_quiesce_not_supported, /* devo_quiesce */ 125 }; 126 127 static struct modldrv modldrv = { 128 &mod_driverops, /* This one is a driver */ 129 "Pmugpio Driver", /* Name of the module. */ 130 &pmugpio_ops, /* Driver ops */ 131 }; 132 133 static struct modlinkage modlinkage = { 134 MODREV_1, (void *)&modldrv, NULL 135 }; 136 137 int 138 _init(void) 139 { 140 int error; 141 142 /* Initialize the soft state structures */ 143 if ((error = ddi_soft_state_init(&pmugpio_statep, 144 sizeof (pmugpio_state_t), 1)) != 0) { 145 return (error); 146 } 147 148 /* Install the loadable module */ 149 if ((error = mod_install(&modlinkage)) != 0) { 150 ddi_soft_state_fini(&pmugpio_statep); 151 } 152 return (error); 153 } 154 155 int 156 _info(struct modinfo *modinfop) 157 { 158 return (mod_info(&modlinkage, modinfop)); 159 } 160 161 int 162 _fini(void) 163 { 164 int error; 165 166 error = mod_remove(&modlinkage); 167 if (error == 0) { 168 /* Release per module resources */ 169 ddi_soft_state_fini(&pmugpio_statep); 170 } 171 return (error); 172 } 173 174 static int 175 pmugpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 176 { 177 int instance; 178 pmugpio_state_t *pmugpio_ptr = NULL; 179 180 switch (cmd) { 181 case DDI_ATTACH: 182 break; 183 case DDI_RESUME: 184 return (DDI_SUCCESS); 185 default: 186 return (DDI_FAILURE); 187 } 188 189 /* Get the instance and create soft state */ 190 instance = ddi_get_instance(dip); 191 if (ddi_soft_state_zalloc(pmugpio_statep, instance) != 0) { 192 return (DDI_FAILURE); 193 } 194 pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance); 195 if (pmugpio_ptr == NULL) { 196 return (DDI_FAILURE); 197 } 198 199 if (pmugpio_map_regs(dip, pmugpio_ptr) != DDI_SUCCESS) { 200 ddi_soft_state_free(pmugpio_statep, instance); 201 return (DDI_FAILURE); 202 } 203 204 /* Display information in the banner */ 205 ddi_report_dev(dip); 206 207 /* Save the dip */ 208 pmugpio_dip = dip; 209 210 return (DDI_SUCCESS); 211 } 212 213 /* ARGSUSED */ 214 static int 215 pmugpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 216 { 217 /* Pointer to soft state */ 218 switch (cmd) { 219 case DDI_SUSPEND: 220 return (DDI_SUCCESS); 221 default: 222 return (DDI_FAILURE); 223 } 224 } 225 226 /* ARGSUSED */ 227 static int 228 pmugpio_info(dev_info_t *dip, ddi_info_cmd_t infocmd, 229 void *arg, void **result) 230 { 231 dev_t dev; 232 int instance, error; 233 234 switch (infocmd) { 235 case DDI_INFO_DEVT2DEVINFO: 236 *result = (void *)pmugpio_dip; 237 error = DDI_SUCCESS; 238 break; 239 case DDI_INFO_DEVT2INSTANCE: 240 dev = (dev_t)arg; 241 instance = getminor(dev); 242 *result = (void *)(uintptr_t)instance; 243 error = DDI_SUCCESS; 244 break; 245 default: 246 error = DDI_FAILURE; 247 } 248 return (error); 249 } 250 251 void 252 pmugpio_watchdog_pat(void) 253 { 254 dev_info_t *dip = pmugpio_dip; 255 int instance; 256 pmugpio_state_t *pmugpio_ptr; 257 hrtime_t now; 258 uint8_t value; 259 260 if (dip == NULL) { 261 return; 262 } 263 instance = ddi_get_instance(dip); 264 pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance); 265 if (pmugpio_ptr == NULL) { 266 return; 267 } 268 /* 269 * The RMC can read interrupts either high to low OR low to high. As 270 * a result all that needs to happen is that when we hit the time to 271 * send an signal we simply need to change the state. 272 */ 273 now = gethrtime(); 274 if ((now - pmugpio_ptr->hw_last_pat) >= PMUGPIO_TWO_SEC) { 275 /* 276 * fetch current reg value and invert it 277 */ 278 switch (pmugpio_ptr->access_type) { 279 case PMUGPIO_CPLD: 280 value = (CPLD_WATCHDOG ^ 281 ddi_get8(pmugpio_ptr->pmugpio_watchdog_reg_handle, 282 pmugpio_ptr->pmugpio_watchdog_reg)); 283 284 ddi_put8(pmugpio_ptr->pmugpio_watchdog_reg_handle, 285 pmugpio_ptr->pmugpio_watchdog_reg, value); 286 break; 287 288 case PMUGPIO_MBC: 289 value = (uint8_t)(MBC_WATCHDOG ^ 290 ddi_get8(pmugpio_ptr->pmugpio_watchdog_reg_handle, 291 pmugpio_ptr->pmugpio_watchdog_reg)); 292 293 ddi_put8(pmugpio_ptr->pmugpio_watchdog_reg_handle, 294 pmugpio_ptr->pmugpio_watchdog_reg, value); 295 break; 296 297 case PMUGPIO_OTHER: 298 value = (uint8_t)(0xff ^ 299 ddi_get8(pmugpio_ptr->pmugpio_watchdog_reg_handle, 300 pmugpio_ptr->pmugpio_watchdog_reg)); 301 302 ddi_put8(pmugpio_ptr->pmugpio_watchdog_reg_handle, 303 pmugpio_ptr->pmugpio_watchdog_reg, value); 304 break; 305 306 default: 307 cmn_err(CE_WARN, "pmugpio_watchdog_pat: Invalid type"); 308 } 309 pmugpio_ptr->hw_last_pat = now; 310 } 311 } 312 313 void 314 pmugpio_reset(void) 315 { 316 dev_info_t *dip = pmugpio_dip; 317 int instance; 318 pmugpio_state_t *pmugpio_ptr; 319 uint8_t value; 320 321 if (dip == NULL) { 322 return; 323 } 324 instance = ddi_get_instance(dip); 325 pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance); 326 if (pmugpio_ptr == NULL) { 327 return; 328 } 329 330 /* 331 * For Chalupa, turn all bits on then off again - pmubus nexus 332 * will ensure that only unmasked bit is affected. 333 * For CPLD and MBC, turn just reset bit on, then off. 334 */ 335 switch (pmugpio_ptr->access_type) { 336 case PMUGPIO_CPLD: 337 value = ddi_get8(pmugpio_ptr->pmugpio_reset_reg_handle, 338 pmugpio_ptr->pmugpio_reset_reg); 339 ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle, 340 pmugpio_ptr->pmugpio_reset_reg, (value | CPLD_RESET_SC)); 341 342 drv_usecwait(CPLD_RESET_DELAY); 343 344 ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle, 345 pmugpio_ptr->pmugpio_reset_reg, (value & ~CPLD_RESET_SC)); 346 break; 347 348 case PMUGPIO_MBC: 349 value = ddi_get8(pmugpio_ptr->pmugpio_reset_reg_handle, 350 pmugpio_ptr->pmugpio_reset_reg); 351 ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle, 352 pmugpio_ptr->pmugpio_reset_reg, 353 (value | MBC_PPC_RESET)); 354 ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle, 355 pmugpio_ptr->pmugpio_reset_reg, 356 (value & ~MBC_PPC_RESET)); 357 break; 358 359 case PMUGPIO_OTHER: 360 ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle, 361 pmugpio_ptr->pmugpio_reset_reg, ~0); 362 ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle, 363 pmugpio_ptr->pmugpio_reset_reg, 0); 364 break; 365 366 default: 367 cmn_err(CE_WARN, "pmugpio_reset: Invalid type"); 368 } 369 } 370 371 static int 372 pmugpio_map_regs(dev_info_t *dip, pmugpio_state_t *pmugpio_ptr) 373 { 374 ddi_device_acc_attr_t attr; 375 char *pmugpio_type; 376 377 /* The host controller will be little endian */ 378 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 379 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 380 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 381 382 /* 383 * Determine access type per FWARC 2005/686. 384 * For Boston and Seattle, the OBP gpio device contains a property 385 * named "gpio-device-type". 386 * 387 * Boston: gpio-device-type = SUNW,mbc 388 * Seattle: gpio-device-type = SUNW,cpld 389 * 390 * If this property does not exist, we are a legacy Chalupa. 391 */ 392 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 393 PMUGPIO_DEVICE_TYPE, &pmugpio_type) == DDI_PROP_SUCCESS) { 394 if (strcmp(pmugpio_type, "SUNW,mbc") == 0) 395 pmugpio_ptr->access_type = PMUGPIO_MBC; 396 else if (strcmp(pmugpio_type, "SUNW,cpld") == 0) 397 pmugpio_ptr->access_type = PMUGPIO_CPLD; 398 else { 399 cmn_err(CE_WARN, "unexpected gpio-device-type: %s\n", 400 pmugpio_type); 401 ddi_prop_free(pmugpio_type); 402 return (DDI_FAILURE); 403 } 404 ddi_prop_free(pmugpio_type); 405 } 406 else 407 pmugpio_ptr->access_type = PMUGPIO_OTHER; 408 409 switch (pmugpio_ptr->access_type) { 410 case PMUGPIO_CPLD: 411 case PMUGPIO_MBC: 412 if (ddi_regs_map_setup(dip, 0, 413 (caddr_t *)&pmugpio_ptr->pmugpio_reset_reg, 0, 1, &attr, 414 &pmugpio_ptr->pmugpio_reset_reg_handle) != DDI_SUCCESS) 415 return (DDI_FAILURE); 416 /* MBC and CPLD have reset and watchdog bits in same reg. */ 417 pmugpio_ptr->pmugpio_watchdog_reg_handle = 418 pmugpio_ptr->pmugpio_reset_reg_handle; 419 pmugpio_ptr->pmugpio_watchdog_reg = 420 pmugpio_ptr->pmugpio_reset_reg; 421 break; 422 423 case PMUGPIO_OTHER: 424 if (ddi_regs_map_setup(dip, 1, 425 (caddr_t *)&pmugpio_ptr->pmugpio_watchdog_reg, 0, 1, &attr, 426 &pmugpio_ptr->pmugpio_watchdog_reg_handle) != DDI_SUCCESS) { 427 return (DDI_FAILURE); 428 } 429 if (ddi_regs_map_setup(dip, 0, 430 (caddr_t *)&pmugpio_ptr->pmugpio_reset_reg, 0, 1, &attr, 431 &pmugpio_ptr->pmugpio_reset_reg_handle) != DDI_SUCCESS) { 432 ddi_regs_map_free( 433 &pmugpio_ptr->pmugpio_watchdog_reg_handle); 434 return (DDI_FAILURE); 435 } 436 break; 437 438 default: 439 cmn_err(CE_WARN, "pmugpio_map_regs: Invalid type"); 440 return (DDI_FAILURE); 441 } 442 443 return (DDI_SUCCESS); 444 } 445