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