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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
i2c_client_register(dev_info_t * dip,i2c_client_hdl_t * i2c_hdl)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
i2c_client_unregister(i2c_client_hdl_t hdl)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
i2c_transfer(i2c_client_hdl_t hdl,i2c_transfer_t * i2c_tran)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
i2c_transfer_alloc(i2c_client_hdl_t hdl,i2c_transfer_t ** i2c,ushort_t wlen,ushort_t rlen,uint_t flags)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
i2c_transfer_free(i2c_client_hdl_t hdl,i2c_transfer_t * i2c_tran)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
i2c_nexus_register(dev_info_t * dip,i2c_nexus_reg_t * nexus_reg)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
i2c_nexus_unregister(dev_info_t * dip)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