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