1/* 2 * Copyright 2012 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 portions 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 23 */ 24#include "nv50.h" 25 26#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args) 27#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args) 28 29static void 30auxch_fini(struct nvkm_i2c *aux, int ch) 31{ 32 nv_mask(aux, 0x00d954 + (ch * 0x50), 0x00310000, 0x00000000); 33} 34 35static int 36auxch_init(struct nvkm_i2c *aux, int ch) 37{ 38 const u32 unksel = 1; /* nfi which to use, or if it matters.. */ 39 const u32 ureq = unksel ? 0x00100000 : 0x00200000; 40 const u32 urep = unksel ? 0x01000000 : 0x02000000; 41 u32 ctrl, timeout; 42 43 /* wait up to 1ms for any previous transaction to be done... */ 44 timeout = 1000; 45 do { 46 ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50)); 47 udelay(1); 48 if (!timeout--) { 49 AUX_ERR("begin idle timeout 0x%08x\n", ctrl); 50 return -EBUSY; 51 } 52 } while (ctrl & 0x03010000); 53 54 /* set some magic, and wait up to 1ms for it to appear */ 55 nv_mask(aux, 0x00d954 + (ch * 0x50), 0x00300000, ureq); 56 timeout = 1000; 57 do { 58 ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50)); 59 udelay(1); 60 if (!timeout--) { 61 AUX_ERR("magic wait 0x%08x\n", ctrl); 62 auxch_fini(aux, ch); 63 return -EBUSY; 64 } 65 } while ((ctrl & 0x03000000) != urep); 66 67 return 0; 68} 69 70int 71gm204_aux(struct nvkm_i2c_port *base, bool retry, 72 u8 type, u32 addr, u8 *data, u8 size) 73{ 74 struct nvkm_i2c *aux = nvkm_i2c(base); 75 struct nv50_i2c_port *port = (void *)base; 76 u32 ctrl, stat, timeout, retries; 77 u32 xbuf[4] = {}; 78 int ch = port->addr; 79 int ret, i; 80 81 AUX_DBG("%d: 0x%08x %d\n", type, addr, size); 82 83 ret = auxch_init(aux, ch); 84 if (ret) 85 goto out; 86 87 stat = nv_rd32(aux, 0x00d958 + (ch * 0x50)); 88 if (!(stat & 0x10000000)) { 89 AUX_DBG("sink not detected\n"); 90 ret = -ENXIO; 91 goto out; 92 } 93 94 if (!(type & 1)) { 95 memcpy(xbuf, data, size); 96 for (i = 0; i < 16; i += 4) { 97 AUX_DBG("wr 0x%08x\n", xbuf[i / 4]); 98 nv_wr32(aux, 0x00d930 + (ch * 0x50) + i, xbuf[i / 4]); 99 } 100 } 101 102 ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50)); 103 ctrl &= ~0x0001f0ff; 104 ctrl |= type << 12; 105 ctrl |= size - 1; 106 nv_wr32(aux, 0x00d950 + (ch * 0x50), addr); 107 108 /* (maybe) retry transaction a number of times on failure... */ 109 for (retries = 0; !ret && retries < 32; retries++) { 110 /* reset, and delay a while if this is a retry */ 111 nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x80000000 | ctrl); 112 nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x00000000 | ctrl); 113 if (retries) 114 udelay(400); 115 116 /* transaction request, wait up to 1ms for it to complete */ 117 nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x00010000 | ctrl); 118 119 timeout = 1000; 120 do { 121 ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50)); 122 udelay(1); 123 if (!timeout--) { 124 AUX_ERR("tx req timeout 0x%08x\n", ctrl); 125 ret = -EIO; 126 goto out; 127 } 128 } while (ctrl & 0x00010000); 129 ret = 1; 130 131 /* read status, and check if transaction completed ok */ 132 stat = nv_mask(aux, 0x00d958 + (ch * 0x50), 0, 0); 133 if ((stat & 0x000f0000) == 0x00080000 || 134 (stat & 0x000f0000) == 0x00020000) 135 ret = retry ? 0 : 1; 136 if ((stat & 0x00000100)) 137 ret = -ETIMEDOUT; 138 if ((stat & 0x00000e00)) 139 ret = -EIO; 140 141 AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat); 142 } 143 144 if (type & 1) { 145 for (i = 0; i < 16; i += 4) { 146 xbuf[i / 4] = nv_rd32(aux, 0x00d940 + (ch * 0x50) + i); 147 AUX_DBG("rd 0x%08x\n", xbuf[i / 4]); 148 } 149 memcpy(data, xbuf, size); 150 } 151 152out: 153 auxch_fini(aux, ch); 154 return ret < 0 ? ret : (stat & 0x000f0000) >> 16; 155} 156 157static const struct nvkm_i2c_func 158gm204_aux_func = { 159 .aux = gm204_aux, 160}; 161 162int 163gm204_aux_port_ctor(struct nvkm_object *parent, 164 struct nvkm_object *engine, 165 struct nvkm_oclass *oclass, void *data, u32 index, 166 struct nvkm_object **pobject) 167{ 168 struct dcb_i2c_entry *info = data; 169 struct nv50_i2c_port *port; 170 int ret; 171 172 ret = nvkm_i2c_port_create(parent, engine, oclass, index, 173 &nvkm_i2c_aux_algo, &gm204_aux_func, &port); 174 *pobject = nv_object(port); 175 if (ret) 176 return ret; 177 178 port->base.aux = info->auxch; 179 port->addr = info->auxch; 180 return 0; 181} 182 183struct nvkm_oclass 184gm204_i2c_sclass[] = { 185 { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), 186 .ofuncs = &(struct nvkm_ofuncs) { 187 .ctor = gf110_i2c_port_ctor, 188 .dtor = _nvkm_i2c_port_dtor, 189 .init = nv50_i2c_port_init, 190 .fini = _nvkm_i2c_port_fini, 191 }, 192 }, 193 { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX), 194 .ofuncs = &(struct nvkm_ofuncs) { 195 .ctor = gm204_aux_port_ctor, 196 .dtor = _nvkm_i2c_port_dtor, 197 .init = _nvkm_i2c_port_init, 198 .fini = _nvkm_i2c_port_fini, 199 }, 200 }, 201 {} 202}; 203 204struct nvkm_oclass * 205gm204_i2c_oclass = &(struct nvkm_i2c_impl) { 206 .base.handle = NV_SUBDEV(I2C, 0x24), 207 .base.ofuncs = &(struct nvkm_ofuncs) { 208 .ctor = _nvkm_i2c_ctor, 209 .dtor = _nvkm_i2c_dtor, 210 .init = _nvkm_i2c_init, 211 .fini = _nvkm_i2c_fini, 212 }, 213 .sclass = gm204_i2c_sclass, 214 .pad_x = &nv04_i2c_pad_oclass, 215 .pad_s = &gm204_i2c_pad_oclass, 216 .aux = 8, 217 .aux_stat = gk104_aux_stat, 218 .aux_mask = gk104_aux_mask, 219}.base; 220