xref: /dflybsd-src/sys/dev/drm/linux_i2c.c (revision 3f2dd94a569761201b5b0a18b2f697f97fe1b9dc)
19f4ca867SFrançois Tigeot /*
2*3f2dd94aSFrançois Tigeot  * Copyright (c) 2016-2020 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 
351dedbd3bSFrançois Tigeot static void i2c_default_lock_bus(struct i2c_adapter *, unsigned int);
361dedbd3bSFrançois Tigeot static int i2c_default_trylock_bus(struct i2c_adapter *, unsigned int);
371dedbd3bSFrançois Tigeot static void i2c_default_unlock_bus(struct i2c_adapter *, unsigned int);
381dedbd3bSFrançois Tigeot 
391dedbd3bSFrançois Tigeot static const struct i2c_lock_operations i2c_default_lock_ops = {
401dedbd3bSFrançois Tigeot 	.lock_bus =    i2c_default_lock_bus,
411dedbd3bSFrançois Tigeot 	.trylock_bus = i2c_default_trylock_bus,
421dedbd3bSFrançois Tigeot 	.unlock_bus =  i2c_default_unlock_bus,
431dedbd3bSFrançois Tigeot };
441dedbd3bSFrançois Tigeot 
459f4ca867SFrançois Tigeot int
i2c_add_adapter(struct i2c_adapter * adapter)469f4ca867SFrançois Tigeot i2c_add_adapter(struct i2c_adapter *adapter)
479f4ca867SFrançois Tigeot {
489f4ca867SFrançois Tigeot 	/* Linux registers a unique bus number here */
491dedbd3bSFrançois Tigeot 
501dedbd3bSFrançois Tigeot 	/* Setup default locking functions */
511dedbd3bSFrançois Tigeot 	if (!adapter->lock_ops)
521dedbd3bSFrançois Tigeot 		adapter->lock_ops = &i2c_default_lock_ops;
531dedbd3bSFrançois Tigeot 
549f4ca867SFrançois Tigeot 	return 0;
559f4ca867SFrançois Tigeot }
569f4ca867SFrançois Tigeot 
579f4ca867SFrançois Tigeot void
i2c_del_adapter(struct i2c_adapter * adapter)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 
63*3f2dd94aSFrançois Tigeot int
__i2c_transfer(struct i2c_adapter * adapter,struct i2c_msg * msgs,int num)64*3f2dd94aSFrançois Tigeot __i2c_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
65*3f2dd94aSFrançois Tigeot {
66*3f2dd94aSFrançois Tigeot 	uint64_t start_ticks;
67*3f2dd94aSFrançois Tigeot 	int ret, tries = 0;
68*3f2dd94aSFrançois Tigeot 
69*3f2dd94aSFrançois Tigeot 	start_ticks = ticks;
70*3f2dd94aSFrançois Tigeot 	do {
71*3f2dd94aSFrançois Tigeot 		ret = adapter->algo->master_xfer(adapter, msgs, num);
72*3f2dd94aSFrançois Tigeot 		if (ticks > start_ticks + adapter->timeout)
73*3f2dd94aSFrançois Tigeot 			break;
74*3f2dd94aSFrançois Tigeot 		if (ret != -EAGAIN)
75*3f2dd94aSFrançois Tigeot 			break;
76*3f2dd94aSFrançois Tigeot 		tries++;
77*3f2dd94aSFrançois Tigeot 	} while (tries < adapter->retries);
78*3f2dd94aSFrançois Tigeot 
79*3f2dd94aSFrançois Tigeot 	return ret;
80*3f2dd94aSFrançois Tigeot }
81*3f2dd94aSFrançois Tigeot 
829f4ca867SFrançois Tigeot /*
839f4ca867SFrançois Tigeot  * i2c_transfer()
849f4ca867SFrançois Tigeot  * The original Linux implementation does:
859f4ca867SFrançois Tigeot  * 1. return -EOPNOTSUPP if adapter->algo->master_xfer is NULL
869f4ca867SFrançois Tigeot  * 2. try to transfer msgs by calling adapter->algo->master_xfer()
879f4ca867SFrançois Tigeot  * 3. if it took more ticks than adapter->timeout, fail
889f4ca867SFrançois Tigeot  * 4. if the transfer failed, retry up to adapter->retries times
899f4ca867SFrançois Tigeot  * 5. return the result of the last call of adapter->algo->master_xfer()
909f4ca867SFrançois Tigeot  */
919f4ca867SFrançois Tigeot int
i2c_transfer(struct i2c_adapter * adapter,struct i2c_msg * msgs,int num)929f4ca867SFrançois Tigeot i2c_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
939f4ca867SFrançois Tigeot {
94*3f2dd94aSFrançois Tigeot 	int ret;
959f4ca867SFrançois Tigeot 
969f4ca867SFrançois Tigeot 	if (adapter->algo->master_xfer == NULL)
979f4ca867SFrançois Tigeot 		return -EOPNOTSUPP;
989f4ca867SFrançois Tigeot 
991dedbd3bSFrançois Tigeot 	adapter->lock_ops->lock_bus(adapter, I2C_LOCK_SEGMENT);
100*3f2dd94aSFrançois Tigeot 	ret = __i2c_transfer(adapter, msgs, num);
1011dedbd3bSFrançois Tigeot 	adapter->lock_ops->unlock_bus(adapter, I2C_LOCK_SEGMENT);
1029f4ca867SFrançois Tigeot 
1039f4ca867SFrançois Tigeot 	return ret;
1049f4ca867SFrançois Tigeot }
1059f4ca867SFrançois Tigeot 
1069f4ca867SFrançois Tigeot static int
bit_xfer(struct i2c_adapter * i2c_adap,struct i2c_msg msgs[],int num)1079f4ca867SFrançois Tigeot bit_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
1089f4ca867SFrançois Tigeot {
1099f4ca867SFrançois Tigeot 	/* XXX Linux really does try to transfer some data here */
1109f4ca867SFrançois Tigeot 	return 0;
1119f4ca867SFrançois Tigeot }
1129f4ca867SFrançois Tigeot 
1139f4ca867SFrançois Tigeot static uint32_t
bit_func(struct i2c_adapter * adap)1149f4ca867SFrançois Tigeot bit_func(struct i2c_adapter *adap)
1159f4ca867SFrançois Tigeot {
1169f4ca867SFrançois Tigeot 	return (I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
1179f4ca867SFrançois Tigeot 		I2C_FUNC_SMBUS_READ_BLOCK_DATA |
1189f4ca867SFrançois Tigeot 		I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
1199f4ca867SFrançois Tigeot 		I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING);
1209f4ca867SFrançois Tigeot }
1219f4ca867SFrançois Tigeot 
1229f4ca867SFrançois Tigeot const struct i2c_algorithm i2c_bit_algo = {
1239f4ca867SFrançois Tigeot 	.master_xfer	= bit_xfer,
1249f4ca867SFrançois Tigeot 	.functionality	= bit_func,
1259f4ca867SFrançois Tigeot };
126fabf443aSFrançois Tigeot 
127fabf443aSFrançois Tigeot int
i2c_bit_add_bus(struct i2c_adapter * adapter)128fabf443aSFrançois Tigeot i2c_bit_add_bus(struct i2c_adapter *adapter)
129fabf443aSFrançois Tigeot {
130fabf443aSFrançois Tigeot 	adapter->algo = &i2c_bit_algo;
131fabf443aSFrançois Tigeot 	adapter->retries = 2;
132fabf443aSFrançois Tigeot 
133fabf443aSFrançois Tigeot 	return 0;
134fabf443aSFrançois Tigeot }
1357d061d0fSFrançois Tigeot 
1367d061d0fSFrançois Tigeot struct i2c_client *
i2c_new_device(struct i2c_adapter * adap,struct i2c_board_info const * info)1377d061d0fSFrançois Tigeot i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
1387d061d0fSFrançois Tigeot {
1397d061d0fSFrançois Tigeot 	struct i2c_client *client;
1407d061d0fSFrançois Tigeot 
1417d061d0fSFrançois Tigeot 	client = kzalloc(sizeof(*client), GFP_KERNEL);
1427d061d0fSFrançois Tigeot 	if (client == NULL)
1437d061d0fSFrançois Tigeot 		goto done;
1447d061d0fSFrançois Tigeot 
1457d061d0fSFrançois Tigeot 	client->adapter = adap;
1467d061d0fSFrançois Tigeot 
1477d061d0fSFrançois Tigeot 	strlcpy(client->name, info->type, sizeof(client->name));
1487d061d0fSFrançois Tigeot 	client->addr = info->addr;
1497d061d0fSFrançois Tigeot 
1507d061d0fSFrançois Tigeot done:
1517d061d0fSFrançois Tigeot 	return client;
1527d061d0fSFrançois Tigeot }
1531dedbd3bSFrançois Tigeot 
1541dedbd3bSFrançois Tigeot /* Default locking functions */
1551dedbd3bSFrançois Tigeot 
1561dedbd3bSFrançois Tigeot static void
i2c_default_lock_bus(struct i2c_adapter * adapter,unsigned int flags)1571dedbd3bSFrançois Tigeot i2c_default_lock_bus(struct i2c_adapter *adapter, unsigned int flags)
1581dedbd3bSFrançois Tigeot {
1591dedbd3bSFrançois Tigeot 	lockmgr(&i2c_lock, LK_EXCLUSIVE);
1601dedbd3bSFrançois Tigeot }
1611dedbd3bSFrançois Tigeot 
1621dedbd3bSFrançois Tigeot static int
i2c_default_trylock_bus(struct i2c_adapter * adapter,unsigned int flags)1631dedbd3bSFrançois Tigeot i2c_default_trylock_bus(struct i2c_adapter *adapter, unsigned int flags)
1641dedbd3bSFrançois Tigeot {
1651dedbd3bSFrançois Tigeot 	return mutex_trylock(&i2c_lock);
1661dedbd3bSFrançois Tigeot }
1671dedbd3bSFrançois Tigeot 
1681dedbd3bSFrançois Tigeot static void
i2c_default_unlock_bus(struct i2c_adapter * adapter,unsigned int flags)1691dedbd3bSFrançois Tigeot i2c_default_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
1701dedbd3bSFrançois Tigeot {
1711dedbd3bSFrançois Tigeot 	lockmgr(&i2c_lock, LK_RELEASE);
1721dedbd3bSFrançois Tigeot }
173