1/* $NetBSD: nouveau_nvkm_subdev_i2c_bus.c,v 1.5 2021/12/18 23:45:40 riastradh Exp $ */ 2 3/* 4 * Copyright 2015 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 <bskeggs@redhat.com> 25 */ 26#include <sys/cdefs.h> 27__KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_i2c_bus.c,v 1.5 2021/12/18 23:45:40 riastradh Exp $"); 28 29#include "bus.h" 30#include "pad.h" 31 32#include <core/option.h> 33 34#include <linux/nbsd-namespace.h> 35 36/******************************************************************************* 37 * i2c-algo-bit 38 ******************************************************************************/ 39static int 40nvkm_i2c_bus_pre_xfer(struct i2c_adapter *adap) 41{ 42 struct nvkm_i2c_bus *bus = container_of(adap, typeof(*bus), i2c); 43 return nvkm_i2c_bus_acquire(bus); 44} 45 46static void 47nvkm_i2c_bus_post_xfer(struct i2c_adapter *adap) 48{ 49 struct nvkm_i2c_bus *bus = container_of(adap, typeof(*bus), i2c); 50 return nvkm_i2c_bus_release(bus); 51} 52 53static void 54nvkm_i2c_bus_setscl(void *data, int state) 55{ 56 struct nvkm_i2c_bus *bus = data; 57 bus->func->drive_scl(bus, state); 58} 59 60static void 61nvkm_i2c_bus_setsda(void *data, int state) 62{ 63 struct nvkm_i2c_bus *bus = data; 64 bus->func->drive_sda(bus, state); 65} 66 67static int 68nvkm_i2c_bus_getscl(void *data) 69{ 70 struct nvkm_i2c_bus *bus = data; 71 return bus->func->sense_scl(bus); 72} 73 74static int 75nvkm_i2c_bus_getsda(void *data) 76{ 77 struct nvkm_i2c_bus *bus = data; 78 return bus->func->sense_sda(bus); 79} 80 81/******************************************************************************* 82 * !i2c-algo-bit (off-chip i2c bus / hw i2c / internal bit-banging algo) 83 ******************************************************************************/ 84static int 85nvkm_i2c_bus_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 86{ 87 struct nvkm_i2c_bus *bus = container_of(adap, typeof(*bus), i2c); 88 int ret; 89 90 ret = nvkm_i2c_bus_acquire(bus); 91 if (ret) 92 return ret; 93 94 ret = bus->func->xfer(bus, msgs, num); 95 nvkm_i2c_bus_release(bus); 96 return ret; 97} 98 99static u32 100nvkm_i2c_bus_func(struct i2c_adapter *adap) 101{ 102 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 103} 104 105static const struct i2c_algorithm 106nvkm_i2c_bus_algo = { 107 .master_xfer = nvkm_i2c_bus_xfer, 108 .functionality = nvkm_i2c_bus_func, 109}; 110 111/******************************************************************************* 112 * nvkm_i2c_bus base 113 ******************************************************************************/ 114void 115nvkm_i2c_bus_init(struct nvkm_i2c_bus *bus) 116{ 117 BUS_TRACE(bus, "init"); 118 if (bus->func->init) 119 bus->func->init(bus); 120 121 mutex_lock(&bus->mutex); 122 bus->enabled = true; 123 mutex_unlock(&bus->mutex); 124} 125 126void 127nvkm_i2c_bus_fini(struct nvkm_i2c_bus *bus) 128{ 129 BUS_TRACE(bus, "fini"); 130 mutex_lock(&bus->mutex); 131 bus->enabled = false; 132 mutex_unlock(&bus->mutex); 133} 134 135void 136nvkm_i2c_bus_release(struct nvkm_i2c_bus *bus) 137{ 138 struct nvkm_i2c_pad *pad = bus->pad; 139 BUS_TRACE(bus, "release"); 140 nvkm_i2c_pad_release(pad); 141 mutex_unlock(&bus->mutex); 142} 143 144int 145nvkm_i2c_bus_acquire(struct nvkm_i2c_bus *bus) 146{ 147 struct nvkm_i2c_pad *pad = bus->pad; 148 int ret; 149 150 BUS_TRACE(bus, "acquire"); 151 mutex_lock(&bus->mutex); 152 153 if (bus->enabled) 154 ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_I2C); 155 else 156 ret = -EIO; 157 158 if (ret) 159 mutex_unlock(&bus->mutex); 160 return ret; 161} 162 163int 164nvkm_i2c_bus_probe(struct nvkm_i2c_bus *bus, const char *what, 165 struct nvkm_i2c_bus_probe *info, 166 bool (*match)(struct nvkm_i2c_bus *, 167 struct i2c_board_info *, void *), void *data) 168{ 169 int i; 170 171 BUS_DBG(bus, "probing %ss", what); 172 for (i = 0; info[i].dev.addr; i++) { 173 u8 orig_udelay = 0; 174 175 if ((bus->i2c.algo == &i2c_bit_algo) && (info[i].udelay != 0)) { 176 struct i2c_algo_bit_data *algo = bus->i2c.algo_data; 177 BUS_DBG(bus, "%dms delay instead of %dms", 178 info[i].udelay, algo->udelay); 179 orig_udelay = algo->udelay; 180 algo->udelay = info[i].udelay; 181 } 182 183 if (nvkm_probe_i2c(&bus->i2c, info[i].dev.addr) && 184 (!match || match(bus, &info[i].dev, data))) { 185 BUS_DBG(bus, "detected %s: %s", 186 what, info[i].dev.type); 187 return i; 188 } 189 190 if (orig_udelay) { 191 struct i2c_algo_bit_data *algo = bus->i2c.algo_data; 192 algo->udelay = orig_udelay; 193 } 194 } 195 196 BUS_DBG(bus, "no devices found."); 197 return -ENODEV; 198} 199 200void 201nvkm_i2c_bus_del(struct nvkm_i2c_bus **pbus) 202{ 203 struct nvkm_i2c_bus *bus = *pbus; 204 if (bus && !WARN_ON(!bus->func)) { 205 BUS_TRACE(bus, "dtor"); 206 list_del(&bus->head); 207 i2c_del_adapter(&bus->i2c); 208 mutex_destroy(&bus->mutex); 209 kfree(bus->i2c.algo_data); 210 kfree(*pbus); 211 *pbus = NULL; 212 } 213} 214 215int 216nvkm_i2c_bus_ctor(const struct nvkm_i2c_bus_func *func, 217 struct nvkm_i2c_pad *pad, int id, 218 struct nvkm_i2c_bus *bus) 219{ 220 struct nvkm_device *device = pad->i2c->subdev.device; 221 struct i2c_algo_bit_data *bit; 222#ifndef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT 223 const bool internal = false; 224#else 225 const bool internal = true; 226#endif 227 int ret; 228 229 bus->func = func; 230 bus->pad = pad; 231 bus->id = id; 232 mutex_init(&bus->mutex); 233 list_add_tail(&bus->head, &pad->i2c->bus); 234 BUS_TRACE(bus, "ctor"); 235 236 snprintf(bus->i2c.name, sizeof(bus->i2c.name), "nvkm-%s-bus-%04x", 237 dev_name(device->dev), id); 238 bus->i2c.owner = THIS_MODULE; 239 bus->i2c.dev.parent = device->dev; 240 241 if ( bus->func->drive_scl && 242 !nvkm_boolopt(device->cfgopt, "NvI2C", internal)) { 243 if (!(bit = kzalloc(sizeof(*bit), GFP_KERNEL))) 244 return -ENOMEM; 245 bit->udelay = 10; 246 bit->timeout = usecs_to_jiffies(2200); 247 bit->data = bus; 248 bit->pre_xfer = nvkm_i2c_bus_pre_xfer; 249 bit->post_xfer = nvkm_i2c_bus_post_xfer; 250 bit->setscl = nvkm_i2c_bus_setscl; 251 bit->setsda = nvkm_i2c_bus_setsda; 252 bit->getscl = nvkm_i2c_bus_getscl; 253 bit->getsda = nvkm_i2c_bus_getsda; 254 bus->i2c.algo_data = bit; 255 ret = i2c_bit_add_bus(&bus->i2c); 256 } else { 257 bus->i2c.algo = &nvkm_i2c_bus_algo; 258 ret = i2c_add_adapter(&bus->i2c); 259 } 260 261 return ret; 262} 263 264int 265nvkm_i2c_bus_new_(const struct nvkm_i2c_bus_func *func, 266 struct nvkm_i2c_pad *pad, int id, 267 struct nvkm_i2c_bus **pbus) 268{ 269 if (!(*pbus = kzalloc(sizeof(**pbus), GFP_KERNEL))) 270 return -ENOMEM; 271 return nvkm_i2c_bus_ctor(func, pad, id, *pbus); 272} 273