Home | History | Annotate | Line # | Download | only in i2c
      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 
     34 static int
     35 nvkm_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;
     83 out:
     84 	nvkm_i2c_aux_release(aux);
     85 	return ret;
     86 }
     87 
     88 static u32
     89 nvkm_i2c_aux_i2c_func(struct i2c_adapter *adap)
     90 {
     91 	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
     92 }
     93 
     94 static const struct i2c_algorithm
     95 nvkm_i2c_aux_i2c_algo = {
     96 	.master_xfer = nvkm_i2c_aux_i2c_xfer,
     97 	.functionality = nvkm_i2c_aux_i2c_func
     98 };
     99 
    100 void
    101 nvkm_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 
    111 void
    112 nvkm_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 
    120 int
    121 nvkm_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 
    139 int
    140 nvkm_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 
    150 int
    151 nvkm_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 
    158 void
    159 nvkm_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 
    172 void
    173 nvkm_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 
    181 void
    182 nvkm_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 
    190 int
    191 nvkm_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 
    212 int
    213 nvkm_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