1/* $NetBSD: nouveau_subdev_i2c_bit.c,v 1.1.1.1 2014/08/06 12:36:30 riastradh Exp $ */
2
3/*
4 * Copyright 2012 Red Hat Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors: Ben Skeggs
25 */
26
27#include <sys/cdefs.h>
28__KERNEL_RCSID(0, "$NetBSD: nouveau_subdev_i2c_bit.c,v 1.1.1.1 2014/08/06 12:36:30 riastradh Exp $");
29
30#include "subdev/i2c.h"
31
32#ifdef CONFIG_NOUVEAU_I2C_INTERNAL
33#define T_TIMEOUT 2200000
34#define T_RISEFALL 1000
35#define T_HOLD 5000
36
37static inline void
38i2c_drive_scl(struct nouveau_i2c_port *port, int state)
39{
40 port->func->drive_scl(port, state);
41}
42
43static inline void
44i2c_drive_sda(struct nouveau_i2c_port *port, int state)
45{
46 port->func->drive_sda(port, state);
47}
48
49static inline int
50i2c_sense_scl(struct nouveau_i2c_port *port)
51{
52 return port->func->sense_scl(port);
53}
54
55static inline int
56i2c_sense_sda(struct nouveau_i2c_port *port)
57{
58 return port->func->sense_sda(port);
59}
60
61static void
62i2c_delay(struct nouveau_i2c_port *port, u32 nsec)
63{
64 udelay((nsec + 500) / 1000);
65}
66
67static bool
68i2c_raise_scl(struct nouveau_i2c_port *port)
69{
70 u32 timeout = T_TIMEOUT / T_RISEFALL;
71
72 i2c_drive_scl(port, 1);
73 do {
74 i2c_delay(port, T_RISEFALL);
75 } while (!i2c_sense_scl(port) && --timeout);
76
77 return timeout != 0;
78}
79
80static int
81i2c_start(struct nouveau_i2c_port *port)
82{
83 int ret = 0;
84
85 if (!i2c_sense_scl(port) ||
86 !i2c_sense_sda(port)) {
87 i2c_drive_scl(port, 0);
88 i2c_drive_sda(port, 1);
89 if (!i2c_raise_scl(port))
90 ret = -EBUSY;
91 }
92
93 i2c_drive_sda(port, 0);
94 i2c_delay(port, T_HOLD);
95 i2c_drive_scl(port, 0);
96 i2c_delay(port, T_HOLD);
97 return ret;
98}
99
100static void
101i2c_stop(struct nouveau_i2c_port *port)
102{
103 i2c_drive_scl(port, 0);
104 i2c_drive_sda(port, 0);
105 i2c_delay(port, T_RISEFALL);
106
107 i2c_drive_scl(port, 1);
108 i2c_delay(port, T_HOLD);
109 i2c_drive_sda(port, 1);
110 i2c_delay(port, T_HOLD);
111}
112
113static int
114i2c_bitw(struct nouveau_i2c_port *port, int sda)
115{
116 i2c_drive_sda(port, sda);
117 i2c_delay(port, T_RISEFALL);
118
119 if (!i2c_raise_scl(port))
120 return -ETIMEDOUT;
121 i2c_delay(port, T_HOLD);
122
123 i2c_drive_scl(port, 0);
124 i2c_delay(port, T_HOLD);
125 return 0;
126}
127
128static int
129i2c_bitr(struct nouveau_i2c_port *port)
130{
131 int sda;
132
133 i2c_drive_sda(port, 1);
134 i2c_delay(port, T_RISEFALL);
135
136 if (!i2c_raise_scl(port))
137 return -ETIMEDOUT;
138 i2c_delay(port, T_HOLD);
139
140 sda = i2c_sense_sda(port);
141
142 i2c_drive_scl(port, 0);
143 i2c_delay(port, T_HOLD);
144 return sda;
145}
146
147static int
148i2c_get_byte(struct nouveau_i2c_port *port, u8 *byte, bool last)
149{
150 int i, bit;
151
152 *byte = 0;
153 for (i = 7; i >= 0; i--) {
154 bit = i2c_bitr(port);
155 if (bit < 0)
156 return bit;
157 *byte |= bit << i;
158 }
159
160 return i2c_bitw(port, last ? 1 : 0);
161}
162
163static int
164i2c_put_byte(struct nouveau_i2c_port *port, u8 byte)
165{
166 int i, ret;
167 for (i = 7; i >= 0; i--) {
168 ret = i2c_bitw(port, !!(byte & (1 << i)));
169 if (ret < 0)
170 return ret;
171 }
172
173 ret = i2c_bitr(port);
174 if (ret == 1) /* nack */
175 ret = -EIO;
176 return ret;
177}
178
179static int
180i2c_addr(struct nouveau_i2c_port *port, struct i2c_msg *msg)
181{
182 u32 addr = msg->addr << 1;
183 if (msg->flags & I2C_M_RD)
184 addr |= 1;
185 return i2c_put_byte(port, addr);
186}
187
188static int
189i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
190{
191 struct nouveau_i2c_port *port = adap->algo_data;
192 struct i2c_msg *msg = msgs;
193 int ret = 0, mcnt = num;
194
195 if (port->func->acquire)
196 port->func->acquire(port);
197
198 while (!ret && mcnt--) {
199 u8 remaining = msg->len;
200 u8 *ptr = msg->buf;
201
202 ret = i2c_start(port);
203 if (ret == 0)
204 ret = i2c_addr(port, msg);
205
206 if (msg->flags & I2C_M_RD) {
207 while (!ret && remaining--)
208 ret = i2c_get_byte(port, ptr++, !remaining);
209 } else {
210 while (!ret && remaining--)
211 ret = i2c_put_byte(port, *ptr++);
212 }
213
214 msg++;
215 }
216
217 i2c_stop(port);
218 return (ret < 0) ? ret : num;
219}
220#else
221static int
222i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
223{
224 return -ENODEV;
225}
226#endif
227
228static u32
229i2c_bit_func(struct i2c_adapter *adap)
230{
231 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
232}
233
234const struct i2c_algorithm nouveau_i2c_bit_algo = {
235 .master_xfer = i2c_bit_xfer,
236 .functionality = i2c_bit_func
237};
238