xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c (revision f8bade6c9a6213c2c5ba6e5bf32415ecab6e41e5)
12aa5eac5SBen Skeggs /*
22aa5eac5SBen Skeggs  * Copyright 2015 Red Hat Inc.
32aa5eac5SBen Skeggs  *
42aa5eac5SBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
52aa5eac5SBen Skeggs  * copy of this software and associated documentation files (the "Software"),
62aa5eac5SBen Skeggs  * to deal in the Software without restriction, including without limitation
72aa5eac5SBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
82aa5eac5SBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
92aa5eac5SBen Skeggs  * Software is furnished to do so, subject to the following conditions:
102aa5eac5SBen Skeggs  *
112aa5eac5SBen Skeggs  * The above copyright notice and this permission notice shall be included in
122aa5eac5SBen Skeggs  * all copies or substantial busions of the Software.
132aa5eac5SBen Skeggs  *
142aa5eac5SBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
152aa5eac5SBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
162aa5eac5SBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
172aa5eac5SBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
182aa5eac5SBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
192aa5eac5SBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
202aa5eac5SBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
212aa5eac5SBen Skeggs  *
222aa5eac5SBen Skeggs  * Authors: Ben Skeggs <bskeggs@redhat.com>
232aa5eac5SBen Skeggs  */
242aa5eac5SBen Skeggs #define g94_i2c_aux(p) container_of((p), struct g94_i2c_aux, base)
252aa5eac5SBen Skeggs #include "aux.h"
262aa5eac5SBen Skeggs 
272aa5eac5SBen Skeggs struct g94_i2c_aux {
282aa5eac5SBen Skeggs 	struct nvkm_i2c_aux base;
292aa5eac5SBen Skeggs 	int ch;
302aa5eac5SBen Skeggs };
312aa5eac5SBen Skeggs 
322aa5eac5SBen Skeggs static void
g94_i2c_aux_fini(struct g94_i2c_aux * aux)332aa5eac5SBen Skeggs g94_i2c_aux_fini(struct g94_i2c_aux *aux)
342aa5eac5SBen Skeggs {
352aa5eac5SBen Skeggs 	struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
362aa5eac5SBen Skeggs 	nvkm_mask(device, 0x00e4e4 + (aux->ch * 0x50), 0x00310000, 0x00000000);
372aa5eac5SBen Skeggs }
382aa5eac5SBen Skeggs 
392aa5eac5SBen Skeggs static int
g94_i2c_aux_init(struct g94_i2c_aux * aux)402aa5eac5SBen Skeggs g94_i2c_aux_init(struct g94_i2c_aux *aux)
412aa5eac5SBen Skeggs {
422aa5eac5SBen Skeggs 	struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
432aa5eac5SBen Skeggs 	const u32 unksel = 1; /* nfi which to use, or if it matters.. */
442aa5eac5SBen Skeggs 	const u32 ureq = unksel ? 0x00100000 : 0x00200000;
452aa5eac5SBen Skeggs 	const u32 urep = unksel ? 0x01000000 : 0x02000000;
462aa5eac5SBen Skeggs 	u32 ctrl, timeout;
472aa5eac5SBen Skeggs 
482aa5eac5SBen Skeggs 	/* wait up to 1ms for any previous transaction to be done... */
492aa5eac5SBen Skeggs 	timeout = 1000;
502aa5eac5SBen Skeggs 	do {
512aa5eac5SBen Skeggs 		ctrl = nvkm_rd32(device, 0x00e4e4 + (aux->ch * 0x50));
522aa5eac5SBen Skeggs 		udelay(1);
532aa5eac5SBen Skeggs 		if (!timeout--) {
542aa5eac5SBen Skeggs 			AUX_ERR(&aux->base, "begin idle timeout %08x", ctrl);
552aa5eac5SBen Skeggs 			return -EBUSY;
562aa5eac5SBen Skeggs 		}
572aa5eac5SBen Skeggs 	} while (ctrl & 0x03010000);
582aa5eac5SBen Skeggs 
592aa5eac5SBen Skeggs 	/* set some magic, and wait up to 1ms for it to appear */
602aa5eac5SBen Skeggs 	nvkm_mask(device, 0x00e4e4 + (aux->ch * 0x50), 0x00300000, ureq);
612aa5eac5SBen Skeggs 	timeout = 1000;
622aa5eac5SBen Skeggs 	do {
632aa5eac5SBen Skeggs 		ctrl = nvkm_rd32(device, 0x00e4e4 + (aux->ch * 0x50));
642aa5eac5SBen Skeggs 		udelay(1);
652aa5eac5SBen Skeggs 		if (!timeout--) {
662aa5eac5SBen Skeggs 			AUX_ERR(&aux->base, "magic wait %08x", ctrl);
672aa5eac5SBen Skeggs 			g94_i2c_aux_fini(aux);
682aa5eac5SBen Skeggs 			return -EBUSY;
692aa5eac5SBen Skeggs 		}
702aa5eac5SBen Skeggs 	} while ((ctrl & 0x03000000) != urep);
712aa5eac5SBen Skeggs 
722aa5eac5SBen Skeggs 	return 0;
732aa5eac5SBen Skeggs }
742aa5eac5SBen Skeggs 
7513a86519SBen Skeggs int
g94_i2c_aux_xfer(struct nvkm_i2c_aux * obj,bool retry,u8 type,u32 addr,u8 * data,u8 * size)762aa5eac5SBen Skeggs g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
771af5c410SBen Skeggs 		 u8 type, u32 addr, u8 *data, u8 *size)
782aa5eac5SBen Skeggs {
792aa5eac5SBen Skeggs 	struct g94_i2c_aux *aux = g94_i2c_aux(obj);
80*8ad95edcSBen Skeggs 	struct nvkm_i2c *i2c = aux->base.pad->i2c;
81*8ad95edcSBen Skeggs 	struct nvkm_device *device = i2c->subdev.device;
822aa5eac5SBen Skeggs 	const u32 base = aux->ch * 0x50;
83f1963a47SBen Skeggs 	u32 ctrl, stat, timeout, retries = 0;
842aa5eac5SBen Skeggs 	u32 xbuf[4] = {};
852aa5eac5SBen Skeggs 	int ret, i;
862aa5eac5SBen Skeggs 
871af5c410SBen Skeggs 	AUX_TRACE(&aux->base, "%d: %08x %d", type, addr, *size);
882aa5eac5SBen Skeggs 
892aa5eac5SBen Skeggs 	ret = g94_i2c_aux_init(aux);
902aa5eac5SBen Skeggs 	if (ret < 0)
912aa5eac5SBen Skeggs 		goto out;
922aa5eac5SBen Skeggs 
932aa5eac5SBen Skeggs 	stat = nvkm_rd32(device, 0x00e4e8 + base);
942aa5eac5SBen Skeggs 	if (!(stat & 0x10000000)) {
952aa5eac5SBen Skeggs 		AUX_TRACE(&aux->base, "sink not detected");
962aa5eac5SBen Skeggs 		ret = -ENXIO;
972aa5eac5SBen Skeggs 		goto out;
982aa5eac5SBen Skeggs 	}
992aa5eac5SBen Skeggs 
100*8ad95edcSBen Skeggs 	nvkm_i2c_aux_autodpcd(i2c, aux->ch, false);
101*8ad95edcSBen Skeggs 
1022aa5eac5SBen Skeggs 	if (!(type & 1)) {
1031af5c410SBen Skeggs 		memcpy(xbuf, data, *size);
1042aa5eac5SBen Skeggs 		for (i = 0; i < 16; i += 4) {
1052aa5eac5SBen Skeggs 			AUX_TRACE(&aux->base, "wr %08x", xbuf[i / 4]);
1062aa5eac5SBen Skeggs 			nvkm_wr32(device, 0x00e4c0 + base + i, xbuf[i / 4]);
1072aa5eac5SBen Skeggs 		}
1082aa5eac5SBen Skeggs 	}
1092aa5eac5SBen Skeggs 
1102aa5eac5SBen Skeggs 	ctrl  = nvkm_rd32(device, 0x00e4e4 + base);
11113a86519SBen Skeggs 	ctrl &= ~0x0001f1ff;
1122aa5eac5SBen Skeggs 	ctrl |= type << 12;
11313a86519SBen Skeggs 	ctrl |= (*size ? (*size - 1) : 0x00000100);
1142aa5eac5SBen Skeggs 	nvkm_wr32(device, 0x00e4e0 + base, addr);
1152aa5eac5SBen Skeggs 
1162aa5eac5SBen Skeggs 	/* (maybe) retry transaction a number of times on failure... */
117f1963a47SBen Skeggs 	do {
1182aa5eac5SBen Skeggs 		/* reset, and delay a while if this is a retry */
1192aa5eac5SBen Skeggs 		nvkm_wr32(device, 0x00e4e4 + base, 0x80000000 | ctrl);
1202aa5eac5SBen Skeggs 		nvkm_wr32(device, 0x00e4e4 + base, 0x00000000 | ctrl);
1212aa5eac5SBen Skeggs 		if (retries)
1222aa5eac5SBen Skeggs 			udelay(400);
1232aa5eac5SBen Skeggs 
1240156e76dSBen Skeggs 		/* transaction request, wait up to 2ms for it to complete */
1252aa5eac5SBen Skeggs 		nvkm_wr32(device, 0x00e4e4 + base, 0x00010000 | ctrl);
1262aa5eac5SBen Skeggs 
1270156e76dSBen Skeggs 		timeout = 2000;
1282aa5eac5SBen Skeggs 		do {
1292aa5eac5SBen Skeggs 			ctrl = nvkm_rd32(device, 0x00e4e4 + base);
1302aa5eac5SBen Skeggs 			udelay(1);
1312aa5eac5SBen Skeggs 			if (!timeout--) {
1322aa5eac5SBen Skeggs 				AUX_ERR(&aux->base, "timeout %08x", ctrl);
1332aa5eac5SBen Skeggs 				ret = -EIO;
134*8ad95edcSBen Skeggs 				goto out_err;
1352aa5eac5SBen Skeggs 			}
1362aa5eac5SBen Skeggs 		} while (ctrl & 0x00010000);
137f1963a47SBen Skeggs 		ret = 0;
1382aa5eac5SBen Skeggs 
1392aa5eac5SBen Skeggs 		/* read status, and check if transaction completed ok */
1402aa5eac5SBen Skeggs 		stat = nvkm_mask(device, 0x00e4e8 + base, 0, 0);
1412aa5eac5SBen Skeggs 		if ((stat & 0x000f0000) == 0x00080000 ||
1422aa5eac5SBen Skeggs 		    (stat & 0x000f0000) == 0x00020000)
143f1963a47SBen Skeggs 			ret = 1;
1442aa5eac5SBen Skeggs 		if ((stat & 0x00000100))
1452aa5eac5SBen Skeggs 			ret = -ETIMEDOUT;
1462aa5eac5SBen Skeggs 		if ((stat & 0x00000e00))
1472aa5eac5SBen Skeggs 			ret = -EIO;
1482aa5eac5SBen Skeggs 
1492aa5eac5SBen Skeggs 		AUX_TRACE(&aux->base, "%02d %08x %08x", retries, ctrl, stat);
150f1963a47SBen Skeggs 	} while (ret && retry && retries++ < 32);
1512aa5eac5SBen Skeggs 
1522aa5eac5SBen Skeggs 	if (type & 1) {
1532aa5eac5SBen Skeggs 		for (i = 0; i < 16; i += 4) {
1542aa5eac5SBen Skeggs 			xbuf[i / 4] = nvkm_rd32(device, 0x00e4d0 + base + i);
1552aa5eac5SBen Skeggs 			AUX_TRACE(&aux->base, "rd %08x", xbuf[i / 4]);
1562aa5eac5SBen Skeggs 		}
1571af5c410SBen Skeggs 		memcpy(data, xbuf, *size);
1585c68d91eSBen Skeggs 		*size = stat & 0x0000001f;
1592aa5eac5SBen Skeggs 	}
160*8ad95edcSBen Skeggs out_err:
161*8ad95edcSBen Skeggs 	nvkm_i2c_aux_autodpcd(i2c, aux->ch, true);
1622aa5eac5SBen Skeggs out:
1632aa5eac5SBen Skeggs 	g94_i2c_aux_fini(aux);
1642aa5eac5SBen Skeggs 	return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
1652aa5eac5SBen Skeggs }
1662aa5eac5SBen Skeggs 
1672aa5eac5SBen Skeggs int
g94_i2c_aux_new_(const struct nvkm_i2c_aux_func * func,struct nvkm_i2c_pad * pad,int index,u8 drive,struct nvkm_i2c_aux ** paux)16813a86519SBen Skeggs g94_i2c_aux_new_(const struct nvkm_i2c_aux_func *func,
16913a86519SBen Skeggs 		 struct nvkm_i2c_pad *pad, int index, u8 drive,
1702aa5eac5SBen Skeggs 		 struct nvkm_i2c_aux **paux)
1712aa5eac5SBen Skeggs {
1722aa5eac5SBen Skeggs 	struct g94_i2c_aux *aux;
1732aa5eac5SBen Skeggs 
1742aa5eac5SBen Skeggs 	if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL)))
1752aa5eac5SBen Skeggs 		return -ENOMEM;
1762aa5eac5SBen Skeggs 	*paux = &aux->base;
1772aa5eac5SBen Skeggs 
17813a86519SBen Skeggs 	nvkm_i2c_aux_ctor(func, pad, index, &aux->base);
1792aa5eac5SBen Skeggs 	aux->ch = drive;
1802aa5eac5SBen Skeggs 	aux->base.intr = 1 << aux->ch;
1812aa5eac5SBen Skeggs 	return 0;
1822aa5eac5SBen Skeggs }
18313a86519SBen Skeggs 
18413a86519SBen Skeggs static const struct nvkm_i2c_aux_func
18513a86519SBen Skeggs g94_i2c_aux = {
18613a86519SBen Skeggs 	.xfer = g94_i2c_aux_xfer,
18713a86519SBen Skeggs };
18813a86519SBen Skeggs 
18913a86519SBen Skeggs int
g94_i2c_aux_new(struct nvkm_i2c_pad * pad,int index,u8 drive,struct nvkm_i2c_aux ** paux)19013a86519SBen Skeggs g94_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive,
19113a86519SBen Skeggs 		struct nvkm_i2c_aux **paux)
19213a86519SBen Skeggs {
19313a86519SBen Skeggs 	return g94_i2c_aux_new_(&g94_i2c_aux, pad, index, drive, paux);
19413a86519SBen Skeggs }
195