xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_i2c.c (revision e649599051d903f0517a74d88701ab274c4a03fb)
1*e6495990Sriastradh /*	$NetBSD: linux_i2c.c,v 1.7 2022/05/22 18:41:14 riastradh Exp $	*/
26cb10275Sriastradh 
36cb10275Sriastradh /*-
4851c4ebcSriastradh  * Copyright (c) 2015 The NetBSD Foundation, Inc.
56cb10275Sriastradh  * All rights reserved.
66cb10275Sriastradh  *
76cb10275Sriastradh  * This code is derived from software contributed to The NetBSD Foundation
86cb10275Sriastradh  * by Taylor R. Campbell.
96cb10275Sriastradh  *
106cb10275Sriastradh  * Redistribution and use in source and binary forms, with or without
116cb10275Sriastradh  * modification, are permitted provided that the following conditions
126cb10275Sriastradh  * are met:
136cb10275Sriastradh  * 1. Redistributions of source code must retain the above copyright
146cb10275Sriastradh  *    notice, this list of conditions and the following disclaimer.
156cb10275Sriastradh  * 2. Redistributions in binary form must reproduce the above copyright
166cb10275Sriastradh  *    notice, this list of conditions and the following disclaimer in the
176cb10275Sriastradh  *    documentation and/or other materials provided with the distribution.
186cb10275Sriastradh  *
196cb10275Sriastradh  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
206cb10275Sriastradh  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
216cb10275Sriastradh  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
226cb10275Sriastradh  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
236cb10275Sriastradh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
246cb10275Sriastradh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
256cb10275Sriastradh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
266cb10275Sriastradh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
276cb10275Sriastradh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
286cb10275Sriastradh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
296cb10275Sriastradh  * POSSIBILITY OF SUCH DAMAGE.
306cb10275Sriastradh  */
316cb10275Sriastradh 
326cb10275Sriastradh #include <sys/cdefs.h>
33*e6495990Sriastradh __KERNEL_RCSID(0, "$NetBSD: linux_i2c.c,v 1.7 2022/05/22 18:41:14 riastradh Exp $");
346cb10275Sriastradh 
356cb10275Sriastradh #include <sys/types.h>
366cb10275Sriastradh #include <sys/errno.h>
37851c4ebcSriastradh #include <sys/kmem.h>
386cb10275Sriastradh #include <sys/queue.h>		/* XXX include order botch: i2cvar.h needs */
396cb10275Sriastradh 
406cb10275Sriastradh #include <dev/i2c/i2cvar.h>
416cb10275Sriastradh #include <dev/i2c/i2c_bitbang.h> /* XXX include order botch */
426cb10275Sriastradh 
436cb10275Sriastradh #include <linux/i2c.h>
446cb10275Sriastradh #include <linux/i2c-algo-bit.h>
456cb10275Sriastradh 
466cb10275Sriastradh static int	netbsd_i2c_transfer(i2c_tag_t, struct i2c_msg *, int);
476cb10275Sriastradh static i2c_op_t	linux_i2c_flags_op(uint16_t, bool);
486cb10275Sriastradh static int	linux_i2c_flags_flags(uint16_t);
496cb10275Sriastradh static uint32_t	linux_i2cbb_functionality(struct i2c_adapter *);
506cb10275Sriastradh static int	linux_i2cbb_xfer(struct i2c_adapter *, struct i2c_msg *, int);
516cb10275Sriastradh static void	linux_i2cbb_set_bits(void *, uint32_t);
526cb10275Sriastradh static uint32_t	linux_i2cbb_read_bits(void *);
536cb10275Sriastradh static void	linux_i2cbb_set_dir(void *, uint32_t);
546cb10275Sriastradh static int	linux_i2cbb_send_start(void *, int);
556cb10275Sriastradh static int	linux_i2cbb_send_stop(void *, int);
566cb10275Sriastradh static int	linux_i2cbb_initiate_xfer(void *, i2c_addr_t, int);
576cb10275Sriastradh static int	linux_i2cbb_read_byte(void *, uint8_t *, int);
586cb10275Sriastradh static int	linux_i2cbb_write_byte(void *, uint8_t, int);
596cb10275Sriastradh 
60851c4ebcSriastradh /*
61851c4ebcSriastradh  * Client operations: operations with a particular i2c slave device.
62851c4ebcSriastradh  */
63851c4ebcSriastradh 
64851c4ebcSriastradh struct i2c_client *
i2c_new_device(struct i2c_adapter * adapter,const struct i2c_board_info * info)65851c4ebcSriastradh i2c_new_device(struct i2c_adapter *adapter, const struct i2c_board_info *info)
66851c4ebcSriastradh {
67851c4ebcSriastradh 	struct i2c_client *client;
68851c4ebcSriastradh 
69851c4ebcSriastradh 	client = kmem_alloc(sizeof(*client), KM_SLEEP);
70851c4ebcSriastradh 	client->adapter = adapter;
71851c4ebcSriastradh 	client->addr = info->addr;
72851c4ebcSriastradh 	client->flags = info->flags;
73851c4ebcSriastradh 
74851c4ebcSriastradh 	return client;
75851c4ebcSriastradh }
76851c4ebcSriastradh 
77851c4ebcSriastradh void
i2c_unregister_device(struct i2c_client * client)78851c4ebcSriastradh i2c_unregister_device(struct i2c_client *client)
79851c4ebcSriastradh {
80851c4ebcSriastradh 
81851c4ebcSriastradh 	kmem_free(client, sizeof(*client));
82851c4ebcSriastradh }
83851c4ebcSriastradh 
84851c4ebcSriastradh int
i2c_master_send(const struct i2c_client * client,const char * buf,int count)85851c4ebcSriastradh i2c_master_send(const struct i2c_client *client, const char *buf, int count)
86851c4ebcSriastradh {
87851c4ebcSriastradh 	struct i2c_msg msg = {
88851c4ebcSriastradh 		.addr = client->addr,
89851c4ebcSriastradh 		.flags = client->flags & I2C_M_TEN,
90851c4ebcSriastradh 		.len = count,
91851c4ebcSriastradh 		.buf = __UNCONST(buf),
92851c4ebcSriastradh 	};
93851c4ebcSriastradh 	int ret;
94851c4ebcSriastradh 
95851c4ebcSriastradh 	KASSERT(0 <= count);
96851c4ebcSriastradh 
97851c4ebcSriastradh 	ret = i2c_transfer(client->adapter, &msg, 1);
98851c4ebcSriastradh 	if (ret <= 0)
99851c4ebcSriastradh 		return ret;
100851c4ebcSriastradh 
101851c4ebcSriastradh 	return count;
102851c4ebcSriastradh }
103851c4ebcSriastradh 
104851c4ebcSriastradh int
i2c_master_recv(const struct i2c_client * client,char * buf,int count)105851c4ebcSriastradh i2c_master_recv(const struct i2c_client *client, char *buf, int count)
106851c4ebcSriastradh {
107851c4ebcSriastradh 	struct i2c_msg msg = {
108851c4ebcSriastradh 		.addr = client->addr,
109851c4ebcSriastradh 		.flags = (client->flags & I2C_M_TEN) | I2C_M_RD,
110851c4ebcSriastradh 		.len = count,
111851c4ebcSriastradh 		.buf = buf,
112851c4ebcSriastradh 	};
113851c4ebcSriastradh 	int ret;
114851c4ebcSriastradh 
115851c4ebcSriastradh 	ret = i2c_transfer(client->adapter, &msg, 1);
116851c4ebcSriastradh 	if (ret <= 0)
117851c4ebcSriastradh 		return ret;
118851c4ebcSriastradh 
119851c4ebcSriastradh 	return count;
120851c4ebcSriastradh }
121851c4ebcSriastradh 
122851c4ebcSriastradh /*
123851c4ebcSriastradh  * Adapter operations: operations over an i2c bus via a particular
124851c4ebcSriastradh  * controller.
125851c4ebcSriastradh  */
126851c4ebcSriastradh 
1276cb10275Sriastradh int
__i2c_transfer(struct i2c_adapter * adapter,struct i2c_msg * msgs,int n)1289e5fbd4fSriastradh __i2c_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int n)
1299e5fbd4fSriastradh {
130*e6495990Sriastradh 	unsigned timeout = hz;	/* XXX adapter->timeout */
131*e6495990Sriastradh 	unsigned start = getticks();
132*e6495990Sriastradh 	int ret, nretries = 0;
1339e5fbd4fSriastradh 
134*e6495990Sriastradh 	do {
135*e6495990Sriastradh 		ret = (*adapter->algo->master_xfer)(adapter, msgs, n);
136*e6495990Sriastradh 		if (ret != -EAGAIN)
137*e6495990Sriastradh 			break;
138*e6495990Sriastradh 	} while (nretries++ < adapter->retries &&
139*e6495990Sriastradh 	    getticks() - start < timeout);
140*e6495990Sriastradh 
141*e6495990Sriastradh 	return ret;
1429e5fbd4fSriastradh }
1439e5fbd4fSriastradh 
1449e5fbd4fSriastradh int
i2c_transfer(struct i2c_adapter * adapter,struct i2c_msg * msgs,int n)1456cb10275Sriastradh i2c_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int n)
1466cb10275Sriastradh {
1475635a5e7Sriastradh 	int ret;
1486cb10275Sriastradh 
1495635a5e7Sriastradh 	if (adapter->lock_ops)
1505635a5e7Sriastradh 		(*adapter->lock_ops->lock_bus)(adapter, 0);
1519e5fbd4fSriastradh 	ret = __i2c_transfer(adapter, msgs, n);
1525635a5e7Sriastradh 	if (adapter->lock_ops)
1535635a5e7Sriastradh 		(*adapter->lock_ops->unlock_bus)(adapter, 0);
1545635a5e7Sriastradh 
1555635a5e7Sriastradh 	return ret;
1566cb10275Sriastradh }
1576cb10275Sriastradh 
1586cb10275Sriastradh static int
netbsd_i2c_transfer(i2c_tag_t i2c,struct i2c_msg * msgs,int n)1596cb10275Sriastradh netbsd_i2c_transfer(i2c_tag_t i2c, struct i2c_msg *msgs, int n)
1606cb10275Sriastradh {
1616cb10275Sriastradh 	int i;
1626cb10275Sriastradh 	int error;
1636cb10275Sriastradh 
1646cb10275Sriastradh 	for (i = 0; i < n; i++) {
1656cb10275Sriastradh 		const i2c_op_t op = linux_i2c_flags_op(msgs[i].flags,
1666cb10275Sriastradh 		    ((i + 1) == n));
1676cb10275Sriastradh 		const int flags = linux_i2c_flags_flags(msgs[i].flags);
1686cb10275Sriastradh 
1696cb10275Sriastradh 		switch (op) {
1706cb10275Sriastradh 		case I2C_OP_READ:
1716cb10275Sriastradh 		case I2C_OP_READ_WITH_STOP:
1726cb10275Sriastradh 			error = iic_exec(i2c, op, msgs[i].addr,
1736cb10275Sriastradh 			    NULL, 0, msgs[i].buf, msgs[i].len, flags);
1746cb10275Sriastradh 			break;
1756cb10275Sriastradh 
1766cb10275Sriastradh 		case I2C_OP_WRITE:
1776cb10275Sriastradh 		case I2C_OP_WRITE_WITH_STOP:
1786cb10275Sriastradh 			error = iic_exec(i2c, op, msgs[i].addr,
1796cb10275Sriastradh 			    msgs[i].buf, msgs[i].len, NULL, 0, flags);
1806cb10275Sriastradh 			break;
1816cb10275Sriastradh 
1826cb10275Sriastradh 		default:
1836cb10275Sriastradh 			error = EINVAL;
1846cb10275Sriastradh 		}
1856cb10275Sriastradh 
1866cb10275Sriastradh 		if (error)
1876cb10275Sriastradh 			/* XXX errno NetBSD->Linux */
1886cb10275Sriastradh 			return -error;
1896cb10275Sriastradh 	}
1906cb10275Sriastradh 
1916cb10275Sriastradh 	return n;
1926cb10275Sriastradh }
1936cb10275Sriastradh 
1946cb10275Sriastradh static i2c_op_t
linux_i2c_flags_op(uint16_t flags,bool stop)1956cb10275Sriastradh linux_i2c_flags_op(uint16_t flags, bool stop)
1966cb10275Sriastradh {
1976cb10275Sriastradh 
1982faf8d7dSriastradh 	if (ISSET(flags, I2C_M_STOP))
1992faf8d7dSriastradh 		stop = true;
2002faf8d7dSriastradh 
2016cb10275Sriastradh 	if (ISSET(flags, I2C_M_RD))
2026cb10275Sriastradh 		return (stop? I2C_OP_READ_WITH_STOP : I2C_OP_READ);
2036cb10275Sriastradh 	else
2046cb10275Sriastradh 		return (stop? I2C_OP_WRITE_WITH_STOP : I2C_OP_WRITE);
2056cb10275Sriastradh }
2066cb10275Sriastradh 
2076cb10275Sriastradh static int
linux_i2c_flags_flags(uint16_t flags __unused)2086cb10275Sriastradh linux_i2c_flags_flags(uint16_t flags __unused)
2096cb10275Sriastradh {
2106cb10275Sriastradh 
2116cb10275Sriastradh 	return 0;
2126cb10275Sriastradh }
2136cb10275Sriastradh 
214851c4ebcSriastradh /* Bit-banging */
215851c4ebcSriastradh 
2166cb10275Sriastradh const struct i2c_algorithm i2c_bit_algo = {
2176cb10275Sriastradh 	.master_xfer	= linux_i2cbb_xfer,
2186cb10275Sriastradh 	.functionality	= linux_i2cbb_functionality,
2196cb10275Sriastradh };
2206cb10275Sriastradh 
2216cb10275Sriastradh static uint32_t
linux_i2cbb_functionality(struct i2c_adapter * adapter __unused)2226cb10275Sriastradh linux_i2cbb_functionality(struct i2c_adapter *adapter __unused)
2236cb10275Sriastradh {
2246cb10275Sriastradh 	uint32_t functions = 0;
2256cb10275Sriastradh 
2266cb10275Sriastradh 	functions |= I2C_FUNC_I2C;
2276cb10275Sriastradh 	functions |= I2C_FUNC_NOSTART;
2286cb10275Sriastradh 	functions |= I2C_FUNC_SMBUS_EMUL;
2296cb10275Sriastradh 	functions |= I2C_FUNC_SMBUS_READ_BLOCK_DATA;
2306cb10275Sriastradh 	functions |= I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
2316cb10275Sriastradh #if 0
2326cb10275Sriastradh 	functions |= I2C_FUNC_10BIT_ADDR;
2336cb10275Sriastradh 	functions |= I2C_FUNC_PROTOCOL_MANGLING;
2346cb10275Sriastradh #endif
2356cb10275Sriastradh 
2366cb10275Sriastradh 	return functions;
2376cb10275Sriastradh }
2386cb10275Sriastradh 
2396cb10275Sriastradh static int
linux_i2cbb_xfer(struct i2c_adapter * adapter,struct i2c_msg * msgs,int n)2406cb10275Sriastradh linux_i2cbb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int n)
2416cb10275Sriastradh {
2426cb10275Sriastradh 	struct i2c_algo_bit_data *const abd = adapter->algo_data;
2436cb10275Sriastradh 	struct i2c_controller controller = {
2446cb10275Sriastradh 		.ic_cookie		= abd,
2456cb10275Sriastradh 		.ic_send_start		= linux_i2cbb_send_start,
2466cb10275Sriastradh 		.ic_send_stop		= linux_i2cbb_send_stop,
2476cb10275Sriastradh 		.ic_initiate_xfer	= linux_i2cbb_initiate_xfer,
2486cb10275Sriastradh 		.ic_read_byte		= linux_i2cbb_read_byte,
2496cb10275Sriastradh 		.ic_write_byte		= linux_i2cbb_write_byte,
2506cb10275Sriastradh 	};
2516cb10275Sriastradh 	i2c_tag_t i2c = &controller;
2526cb10275Sriastradh 	int error;
2536cb10275Sriastradh 
2546cb10275Sriastradh 	if (abd->pre_xfer) {
2556cb10275Sriastradh 		error = (*abd->pre_xfer)(adapter);
2566cb10275Sriastradh 		if (error)
2576cb10275Sriastradh 			return error;
2586cb10275Sriastradh 	}
2596cb10275Sriastradh 
2606cb10275Sriastradh 	error = netbsd_i2c_transfer(i2c, msgs, n);
2616cb10275Sriastradh 
2626cb10275Sriastradh 	if (abd->post_xfer)
2636cb10275Sriastradh 		(*abd->post_xfer)(adapter);
2646cb10275Sriastradh 
2656cb10275Sriastradh 	return error;
2666cb10275Sriastradh }
2676cb10275Sriastradh 
2686cb10275Sriastradh #define	LI2CBB_SDA	0x01
2696cb10275Sriastradh #define	LI2CBB_SCL	0x02
2706cb10275Sriastradh #define	LI2CBB_INPUT	0x04
2716cb10275Sriastradh #define	LI2CBB_OUTPUT	0x08
2726cb10275Sriastradh 
2736cb10275Sriastradh static struct i2c_bitbang_ops linux_i2cbb_ops = {
2746cb10275Sriastradh 	.ibo_set_bits	= linux_i2cbb_set_bits,
2756cb10275Sriastradh 	.ibo_set_dir	= linux_i2cbb_set_dir,
2766cb10275Sriastradh 	.ibo_read_bits	= linux_i2cbb_read_bits,
2776cb10275Sriastradh 	.ibo_bits	= {
2786cb10275Sriastradh 		[I2C_BIT_SDA]		= LI2CBB_SDA,
2796cb10275Sriastradh 		[I2C_BIT_SCL]		= LI2CBB_SCL,
2806cb10275Sriastradh 		[I2C_BIT_INPUT]		= LI2CBB_INPUT,
2816cb10275Sriastradh 		[I2C_BIT_OUTPUT]	= LI2CBB_OUTPUT,
2826cb10275Sriastradh 	},
2836cb10275Sriastradh };
2846cb10275Sriastradh 
2856cb10275Sriastradh static void
linux_i2cbb_set_bits(void * cookie,uint32_t bits)2866cb10275Sriastradh linux_i2cbb_set_bits(void *cookie, uint32_t bits)
2876cb10275Sriastradh {
2886cb10275Sriastradh 	struct i2c_algo_bit_data *const abd = cookie;
2896cb10275Sriastradh 
2906cb10275Sriastradh 	(*abd->setsda)(abd->data, (ISSET(bits, LI2CBB_SDA)? 1 : 0));
2916cb10275Sriastradh 	(*abd->setscl)(abd->data, (ISSET(bits, LI2CBB_SCL)? 1 : 0));
2926cb10275Sriastradh }
2936cb10275Sriastradh 
2946cb10275Sriastradh static uint32_t
linux_i2cbb_read_bits(void * cookie)2956cb10275Sriastradh linux_i2cbb_read_bits(void *cookie)
2966cb10275Sriastradh {
2976cb10275Sriastradh 	struct i2c_algo_bit_data *const abd = cookie;
2986cb10275Sriastradh 	uint32_t bits = 0;
2996cb10275Sriastradh 
3006cb10275Sriastradh 	if ((*abd->getsda)(abd->data))
3016cb10275Sriastradh 		bits |= LI2CBB_SDA;
3026cb10275Sriastradh 	if ((*abd->getscl)(abd->data))
3036cb10275Sriastradh 		bits |= LI2CBB_SCL;
3046cb10275Sriastradh 
3056cb10275Sriastradh 	return bits;
3066cb10275Sriastradh }
3076cb10275Sriastradh 
3086cb10275Sriastradh static void
linux_i2cbb_set_dir(void * cookie __unused,uint32_t bits __unused)3096cb10275Sriastradh linux_i2cbb_set_dir(void *cookie __unused, uint32_t bits __unused)
3106cb10275Sriastradh {
3116cb10275Sriastradh 	/* Linux doesn't do anything here...  */
3126cb10275Sriastradh }
3136cb10275Sriastradh 
3146cb10275Sriastradh static int
linux_i2cbb_send_start(void * cookie,int flags)3156cb10275Sriastradh linux_i2cbb_send_start(void *cookie, int flags)
3166cb10275Sriastradh {
3176cb10275Sriastradh 
3186cb10275Sriastradh 	return i2c_bitbang_send_start(cookie, flags, &linux_i2cbb_ops);
3196cb10275Sriastradh }
3206cb10275Sriastradh 
3216cb10275Sriastradh static int
linux_i2cbb_send_stop(void * cookie,int flags)3226cb10275Sriastradh linux_i2cbb_send_stop(void *cookie, int flags)
3236cb10275Sriastradh {
3246cb10275Sriastradh 
3256cb10275Sriastradh 	return i2c_bitbang_send_stop(cookie, flags, &linux_i2cbb_ops);
3266cb10275Sriastradh }
3276cb10275Sriastradh 
3286cb10275Sriastradh static int
linux_i2cbb_initiate_xfer(void * cookie,i2c_addr_t addr,int flags)3296cb10275Sriastradh linux_i2cbb_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
3306cb10275Sriastradh {
3316cb10275Sriastradh 
3326cb10275Sriastradh 	return i2c_bitbang_initiate_xfer(cookie, addr, flags,
3336cb10275Sriastradh 	    &linux_i2cbb_ops);
3346cb10275Sriastradh }
3356cb10275Sriastradh 
3366cb10275Sriastradh static int
linux_i2cbb_read_byte(void * cookie,uint8_t * bytep,int flags)3376cb10275Sriastradh linux_i2cbb_read_byte(void *cookie, uint8_t *bytep, int flags)
3386cb10275Sriastradh {
3396cb10275Sriastradh 
3406cb10275Sriastradh 	return i2c_bitbang_read_byte(cookie, bytep, flags, &linux_i2cbb_ops);
3416cb10275Sriastradh }
3426cb10275Sriastradh 
3436cb10275Sriastradh static int
linux_i2cbb_write_byte(void * cookie,uint8_t byte,int flags)3446cb10275Sriastradh linux_i2cbb_write_byte(void *cookie, uint8_t byte, int flags)
3456cb10275Sriastradh {
3466cb10275Sriastradh 
3476cb10275Sriastradh 	return i2c_bitbang_write_byte(cookie, byte, flags, &linux_i2cbb_ops);
3486cb10275Sriastradh }
349