Commit | Line | Data |
---|---|---|
5effecd4 BS |
1 | /* |
2 | * Copyright 2013 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 <bskeggs@redhat.com> | |
23 | */ | |
24 | ||
37da5b87 | 25 | #include "port.h" |
5effecd4 BS |
26 | |
27 | struct anx9805_i2c_port { | |
28 | struct nouveau_i2c_port base; | |
29 | u32 addr; | |
30 | u32 ctrl; | |
31 | }; | |
32 | ||
33 | static int | |
34 | anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh) | |
35 | { | |
36 | struct anx9805_i2c_port *chan = (void *)port; | |
37 | struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent; | |
38 | u8 tmp, i; | |
39 | ||
37da5b87 BS |
40 | DBG("ANX9805 train %d 0x%02x %d\n", link_nr, link_bw, enh); |
41 | ||
5effecd4 BS |
42 | nv_wri2cr(mast, chan->addr, 0xa0, link_bw); |
43 | nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00)); | |
44 | nv_wri2cr(mast, chan->addr, 0xa2, 0x01); | |
45 | nv_wri2cr(mast, chan->addr, 0xa8, 0x01); | |
46 | ||
47 | i = 0; | |
48 | while ((tmp = nv_rdi2cr(mast, chan->addr, 0xa8)) & 0x01) { | |
49 | mdelay(5); | |
50 | if (i++ == 100) { | |
51 | nv_error(port, "link training timed out\n"); | |
52 | return -ETIMEDOUT; | |
53 | } | |
54 | } | |
55 | ||
56 | if (tmp & 0x70) { | |
57 | nv_error(port, "link training failed: 0x%02x\n", tmp); | |
58 | return -EIO; | |
59 | } | |
60 | ||
61 | return 1; | |
62 | } | |
63 | ||
64 | static int | |
842c2953 BS |
65 | anx9805_aux(struct nouveau_i2c_port *port, bool retry, |
66 | u8 type, u32 addr, u8 *data, u8 size) | |
5effecd4 BS |
67 | { |
68 | struct anx9805_i2c_port *chan = (void *)port; | |
69 | struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent; | |
70 | int i, ret = -ETIMEDOUT; | |
37da5b87 | 71 | u8 buf[16] = {}; |
5effecd4 BS |
72 | u8 tmp; |
73 | ||
37da5b87 BS |
74 | DBG("%02x %05x %d\n", type, addr, size); |
75 | ||
5effecd4 BS |
76 | tmp = nv_rdi2cr(mast, chan->ctrl, 0x07) & ~0x04; |
77 | nv_wri2cr(mast, chan->ctrl, 0x07, tmp | 0x04); | |
78 | nv_wri2cr(mast, chan->ctrl, 0x07, tmp); | |
79 | nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); | |
80 | ||
81 | nv_wri2cr(mast, chan->addr, 0xe4, 0x80); | |
37da5b87 BS |
82 | if (!(type & 1)) { |
83 | memcpy(buf, data, size); | |
84 | DBG("%16ph", buf); | |
85 | for (i = 0; i < size; i++) | |
86 | nv_wri2cr(mast, chan->addr, 0xf0 + i, buf[i]); | |
87 | } | |
5effecd4 BS |
88 | nv_wri2cr(mast, chan->addr, 0xe5, ((size - 1) << 4) | type); |
89 | nv_wri2cr(mast, chan->addr, 0xe6, (addr & 0x000ff) >> 0); | |
90 | nv_wri2cr(mast, chan->addr, 0xe7, (addr & 0x0ff00) >> 8); | |
91 | nv_wri2cr(mast, chan->addr, 0xe8, (addr & 0xf0000) >> 16); | |
92 | nv_wri2cr(mast, chan->addr, 0xe9, 0x01); | |
93 | ||
94 | i = 0; | |
95 | while ((tmp = nv_rdi2cr(mast, chan->addr, 0xe9)) & 0x01) { | |
96 | mdelay(5); | |
97 | if (i++ == 32) | |
98 | goto done; | |
99 | } | |
100 | ||
101 | if ((tmp = nv_rdi2cr(mast, chan->ctrl, 0xf7)) & 0x01) { | |
102 | ret = -EIO; | |
103 | goto done; | |
104 | } | |
105 | ||
37da5b87 BS |
106 | if (type & 1) { |
107 | for (i = 0; i < size; i++) | |
108 | buf[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i); | |
109 | DBG("%16ph", buf); | |
110 | memcpy(data, buf, size); | |
111 | } | |
112 | ||
5effecd4 BS |
113 | ret = 0; |
114 | done: | |
115 | nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); | |
116 | return ret; | |
117 | } | |
118 | ||
119 | static const struct nouveau_i2c_func | |
120 | anx9805_aux_func = { | |
121 | .aux = anx9805_aux, | |
122 | .lnk_ctl = anx9805_train, | |
123 | }; | |
124 | ||
125 | static int | |
126 | anx9805_aux_chan_ctor(struct nouveau_object *parent, | |
127 | struct nouveau_object *engine, | |
128 | struct nouveau_oclass *oclass, void *data, u32 index, | |
129 | struct nouveau_object **pobject) | |
130 | { | |
131 | struct nouveau_i2c_port *mast = (void *)parent; | |
132 | struct anx9805_i2c_port *chan; | |
133 | int ret; | |
134 | ||
135 | ret = nouveau_i2c_port_create(parent, engine, oclass, index, | |
c865534f IM |
136 | &nouveau_i2c_aux_algo, &anx9805_aux_func, |
137 | &chan); | |
5effecd4 BS |
138 | *pobject = nv_object(chan); |
139 | if (ret) | |
140 | return ret; | |
141 | ||
142 | switch ((oclass->handle & 0xff00) >> 8) { | |
143 | case 0x0d: | |
144 | chan->addr = 0x38; | |
145 | chan->ctrl = 0x39; | |
146 | break; | |
147 | case 0x0e: | |
148 | chan->addr = 0x3c; | |
149 | chan->ctrl = 0x3b; | |
150 | break; | |
151 | default: | |
152 | BUG_ON(1); | |
153 | } | |
154 | ||
155 | if (mast->adapter.algo == &i2c_bit_algo) { | |
156 | struct i2c_algo_bit_data *algo = mast->adapter.algo_data; | |
157 | algo->udelay = max(algo->udelay, 40); | |
158 | } | |
5effecd4 BS |
159 | return 0; |
160 | } | |
161 | ||
162 | static struct nouveau_ofuncs | |
163 | anx9805_aux_ofuncs = { | |
164 | .ctor = anx9805_aux_chan_ctor, | |
165 | .dtor = _nouveau_i2c_port_dtor, | |
166 | .init = _nouveau_i2c_port_init, | |
167 | .fini = _nouveau_i2c_port_fini, | |
168 | }; | |
169 | ||
170 | static int | |
171 | anx9805_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) | |
172 | { | |
173 | struct anx9805_i2c_port *port = adap->algo_data; | |
174 | struct nouveau_i2c_port *mast = (void *)nv_object(port)->parent; | |
175 | struct i2c_msg *msg = msgs; | |
176 | int ret = -ETIMEDOUT; | |
177 | int i, j, cnt = num; | |
178 | u8 seg = 0x00, off = 0x00, tmp; | |
179 | ||
180 | tmp = nv_rdi2cr(mast, port->ctrl, 0x07) & ~0x10; | |
181 | nv_wri2cr(mast, port->ctrl, 0x07, tmp | 0x10); | |
182 | nv_wri2cr(mast, port->ctrl, 0x07, tmp); | |
183 | nv_wri2cr(mast, port->addr, 0x43, 0x05); | |
184 | mdelay(5); | |
185 | ||
186 | while (cnt--) { | |
187 | if ( (msg->flags & I2C_M_RD) && msg->addr == 0x50) { | |
188 | nv_wri2cr(mast, port->addr, 0x40, msg->addr << 1); | |
189 | nv_wri2cr(mast, port->addr, 0x41, seg); | |
190 | nv_wri2cr(mast, port->addr, 0x42, off); | |
191 | nv_wri2cr(mast, port->addr, 0x44, msg->len); | |
192 | nv_wri2cr(mast, port->addr, 0x45, 0x00); | |
193 | nv_wri2cr(mast, port->addr, 0x43, 0x01); | |
194 | for (i = 0; i < msg->len; i++) { | |
195 | j = 0; | |
196 | while (nv_rdi2cr(mast, port->addr, 0x46) & 0x10) { | |
197 | mdelay(5); | |
198 | if (j++ == 32) | |
199 | goto done; | |
200 | } | |
201 | msg->buf[i] = nv_rdi2cr(mast, port->addr, 0x47); | |
202 | } | |
203 | } else | |
204 | if (!(msg->flags & I2C_M_RD)) { | |
205 | if (msg->addr == 0x50 && msg->len == 0x01) { | |
206 | off = msg->buf[0]; | |
207 | } else | |
208 | if (msg->addr == 0x30 && msg->len == 0x01) { | |
209 | seg = msg->buf[0]; | |
210 | } else | |
211 | goto done; | |
212 | } else { | |
213 | goto done; | |
214 | } | |
215 | msg++; | |
216 | } | |
217 | ||
218 | ret = num; | |
219 | done: | |
220 | nv_wri2cr(mast, port->addr, 0x43, 0x00); | |
221 | return ret; | |
222 | } | |
223 | ||
224 | static u32 | |
225 | anx9805_func(struct i2c_adapter *adap) | |
226 | { | |
227 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | |
228 | } | |
229 | ||
230 | static const struct i2c_algorithm | |
231 | anx9805_i2c_algo = { | |
232 | .master_xfer = anx9805_xfer, | |
233 | .functionality = anx9805_func | |
234 | }; | |
235 | ||
236 | static const struct nouveau_i2c_func | |
237 | anx9805_i2c_func = { | |
238 | }; | |
239 | ||
240 | static int | |
241 | anx9805_ddc_port_ctor(struct nouveau_object *parent, | |
242 | struct nouveau_object *engine, | |
243 | struct nouveau_oclass *oclass, void *data, u32 index, | |
244 | struct nouveau_object **pobject) | |
245 | { | |
246 | struct nouveau_i2c_port *mast = (void *)parent; | |
247 | struct anx9805_i2c_port *port; | |
248 | int ret; | |
249 | ||
250 | ret = nouveau_i2c_port_create(parent, engine, oclass, index, | |
c865534f IM |
251 | &anx9805_i2c_algo, &anx9805_i2c_func, |
252 | &port); | |
5effecd4 BS |
253 | *pobject = nv_object(port); |
254 | if (ret) | |
255 | return ret; | |
256 | ||
257 | switch ((oclass->handle & 0xff00) >> 8) { | |
258 | case 0x0d: | |
259 | port->addr = 0x3d; | |
260 | port->ctrl = 0x39; | |
261 | break; | |
262 | case 0x0e: | |
263 | port->addr = 0x3f; | |
264 | port->ctrl = 0x3b; | |
265 | break; | |
266 | default: | |
267 | BUG_ON(1); | |
268 | } | |
269 | ||
270 | if (mast->adapter.algo == &i2c_bit_algo) { | |
271 | struct i2c_algo_bit_data *algo = mast->adapter.algo_data; | |
272 | algo->udelay = max(algo->udelay, 40); | |
273 | } | |
5effecd4 BS |
274 | return 0; |
275 | } | |
276 | ||
277 | static struct nouveau_ofuncs | |
278 | anx9805_ddc_ofuncs = { | |
279 | .ctor = anx9805_ddc_port_ctor, | |
280 | .dtor = _nouveau_i2c_port_dtor, | |
281 | .init = _nouveau_i2c_port_init, | |
282 | .fini = _nouveau_i2c_port_fini, | |
283 | }; | |
284 | ||
285 | struct nouveau_oclass | |
286 | nouveau_anx9805_sclass[] = { | |
287 | { .handle = NV_I2C_TYPE_EXTDDC(0x0d), .ofuncs = &anx9805_ddc_ofuncs }, | |
288 | { .handle = NV_I2C_TYPE_EXTAUX(0x0d), .ofuncs = &anx9805_aux_ofuncs }, | |
289 | { .handle = NV_I2C_TYPE_EXTDDC(0x0e), .ofuncs = &anx9805_ddc_ofuncs }, | |
290 | { .handle = NV_I2C_TYPE_EXTAUX(0x0e), .ofuncs = &anx9805_aux_ofuncs }, | |
291 | {} | |
292 | }; |