linux/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.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 gm200_i2c_aux(p) container_of((p), struct gm200_i2c_aux, base)
  25#include "aux.h"
  26
  27struct gm200_i2c_aux {
  28        struct nvkm_i2c_aux base;
  29        int ch;
  30};
  31
  32static void
  33gm200_i2c_aux_fini(struct gm200_i2c_aux *aux)
  34{
  35        struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
  36        nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00710000, 0x00000000);
  37}
  38
  39static int
  40gm200_i2c_aux_init(struct gm200_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, 0x00d954 + (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 & 0x07010000);
  58
  59        /* set some magic, and wait up to 1ms for it to appear */
  60        nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00700000, ureq);
  61        timeout = 1000;
  62        do {
  63                ctrl = nvkm_rd32(device, 0x00d954 + (aux->ch * 0x50));
  64                udelay(1);
  65                if (!timeout--) {
  66                        AUX_ERR(&aux->base, "magic wait %08x", ctrl);
  67                        gm200_i2c_aux_fini(aux);
  68                        return -EBUSY;
  69                }
  70        } while ((ctrl & 0x07000000) != urep);
  71
  72        return 0;
  73}
  74
  75static int
  76gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
  77                   u8 type, u32 addr, u8 *data, u8 *size)
  78{
  79        struct gm200_i2c_aux *aux = gm200_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 = gm200_i2c_aux_init(aux);
  90        if (ret < 0)
  91                goto out;
  92
  93        stat = nvkm_rd32(device, 0x00d958 + 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, 0x00d930 + base + i, xbuf[i / 4]);
 107                }
 108        }
 109
 110        ctrl  = nvkm_rd32(device, 0x00d954 + base);
 111        ctrl &= ~0x0001f1ff;
 112        ctrl |= type << 12;
 113        ctrl |= (*size ? (*size - 1) : 0x00000100);
 114        nvkm_wr32(device, 0x00d950 + 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, 0x00d954 + base, 0x80000000 | ctrl);
 120                nvkm_wr32(device, 0x00d954 + 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, 0x00d954 + base, 0x00010000 | ctrl);
 126
 127                timeout = 2000;
 128                do {
 129                        ctrl = nvkm_rd32(device, 0x00d954 + 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, 0x00d958 + 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, 0x00d940 + base + i);
 155                        AUX_TRACE(&aux->base, "rd %08x", xbuf[i / 4]);
 156                }
 157                memcpy(data, xbuf, *size);
 158                *size = stat & 0x0000001f;
 159        }
 160
 161out_err:
 162        nvkm_i2c_aux_autodpcd(i2c, aux->ch, true);
 163out:
 164        gm200_i2c_aux_fini(aux);
 165        return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
 166}
 167
 168static const struct nvkm_i2c_aux_func
 169gm200_i2c_aux_func = {
 170        .address_only = true,
 171        .xfer = gm200_i2c_aux_xfer,
 172};
 173
 174int
 175gm200_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive,
 176                struct nvkm_i2c_aux **paux)
 177{
 178        struct gm200_i2c_aux *aux;
 179
 180        if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL)))
 181                return -ENOMEM;
 182        *paux = &aux->base;
 183
 184        nvkm_i2c_aux_ctor(&gm200_i2c_aux_func, pad, index, &aux->base);
 185        aux->ch = drive;
 186        aux->base.intr = 1 << aux->ch;
 187        return 0;
 188}
 189
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.