1 /* 2 * Copyright 2006 Sun Microsystems, Inc. 3 * All rights reserved. Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 #include <sys/types.h> 9 #include <sys/conf.h> 10 #include <sys/kmem.h> 11 #include <sys/open.h> 12 #include <sys/ddi.h> 13 #include <sys/sunddi.h> 14 #include <sys/file.h> 15 #include <sys/modctl.h> 16 #include <sys/i2c/misc/i2c_svc.h> 17 #include <sys/i2c/misc/i2c_svc_impl.h> 18 19 kmutex_t i2c_svc_mutex; 20 21 static struct modldrv i2c_modldrv = { 22 &mod_miscops, /* type of module - misc */ 23 "I2C module ver %I%", 24 NULL, 25 }; 26 27 static struct modlinkage i2c_modlinkage = { 28 MODREV_1, 29 &i2c_modldrv, 30 0 31 }; 32 33 i2c_nexus_reg_list_t *nexus_reg_list_head = NULL; 34 35 int i2csvcdebug = 0; 36 37 int 38 _init(void) 39 { 40 int error; 41 42 if ((error = mod_install(&i2c_modlinkage)) == 0) { 43 mutex_init(&i2c_svc_mutex, NULL, MUTEX_DRIVER, NULL); 44 } 45 46 return (error); 47 } 48 49 int 50 _fini(void) 51 { 52 int error; 53 54 if ((error = mod_remove(&i2c_modlinkage)) == 0) { 55 mutex_destroy(&i2c_svc_mutex); 56 } 57 58 return (error); 59 } 60 61 int 62 _info(struct modinfo *modinfop) 63 { 64 return (mod_info(&i2c_modlinkage, modinfop)); 65 } 66 67 /* 68 * i2c_client_register is called by I2C client drivers, 69 * typically in attach, but before starting any bus transfers. 70 * 71 * dip - the client device's dip. 72 * i2c_hdl - pointer to a handle returned on success. 73 * 74 */ 75 int 76 i2c_client_register(dev_info_t *dip, i2c_client_hdl_t *i2c_hdl) 77 { 78 dev_info_t *pdip; 79 i2c_client_hdl_t hdl; 80 i2c_nexus_reg_list_t *reg_list; 81 82 pdip = ddi_get_parent(dip); 83 84 mutex_enter(&i2c_svc_mutex); 85 86 reg_list = nexus_reg_list_head; 87 /* 88 * search parent reg list to find dip's parent. 89 */ 90 for (; reg_list != NULL; reg_list = reg_list->next) { 91 if (reg_list->dip == pdip) { 92 break; 93 } 94 } 95 96 mutex_exit(&i2c_svc_mutex); 97 98 if (reg_list == NULL) { 99 100 return (I2C_FAILURE); 101 } 102 103 hdl = kmem_alloc(sizeof (struct i2c_client_hdl_impl), KM_SLEEP); 104 105 CHDL(hdl)->chdl_dip = dip; 106 CHDL(hdl)->chdl_nexus_reg = ®_list->nexus_reg; 107 *i2c_hdl = hdl; 108 109 return (I2C_SUCCESS); 110 } 111 112 /* 113 * i2c_client_unregister() is called by the I2C client driver 114 * when it no longer wishes to transmit on the I2C bus, typically 115 * during its detach routine. 116 * 117 * hdl - handle previously returned by i2c_client_register(). 118 * 119 */ 120 void 121 i2c_client_unregister(i2c_client_hdl_t hdl) 122 { 123 kmem_free(hdl, sizeof (struct i2c_client_hdl_impl)); 124 } 125 126 /* 127 * i2c_transfer() is called by client drivers to handle 128 * I2C data transfers. It performs some basic sanity checking of 129 * flags vs. i2c_len and i2c_wlen values, and then calls the 130 * parent's i2c_transfer() function to handle the actual transfer. 131 */ 132 int 133 i2c_transfer(i2c_client_hdl_t hdl, i2c_transfer_t *i2c_tran) 134 { 135 switch (i2c_tran->i2c_flags) { 136 case I2C_WR: 137 if (i2c_tran->i2c_wlen == 0) { 138 139 return (EINVAL); 140 } 141 break; 142 case I2C_RD: 143 if (i2c_tran->i2c_rlen == 0) { 144 145 return (EINVAL); 146 } 147 break; 148 case I2C_WR_RD: 149 if (i2c_tran->i2c_wlen == 0 || i2c_tran->i2c_rlen == 0) { 150 151 return (EINVAL); 152 } 153 break; 154 default: 155 156 return (EINVAL); 157 } 158 159 if (CHDL(hdl)->chdl_nexus_reg->i2c_nexus_transfer != NULL) { 160 (*CHDL(hdl)->chdl_nexus_reg->i2c_nexus_transfer) 161 (CHDL(hdl)->chdl_dip, i2c_tran); 162 163 return (i2c_tran->i2c_result); 164 } else { 165 166 return (ENOTSUP); 167 } 168 } 169 170 /* 171 * i2c_transfer_alloc() allocates a i2c_transfer structure along 172 * with read and write buffers of size rlen and wlen respectively. 173 * 174 * i2c_hdl - handle returned previously by i2c_client_register() 175 * i2c - address of pointer to allocated buffer returned on success. 176 * wlen - write size buffer to allocate. May be 0. 177 * rlen - read size buffer to allocate. May be 0. 178 * flags - I2C_SLEEP or I2C_NOSLEEP 179 */ 180 /*ARGSUSED*/ 181 int 182 i2c_transfer_alloc(i2c_client_hdl_t hdl, 183 i2c_transfer_t **i2c, 184 ushort_t wlen, 185 ushort_t rlen, 186 uint_t flags) 187 { 188 i2c_transfer_alloc_t *i2cw; 189 int sleep; 190 int size; 191 192 /* 193 * set i2c to NULL in case the caller just checks i2c 194 * to determine failures. 195 */ 196 *i2c = NULL; 197 198 if (flags & I2C_SLEEP) { 199 sleep = KM_SLEEP; 200 } else if (flags & I2C_NOSLEEP) { 201 sleep = KM_NOSLEEP; 202 } else { 203 sleep = KM_NOSLEEP; 204 } 205 206 size = sizeof (i2c_transfer_alloc_t) + rlen + wlen; 207 208 if ((i2cw = kmem_zalloc(size, sleep)) == NULL) { 209 210 return (I2C_FAILURE); 211 } 212 213 i2cw->i2cw_size = size; 214 i2cw->i2cw_i2ct.i2c_wlen = wlen; 215 i2cw->i2cw_i2ct.i2c_rlen = rlen; 216 if (wlen != 0) { 217 i2cw->i2cw_i2ct.i2c_wbuf = (uchar_t *)i2cw + 218 sizeof (i2c_transfer_alloc_t); 219 } 220 if (rlen != 0) { 221 i2cw->i2cw_i2ct.i2c_rbuf = (uchar_t *)i2cw + 222 sizeof (i2c_transfer_alloc_t) + wlen; 223 } 224 *i2c = (i2c_transfer_t *)i2cw; 225 226 return (I2C_SUCCESS); 227 } 228 229 /* 230 * i2c_transfer_free() is called to free a buffer previously 231 * allocated by i2c_transfer_allocate(). 232 * 233 * i2c_hdl - handle returned previously by i2c_client_register() 234 * i2c - buffer previously allocated by i2c_transfer_allocate() 235 */ 236 /*ARGSUSED*/ 237 void 238 i2c_transfer_free(i2c_client_hdl_t hdl, i2c_transfer_t *i2c_tran) 239 { 240 i2c_transfer_alloc_t *i2cw = (i2c_transfer_alloc_t *)i2c_tran; 241 242 kmem_free(i2cw, i2cw->i2cw_size); 243 } 244 245 /* 246 * i2c_nexus_register() is called by the nexus driver to inform 247 * I2C services that it is ready to accept transactions, and 248 * give the I2C services a vector of functions. 249 * 250 * dip - dip of the bus controller 251 * nexus_reg - pointer to reg structure of vector functions 252 */ 253 void 254 i2c_nexus_register(dev_info_t *dip, i2c_nexus_reg_t *nexus_reg) 255 { 256 i2c_nexus_reg_list_t *nexus_reglist; 257 258 nexus_reglist = kmem_alloc(sizeof (struct i2c_nexus_reg_list), 259 KM_SLEEP); 260 261 mutex_enter(&i2c_svc_mutex); 262 nexus_reglist->next = nexus_reg_list_head; 263 nexus_reg_list_head = nexus_reglist; 264 mutex_exit(&i2c_svc_mutex); 265 266 nexus_reglist->nexus_reg = *nexus_reg; 267 nexus_reglist->dip = dip; 268 } 269 270 /* 271 * i2c_nexus_unregister() is called by the nexus driver when 272 * it is no longer able to accept transactions for its I2C 273 * children. 274 * 275 * dip - dev_info pointer passed to i2c_nexus_register(). 276 */ 277 void 278 i2c_nexus_unregister(dev_info_t *dip) 279 { 280 i2c_nexus_reg_list_t **reg_list; 281 i2c_nexus_reg_list_t *save = NULL; 282 283 mutex_enter(&i2c_svc_mutex); 284 285 reg_list = &nexus_reg_list_head; 286 287 /* 288 * reg_list is the address of the pointer to an element on 289 * the reg list. It starts out being the address of the 290 * list head, but then is changed to the address of the 291 * next pointer in a list element. Once the element to 292 * delete is found, then we change the pointer to the 293 * address found in the next pointer of the element to 294 * be deleted. 295 */ 296 for (; *reg_list != NULL; reg_list = &(*reg_list)->next) { 297 if ((*reg_list)->dip == dip) { 298 save = *reg_list; 299 /* prev next pointer adjusted to point */ 300 *reg_list = (*reg_list)->next; 301 break; 302 } 303 } 304 mutex_exit(&i2c_svc_mutex); 305 if (save != NULL) { 306 kmem_free(save, sizeof (i2c_nexus_reg_list_t)); 307 } else { 308 cmn_err(CE_WARN, "could not find nexus reg to free"); 309 } 310 } 311