xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/nouveau/nvkm/subdev/i2c/nouveau_nvkm_subdev_i2c_anx9805.c (revision 41ec02673d281bbb3d38e6c78504ce6e30c228c1)
1 /*	$NetBSD: nouveau_nvkm_subdev_i2c_anx9805.c,v 1.3 2021/12/18 23:45:40 riastradh Exp $	*/
2 
3 /*
4  * Copyright 2013 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 <bskeggs@redhat.com>
25  */
26 #include <sys/cdefs.h>
27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_i2c_anx9805.c,v 1.3 2021/12/18 23:45:40 riastradh Exp $");
28 
29 #define anx9805_pad(p) container_of((p), struct anx9805_pad, base)
30 #define anx9805_bus(p) container_of((p), struct anx9805_bus, base)
31 #define anx9805_aux(p) container_of((p), struct anx9805_aux, base)
32 #include "aux.h"
33 #include "bus.h"
34 
35 struct anx9805_pad {
36 	struct nvkm_i2c_pad base;
37 	struct nvkm_i2c_bus *bus;
38 	u8 addr;
39 };
40 
41 struct anx9805_bus {
42 	struct nvkm_i2c_bus base;
43 	struct anx9805_pad *pad;
44 	u8 addr;
45 };
46 
47 static int
anx9805_bus_xfer(struct nvkm_i2c_bus * base,struct i2c_msg * msgs,int num)48 anx9805_bus_xfer(struct nvkm_i2c_bus *base, struct i2c_msg *msgs, int num)
49 {
50 	struct anx9805_bus *bus = anx9805_bus(base);
51 	struct anx9805_pad *pad = bus->pad;
52 	struct i2c_adapter *adap = &pad->bus->i2c;
53 	struct i2c_msg *msg = msgs;
54 	int ret = -ETIMEDOUT;
55 	int i, j, cnt = num;
56 	u8 seg = 0x00, off = 0x00, tmp;
57 
58 	tmp = nvkm_rdi2cr(adap, pad->addr, 0x07) & ~0x10;
59 	nvkm_wri2cr(adap, pad->addr, 0x07, tmp | 0x10);
60 	nvkm_wri2cr(adap, pad->addr, 0x07, tmp);
61 	nvkm_wri2cr(adap, bus->addr, 0x43, 0x05);
62 	mdelay(5);
63 
64 	while (cnt--) {
65 		if ( (msg->flags & I2C_M_RD) && msg->addr == 0x50) {
66 			nvkm_wri2cr(adap, bus->addr, 0x40, msg->addr << 1);
67 			nvkm_wri2cr(adap, bus->addr, 0x41, seg);
68 			nvkm_wri2cr(adap, bus->addr, 0x42, off);
69 			nvkm_wri2cr(adap, bus->addr, 0x44, msg->len);
70 			nvkm_wri2cr(adap, bus->addr, 0x45, 0x00);
71 			nvkm_wri2cr(adap, bus->addr, 0x43, 0x01);
72 			for (i = 0; i < msg->len; i++) {
73 				j = 0;
74 				while (nvkm_rdi2cr(adap, bus->addr, 0x46) & 0x10) {
75 					mdelay(5);
76 					if (j++ == 32)
77 						goto done;
78 				}
79 				msg->buf[i] = nvkm_rdi2cr(adap, bus->addr, 0x47);
80 			}
81 		} else
82 		if (!(msg->flags & I2C_M_RD)) {
83 			if (msg->addr == 0x50 && msg->len == 0x01) {
84 				off = msg->buf[0];
85 			} else
86 			if (msg->addr == 0x30 && msg->len == 0x01) {
87 				seg = msg->buf[0];
88 			} else
89 				goto done;
90 		} else {
91 			goto done;
92 		}
93 		msg++;
94 	}
95 
96 	ret = num;
97 done:
98 	nvkm_wri2cr(adap, bus->addr, 0x43, 0x00);
99 	return ret;
100 }
101 
102 static const struct nvkm_i2c_bus_func
103 anx9805_bus_func = {
104 	.xfer = anx9805_bus_xfer,
105 };
106 
107 static int
anx9805_bus_new(struct nvkm_i2c_pad * base,int id,u8 drive,struct nvkm_i2c_bus ** pbus)108 anx9805_bus_new(struct nvkm_i2c_pad *base, int id, u8 drive,
109 		struct nvkm_i2c_bus **pbus)
110 {
111 	struct anx9805_pad *pad = anx9805_pad(base);
112 	struct anx9805_bus *bus;
113 	int ret;
114 
115 	if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL)))
116 		return -ENOMEM;
117 	*pbus = &bus->base;
118 	bus->pad = pad;
119 
120 	ret = nvkm_i2c_bus_ctor(&anx9805_bus_func, &pad->base, id, &bus->base);
121 	if (ret)
122 		return ret;
123 
124 	switch (pad->addr) {
125 	case 0x39: bus->addr = 0x3d; break;
126 	case 0x3b: bus->addr = 0x3f; break;
127 	default:
128 		return -ENOSYS;
129 	}
130 
131 	return 0;
132 }
133 
134 struct anx9805_aux {
135 	struct nvkm_i2c_aux base;
136 	struct anx9805_pad *pad;
137 	u8 addr;
138 };
139 
140 static int
anx9805_aux_xfer(struct nvkm_i2c_aux * base,bool retry,u8 type,u32 addr,u8 * data,u8 * size)141 anx9805_aux_xfer(struct nvkm_i2c_aux *base, bool retry,
142 		 u8 type, u32 addr, u8 *data, u8 *size)
143 {
144 	struct anx9805_aux *aux = anx9805_aux(base);
145 	struct anx9805_pad *pad = aux->pad;
146 	struct i2c_adapter *adap = &pad->bus->i2c;
147 	int i, ret = -ETIMEDOUT;
148 	u8 buf[16] = {};
149 	u8 tmp;
150 
151 	AUX_DBG(&aux->base, "%02x %05x %d", type, addr, *size);
152 
153 	tmp = nvkm_rdi2cr(adap, pad->addr, 0x07) & ~0x04;
154 	nvkm_wri2cr(adap, pad->addr, 0x07, tmp | 0x04);
155 	nvkm_wri2cr(adap, pad->addr, 0x07, tmp);
156 	nvkm_wri2cr(adap, pad->addr, 0xf7, 0x01);
157 
158 	nvkm_wri2cr(adap, aux->addr, 0xe4, 0x80);
159 	if (!(type & 1)) {
160 		memcpy(buf, data, *size);
161 		AUX_DBG(&aux->base, "%16ph", buf);
162 		for (i = 0; i < *size; i++)
163 			nvkm_wri2cr(adap, aux->addr, 0xf0 + i, buf[i]);
164 	}
165 	nvkm_wri2cr(adap, aux->addr, 0xe5, ((*size - 1) << 4) | type);
166 	nvkm_wri2cr(adap, aux->addr, 0xe6, (addr & 0x000ff) >>  0);
167 	nvkm_wri2cr(adap, aux->addr, 0xe7, (addr & 0x0ff00) >>  8);
168 	nvkm_wri2cr(adap, aux->addr, 0xe8, (addr & 0xf0000) >> 16);
169 	nvkm_wri2cr(adap, aux->addr, 0xe9, 0x01);
170 
171 	i = 0;
172 	while ((tmp = nvkm_rdi2cr(adap, aux->addr, 0xe9)) & 0x01) {
173 		mdelay(5);
174 		if (i++ == 32)
175 			goto done;
176 	}
177 
178 	if ((tmp = nvkm_rdi2cr(adap, pad->addr, 0xf7)) & 0x01) {
179 		ret = -EIO;
180 		goto done;
181 	}
182 
183 	if (type & 1) {
184 		for (i = 0; i < *size; i++)
185 			buf[i] = nvkm_rdi2cr(adap, aux->addr, 0xf0 + i);
186 		AUX_DBG(&aux->base, "%16ph", buf);
187 		memcpy(data, buf, *size);
188 	}
189 
190 	ret = 0;
191 done:
192 	nvkm_wri2cr(adap, pad->addr, 0xf7, 0x01);
193 	return ret;
194 }
195 
196 static int
anx9805_aux_lnk_ctl(struct nvkm_i2c_aux * base,int link_nr,int link_bw,bool enh)197 anx9805_aux_lnk_ctl(struct nvkm_i2c_aux *base,
198 		    int link_nr, int link_bw, bool enh)
199 {
200 	struct anx9805_aux *aux = anx9805_aux(base);
201 	struct anx9805_pad *pad = aux->pad;
202 	struct i2c_adapter *adap = &pad->bus->i2c;
203 	u8 tmp, i;
204 
205 	AUX_DBG(&aux->base, "ANX9805 train %d %02x %d",
206 		link_nr, link_bw, enh);
207 
208 	nvkm_wri2cr(adap, aux->addr, 0xa0, link_bw);
209 	nvkm_wri2cr(adap, aux->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00));
210 	nvkm_wri2cr(adap, aux->addr, 0xa2, 0x01);
211 	nvkm_wri2cr(adap, aux->addr, 0xa8, 0x01);
212 
213 	i = 0;
214 	while ((tmp = nvkm_rdi2cr(adap, aux->addr, 0xa8)) & 0x01) {
215 		mdelay(5);
216 		if (i++ == 100) {
217 			AUX_ERR(&aux->base, "link training timeout");
218 			return -ETIMEDOUT;
219 		}
220 	}
221 
222 	if (tmp & 0x70) {
223 		AUX_ERR(&aux->base, "link training failed");
224 		return -EIO;
225 	}
226 
227 	return 0;
228 }
229 
230 static const struct nvkm_i2c_aux_func
231 anx9805_aux_func = {
232 	.xfer = anx9805_aux_xfer,
233 	.lnk_ctl = anx9805_aux_lnk_ctl,
234 };
235 
236 static int
anx9805_aux_new(struct nvkm_i2c_pad * base,int id,u8 drive,struct nvkm_i2c_aux ** pbus)237 anx9805_aux_new(struct nvkm_i2c_pad *base, int id, u8 drive,
238 		struct nvkm_i2c_aux **pbus)
239 {
240 	struct anx9805_pad *pad = anx9805_pad(base);
241 	struct anx9805_aux *aux;
242 	int ret;
243 
244 	if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL)))
245 		return -ENOMEM;
246 	*pbus = &aux->base;
247 	aux->pad = pad;
248 
249 	ret = nvkm_i2c_aux_ctor(&anx9805_aux_func, &pad->base, id, &aux->base);
250 	if (ret)
251 		return ret;
252 
253 	switch (pad->addr) {
254 	case 0x39: aux->addr = 0x38; break;
255 	case 0x3b: aux->addr = 0x3c; break;
256 	default:
257 		return -ENOSYS;
258 	}
259 
260 	return 0;
261 }
262 
263 static const struct nvkm_i2c_pad_func
264 anx9805_pad_func = {
265 	.bus_new_4 = anx9805_bus_new,
266 	.aux_new_6 = anx9805_aux_new,
267 };
268 
269 int
anx9805_pad_new(struct nvkm_i2c_bus * bus,int id,u8 addr,struct nvkm_i2c_pad ** ppad)270 anx9805_pad_new(struct nvkm_i2c_bus *bus, int id, u8 addr,
271 		struct nvkm_i2c_pad **ppad)
272 {
273 	struct anx9805_pad *pad;
274 
275 	if (!(pad = kzalloc(sizeof(*pad), GFP_KERNEL)))
276 		return -ENOMEM;
277 	*ppad = &pad->base;
278 
279 	nvkm_i2c_pad_ctor(&anx9805_pad_func, bus->pad->i2c, id, &pad->base);
280 	pad->bus = bus;
281 	pad->addr = addr;
282 	return 0;
283 }
284