xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c (revision f8bade6c9a6213c2c5ba6e5bf32415ecab6e41e5)
1db1eb528SBen Skeggs /*
2db1eb528SBen Skeggs  * Copyright 2015 Red Hat Inc.
3db1eb528SBen Skeggs  *
4db1eb528SBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5db1eb528SBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6db1eb528SBen Skeggs  * to deal in the Software without restriction, including without limitation
7db1eb528SBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8db1eb528SBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9db1eb528SBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10db1eb528SBen Skeggs  *
11db1eb528SBen Skeggs  * The above copyright notice and this permission notice shall be included in
12db1eb528SBen Skeggs  * all copies or substantial busions of the Software.
13db1eb528SBen Skeggs  *
14db1eb528SBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15db1eb528SBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16db1eb528SBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17db1eb528SBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18db1eb528SBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19db1eb528SBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20db1eb528SBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21db1eb528SBen Skeggs  *
22db1eb528SBen Skeggs  * Authors: Ben Skeggs <bskeggs@redhat.com>
23db1eb528SBen Skeggs  */
24db1eb528SBen Skeggs #define gm200_i2c_aux(p) container_of((p), struct gm200_i2c_aux, base)
25db1eb528SBen Skeggs #include "aux.h"
26db1eb528SBen Skeggs 
27db1eb528SBen Skeggs struct gm200_i2c_aux {
28db1eb528SBen Skeggs 	struct nvkm_i2c_aux base;
29db1eb528SBen Skeggs 	int ch;
30db1eb528SBen Skeggs };
31db1eb528SBen Skeggs 
32db1eb528SBen Skeggs static void
gm200_i2c_aux_fini(struct gm200_i2c_aux * aux)33db1eb528SBen Skeggs gm200_i2c_aux_fini(struct gm200_i2c_aux *aux)
34db1eb528SBen Skeggs {
35db1eb528SBen Skeggs 	struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
36*ba6e9ab0SBen Skeggs 	nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00710000, 0x00000000);
37db1eb528SBen Skeggs }
38db1eb528SBen Skeggs 
39db1eb528SBen Skeggs static int
gm200_i2c_aux_init(struct gm200_i2c_aux * aux)40db1eb528SBen Skeggs gm200_i2c_aux_init(struct gm200_i2c_aux *aux)
41db1eb528SBen Skeggs {
42db1eb528SBen Skeggs 	struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
43db1eb528SBen Skeggs 	const u32 unksel = 1; /* nfi which to use, or if it matters.. */
44db1eb528SBen Skeggs 	const u32 ureq = unksel ? 0x00100000 : 0x00200000;
45db1eb528SBen Skeggs 	const u32 urep = unksel ? 0x01000000 : 0x02000000;
46db1eb528SBen Skeggs 	u32 ctrl, timeout;
47db1eb528SBen Skeggs 
48db1eb528SBen Skeggs 	/* wait up to 1ms for any previous transaction to be done... */
49db1eb528SBen Skeggs 	timeout = 1000;
50db1eb528SBen Skeggs 	do {
51db1eb528SBen Skeggs 		ctrl = nvkm_rd32(device, 0x00d954 + (aux->ch * 0x50));
52db1eb528SBen Skeggs 		udelay(1);
53db1eb528SBen Skeggs 		if (!timeout--) {
54db1eb528SBen Skeggs 			AUX_ERR(&aux->base, "begin idle timeout %08x", ctrl);
55db1eb528SBen Skeggs 			return -EBUSY;
56db1eb528SBen Skeggs 		}
57*ba6e9ab0SBen Skeggs 	} while (ctrl & 0x07010000);
58db1eb528SBen Skeggs 
59db1eb528SBen Skeggs 	/* set some magic, and wait up to 1ms for it to appear */
60*ba6e9ab0SBen Skeggs 	nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00700000, ureq);
61db1eb528SBen Skeggs 	timeout = 1000;
62db1eb528SBen Skeggs 	do {
63db1eb528SBen Skeggs 		ctrl = nvkm_rd32(device, 0x00d954 + (aux->ch * 0x50));
64db1eb528SBen Skeggs 		udelay(1);
65db1eb528SBen Skeggs 		if (!timeout--) {
66db1eb528SBen Skeggs 			AUX_ERR(&aux->base, "magic wait %08x", ctrl);
67db1eb528SBen Skeggs 			gm200_i2c_aux_fini(aux);
68db1eb528SBen Skeggs 			return -EBUSY;
69db1eb528SBen Skeggs 		}
70*ba6e9ab0SBen Skeggs 	} while ((ctrl & 0x07000000) != urep);
71db1eb528SBen Skeggs 
72db1eb528SBen Skeggs 	return 0;
73db1eb528SBen Skeggs }
74db1eb528SBen Skeggs 
75db1eb528SBen Skeggs static int
gm200_i2c_aux_xfer(struct nvkm_i2c_aux * obj,bool retry,u8 type,u32 addr,u8 * data,u8 * size)76db1eb528SBen Skeggs gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
771af5c410SBen Skeggs 		   u8 type, u32 addr, u8 *data, u8 *size)
78db1eb528SBen Skeggs {
79db1eb528SBen Skeggs 	struct gm200_i2c_aux *aux = gm200_i2c_aux(obj);
808ad95edcSBen Skeggs 	struct nvkm_i2c *i2c = aux->base.pad->i2c;
818ad95edcSBen Skeggs 	struct nvkm_device *device = i2c->subdev.device;
82db1eb528SBen Skeggs 	const u32 base = aux->ch * 0x50;
83f1963a47SBen Skeggs 	u32 ctrl, stat, timeout, retries = 0;
84db1eb528SBen Skeggs 	u32 xbuf[4] = {};
85db1eb528SBen Skeggs 	int ret, i;
86db1eb528SBen Skeggs 
871af5c410SBen Skeggs 	AUX_TRACE(&aux->base, "%d: %08x %d", type, addr, *size);
88db1eb528SBen Skeggs 
89db1eb528SBen Skeggs 	ret = gm200_i2c_aux_init(aux);
90db1eb528SBen Skeggs 	if (ret < 0)
91db1eb528SBen Skeggs 		goto out;
92db1eb528SBen Skeggs 
93db1eb528SBen Skeggs 	stat = nvkm_rd32(device, 0x00d958 + base);
94db1eb528SBen Skeggs 	if (!(stat & 0x10000000)) {
95db1eb528SBen Skeggs 		AUX_TRACE(&aux->base, "sink not detected");
96db1eb528SBen Skeggs 		ret = -ENXIO;
97db1eb528SBen Skeggs 		goto out;
98db1eb528SBen Skeggs 	}
99db1eb528SBen Skeggs 
1008ad95edcSBen Skeggs 	nvkm_i2c_aux_autodpcd(i2c, aux->ch, false);
1018ad95edcSBen Skeggs 
102db1eb528SBen Skeggs 	if (!(type & 1)) {
1031af5c410SBen Skeggs 		memcpy(xbuf, data, *size);
104db1eb528SBen Skeggs 		for (i = 0; i < 16; i += 4) {
105db1eb528SBen Skeggs 			AUX_TRACE(&aux->base, "wr %08x", xbuf[i / 4]);
106db1eb528SBen Skeggs 			nvkm_wr32(device, 0x00d930 + base + i, xbuf[i / 4]);
107db1eb528SBen Skeggs 		}
108db1eb528SBen Skeggs 	}
109db1eb528SBen Skeggs 
110db1eb528SBen Skeggs 	ctrl  = nvkm_rd32(device, 0x00d954 + base);
11113a86519SBen Skeggs 	ctrl &= ~0x0001f1ff;
112db1eb528SBen Skeggs 	ctrl |= type << 12;
11313a86519SBen Skeggs 	ctrl |= (*size ? (*size - 1) : 0x00000100);
114db1eb528SBen Skeggs 	nvkm_wr32(device, 0x00d950 + base, addr);
115db1eb528SBen Skeggs 
116db1eb528SBen Skeggs 	/* (maybe) retry transaction a number of times on failure... */
117f1963a47SBen Skeggs 	do {
118db1eb528SBen Skeggs 		/* reset, and delay a while if this is a retry */
119db1eb528SBen Skeggs 		nvkm_wr32(device, 0x00d954 + base, 0x80000000 | ctrl);
120db1eb528SBen Skeggs 		nvkm_wr32(device, 0x00d954 + base, 0x00000000 | ctrl);
121db1eb528SBen Skeggs 		if (retries)
122db1eb528SBen Skeggs 			udelay(400);
123db1eb528SBen Skeggs 
1240156e76dSBen Skeggs 		/* transaction request, wait up to 2ms for it to complete */
125db1eb528SBen Skeggs 		nvkm_wr32(device, 0x00d954 + base, 0x00010000 | ctrl);
126db1eb528SBen Skeggs 
1270156e76dSBen Skeggs 		timeout = 2000;
128db1eb528SBen Skeggs 		do {
129db1eb528SBen Skeggs 			ctrl = nvkm_rd32(device, 0x00d954 + base);
130db1eb528SBen Skeggs 			udelay(1);
131db1eb528SBen Skeggs 			if (!timeout--) {
132db1eb528SBen Skeggs 				AUX_ERR(&aux->base, "timeout %08x", ctrl);
133db1eb528SBen Skeggs 				ret = -EIO;
1348ad95edcSBen Skeggs 				goto out_err;
135db1eb528SBen Skeggs 			}
136db1eb528SBen Skeggs 		} while (ctrl & 0x00010000);
137f1963a47SBen Skeggs 		ret = 0;
138db1eb528SBen Skeggs 
139db1eb528SBen Skeggs 		/* read status, and check if transaction completed ok */
140db1eb528SBen Skeggs 		stat = nvkm_mask(device, 0x00d958 + base, 0, 0);
141db1eb528SBen Skeggs 		if ((stat & 0x000f0000) == 0x00080000 ||
142db1eb528SBen Skeggs 		    (stat & 0x000f0000) == 0x00020000)
143f1963a47SBen Skeggs 			ret = 1;
144db1eb528SBen Skeggs 		if ((stat & 0x00000100))
145db1eb528SBen Skeggs 			ret = -ETIMEDOUT;
146db1eb528SBen Skeggs 		if ((stat & 0x00000e00))
147db1eb528SBen Skeggs 			ret = -EIO;
148db1eb528SBen Skeggs 
149db1eb528SBen Skeggs 		AUX_TRACE(&aux->base, "%02d %08x %08x", retries, ctrl, stat);
150f1963a47SBen Skeggs 	} while (ret && retry && retries++ < 32);
151db1eb528SBen Skeggs 
152db1eb528SBen Skeggs 	if (type & 1) {
153db1eb528SBen Skeggs 		for (i = 0; i < 16; i += 4) {
154db1eb528SBen Skeggs 			xbuf[i / 4] = nvkm_rd32(device, 0x00d940 + base + i);
155db1eb528SBen Skeggs 			AUX_TRACE(&aux->base, "rd %08x", xbuf[i / 4]);
156db1eb528SBen Skeggs 		}
1571af5c410SBen Skeggs 		memcpy(data, xbuf, *size);
1585c68d91eSBen Skeggs 		*size = stat & 0x0000001f;
159db1eb528SBen Skeggs 	}
160db1eb528SBen Skeggs 
1618ad95edcSBen Skeggs out_err:
1628ad95edcSBen Skeggs 	nvkm_i2c_aux_autodpcd(i2c, aux->ch, true);
163db1eb528SBen Skeggs out:
164db1eb528SBen Skeggs 	gm200_i2c_aux_fini(aux);
165db1eb528SBen Skeggs 	return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
166db1eb528SBen Skeggs }
167db1eb528SBen Skeggs 
168db1eb528SBen Skeggs static const struct nvkm_i2c_aux_func
169db1eb528SBen Skeggs gm200_i2c_aux_func = {
17013a86519SBen Skeggs 	.address_only = true,
171db1eb528SBen Skeggs 	.xfer = gm200_i2c_aux_xfer,
172db1eb528SBen Skeggs };
173db1eb528SBen Skeggs 
174db1eb528SBen Skeggs int
gm200_i2c_aux_new(struct nvkm_i2c_pad * pad,int index,u8 drive,struct nvkm_i2c_aux ** paux)175db1eb528SBen Skeggs gm200_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive,
176db1eb528SBen Skeggs 		struct nvkm_i2c_aux **paux)
177db1eb528SBen Skeggs {
178db1eb528SBen Skeggs 	struct gm200_i2c_aux *aux;
179db1eb528SBen Skeggs 
180db1eb528SBen Skeggs 	if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL)))
181db1eb528SBen Skeggs 		return -ENOMEM;
182db1eb528SBen Skeggs 	*paux = &aux->base;
183db1eb528SBen Skeggs 
184db1eb528SBen Skeggs 	nvkm_i2c_aux_ctor(&gm200_i2c_aux_func, pad, index, &aux->base);
185db1eb528SBen Skeggs 	aux->ch = drive;
186db1eb528SBen Skeggs 	aux->base.intr = 1 << aux->ch;
187db1eb528SBen Skeggs 	return 0;
188db1eb528SBen Skeggs }
189