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