aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2015-01-13 07:13:14 -0500
committerBen Skeggs <bskeggs@redhat.com>2015-01-21 21:15:10 -0500
commitc39f472e9f14e49a9bc091977ced0ec45fc00c57 (patch)
tree75af3291cccda2482913cc0044888a8a86f4841b /drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c
parent055a65d5987a7f246c3fc2297158286882dbdbcf (diff)
drm/nouveau: remove symlinks, move core/ to nvkm/ (no code changes)
The symlinks were annoying some people, and they're not used anywhere else in the kernel tree. The include directory structure has been changed so that symlinks aren't needed anymore. NVKM has been moved from core/ to nvkm/ to make it more obvious as to what the directory is for, and as some minor prep for when NVKM gets split out into its own module (virt) at a later date. Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c')
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c234
1 files changed, 234 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c
new file mode 100644
index 000000000000..813ffc96e864
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c
@@ -0,0 +1,234 @@
1/*
2 * Copyright 2012 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
25#include "priv.h"
26
27#ifdef CONFIG_NOUVEAU_I2C_INTERNAL
28#define T_TIMEOUT 2200000
29#define T_RISEFALL 1000
30#define T_HOLD 5000
31
32static inline void
33i2c_drive_scl(struct nouveau_i2c_port *port, int state)
34{
35 port->func->drive_scl(port, state);
36}
37
38static inline void
39i2c_drive_sda(struct nouveau_i2c_port *port, int state)
40{
41 port->func->drive_sda(port, state);
42}
43
44static inline int
45i2c_sense_scl(struct nouveau_i2c_port *port)
46{
47 return port->func->sense_scl(port);
48}
49
50static inline int
51i2c_sense_sda(struct nouveau_i2c_port *port)
52{
53 return port->func->sense_sda(port);
54}
55
56static void
57i2c_delay(struct nouveau_i2c_port *port, u32 nsec)
58{
59 udelay((nsec + 500) / 1000);
60}
61
62static bool
63i2c_raise_scl(struct nouveau_i2c_port *port)
64{
65 u32 timeout = T_TIMEOUT / T_RISEFALL;
66
67 i2c_drive_scl(port, 1);
68 do {
69 i2c_delay(port, T_RISEFALL);
70 } while (!i2c_sense_scl(port) && --timeout);
71
72 return timeout != 0;
73}
74
75static int
76i2c_start(struct nouveau_i2c_port *port)
77{
78 int ret = 0;
79
80 if (!i2c_sense_scl(port) ||
81 !i2c_sense_sda(port)) {
82 i2c_drive_scl(port, 0);
83 i2c_drive_sda(port, 1);
84 if (!i2c_raise_scl(port))
85 ret = -EBUSY;
86 }
87
88 i2c_drive_sda(port, 0);
89 i2c_delay(port, T_HOLD);
90 i2c_drive_scl(port, 0);
91 i2c_delay(port, T_HOLD);
92 return ret;
93}
94
95static void
96i2c_stop(struct nouveau_i2c_port *port)
97{
98 i2c_drive_scl(port, 0);
99 i2c_drive_sda(port, 0);
100 i2c_delay(port, T_RISEFALL);
101
102 i2c_drive_scl(port, 1);
103 i2c_delay(port, T_HOLD);
104 i2c_drive_sda(port, 1);
105 i2c_delay(port, T_HOLD);
106}
107
108static int
109i2c_bitw(struct nouveau_i2c_port *port, int sda)
110{
111 i2c_drive_sda(port, sda);
112 i2c_delay(port, T_RISEFALL);
113
114 if (!i2c_raise_scl(port))
115 return -ETIMEDOUT;
116 i2c_delay(port, T_HOLD);
117
118 i2c_drive_scl(port, 0);
119 i2c_delay(port, T_HOLD);
120 return 0;
121}
122
123static int
124i2c_bitr(struct nouveau_i2c_port *port)
125{
126 int sda;
127
128 i2c_drive_sda(port, 1);
129 i2c_delay(port, T_RISEFALL);
130
131 if (!i2c_raise_scl(port))
132 return -ETIMEDOUT;
133 i2c_delay(port, T_HOLD);
134
135 sda = i2c_sense_sda(port);
136
137 i2c_drive_scl(port, 0);
138 i2c_delay(port, T_HOLD);
139 return sda;
140}
141
142static int
143i2c_get_byte(struct nouveau_i2c_port *port, u8 *byte, bool last)
144{
145 int i, bit;
146
147 *byte = 0;
148 for (i = 7; i >= 0; i--) {
149 bit = i2c_bitr(port);
150 if (bit < 0)
151 return bit;
152 *byte |= bit << i;
153 }
154
155 return i2c_bitw(port, last ? 1 : 0);
156}
157
158static int
159i2c_put_byte(struct nouveau_i2c_port *port, u8 byte)
160{
161 int i, ret;
162 for (i = 7; i >= 0; i--) {
163 ret = i2c_bitw(port, !!(byte & (1 << i)));
164 if (ret < 0)
165 return ret;
166 }
167
168 ret = i2c_bitr(port);
169 if (ret == 1) /* nack */
170 ret = -EIO;
171 return ret;
172}
173
174static int
175i2c_addr(struct nouveau_i2c_port *port, struct i2c_msg *msg)
176{
177 u32 addr = msg->addr << 1;
178 if (msg->flags & I2C_M_RD)
179 addr |= 1;
180 return i2c_put_byte(port, addr);
181}
182
183static int
184i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
185{
186 struct nouveau_i2c_port *port = adap->algo_data;
187 struct i2c_msg *msg = msgs;
188 int ret = 0, mcnt = num;
189
190 ret = nouveau_i2c(port)->acquire(port, nsecs_to_jiffies(T_TIMEOUT));
191 if (ret)
192 return ret;
193
194 while (!ret && mcnt--) {
195 u8 remaining = msg->len;
196 u8 *ptr = msg->buf;
197
198 ret = i2c_start(port);
199 if (ret == 0)
200 ret = i2c_addr(port, msg);
201
202 if (msg->flags & I2C_M_RD) {
203 while (!ret && remaining--)
204 ret = i2c_get_byte(port, ptr++, !remaining);
205 } else {
206 while (!ret && remaining--)
207 ret = i2c_put_byte(port, *ptr++);
208 }
209
210 msg++;
211 }
212
213 i2c_stop(port);
214 nouveau_i2c(port)->release(port);
215 return (ret < 0) ? ret : num;
216}
217#else
218static int
219i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
220{
221 return -ENODEV;
222}
223#endif
224
225static u32
226i2c_bit_func(struct i2c_adapter *adap)
227{
228 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
229}
230
231const struct i2c_algorithm nouveau_i2c_bit_algo = {
232 .master_xfer = i2c_bit_xfer,
233 .functionality = i2c_bit_func
234};