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