0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025 #include <linux/string_helpers.h>
0026
0027 #include "aux.h"
0028 #include "pad.h"
0029
0030 static int
0031 nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
0032 {
0033 struct nvkm_i2c_aux *aux = container_of(adap, typeof(*aux), i2c);
0034 struct i2c_msg *msg = msgs;
0035 int ret, mcnt = num;
0036
0037 ret = nvkm_i2c_aux_acquire(aux);
0038 if (ret)
0039 return ret;
0040
0041 while (mcnt--) {
0042 u8 remaining = msg->len;
0043 u8 *ptr = msg->buf;
0044
0045 while (remaining) {
0046 u8 cnt, retries, cmd;
0047
0048 if (msg->flags & I2C_M_RD)
0049 cmd = 1;
0050 else
0051 cmd = 0;
0052
0053 if (mcnt || remaining > 16)
0054 cmd |= 4;
0055
0056 for (retries = 0, cnt = 0;
0057 retries < 32 && !cnt;
0058 retries++) {
0059 cnt = min_t(u8, remaining, 16);
0060 ret = aux->func->xfer(aux, true, cmd,
0061 msg->addr, ptr, &cnt);
0062 if (ret < 0)
0063 goto out;
0064 }
0065 if (!cnt) {
0066 AUX_TRACE(aux, "no data after 32 retries");
0067 ret = -EIO;
0068 goto out;
0069 }
0070
0071 ptr += cnt;
0072 remaining -= cnt;
0073 }
0074
0075 msg++;
0076 }
0077
0078 ret = num;
0079 out:
0080 nvkm_i2c_aux_release(aux);
0081 return ret;
0082 }
0083
0084 static u32
0085 nvkm_i2c_aux_i2c_func(struct i2c_adapter *adap)
0086 {
0087 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
0088 }
0089
0090 static const struct i2c_algorithm
0091 nvkm_i2c_aux_i2c_algo = {
0092 .master_xfer = nvkm_i2c_aux_i2c_xfer,
0093 .functionality = nvkm_i2c_aux_i2c_func
0094 };
0095
0096 void
0097 nvkm_i2c_aux_monitor(struct nvkm_i2c_aux *aux, bool monitor)
0098 {
0099 struct nvkm_i2c_pad *pad = aux->pad;
0100 AUX_TRACE(aux, "monitor: %s", str_yes_no(monitor));
0101 if (monitor)
0102 nvkm_i2c_pad_mode(pad, NVKM_I2C_PAD_AUX);
0103 else
0104 nvkm_i2c_pad_mode(pad, NVKM_I2C_PAD_OFF);
0105 }
0106
0107 void
0108 nvkm_i2c_aux_release(struct nvkm_i2c_aux *aux)
0109 {
0110 struct nvkm_i2c_pad *pad = aux->pad;
0111 AUX_TRACE(aux, "release");
0112 nvkm_i2c_pad_release(pad);
0113 mutex_unlock(&aux->mutex);
0114 }
0115
0116 int
0117 nvkm_i2c_aux_acquire(struct nvkm_i2c_aux *aux)
0118 {
0119 struct nvkm_i2c_pad *pad = aux->pad;
0120 int ret;
0121
0122 AUX_TRACE(aux, "acquire");
0123 mutex_lock(&aux->mutex);
0124
0125 if (aux->enabled)
0126 ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_AUX);
0127 else
0128 ret = -EIO;
0129
0130 if (ret)
0131 mutex_unlock(&aux->mutex);
0132 return ret;
0133 }
0134
0135 int
0136 nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *aux, bool retry, u8 type,
0137 u32 addr, u8 *data, u8 *size)
0138 {
0139 if (!*size && !aux->func->address_only) {
0140 AUX_ERR(aux, "address-only transaction dropped");
0141 return -ENOSYS;
0142 }
0143 return aux->func->xfer(aux, retry, type, addr, data, size);
0144 }
0145
0146 int
0147 nvkm_i2c_aux_lnk_ctl(struct nvkm_i2c_aux *aux, int nr, int bw, bool ef)
0148 {
0149 if (aux->func->lnk_ctl)
0150 return aux->func->lnk_ctl(aux, nr, bw, ef);
0151 return -ENODEV;
0152 }
0153
0154 void
0155 nvkm_i2c_aux_del(struct nvkm_i2c_aux **paux)
0156 {
0157 struct nvkm_i2c_aux *aux = *paux;
0158 if (aux && !WARN_ON(!aux->func)) {
0159 AUX_TRACE(aux, "dtor");
0160 list_del(&aux->head);
0161 i2c_del_adapter(&aux->i2c);
0162 kfree(*paux);
0163 *paux = NULL;
0164 }
0165 }
0166
0167 void
0168 nvkm_i2c_aux_init(struct nvkm_i2c_aux *aux)
0169 {
0170 AUX_TRACE(aux, "init");
0171 mutex_lock(&aux->mutex);
0172 aux->enabled = true;
0173 mutex_unlock(&aux->mutex);
0174 }
0175
0176 void
0177 nvkm_i2c_aux_fini(struct nvkm_i2c_aux *aux)
0178 {
0179 AUX_TRACE(aux, "fini");
0180 mutex_lock(&aux->mutex);
0181 aux->enabled = false;
0182 mutex_unlock(&aux->mutex);
0183 }
0184
0185 int
0186 nvkm_i2c_aux_ctor(const struct nvkm_i2c_aux_func *func,
0187 struct nvkm_i2c_pad *pad, int id,
0188 struct nvkm_i2c_aux *aux)
0189 {
0190 struct nvkm_device *device = pad->i2c->subdev.device;
0191
0192 aux->func = func;
0193 aux->pad = pad;
0194 aux->id = id;
0195 mutex_init(&aux->mutex);
0196 list_add_tail(&aux->head, &pad->i2c->aux);
0197 AUX_TRACE(aux, "ctor");
0198
0199 snprintf(aux->i2c.name, sizeof(aux->i2c.name), "nvkm-%s-aux-%04x",
0200 dev_name(device->dev), id);
0201 aux->i2c.owner = THIS_MODULE;
0202 aux->i2c.dev.parent = device->dev;
0203 aux->i2c.algo = &nvkm_i2c_aux_i2c_algo;
0204 return i2c_add_adapter(&aux->i2c);
0205 }
0206
0207 int
0208 nvkm_i2c_aux_new_(const struct nvkm_i2c_aux_func *func,
0209 struct nvkm_i2c_pad *pad, int id,
0210 struct nvkm_i2c_aux **paux)
0211 {
0212 if (!(*paux = kzalloc(sizeof(**paux), GFP_KERNEL)))
0213 return -ENOMEM;
0214 return nvkm_i2c_aux_ctor(func, pad, id, *paux);
0215 }