1 /* 2 * Copyright (c) 1999-2001 by Sun Microsystems, Inc. 3 * All rights reserved. 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 if (flags & I2C_SLEEP) { 193 sleep = KM_SLEEP; 194 } else if (flags & I2C_NOSLEEP) { 195 sleep = KM_NOSLEEP; 196 } else { 197 sleep = KM_NOSLEEP; 198 } 199 200 size = sizeof (i2c_transfer_alloc_t) + rlen + wlen; 201 202 if ((i2cw = kmem_zalloc(size, sleep)) == NULL) { 203 204 return (I2C_FAILURE); 205 } 206 207 i2cw->i2cw_size = size; 208 i2cw->i2cw_i2ct.i2c_wlen = wlen; 209 i2cw->i2cw_i2ct.i2c_rlen = rlen; 210 if (wlen != 0) { 211 i2cw->i2cw_i2ct.i2c_wbuf = (uchar_t *)i2cw + 212 sizeof (i2c_transfer_alloc_t); 213 } 214 if (rlen != 0) { 215 i2cw->i2cw_i2ct.i2c_rbuf = (uchar_t *)i2cw + 216 sizeof (i2c_transfer_alloc_t) + wlen; 217 } 218 *i2c = (i2c_transfer_t *)i2cw; 219 220 return (I2C_SUCCESS); 221 } 222 223 /* 224 * i2c_transfer_free() is called to free a buffer previously 225 * allocated by i2c_transfer_allocate(). 226 * 227 * i2c_hdl - handle returned previously by i2c_client_register() 228 * i2c - buffer previously allocated by i2c_transfer_allocate() 229 */ 230 /*ARGSUSED*/ 231 void 232 i2c_transfer_free(i2c_client_hdl_t hdl, i2c_transfer_t *i2c_tran) 233 { 234 i2c_transfer_alloc_t *i2cw = (i2c_transfer_alloc_t *)i2c_tran; 235 236 kmem_free(i2cw, i2cw->i2cw_size); 237 } 238 239 /* 240 * i2c_nexus_register() is called by the nexus driver to inform 241 * I2C services that it is ready to accept transactions, and 242 * give the I2C services a vector of functions. 243 * 244 * dip - dip of the bus controller 245 * nexus_reg - pointer to reg structure of vector functions 246 */ 247 void 248 i2c_nexus_register(dev_info_t *dip, i2c_nexus_reg_t *nexus_reg) 249 { 250 i2c_nexus_reg_list_t *nexus_reglist; 251 252 nexus_reglist = kmem_alloc(sizeof (struct i2c_nexus_reg_list), 253 KM_SLEEP); 254 255 mutex_enter(&i2c_svc_mutex); 256 nexus_reglist->next = nexus_reg_list_head; 257 nexus_reg_list_head = nexus_reglist; 258 mutex_exit(&i2c_svc_mutex); 259 260 nexus_reglist->nexus_reg = *nexus_reg; 261 nexus_reglist->dip = dip; 262 } 263 264 /* 265 * i2c_nexus_unregister() is called by the nexus driver when 266 * it is no longer able to accept transactions for its I2C 267 * children. 268 * 269 * dip - dev_info pointer passed to i2c_nexus_register(). 270 */ 271 void 272 i2c_nexus_unregister(dev_info_t *dip) 273 { 274 i2c_nexus_reg_list_t **reg_list; 275 i2c_nexus_reg_list_t *save = NULL; 276 277 mutex_enter(&i2c_svc_mutex); 278 279 reg_list = &nexus_reg_list_head; 280 281 /* 282 * reg_list is the address of the pointer to an element on 283 * the reg list. It starts out being the address of the 284 * list head, but then is changed to the address of the 285 * next pointer in a list element. Once the element to 286 * delete is found, then we change the pointer to the 287 * address found in the next pointer of the element to 288 * be deleted. 289 */ 290 for (; *reg_list != NULL; reg_list = &(*reg_list)->next) { 291 if ((*reg_list)->dip == dip) { 292 save = *reg_list; 293 /* prev next pointer adjusted to point */ 294 *reg_list = (*reg_list)->next; 295 break; 296 } 297 } 298 mutex_exit(&i2c_svc_mutex); 299 if (save != NULL) { 300 kmem_free(save, sizeof (i2c_nexus_reg_list_t)); 301 } else { 302 cmn_err(CE_WARN, "could not find nexus reg to free"); 303 } 304 } 305