xref: /linux/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c (revision a976c2951d8f376112361830aa7762beff83a205)
1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs  * Copyright 2013 Red Hat Inc.
3c39f472eSBen Skeggs  *
4c39f472eSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs  * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs  *
11c39f472eSBen Skeggs  * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs  * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs  *
14c39f472eSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17c39f472eSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs  *
22c39f472eSBen Skeggs  * Authors: Ben Skeggs <bskeggs@redhat.com>
23c39f472eSBen Skeggs  */
242aa5eac5SBen Skeggs #define anx9805_pad(p) container_of((p), struct anx9805_pad, base)
252aa5eac5SBen Skeggs #define anx9805_bus(p) container_of((p), struct anx9805_bus, base)
262aa5eac5SBen Skeggs #define anx9805_aux(p) container_of((p), struct anx9805_aux, base)
272aa5eac5SBen Skeggs #include "aux.h"
282aa5eac5SBen Skeggs #include "bus.h"
29c39f472eSBen Skeggs 
302aa5eac5SBen Skeggs struct anx9805_pad {
312aa5eac5SBen Skeggs 	struct nvkm_i2c_pad base;
322aa5eac5SBen Skeggs 	struct nvkm_i2c_bus *bus;
332aa5eac5SBen Skeggs 	u8 addr;
342aa5eac5SBen Skeggs };
352aa5eac5SBen Skeggs 
362aa5eac5SBen Skeggs struct anx9805_bus {
372aa5eac5SBen Skeggs 	struct nvkm_i2c_bus base;
382aa5eac5SBen Skeggs 	struct anx9805_pad *pad;
392aa5eac5SBen Skeggs 	u8 addr;
40c39f472eSBen Skeggs };
41c39f472eSBen Skeggs 
42c39f472eSBen Skeggs static int
anx9805_bus_xfer(struct nvkm_i2c_bus * base,struct i2c_msg * msgs,int num)432aa5eac5SBen Skeggs anx9805_bus_xfer(struct nvkm_i2c_bus *base, struct i2c_msg *msgs, int num)
44c39f472eSBen Skeggs {
452aa5eac5SBen Skeggs 	struct anx9805_bus *bus = anx9805_bus(base);
462aa5eac5SBen Skeggs 	struct anx9805_pad *pad = bus->pad;
472aa5eac5SBen Skeggs 	struct i2c_adapter *adap = &pad->bus->i2c;
48c39f472eSBen Skeggs 	struct i2c_msg *msg = msgs;
49c39f472eSBen Skeggs 	int ret = -ETIMEDOUT;
50c39f472eSBen Skeggs 	int i, j, cnt = num;
51c39f472eSBen Skeggs 	u8 seg = 0x00, off = 0x00, tmp;
52c39f472eSBen Skeggs 
532aa5eac5SBen Skeggs 	tmp = nvkm_rdi2cr(adap, pad->addr, 0x07) & ~0x10;
542aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, pad->addr, 0x07, tmp | 0x10);
552aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, pad->addr, 0x07, tmp);
562aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, bus->addr, 0x43, 0x05);
57c39f472eSBen Skeggs 	mdelay(5);
58c39f472eSBen Skeggs 
59c39f472eSBen Skeggs 	while (cnt--) {
60c39f472eSBen Skeggs 		if ( (msg->flags & I2C_M_RD) && msg->addr == 0x50) {
612aa5eac5SBen Skeggs 			nvkm_wri2cr(adap, bus->addr, 0x40, msg->addr << 1);
622aa5eac5SBen Skeggs 			nvkm_wri2cr(adap, bus->addr, 0x41, seg);
632aa5eac5SBen Skeggs 			nvkm_wri2cr(adap, bus->addr, 0x42, off);
642aa5eac5SBen Skeggs 			nvkm_wri2cr(adap, bus->addr, 0x44, msg->len);
652aa5eac5SBen Skeggs 			nvkm_wri2cr(adap, bus->addr, 0x45, 0x00);
662aa5eac5SBen Skeggs 			nvkm_wri2cr(adap, bus->addr, 0x43, 0x01);
67c39f472eSBen Skeggs 			for (i = 0; i < msg->len; i++) {
68c39f472eSBen Skeggs 				j = 0;
692aa5eac5SBen Skeggs 				while (nvkm_rdi2cr(adap, bus->addr, 0x46) & 0x10) {
70c39f472eSBen Skeggs 					mdelay(5);
71c39f472eSBen Skeggs 					if (j++ == 32)
72c39f472eSBen Skeggs 						goto done;
73c39f472eSBen Skeggs 				}
742aa5eac5SBen Skeggs 				msg->buf[i] = nvkm_rdi2cr(adap, bus->addr, 0x47);
75c39f472eSBen Skeggs 			}
76c39f472eSBen Skeggs 		} else
77c39f472eSBen Skeggs 		if (!(msg->flags & I2C_M_RD)) {
78c39f472eSBen Skeggs 			if (msg->addr == 0x50 && msg->len == 0x01) {
79c39f472eSBen Skeggs 				off = msg->buf[0];
80c39f472eSBen Skeggs 			} else
81c39f472eSBen Skeggs 			if (msg->addr == 0x30 && msg->len == 0x01) {
82c39f472eSBen Skeggs 				seg = msg->buf[0];
83c39f472eSBen Skeggs 			} else
84c39f472eSBen Skeggs 				goto done;
85c39f472eSBen Skeggs 		} else {
86c39f472eSBen Skeggs 			goto done;
87c39f472eSBen Skeggs 		}
88c39f472eSBen Skeggs 		msg++;
89c39f472eSBen Skeggs 	}
90c39f472eSBen Skeggs 
91c39f472eSBen Skeggs 	ret = num;
92c39f472eSBen Skeggs done:
932aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, bus->addr, 0x43, 0x00);
94c39f472eSBen Skeggs 	return ret;
95c39f472eSBen Skeggs }
96c39f472eSBen Skeggs 
972aa5eac5SBen Skeggs static const struct nvkm_i2c_bus_func
982aa5eac5SBen Skeggs anx9805_bus_func = {
992aa5eac5SBen Skeggs 	.xfer = anx9805_bus_xfer,
100c39f472eSBen Skeggs };
101c39f472eSBen Skeggs 
102c39f472eSBen Skeggs static int
anx9805_bus_new(struct nvkm_i2c_pad * base,int id,u8 drive,struct nvkm_i2c_bus ** pbus)1032aa5eac5SBen Skeggs anx9805_bus_new(struct nvkm_i2c_pad *base, int id, u8 drive,
1042aa5eac5SBen Skeggs 		struct nvkm_i2c_bus **pbus)
105c39f472eSBen Skeggs {
1062aa5eac5SBen Skeggs 	struct anx9805_pad *pad = anx9805_pad(base);
1072aa5eac5SBen Skeggs 	struct anx9805_bus *bus;
108c39f472eSBen Skeggs 	int ret;
109c39f472eSBen Skeggs 
1102aa5eac5SBen Skeggs 	if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL)))
1112aa5eac5SBen Skeggs 		return -ENOMEM;
1122aa5eac5SBen Skeggs 	*pbus = &bus->base;
1132aa5eac5SBen Skeggs 	bus->pad = pad;
1142aa5eac5SBen Skeggs 
1152aa5eac5SBen Skeggs 	ret = nvkm_i2c_bus_ctor(&anx9805_bus_func, &pad->base, id, &bus->base);
116c39f472eSBen Skeggs 	if (ret)
117c39f472eSBen Skeggs 		return ret;
118c39f472eSBen Skeggs 
1192aa5eac5SBen Skeggs 	switch (pad->addr) {
1202aa5eac5SBen Skeggs 	case 0x39: bus->addr = 0x3d; break;
1212aa5eac5SBen Skeggs 	case 0x3b: bus->addr = 0x3f; break;
122c39f472eSBen Skeggs 	default:
1232aa5eac5SBen Skeggs 		return -ENOSYS;
124c39f472eSBen Skeggs 	}
125b9ec1424SBen Skeggs 
126c39f472eSBen Skeggs 	return 0;
127c39f472eSBen Skeggs }
128c39f472eSBen Skeggs 
1292aa5eac5SBen Skeggs struct anx9805_aux {
1302aa5eac5SBen Skeggs 	struct nvkm_i2c_aux base;
1312aa5eac5SBen Skeggs 	struct anx9805_pad *pad;
1322aa5eac5SBen Skeggs 	u8 addr;
133c39f472eSBen Skeggs };
134c39f472eSBen Skeggs 
1352aa5eac5SBen Skeggs static int
anx9805_aux_xfer(struct nvkm_i2c_aux * base,bool retry,u8 type,u32 addr,u8 * data,u8 * size)1362aa5eac5SBen Skeggs anx9805_aux_xfer(struct nvkm_i2c_aux *base, bool retry,
137*1af5c410SBen Skeggs 		 u8 type, u32 addr, u8 *data, u8 *size)
1382aa5eac5SBen Skeggs {
1392aa5eac5SBen Skeggs 	struct anx9805_aux *aux = anx9805_aux(base);
1402aa5eac5SBen Skeggs 	struct anx9805_pad *pad = aux->pad;
1412aa5eac5SBen Skeggs 	struct i2c_adapter *adap = &pad->bus->i2c;
1422aa5eac5SBen Skeggs 	int i, ret = -ETIMEDOUT;
1432aa5eac5SBen Skeggs 	u8 buf[16] = {};
1442aa5eac5SBen Skeggs 	u8 tmp;
1452aa5eac5SBen Skeggs 
146*1af5c410SBen Skeggs 	AUX_DBG(&aux->base, "%02x %05x %d", type, addr, *size);
1472aa5eac5SBen Skeggs 
1482aa5eac5SBen Skeggs 	tmp = nvkm_rdi2cr(adap, pad->addr, 0x07) & ~0x04;
1492aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, pad->addr, 0x07, tmp | 0x04);
1502aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, pad->addr, 0x07, tmp);
1512aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, pad->addr, 0xf7, 0x01);
1522aa5eac5SBen Skeggs 
1532aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, aux->addr, 0xe4, 0x80);
1542aa5eac5SBen Skeggs 	if (!(type & 1)) {
155*1af5c410SBen Skeggs 		memcpy(buf, data, *size);
1562aa5eac5SBen Skeggs 		AUX_DBG(&aux->base, "%16ph", buf);
157*1af5c410SBen Skeggs 		for (i = 0; i < *size; i++)
1582aa5eac5SBen Skeggs 			nvkm_wri2cr(adap, aux->addr, 0xf0 + i, buf[i]);
1592aa5eac5SBen Skeggs 	}
160*1af5c410SBen Skeggs 	nvkm_wri2cr(adap, aux->addr, 0xe5, ((*size - 1) << 4) | type);
1612aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, aux->addr, 0xe6, (addr & 0x000ff) >>  0);
1622aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, aux->addr, 0xe7, (addr & 0x0ff00) >>  8);
1632aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, aux->addr, 0xe8, (addr & 0xf0000) >> 16);
1642aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, aux->addr, 0xe9, 0x01);
1652aa5eac5SBen Skeggs 
1662aa5eac5SBen Skeggs 	i = 0;
1672aa5eac5SBen Skeggs 	while ((tmp = nvkm_rdi2cr(adap, aux->addr, 0xe9)) & 0x01) {
1682aa5eac5SBen Skeggs 		mdelay(5);
1692aa5eac5SBen Skeggs 		if (i++ == 32)
1702aa5eac5SBen Skeggs 			goto done;
1712aa5eac5SBen Skeggs 	}
1722aa5eac5SBen Skeggs 
1732aa5eac5SBen Skeggs 	if ((tmp = nvkm_rdi2cr(adap, pad->addr, 0xf7)) & 0x01) {
1742aa5eac5SBen Skeggs 		ret = -EIO;
1752aa5eac5SBen Skeggs 		goto done;
1762aa5eac5SBen Skeggs 	}
1772aa5eac5SBen Skeggs 
1782aa5eac5SBen Skeggs 	if (type & 1) {
179*1af5c410SBen Skeggs 		for (i = 0; i < *size; i++)
1802aa5eac5SBen Skeggs 			buf[i] = nvkm_rdi2cr(adap, aux->addr, 0xf0 + i);
1812aa5eac5SBen Skeggs 		AUX_DBG(&aux->base, "%16ph", buf);
182*1af5c410SBen Skeggs 		memcpy(data, buf, *size);
1832aa5eac5SBen Skeggs 	}
1842aa5eac5SBen Skeggs 
1852aa5eac5SBen Skeggs 	ret = 0;
1862aa5eac5SBen Skeggs done:
1872aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, pad->addr, 0xf7, 0x01);
1882aa5eac5SBen Skeggs 	return ret;
1892aa5eac5SBen Skeggs }
1902aa5eac5SBen Skeggs 
1912aa5eac5SBen Skeggs static int
anx9805_aux_lnk_ctl(struct nvkm_i2c_aux * base,int link_nr,int link_bw,bool enh)1922aa5eac5SBen Skeggs anx9805_aux_lnk_ctl(struct nvkm_i2c_aux *base,
1932aa5eac5SBen Skeggs 		    int link_nr, int link_bw, bool enh)
1942aa5eac5SBen Skeggs {
1952aa5eac5SBen Skeggs 	struct anx9805_aux *aux = anx9805_aux(base);
1962aa5eac5SBen Skeggs 	struct anx9805_pad *pad = aux->pad;
1972aa5eac5SBen Skeggs 	struct i2c_adapter *adap = &pad->bus->i2c;
1982aa5eac5SBen Skeggs 	u8 tmp, i;
1992aa5eac5SBen Skeggs 
2002aa5eac5SBen Skeggs 	AUX_DBG(&aux->base, "ANX9805 train %d %02x %d",
2012aa5eac5SBen Skeggs 		link_nr, link_bw, enh);
2022aa5eac5SBen Skeggs 
2032aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, aux->addr, 0xa0, link_bw);
2042aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, aux->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00));
2052aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, aux->addr, 0xa2, 0x01);
2062aa5eac5SBen Skeggs 	nvkm_wri2cr(adap, aux->addr, 0xa8, 0x01);
2072aa5eac5SBen Skeggs 
2082aa5eac5SBen Skeggs 	i = 0;
2092aa5eac5SBen Skeggs 	while ((tmp = nvkm_rdi2cr(adap, aux->addr, 0xa8)) & 0x01) {
2102aa5eac5SBen Skeggs 		mdelay(5);
2112aa5eac5SBen Skeggs 		if (i++ == 100) {
2122aa5eac5SBen Skeggs 			AUX_ERR(&aux->base, "link training timeout");
2132aa5eac5SBen Skeggs 			return -ETIMEDOUT;
2142aa5eac5SBen Skeggs 		}
2152aa5eac5SBen Skeggs 	}
2162aa5eac5SBen Skeggs 
2172aa5eac5SBen Skeggs 	if (tmp & 0x70) {
2182aa5eac5SBen Skeggs 		AUX_ERR(&aux->base, "link training failed");
2192aa5eac5SBen Skeggs 		return -EIO;
2202aa5eac5SBen Skeggs 	}
2212aa5eac5SBen Skeggs 
2222aa5eac5SBen Skeggs 	return 0;
2232aa5eac5SBen Skeggs }
2242aa5eac5SBen Skeggs 
2252aa5eac5SBen Skeggs static const struct nvkm_i2c_aux_func
2262aa5eac5SBen Skeggs anx9805_aux_func = {
2272aa5eac5SBen Skeggs 	.xfer = anx9805_aux_xfer,
2282aa5eac5SBen Skeggs 	.lnk_ctl = anx9805_aux_lnk_ctl,
229c39f472eSBen Skeggs };
2302aa5eac5SBen Skeggs 
2312aa5eac5SBen Skeggs static int
anx9805_aux_new(struct nvkm_i2c_pad * base,int id,u8 drive,struct nvkm_i2c_aux ** pbus)2322aa5eac5SBen Skeggs anx9805_aux_new(struct nvkm_i2c_pad *base, int id, u8 drive,
2332aa5eac5SBen Skeggs 		struct nvkm_i2c_aux **pbus)
2342aa5eac5SBen Skeggs {
2352aa5eac5SBen Skeggs 	struct anx9805_pad *pad = anx9805_pad(base);
2362aa5eac5SBen Skeggs 	struct anx9805_aux *aux;
2372aa5eac5SBen Skeggs 	int ret;
2382aa5eac5SBen Skeggs 
2392aa5eac5SBen Skeggs 	if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL)))
2402aa5eac5SBen Skeggs 		return -ENOMEM;
2412aa5eac5SBen Skeggs 	*pbus = &aux->base;
2422aa5eac5SBen Skeggs 	aux->pad = pad;
2432aa5eac5SBen Skeggs 
2442aa5eac5SBen Skeggs 	ret = nvkm_i2c_aux_ctor(&anx9805_aux_func, &pad->base, id, &aux->base);
2452aa5eac5SBen Skeggs 	if (ret)
2462aa5eac5SBen Skeggs 		return ret;
2472aa5eac5SBen Skeggs 
2482aa5eac5SBen Skeggs 	switch (pad->addr) {
2492aa5eac5SBen Skeggs 	case 0x39: aux->addr = 0x38; break;
2502aa5eac5SBen Skeggs 	case 0x3b: aux->addr = 0x3c; break;
2512aa5eac5SBen Skeggs 	default:
2522aa5eac5SBen Skeggs 		return -ENOSYS;
2532aa5eac5SBen Skeggs 	}
2542aa5eac5SBen Skeggs 
2552aa5eac5SBen Skeggs 	return 0;
2562aa5eac5SBen Skeggs }
2572aa5eac5SBen Skeggs 
2582aa5eac5SBen Skeggs static const struct nvkm_i2c_pad_func
2592aa5eac5SBen Skeggs anx9805_pad_func = {
2602aa5eac5SBen Skeggs 	.bus_new_4 = anx9805_bus_new,
2612aa5eac5SBen Skeggs 	.aux_new_6 = anx9805_aux_new,
2622aa5eac5SBen Skeggs };
2632aa5eac5SBen Skeggs 
2642aa5eac5SBen Skeggs int
anx9805_pad_new(struct nvkm_i2c_bus * bus,int id,u8 addr,struct nvkm_i2c_pad ** ppad)2652aa5eac5SBen Skeggs anx9805_pad_new(struct nvkm_i2c_bus *bus, int id, u8 addr,
2662aa5eac5SBen Skeggs 		struct nvkm_i2c_pad **ppad)
2672aa5eac5SBen Skeggs {
2682aa5eac5SBen Skeggs 	struct anx9805_pad *pad;
2692aa5eac5SBen Skeggs 
2702aa5eac5SBen Skeggs 	if (!(pad = kzalloc(sizeof(*pad), GFP_KERNEL)))
2712aa5eac5SBen Skeggs 		return -ENOMEM;
2722aa5eac5SBen Skeggs 	*ppad = &pad->base;
2732aa5eac5SBen Skeggs 
2742aa5eac5SBen Skeggs 	nvkm_i2c_pad_ctor(&anx9805_pad_func, bus->pad->i2c, id, &pad->base);
2752aa5eac5SBen Skeggs 	pad->bus = bus;
2762aa5eac5SBen Skeggs 	pad->addr = addr;
2772aa5eac5SBen Skeggs 	return 0;
2782aa5eac5SBen Skeggs }
279