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