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
17kmutex_t i2c_svc_mutex;
18
19static struct modldrv i2c_modldrv = {
20	&mod_miscops,		/* type of module - misc */
21	"I2C module",
22	NULL,
23};
24
25static struct modlinkage i2c_modlinkage = {
26	MODREV_1,
27	&i2c_modldrv,
28	0
29};
30
31i2c_nexus_reg_list_t *nexus_reg_list_head = NULL;
32
33int i2csvcdebug = 0;
34
35int
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
47int
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
59int
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 */
73int
74i2c_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 */
118void
119i2c_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 */
130int
131i2c_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*/
179int
180i2c_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*/
235void
236i2c_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 */
251void
252i2c_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 */
275void
276i2c_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