xref: /dflybsd-src/sys/dev/drm/linux_i2c.c (revision 1dedbd3b06c68f627b7825694444529d5eb2b1bf)
19f4ca867SFrançois Tigeot /*
2fabf443aSFrançois Tigeot  * Copyright (c) 2016-2019 François Tigeot <ftigeot@wolfpond.org>
39f4ca867SFrançois Tigeot  * All rights reserved.
49f4ca867SFrançois Tigeot  *
59f4ca867SFrançois Tigeot  * Redistribution and use in source and binary forms, with or without
69f4ca867SFrançois Tigeot  * modification, are permitted provided that the following conditions
79f4ca867SFrançois Tigeot  * are met:
89f4ca867SFrançois Tigeot  * 1. Redistributions of source code must retain the above copyright
99f4ca867SFrançois Tigeot  *    notice unmodified, this list of conditions, and the following
109f4ca867SFrançois Tigeot  *    disclaimer.
119f4ca867SFrançois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
129f4ca867SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer in the
139f4ca867SFrançois Tigeot  *    documentation and/or other materials provided with the distribution.
149f4ca867SFrançois Tigeot  *
159f4ca867SFrançois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
169f4ca867SFrançois Tigeot  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
179f4ca867SFrançois Tigeot  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
189f4ca867SFrançois Tigeot  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
199f4ca867SFrançois Tigeot  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
209f4ca867SFrançois Tigeot  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
219f4ca867SFrançois Tigeot  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
229f4ca867SFrançois Tigeot  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
239f4ca867SFrançois Tigeot  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
249f4ca867SFrançois Tigeot  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
259f4ca867SFrançois Tigeot  */
269f4ca867SFrançois Tigeot 
279f4ca867SFrançois Tigeot #include <linux/i2c.h>
289f4ca867SFrançois Tigeot #include <linux/i2c-algo-bit.h>
299f4ca867SFrançois Tigeot 
307d061d0fSFrançois Tigeot #include <linux/slab.h>
317d061d0fSFrançois Tigeot 
329f4ca867SFrançois Tigeot static struct lock i2c_lock;
339f4ca867SFrançois Tigeot LOCK_SYSINIT(i2c_lock, &i2c_lock, "i2cl", LK_CANRECURSE);
349f4ca867SFrançois Tigeot 
35*1dedbd3bSFrançois Tigeot static void i2c_default_lock_bus(struct i2c_adapter *, unsigned int);
36*1dedbd3bSFrançois Tigeot static int i2c_default_trylock_bus(struct i2c_adapter *, unsigned int);
37*1dedbd3bSFrançois Tigeot static void i2c_default_unlock_bus(struct i2c_adapter *, unsigned int);
38*1dedbd3bSFrançois Tigeot 
39*1dedbd3bSFrançois Tigeot static const struct i2c_lock_operations i2c_default_lock_ops = {
40*1dedbd3bSFrançois Tigeot 	.lock_bus =    i2c_default_lock_bus,
41*1dedbd3bSFrançois Tigeot 	.trylock_bus = i2c_default_trylock_bus,
42*1dedbd3bSFrançois Tigeot 	.unlock_bus =  i2c_default_unlock_bus,
43*1dedbd3bSFrançois Tigeot };
44*1dedbd3bSFrançois Tigeot 
459f4ca867SFrançois Tigeot int
469f4ca867SFrançois Tigeot i2c_add_adapter(struct i2c_adapter *adapter)
479f4ca867SFrançois Tigeot {
489f4ca867SFrançois Tigeot 	/* Linux registers a unique bus number here */
49*1dedbd3bSFrançois Tigeot 
50*1dedbd3bSFrançois Tigeot 	/* Setup default locking functions */
51*1dedbd3bSFrançois Tigeot 	if (!adapter->lock_ops)
52*1dedbd3bSFrançois Tigeot 		adapter->lock_ops = &i2c_default_lock_ops;
53*1dedbd3bSFrançois Tigeot 
549f4ca867SFrançois Tigeot 	return 0;
559f4ca867SFrançois Tigeot }
569f4ca867SFrançois Tigeot 
579f4ca867SFrançois Tigeot void
589f4ca867SFrançois Tigeot i2c_del_adapter(struct i2c_adapter *adapter)
599f4ca867SFrançois Tigeot {
609f4ca867SFrançois Tigeot 	/* Linux deletes a unique bus number here */
619f4ca867SFrançois Tigeot }
629f4ca867SFrançois Tigeot 
639f4ca867SFrançois Tigeot /*
649f4ca867SFrançois Tigeot  * i2c_transfer()
659f4ca867SFrançois Tigeot  * The original Linux implementation does:
669f4ca867SFrançois Tigeot  * 1. return -EOPNOTSUPP if adapter->algo->master_xfer is NULL
679f4ca867SFrançois Tigeot  * 2. try to transfer msgs by calling adapter->algo->master_xfer()
689f4ca867SFrançois Tigeot  * 3. if it took more ticks than adapter->timeout, fail
699f4ca867SFrançois Tigeot  * 4. if the transfer failed, retry up to adapter->retries times
709f4ca867SFrançois Tigeot  * 5. return the result of the last call of adapter->algo->master_xfer()
719f4ca867SFrançois Tigeot  */
729f4ca867SFrançois Tigeot int
739f4ca867SFrançois Tigeot i2c_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
749f4ca867SFrançois Tigeot {
759f4ca867SFrançois Tigeot 	uint64_t start_ticks;
769f4ca867SFrançois Tigeot 	int ret, tries = 0;
779f4ca867SFrançois Tigeot 
789f4ca867SFrançois Tigeot 	if (adapter->algo->master_xfer == NULL)
799f4ca867SFrançois Tigeot 		return -EOPNOTSUPP;
809f4ca867SFrançois Tigeot 
81*1dedbd3bSFrançois Tigeot 	adapter->lock_ops->lock_bus(adapter, I2C_LOCK_SEGMENT);
829f4ca867SFrançois Tigeot 	start_ticks = ticks;
839f4ca867SFrançois Tigeot 	do {
849f4ca867SFrançois Tigeot 		ret = adapter->algo->master_xfer(adapter, msgs, num);
859f4ca867SFrançois Tigeot 		if (ticks > start_ticks + adapter->timeout)
869f4ca867SFrançois Tigeot 			break;
879f4ca867SFrançois Tigeot 		if (ret != -EAGAIN)
889f4ca867SFrançois Tigeot 			break;
899f4ca867SFrançois Tigeot 		tries++;
909f4ca867SFrançois Tigeot 	} while (tries < adapter->retries);
91*1dedbd3bSFrançois Tigeot 	adapter->lock_ops->unlock_bus(adapter, I2C_LOCK_SEGMENT);
929f4ca867SFrançois Tigeot 
939f4ca867SFrançois Tigeot 	return ret;
949f4ca867SFrançois Tigeot }
959f4ca867SFrançois Tigeot 
969f4ca867SFrançois Tigeot static int
979f4ca867SFrançois Tigeot bit_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
989f4ca867SFrançois Tigeot {
999f4ca867SFrançois Tigeot 	/* XXX Linux really does try to transfer some data here */
1009f4ca867SFrançois Tigeot 	return 0;
1019f4ca867SFrançois Tigeot }
1029f4ca867SFrançois Tigeot 
1039f4ca867SFrançois Tigeot static uint32_t
1049f4ca867SFrançois Tigeot bit_func(struct i2c_adapter *adap)
1059f4ca867SFrançois Tigeot {
1069f4ca867SFrançois Tigeot 	return (I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
1079f4ca867SFrançois Tigeot 		I2C_FUNC_SMBUS_READ_BLOCK_DATA |
1089f4ca867SFrançois Tigeot 		I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
1099f4ca867SFrançois Tigeot 		I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING);
1109f4ca867SFrançois Tigeot }
1119f4ca867SFrançois Tigeot 
1129f4ca867SFrançois Tigeot const struct i2c_algorithm i2c_bit_algo = {
1139f4ca867SFrançois Tigeot 	.master_xfer	= bit_xfer,
1149f4ca867SFrançois Tigeot 	.functionality	= bit_func,
1159f4ca867SFrançois Tigeot };
116fabf443aSFrançois Tigeot 
117fabf443aSFrançois Tigeot int
118fabf443aSFrançois Tigeot i2c_bit_add_bus(struct i2c_adapter *adapter)
119fabf443aSFrançois Tigeot {
120fabf443aSFrançois Tigeot 	adapter->algo = &i2c_bit_algo;
121fabf443aSFrançois Tigeot 	adapter->retries = 2;
122fabf443aSFrançois Tigeot 
123fabf443aSFrançois Tigeot 	return 0;
124fabf443aSFrançois Tigeot }
1257d061d0fSFrançois Tigeot 
1267d061d0fSFrançois Tigeot struct i2c_client *
1277d061d0fSFrançois Tigeot i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
1287d061d0fSFrançois Tigeot {
1297d061d0fSFrançois Tigeot 	struct i2c_client *client;
1307d061d0fSFrançois Tigeot 
1317d061d0fSFrançois Tigeot 	client = kzalloc(sizeof(*client), GFP_KERNEL);
1327d061d0fSFrançois Tigeot 	if (client == NULL)
1337d061d0fSFrançois Tigeot 		goto done;
1347d061d0fSFrançois Tigeot 
1357d061d0fSFrançois Tigeot 	client->adapter = adap;
1367d061d0fSFrançois Tigeot 
1377d061d0fSFrançois Tigeot 	strlcpy(client->name, info->type, sizeof(client->name));
1387d061d0fSFrançois Tigeot 	client->addr = info->addr;
1397d061d0fSFrançois Tigeot 
1407d061d0fSFrançois Tigeot done:
1417d061d0fSFrançois Tigeot 	return client;
1427d061d0fSFrançois Tigeot }
143*1dedbd3bSFrançois Tigeot 
144*1dedbd3bSFrançois Tigeot /* Default locking functions */
145*1dedbd3bSFrançois Tigeot 
146*1dedbd3bSFrançois Tigeot static void
147*1dedbd3bSFrançois Tigeot i2c_default_lock_bus(struct i2c_adapter *adapter, unsigned int flags)
148*1dedbd3bSFrançois Tigeot {
149*1dedbd3bSFrançois Tigeot 	lockmgr(&i2c_lock, LK_EXCLUSIVE);
150*1dedbd3bSFrançois Tigeot }
151*1dedbd3bSFrançois Tigeot 
152*1dedbd3bSFrançois Tigeot static int
153*1dedbd3bSFrançois Tigeot i2c_default_trylock_bus(struct i2c_adapter *adapter, unsigned int flags)
154*1dedbd3bSFrançois Tigeot {
155*1dedbd3bSFrançois Tigeot 	return mutex_trylock(&i2c_lock);
156*1dedbd3bSFrançois Tigeot }
157*1dedbd3bSFrançois Tigeot 
158*1dedbd3bSFrançois Tigeot static void
159*1dedbd3bSFrançois Tigeot i2c_default_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
160*1dedbd3bSFrançois Tigeot {
161*1dedbd3bSFrançois Tigeot 	lockmgr(&i2c_lock, LK_RELEASE);
162*1dedbd3bSFrançois Tigeot }
163