1 /* 2 * Copyright 2009 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 25 #include <linux/string_helpers.h> 26 27 #include "aux.h" 28 #include "pad.h" 29 30 static int 31 nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 32 { 33 struct nvkm_i2c_aux *aux = container_of(adap, typeof(*aux), i2c); 34 struct i2c_msg *msg = msgs; 35 int ret, mcnt = num; 36 37 ret = nvkm_i2c_aux_acquire(aux); 38 if (ret) 39 return ret; 40 41 while (mcnt--) { 42 u8 remaining = msg->len; 43 u8 *ptr = msg->buf; 44 45 while (remaining) { 46 u8 cnt, retries, cmd; 47 48 if (msg->flags & I2C_M_RD) 49 cmd = 1; 50 else 51 cmd = 0; 52 53 if (mcnt || remaining > 16) 54 cmd |= 4; /* MOT */ 55 56 for (retries = 0, cnt = 0; 57 retries < 32 && !cnt; 58 retries++) { 59 cnt = min_t(u8, remaining, 16); 60 ret = aux->func->xfer(aux, true, cmd, 61 msg->addr, ptr, &cnt); 62 if (ret < 0) 63 goto out; 64 } 65 if (!cnt) { 66 AUX_TRACE(aux, "no data after 32 retries"); 67 ret = -EIO; 68 goto out; 69 } 70 71 ptr += cnt; 72 remaining -= cnt; 73 } 74 75 msg++; 76 } 77 78 ret = num; 79 out: 80 nvkm_i2c_aux_release(aux); 81 return ret; 82 } 83 84 static u32 85 nvkm_i2c_aux_i2c_func(struct i2c_adapter *adap) 86 { 87 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 88 } 89 90 static const struct i2c_algorithm 91 nvkm_i2c_aux_i2c_algo = { 92 .master_xfer = nvkm_i2c_aux_i2c_xfer, 93 .functionality = nvkm_i2c_aux_i2c_func 94 }; 95 96 void 97 nvkm_i2c_aux_monitor(struct nvkm_i2c_aux *aux, bool monitor) 98 { 99 struct nvkm_i2c_pad *pad = aux->pad; 100 AUX_TRACE(aux, "monitor: %s", str_yes_no(monitor)); 101 if (monitor) 102 nvkm_i2c_pad_mode(pad, NVKM_I2C_PAD_AUX); 103 else 104 nvkm_i2c_pad_mode(pad, NVKM_I2C_PAD_OFF); 105 } 106 107 void 108 nvkm_i2c_aux_release(struct nvkm_i2c_aux *aux) 109 { 110 struct nvkm_i2c_pad *pad = aux->pad; 111 AUX_TRACE(aux, "release"); 112 nvkm_i2c_pad_release(pad); 113 mutex_unlock(&aux->mutex); 114 } 115 116 int 117 nvkm_i2c_aux_acquire(struct nvkm_i2c_aux *aux) 118 { 119 struct nvkm_i2c_pad *pad = aux->pad; 120 int ret; 121 122 AUX_TRACE(aux, "acquire"); 123 mutex_lock(&aux->mutex); 124 125 if (aux->enabled) 126 ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_AUX); 127 else 128 ret = -EIO; 129 130 if (ret) 131 mutex_unlock(&aux->mutex); 132 return ret; 133 } 134 135 int 136 nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *aux, bool retry, u8 type, 137 u32 addr, u8 *data, u8 *size) 138 { 139 if (!*size && !aux->func->address_only) { 140 AUX_ERR(aux, "address-only transaction dropped"); 141 return -ENOSYS; 142 } 143 return aux->func->xfer(aux, retry, type, addr, data, size); 144 } 145 146 int 147 nvkm_i2c_aux_lnk_ctl(struct nvkm_i2c_aux *aux, int nr, int bw, bool ef) 148 { 149 if (aux->func->lnk_ctl) 150 return aux->func->lnk_ctl(aux, nr, bw, ef); 151 return -ENODEV; 152 } 153 154 void 155 nvkm_i2c_aux_del(struct nvkm_i2c_aux **paux) 156 { 157 struct nvkm_i2c_aux *aux = *paux; 158 if (aux && !WARN_ON(!aux->func)) { 159 AUX_TRACE(aux, "dtor"); 160 list_del(&aux->head); 161 i2c_del_adapter(&aux->i2c); 162 kfree(*paux); 163 *paux = NULL; 164 } 165 } 166 167 void 168 nvkm_i2c_aux_init(struct nvkm_i2c_aux *aux) 169 { 170 AUX_TRACE(aux, "init"); 171 mutex_lock(&aux->mutex); 172 aux->enabled = true; 173 mutex_unlock(&aux->mutex); 174 } 175 176 void 177 nvkm_i2c_aux_fini(struct nvkm_i2c_aux *aux) 178 { 179 AUX_TRACE(aux, "fini"); 180 mutex_lock(&aux->mutex); 181 aux->enabled = false; 182 mutex_unlock(&aux->mutex); 183 } 184 185 int 186 nvkm_i2c_aux_ctor(const struct nvkm_i2c_aux_func *func, 187 struct nvkm_i2c_pad *pad, int id, 188 struct nvkm_i2c_aux *aux) 189 { 190 struct nvkm_device *device = pad->i2c->subdev.device; 191 192 aux->func = func; 193 aux->pad = pad; 194 aux->id = id; 195 mutex_init(&aux->mutex); 196 list_add_tail(&aux->head, &pad->i2c->aux); 197 AUX_TRACE(aux, "ctor"); 198 199 snprintf(aux->i2c.name, sizeof(aux->i2c.name), "nvkm-%s-aux-%04x", 200 dev_name(device->dev), id); 201 aux->i2c.owner = THIS_MODULE; 202 aux->i2c.dev.parent = device->dev; 203 aux->i2c.algo = &nvkm_i2c_aux_i2c_algo; 204 return i2c_add_adapter(&aux->i2c); 205 } 206 207 int 208 nvkm_i2c_aux_new_(const struct nvkm_i2c_aux_func *func, 209 struct nvkm_i2c_pad *pad, int id, 210 struct nvkm_i2c_aux **paux) 211 { 212 if (!(*paux = kzalloc(sizeof(**paux), GFP_KERNEL))) 213 return -ENOMEM; 214 return nvkm_i2c_aux_ctor(func, pad, id, *paux); 215 } 216