216 lines · c
1/*2 * Copyright 2009 Red Hat Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * 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 OR20 * OTHER DEALINGS IN THE SOFTWARE.21 *22 * Authors: Ben Skeggs23 */24 25#include <linux/string_helpers.h>26 27#include "aux.h"28#include "pad.h"29 30static int31nvkm_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 else51 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;79out:80 nvkm_i2c_aux_release(aux);81 return ret;82}83 84static u3285nvkm_i2c_aux_i2c_func(struct i2c_adapter *adap)86{87 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;88}89 90static const struct i2c_algorithm91nvkm_i2c_aux_i2c_algo = {92 .master_xfer = nvkm_i2c_aux_i2c_xfer,93 .functionality = nvkm_i2c_aux_i2c_func94};95 96void97nvkm_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 else104 nvkm_i2c_pad_mode(pad, NVKM_I2C_PAD_OFF);105}106 107void108nvkm_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 116int117nvkm_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 else128 ret = -EIO;129 130 if (ret)131 mutex_unlock(&aux->mutex);132 return ret;133}134 135int136nvkm_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 146int147nvkm_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 154void155nvkm_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 167void168nvkm_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 176void177nvkm_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 185int186nvkm_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 207int208nvkm_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