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 2004 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 * Time periods, in nanoseconds 40 */ 41 #define PMUGPIO_TWO_SEC 2000000000LL 42 43 static dev_info_t *pmugpio_dip; 44 45 typedef struct pmugpio_state { 46 uint8_t *pmugpio_reset_reg; 47 ddi_acc_handle_t pmugpio_reset_reg_handle; 48 uint8_t *pmugpio_watchdog_reg; 49 ddi_acc_handle_t pmugpio_watchdog_reg_handle; 50 hrtime_t hw_last_pat; 51 } pmugpio_state_t; 52 53 static void *pmugpio_statep; 54 55 static int pmugpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 56 static int pmugpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 57 static int pmugpio_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 58 void **result); 59 static int pmugpio_map_regs(dev_info_t *, pmugpio_state_t *); 60 61 struct cb_ops pmugpio_cb_ops = { 62 nulldev, /* open */ 63 nulldev, /* close */ 64 nulldev, /* strategy */ 65 nulldev, /* print */ 66 nulldev, /* dump */ 67 nulldev, /* read */ 68 nulldev, /* write */ 69 nulldev, /* ioctl */ 70 nulldev, /* devmap */ 71 nulldev, /* mmap */ 72 nulldev, /* segmap */ 73 nochpoll, /* poll */ 74 ddi_prop_op, /* cb_prop_op */ 75 NULL, /* streamtab */ 76 D_MP | D_NEW 77 }; 78 79 static struct dev_ops pmugpio_ops = { 80 DEVO_REV, /* Devo_rev */ 81 0, /* Refcnt */ 82 pmugpio_info, /* Info */ 83 nulldev, /* Identify */ 84 nulldev, /* Probe */ 85 pmugpio_attach, /* Attach */ 86 pmugpio_detach, /* Detach */ 87 nodev, /* Reset */ 88 &pmugpio_cb_ops, /* Driver operations */ 89 0, /* Bus operations */ 90 NULL /* Power */ 91 }; 92 93 static struct modldrv modldrv = { 94 &mod_driverops, /* This one is a driver */ 95 "Pmugpio Driver %I%", /* Name of the module. */ 96 &pmugpio_ops, /* Driver ops */ 97 }; 98 99 static struct modlinkage modlinkage = { 100 MODREV_1, (void *)&modldrv, NULL 101 }; 102 103 int 104 _init(void) 105 { 106 int error; 107 108 /* Initialize the soft state structures */ 109 if ((error = ddi_soft_state_init(&pmugpio_statep, 110 sizeof (pmugpio_state_t), 1)) != 0) { 111 return (error); 112 } 113 114 /* Install the loadable module */ 115 if ((error = mod_install(&modlinkage)) != 0) { 116 ddi_soft_state_fini(&pmugpio_statep); 117 } 118 return (error); 119 } 120 121 int 122 _info(struct modinfo *modinfop) 123 { 124 return (mod_info(&modlinkage, modinfop)); 125 } 126 127 int 128 _fini(void) 129 { 130 int error; 131 132 error = mod_remove(&modlinkage); 133 if (error == 0) { 134 /* Release per module resources */ 135 ddi_soft_state_fini(&pmugpio_statep); 136 } 137 return (error); 138 } 139 140 static int 141 pmugpio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 142 { 143 int instance; 144 pmugpio_state_t *pmugpio_ptr = NULL; 145 146 switch (cmd) { 147 case DDI_ATTACH: 148 break; 149 case DDI_RESUME: 150 return (DDI_SUCCESS); 151 default: 152 return (DDI_FAILURE); 153 } 154 155 /* Get the instance and create soft state */ 156 instance = ddi_get_instance(dip); 157 if (ddi_soft_state_zalloc(pmugpio_statep, instance) != 0) { 158 return (DDI_FAILURE); 159 } 160 pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance); 161 if (pmugpio_ptr == NULL) { 162 return (DDI_FAILURE); 163 } 164 165 if (pmugpio_map_regs(dip, pmugpio_ptr) != DDI_SUCCESS) { 166 ddi_soft_state_free(pmugpio_statep, instance); 167 return (DDI_FAILURE); 168 } 169 170 /* Display information in the banner */ 171 ddi_report_dev(dip); 172 173 /* Save the dip */ 174 pmugpio_dip = dip; 175 176 return (DDI_SUCCESS); 177 } 178 179 /* ARGSUSED */ 180 static int 181 pmugpio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 182 { 183 /* Pointer to soft state */ 184 switch (cmd) { 185 case DDI_SUSPEND: 186 return (DDI_SUCCESS); 187 default: 188 return (DDI_FAILURE); 189 } 190 } 191 192 /* ARGSUSED */ 193 static int 194 pmugpio_info(dev_info_t *dip, ddi_info_cmd_t infocmd, 195 void *arg, void **result) 196 { 197 dev_t dev; 198 int instance, error; 199 200 switch (infocmd) { 201 case DDI_INFO_DEVT2DEVINFO: 202 *result = (void *)pmugpio_dip; 203 error = DDI_SUCCESS; 204 break; 205 case DDI_INFO_DEVT2INSTANCE: 206 dev = (dev_t)arg; 207 instance = getminor(dev); 208 *result = (void *)(uintptr_t)instance; 209 error = DDI_SUCCESS; 210 break; 211 default: 212 error = DDI_FAILURE; 213 } 214 return (error); 215 } 216 217 void 218 pmugpio_watchdog_pat(void) 219 { 220 dev_info_t *dip = pmugpio_dip; 221 int instance; 222 pmugpio_state_t *pmugpio_ptr; 223 hrtime_t now; 224 uint8_t value; 225 226 if (dip == NULL) { 227 return; 228 } 229 instance = ddi_get_instance(dip); 230 pmugpio_ptr = ddi_get_soft_state(pmugpio_statep, instance); 231 if (pmugpio_ptr == NULL) { 232 return; 233 } 234 /* 235 * The RMC can read interrupts either high to low OR low to high. As 236 * a result all that needs to happen is that when we hit the time to 237 * send an signal we simply need to change the state. 238 */ 239 now = gethrtime(); 240 if ((now - pmugpio_ptr->hw_last_pat) >= PMUGPIO_TWO_SEC) { 241 /* 242 * fetch current reg value and invert it 243 */ 244 value = (uint8_t)(0xff ^ 245 ddi_get8(pmugpio_ptr->pmugpio_watchdog_reg_handle, 246 pmugpio_ptr->pmugpio_watchdog_reg)); 247 248 ddi_put8(pmugpio_ptr->pmugpio_watchdog_reg_handle, 249 pmugpio_ptr->pmugpio_watchdog_reg, value); 250 pmugpio_ptr->hw_last_pat = now; 251 } 252 } 253 254 void 255 pmugpio_reset(void) 256 { 257 dev_info_t *dip = pmugpio_dip; 258 int instance; 259 pmugpio_state_t *pmugpio_ptr; 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 /* 271 * turn all bits on then off again - pmubus nexus will ensure 272 * that only unmasked bit is affected 273 */ 274 ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle, 275 pmugpio_ptr->pmugpio_reset_reg, ~0); 276 ddi_put8(pmugpio_ptr->pmugpio_reset_reg_handle, 277 pmugpio_ptr->pmugpio_reset_reg, 0); 278 } 279 280 static int 281 pmugpio_map_regs(dev_info_t *dip, pmugpio_state_t *pmugpio_ptr) 282 { 283 ddi_device_acc_attr_t attr; 284 285 /* The host controller will be little endian */ 286 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 287 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 288 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 289 290 if (ddi_regs_map_setup(dip, 1, 291 (caddr_t *)&pmugpio_ptr->pmugpio_watchdog_reg, 0, 1, &attr, 292 &pmugpio_ptr->pmugpio_watchdog_reg_handle) != DDI_SUCCESS) { 293 return (DDI_FAILURE); 294 } 295 if (ddi_regs_map_setup(dip, 0, 296 (caddr_t *)&pmugpio_ptr->pmugpio_reset_reg, 0, 1, &attr, 297 &pmugpio_ptr->pmugpio_reset_reg_handle) != DDI_SUCCESS) { 298 ddi_regs_map_free(&pmugpio_ptr->pmugpio_watchdog_reg_handle); 299 return (DDI_FAILURE); 300 } 301 return (DDI_SUCCESS); 302 } 303