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
  75int
  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_i2c *i2c = aux->base.pad->i2c;
  81        struct nvkm_device *device = i2c->subdev.device;
  82        const u32 base = aux->ch * 0x50;
  83        u32 ctrl, stat, timeout, retries = 0;
  84        u32 xbuf[4] = {};
  85        int ret, i;
  86
  87        AUX_TRACE(&aux->base, "%d: %08x %d", type, addr, *size);
  88
  89        ret = g94_i2c_aux_init(aux);
  90        if (ret < 0)
  91                goto out;
  92
  93        stat = nvkm_rd32(device, 0x00e4e8 + base);
  94        if (!(stat & 0x10000000)) {
  95                AUX_TRACE(&aux->base, "sink not detected");
  96                ret = -ENXIO;
  97                goto out;
  98        }
  99
 100        nvkm_i2c_aux_autodpcd(i2c, aux->ch, false);
 101
 102        if (!(type & 1)) {
 103                memcpy(xbuf, data, *size);
 104                for (i = 0; i < 16; i += 4) {
 105                        AUX_TRACE(&aux->base, "wr %08x", xbuf[i / 4]);
 106                        nvkm_wr32(device, 0x00e4c0 + base + i, xbuf[i / 4]);
 107                }
 108        }
 109
 110        ctrl  = nvkm_rd32(device, 0x00e4e4 + base);
 111        ctrl &= ~0x0001f1ff;
 112        ctrl |= type << 12;
 113        ctrl |= (*size ? (*size - 1) : 0x00000100);
 114        nvkm_wr32(device, 0x00e4e0 + base, addr);
 115
 116        /* (maybe) retry transaction a number of times on failure... */
 117        do {
 118                /* reset, and delay a while if this is a retry */
 119                nvkm_wr32(device, 0x00e4e4 + base, 0x80000000 | ctrl);
 120                nvkm_wr32(device, 0x00e4e4 + base, 0x00000000 | ctrl);
 121                if (retries)
 122                        udelay(400);
 123
 124                /* transaction request, wait up to 2ms for it to complete */
 125                nvkm_wr32(device, 0x00e4e4 + base, 0x00010000 | ctrl);
 126
 127                timeout = 2000;
 128                do {
 129                        ctrl = nvkm_rd32(device, 0x00e4e4 + base);
 130                        udelay(1);
 131                        if (!timeout--) {
 132                                AUX_ERR(&aux->base, "timeout %08x", ctrl);
 133                                ret = -EIO;
 134                                goto out_err;
 135                        }
 136                } while (ctrl & 0x00010000);
 137                ret = 0;
 138
 139                /* read status, and check if transaction completed ok */
 140                stat = nvkm_mask(device, 0x00e4e8 + base, 0, 0);
 141                if ((stat & 0x000f0000) == 0x00080000 ||
 142                    (stat & 0x000f0000) == 0x00020000)
 143                        ret = 1;
 144                if ((stat & 0x00000100))
 145                        ret = -ETIMEDOUT;
 146                if ((stat & 0x00000e00))
 147                        ret = -EIO;
 148
 149                AUX_TRACE(&aux->base, "%02d %08x %08x", retries, ctrl, stat);
 150        } while (ret && retry && retries++ < 32);
 151
 152        if (type & 1) {
 153                for (i = 0; i < 16; i += 4) {
 154                        xbuf[i / 4] = nvkm_rd32(device, 0x00e4d0 + base + i);
 155                        AUX_TRACE(&aux->base, "rd %08x", xbuf[i / 4]);
 156                }
 157                memcpy(data, xbuf, *size);
 158                *size = stat & 0x0000001f;
 159        }
 160out_err:
 161        nvkm_i2c_aux_autodpcd(i2c, aux->ch, true);
 162out:
 163        g94_i2c_aux_fini(aux);
 164        return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
 165}
 166
 167int
 168g94_i2c_aux_new_(const struct nvkm_i2c_aux_func *func,
 169                 struct nvkm_i2c_pad *pad, int index, u8 drive,
 170                 struct nvkm_i2c_aux **paux)
 171{
 172        struct g94_i2c_aux *aux;
 173
 174        if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL)))
 175                return -ENOMEM;
 176        *paux = &aux->base;
 177
 178        nvkm_i2c_aux_ctor(func, pad, index, &aux->base);
 179        aux->ch = drive;
 180        aux->base.intr = 1 << aux->ch;
 181        return 0;
 182}
 183
 184static const struct nvkm_i2c_aux_func
 185g94_i2c_aux = {
 186        .xfer = g94_i2c_aux_xfer,
 187};
 188
 189int
 190g94_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive,
 191                struct nvkm_i2c_aux **paux)
 192{
 193        return g94_i2c_aux_new_(&g94_i2c_aux, pad, index, drive, paux);
 194}
 195