1/* $NetBSD: nouveau_nvkm_subdev_i2c_aux.c,v 1.5 2021/12/18 23:45:40 riastradh Exp $ */ 2 3/* 4 * Copyright 2009 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Ben Skeggs 25 */ 26#include <sys/cdefs.h> 27__KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_i2c_aux.c,v 1.5 2021/12/18 23:45:40 riastradh Exp $"); 28 29#include "aux.h" 30#include "pad.h" 31 32#include <linux/nbsd-namespace.h> 33 34static int 35nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 36{ 37 struct nvkm_i2c_aux *aux = container_of(adap, typeof(*aux), i2c); 38 struct i2c_msg *msg = msgs; 39 int ret, mcnt = num; 40 41 ret = nvkm_i2c_aux_acquire(aux); 42 if (ret) 43 return ret; 44 45 while (mcnt--) { 46 u8 remaining = msg->len; 47 u8 *ptr = msg->buf; 48 49 while (remaining) { 50 u8 cnt, retries, cmd; 51 52 if (msg->flags & I2C_M_RD) 53 cmd = 1; 54 else 55 cmd = 0; 56 57 if (mcnt || remaining > 16) 58 cmd |= 4; /* MOT */ 59 60 for (retries = 0, cnt = 0; 61 retries < 32 && !cnt; 62 retries++) { 63 cnt = min_t(u8, remaining, 16); 64 ret = aux->func->xfer(aux, true, cmd, 65 msg->addr, ptr, &cnt); 66 if (ret < 0) 67 goto out; 68 } 69 if (!cnt) { 70 AUX_TRACE(aux, "no data after 32 retries"); 71 ret = -EIO; 72 goto out; 73 } 74 75 ptr += cnt; 76 remaining -= cnt; 77 } 78 79 msg++; 80 } 81 82 ret = num; 83out: 84 nvkm_i2c_aux_release(aux); 85 return ret; 86} 87 88static u32 89nvkm_i2c_aux_i2c_func(struct i2c_adapter *adap) 90{ 91 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 92} 93 94static const struct i2c_algorithm 95nvkm_i2c_aux_i2c_algo = { 96 .master_xfer = nvkm_i2c_aux_i2c_xfer, 97 .functionality = nvkm_i2c_aux_i2c_func 98}; 99 100void 101nvkm_i2c_aux_monitor(struct nvkm_i2c_aux *aux, bool monitor) 102{ 103 struct nvkm_i2c_pad *pad = aux->pad; 104 AUX_TRACE(aux, "monitor: %s", monitor ? "yes" : "no"); 105 if (monitor) 106 nvkm_i2c_pad_mode(pad, NVKM_I2C_PAD_AUX); 107 else 108 nvkm_i2c_pad_mode(pad, NVKM_I2C_PAD_OFF); 109} 110 111void 112nvkm_i2c_aux_release(struct nvkm_i2c_aux *aux) 113{ 114 struct nvkm_i2c_pad *pad = aux->pad; 115 AUX_TRACE(aux, "release"); 116 nvkm_i2c_pad_release(pad); 117 mutex_unlock(&aux->mutex); 118} 119 120int 121nvkm_i2c_aux_acquire(struct nvkm_i2c_aux *aux) 122{ 123 struct nvkm_i2c_pad *pad = aux->pad; 124 int ret; 125 126 AUX_TRACE(aux, "acquire"); 127 mutex_lock(&aux->mutex); 128 129 if (aux->enabled) 130 ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_AUX); 131 else 132 ret = -EIO; 133 134 if (ret) 135 mutex_unlock(&aux->mutex); 136 return ret; 137} 138 139int 140nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *aux, bool retry, u8 type, 141 u32 addr, u8 *data, u8 *size) 142{ 143 if (!*size && !aux->func->address_only) { 144 AUX_ERR(aux, "address-only transaction dropped"); 145 return -ENOSYS; 146 } 147 return aux->func->xfer(aux, retry, type, addr, data, size); 148} 149 150int 151nvkm_i2c_aux_lnk_ctl(struct nvkm_i2c_aux *aux, int nr, int bw, bool ef) 152{ 153 if (aux->func->lnk_ctl) 154 return aux->func->lnk_ctl(aux, nr, bw, ef); 155 return -ENODEV; 156} 157 158void 159nvkm_i2c_aux_del(struct nvkm_i2c_aux **paux) 160{ 161 struct nvkm_i2c_aux *aux = *paux; 162 if (aux && !WARN_ON(!aux->func)) { 163 AUX_TRACE(aux, "dtor"); 164 list_del(&aux->head); 165 i2c_del_adapter(&aux->i2c); 166 mutex_destroy(&aux->mutex); 167 kfree(*paux); 168 *paux = NULL; 169 } 170} 171 172void 173nvkm_i2c_aux_init(struct nvkm_i2c_aux *aux) 174{ 175 AUX_TRACE(aux, "init"); 176 mutex_lock(&aux->mutex); 177 aux->enabled = true; 178 mutex_unlock(&aux->mutex); 179} 180 181void 182nvkm_i2c_aux_fini(struct nvkm_i2c_aux *aux) 183{ 184 AUX_TRACE(aux, "fini"); 185 mutex_lock(&aux->mutex); 186 aux->enabled = false; 187 mutex_unlock(&aux->mutex); 188} 189 190int 191nvkm_i2c_aux_ctor(const struct nvkm_i2c_aux_func *func, 192 struct nvkm_i2c_pad *pad, int id, 193 struct nvkm_i2c_aux *aux) 194{ 195 struct nvkm_device *device = pad->i2c->subdev.device; 196 197 aux->func = func; 198 aux->pad = pad; 199 aux->id = id; 200 mutex_init(&aux->mutex); 201 list_add_tail(&aux->head, &pad->i2c->aux); 202 AUX_TRACE(aux, "ctor"); 203 204 snprintf(aux->i2c.name, sizeof(aux->i2c.name), "nvkm-%s-aux-%04x", 205 dev_name(device->dev), id); 206 aux->i2c.owner = THIS_MODULE; 207 aux->i2c.dev.parent = device->dev; 208 aux->i2c.algo = &nvkm_i2c_aux_i2c_algo; 209 return i2c_add_adapter(&aux->i2c); 210} 211 212int 213nvkm_i2c_aux_new_(const struct nvkm_i2c_aux_func *func, 214 struct nvkm_i2c_pad *pad, int id, 215 struct nvkm_i2c_aux **paux) 216{ 217 if (!(*paux = kzalloc(sizeof(**paux), GFP_KERNEL))) 218 return -ENOMEM; 219 return nvkm_i2c_aux_ctor(func, pad, id, *paux); 220} 221