brintos

brintos / linux-shallow public Read only

0
0
Text · 4.9 KiB · d063d0d Raw
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