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