Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2009 Red Hat Inc.
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
0021  *
0022  * Authors: Ben Skeggs
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; /* MOT */
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 }