xref: /dragonfly/sys/dev/drm/linux_i2c.c (revision 9e37b890)
1 /*
2  * Copyright (c) 2016-2019 François Tigeot <ftigeot@wolfpond.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <linux/i2c.h>
28 #include <linux/i2c-algo-bit.h>
29 
30 #include <linux/slab.h>
31 
32 static struct lock i2c_lock;
33 LOCK_SYSINIT(i2c_lock, &i2c_lock, "i2cl", LK_CANRECURSE);
34 
35 int
36 i2c_add_adapter(struct i2c_adapter *adapter)
37 {
38 	/* Linux registers a unique bus number here */
39 	return 0;
40 }
41 
42 void
43 i2c_del_adapter(struct i2c_adapter *adapter)
44 {
45 	/* Linux deletes a unique bus number here */
46 }
47 
48 /*
49  * i2c_transfer()
50  * The original Linux implementation does:
51  * 1. return -EOPNOTSUPP if adapter->algo->master_xfer is NULL
52  * 2. try to transfer msgs by calling adapter->algo->master_xfer()
53  * 3. if it took more ticks than adapter->timeout, fail
54  * 4. if the transfer failed, retry up to adapter->retries times
55  * 5. return the result of the last call of adapter->algo->master_xfer()
56  */
57 int
58 i2c_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
59 {
60 	uint64_t start_ticks;
61 	int ret, tries = 0;
62 
63 	if (adapter->algo->master_xfer == NULL)
64 		return -EOPNOTSUPP;
65 
66 	lockmgr(&i2c_lock, LK_EXCLUSIVE);
67 	start_ticks = ticks;
68 	do {
69 		ret = adapter->algo->master_xfer(adapter, msgs, num);
70 		if (ticks > start_ticks + adapter->timeout)
71 			break;
72 		if (ret != -EAGAIN)
73 			break;
74 		tries++;
75 	} while (tries < adapter->retries);
76 	lockmgr(&i2c_lock, LK_RELEASE);
77 
78 	return ret;
79 }
80 
81 static int
82 bit_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
83 {
84 	/* XXX Linux really does try to transfer some data here */
85 	return 0;
86 }
87 
88 static uint32_t
89 bit_func(struct i2c_adapter *adap)
90 {
91 	return (I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
92 		I2C_FUNC_SMBUS_READ_BLOCK_DATA |
93 		I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
94 		I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING);
95 }
96 
97 const struct i2c_algorithm i2c_bit_algo = {
98 	.master_xfer	= bit_xfer,
99 	.functionality	= bit_func,
100 };
101 
102 int
103 i2c_bit_add_bus(struct i2c_adapter *adapter)
104 {
105 	adapter->algo = &i2c_bit_algo;
106 	adapter->retries = 2;
107 
108 	return 0;
109 }
110 
111 struct i2c_client *
112 i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
113 {
114 	struct i2c_client *client;
115 
116 	client = kzalloc(sizeof(*client), GFP_KERNEL);
117 	if (client == NULL)
118 		goto done;
119 
120 	client->adapter = adap;
121 
122 	strlcpy(client->name, info->type, sizeof(client->name));
123 	client->addr = info->addr;
124 
125 done:
126 	return client;
127 }
128