xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_i2c.c (revision aad9773e38ed2370a628a6416e098f9008fc10a7)
1 /*	$NetBSD: linux_i2c.c,v 1.2 2014/03/18 18:20:43 riastradh Exp $	*/
2 
3 /*-
4  * Copyright (c) 2013 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Taylor R. Campbell.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: linux_i2c.c,v 1.2 2014/03/18 18:20:43 riastradh Exp $");
34 
35 #include <sys/types.h>
36 #include <sys/errno.h>
37 #include <sys/queue.h>		/* XXX include order botch: i2cvar.h needs */
38 
39 #include <dev/i2c/i2cvar.h>
40 #include <dev/i2c/i2c_bitbang.h> /* XXX include order botch */
41 
42 #include <linux/i2c.h>
43 #include <linux/i2c-algo-bit.h>
44 
45 static int	netbsd_i2c_transfer(i2c_tag_t, struct i2c_msg *, int);
46 static i2c_op_t	linux_i2c_flags_op(uint16_t, bool);
47 static int	linux_i2c_flags_flags(uint16_t);
48 static uint32_t	linux_i2cbb_functionality(struct i2c_adapter *);
49 static int	linux_i2cbb_xfer(struct i2c_adapter *, struct i2c_msg *, int);
50 static void	linux_i2cbb_set_bits(void *, uint32_t);
51 static uint32_t	linux_i2cbb_read_bits(void *);
52 static void	linux_i2cbb_set_dir(void *, uint32_t);
53 static int	linux_i2cbb_send_start(void *, int);
54 static int	linux_i2cbb_send_stop(void *, int);
55 static int	linux_i2cbb_initiate_xfer(void *, i2c_addr_t, int);
56 static int	linux_i2cbb_read_byte(void *, uint8_t *, int);
57 static int	linux_i2cbb_write_byte(void *, uint8_t, int);
58 
59 int
60 i2c_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int n)
61 {
62 
63 	return (*adapter->algo->master_xfer)(adapter, msgs, n);
64 }
65 
66 static int
67 netbsd_i2c_transfer(i2c_tag_t i2c, struct i2c_msg *msgs, int n)
68 {
69 	int i;
70 	int error;
71 
72 	for (i = 0; i < n; i++) {
73 		const i2c_op_t op = linux_i2c_flags_op(msgs[i].flags,
74 		    ((i + 1) == n));
75 		const int flags = linux_i2c_flags_flags(msgs[i].flags);
76 
77 		switch (op) {
78 		case I2C_OP_READ:
79 		case I2C_OP_READ_WITH_STOP:
80 			error = iic_exec(i2c, op, msgs[i].addr,
81 			    NULL, 0, msgs[i].buf, msgs[i].len, flags);
82 			break;
83 
84 		case I2C_OP_WRITE:
85 		case I2C_OP_WRITE_WITH_STOP:
86 			error = iic_exec(i2c, op, msgs[i].addr,
87 			    msgs[i].buf, msgs[i].len, NULL, 0, flags);
88 			break;
89 
90 		default:
91 			error = EINVAL;
92 		}
93 
94 		if (error)
95 			/* XXX errno NetBSD->Linux */
96 			return -error;
97 	}
98 
99 	return n;
100 }
101 
102 static i2c_op_t
103 linux_i2c_flags_op(uint16_t flags, bool stop)
104 {
105 
106 	if (ISSET(flags, I2C_M_RD))
107 		return (stop? I2C_OP_READ_WITH_STOP : I2C_OP_READ);
108 	else
109 		return (stop? I2C_OP_WRITE_WITH_STOP : I2C_OP_WRITE);
110 }
111 
112 static int
113 linux_i2c_flags_flags(uint16_t flags __unused)
114 {
115 
116 	return 0;
117 }
118 
119 const struct i2c_algorithm i2c_bit_algo = {
120 	.master_xfer	= linux_i2cbb_xfer,
121 	.functionality	= linux_i2cbb_functionality,
122 };
123 
124 static uint32_t
125 linux_i2cbb_functionality(struct i2c_adapter *adapter __unused)
126 {
127 	uint32_t functions = 0;
128 
129 	functions |= I2C_FUNC_I2C;
130 	functions |= I2C_FUNC_NOSTART;
131 	functions |= I2C_FUNC_SMBUS_EMUL;
132 	functions |= I2C_FUNC_SMBUS_READ_BLOCK_DATA;
133 	functions |= I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
134 #if 0
135 	functions |= I2C_FUNC_10BIT_ADDR;
136 	functions |= I2C_FUNC_PROTOCOL_MANGLING;
137 #endif
138 
139 	return functions;
140 }
141 
142 static int
143 linux_i2cbb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int n)
144 {
145 	struct i2c_algo_bit_data *const abd = adapter->algo_data;
146 	struct i2c_controller controller = {
147 		.ic_cookie		= abd,
148 		.ic_send_start		= linux_i2cbb_send_start,
149 		.ic_send_stop		= linux_i2cbb_send_stop,
150 		.ic_initiate_xfer	= linux_i2cbb_initiate_xfer,
151 		.ic_read_byte		= linux_i2cbb_read_byte,
152 		.ic_write_byte		= linux_i2cbb_write_byte,
153 	};
154 	i2c_tag_t i2c = &controller;
155 	int error;
156 
157 	if (abd->pre_xfer) {
158 		error = (*abd->pre_xfer)(adapter);
159 		if (error)
160 			return error;
161 	}
162 
163 	error = netbsd_i2c_transfer(i2c, msgs, n);
164 
165 	if (abd->post_xfer)
166 		(*abd->post_xfer)(adapter);
167 
168 	return error;
169 }
170 
171 #define	LI2CBB_SDA	0x01
172 #define	LI2CBB_SCL	0x02
173 #define	LI2CBB_INPUT	0x04
174 #define	LI2CBB_OUTPUT	0x08
175 
176 static struct i2c_bitbang_ops linux_i2cbb_ops = {
177 	.ibo_set_bits	= linux_i2cbb_set_bits,
178 	.ibo_set_dir	= linux_i2cbb_set_dir,
179 	.ibo_read_bits	= linux_i2cbb_read_bits,
180 	.ibo_bits	= {
181 		[I2C_BIT_SDA]		= LI2CBB_SDA,
182 		[I2C_BIT_SCL]		= LI2CBB_SCL,
183 		[I2C_BIT_INPUT]		= LI2CBB_INPUT,
184 		[I2C_BIT_OUTPUT]	= LI2CBB_OUTPUT,
185 	},
186 };
187 
188 static void
189 linux_i2cbb_set_bits(void *cookie, uint32_t bits)
190 {
191 	struct i2c_algo_bit_data *const abd = cookie;
192 
193 	(*abd->setsda)(abd->data, (ISSET(bits, LI2CBB_SDA)? 1 : 0));
194 	(*abd->setscl)(abd->data, (ISSET(bits, LI2CBB_SCL)? 1 : 0));
195 }
196 
197 static uint32_t
198 linux_i2cbb_read_bits(void *cookie)
199 {
200 	struct i2c_algo_bit_data *const abd = cookie;
201 	uint32_t bits = 0;
202 
203 	if ((*abd->getsda)(abd->data))
204 		bits |= LI2CBB_SDA;
205 	if ((*abd->getscl)(abd->data))
206 		bits |= LI2CBB_SCL;
207 
208 	return bits;
209 }
210 
211 static void
212 linux_i2cbb_set_dir(void *cookie __unused, uint32_t bits __unused)
213 {
214 	/* Linux doesn't do anything here...  */
215 }
216 
217 static int
218 linux_i2cbb_send_start(void *cookie, int flags)
219 {
220 
221 	return i2c_bitbang_send_start(cookie, flags, &linux_i2cbb_ops);
222 }
223 
224 static int
225 linux_i2cbb_send_stop(void *cookie, int flags)
226 {
227 
228 	return i2c_bitbang_send_stop(cookie, flags, &linux_i2cbb_ops);
229 }
230 
231 static int
232 linux_i2cbb_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
233 {
234 
235 	return i2c_bitbang_initiate_xfer(cookie, addr, flags,
236 	    &linux_i2cbb_ops);
237 }
238 
239 static int
240 linux_i2cbb_read_byte(void *cookie, uint8_t *bytep, int flags)
241 {
242 
243 	return i2c_bitbang_read_byte(cookie, bytep, flags, &linux_i2cbb_ops);
244 }
245 
246 static int
247 linux_i2cbb_write_byte(void *cookie, uint8_t byte, int flags)
248 {
249 
250 	return i2c_bitbang_write_byte(cookie, byte, flags, &linux_i2cbb_ops);
251 }
252