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