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