xref: /illumos-gate/usr/src/uts/sun4u/io/i2c/misc/i2c_svc.c (revision 7ff1e9f5d4f5770ee7f13fb603026ed2b07bd99b)
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 = &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