1 /*
2  * Copyright (c) 2017 Linaro Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT arm_versatile_i2c
8 
9 /**
10  * @file
11  * @brief Driver for ARM's SBCon 2-wire serial bus interface
12  *
13  * SBCon is a simple device which allows directly setting and getting the
14  * hardware state of two-bit serial interfaces like I2C.
15  */
16 
17 #include <zephyr/device.h>
18 #include <errno.h>
19 #include <zephyr/drivers/i2c.h>
20 #include "i2c_bitbang.h"
21 
22 /* SBCon hardware registers layout */
23 struct sbcon {
24 	union {
25 		volatile uint32_t SB_CONTROLS; /* Write to set pins high */
26 		volatile uint32_t SB_CONTROL;  /* Read for state of pins */
27 	};
28 	volatile uint32_t SB_CONTROLC;	/* Write to set pins low */
29 };
30 
31 /* Bits values for SCL and SDA lines in struct sbcon registers */
32 #define SCL BIT(0)
33 #define SDA BIT(1)
34 
35 /* Driver config */
36 struct i2c_sbcon_config {
37 	struct sbcon *sbcon;		/* Address of hardware registers */
38 };
39 
40 /* Driver instance data */
41 struct i2c_sbcon_context {
42 	struct i2c_bitbang bitbang;	/* Bit-bang library data */
43 };
44 
i2c_sbcon_set_scl(void * io_context,int state)45 static void i2c_sbcon_set_scl(void *io_context, int state)
46 {
47 	struct sbcon *sbcon = io_context;
48 
49 	if (state) {
50 		sbcon->SB_CONTROLS = SCL;
51 	} else {
52 		sbcon->SB_CONTROLC = SCL;
53 	}
54 }
55 
i2c_sbcon_set_sda(void * io_context,int state)56 static void i2c_sbcon_set_sda(void *io_context, int state)
57 {
58 	struct sbcon *sbcon = io_context;
59 
60 	if (state) {
61 		sbcon->SB_CONTROLS = SDA;
62 	} else {
63 		sbcon->SB_CONTROLC = SDA;
64 	}
65 }
66 
i2c_sbcon_get_sda(void * io_context)67 static int i2c_sbcon_get_sda(void *io_context)
68 {
69 	struct sbcon *sbcon = io_context;
70 
71 	return sbcon->SB_CONTROL & SDA;
72 }
73 
74 static const struct i2c_bitbang_io io_fns = {
75 	.set_scl = &i2c_sbcon_set_scl,
76 	.set_sda = &i2c_sbcon_set_sda,
77 	.get_sda = &i2c_sbcon_get_sda,
78 };
79 
i2c_sbcon_configure(const struct device * dev,uint32_t dev_config)80 static int i2c_sbcon_configure(const struct device *dev, uint32_t dev_config)
81 {
82 	struct i2c_sbcon_context *context = dev->data;
83 
84 	return i2c_bitbang_configure(&context->bitbang, dev_config);
85 }
86 
i2c_sbcon_transfer(const struct device * dev,struct i2c_msg * msgs,uint8_t num_msgs,uint16_t slave_address)87 static int i2c_sbcon_transfer(const struct device *dev, struct i2c_msg *msgs,
88 				uint8_t num_msgs, uint16_t slave_address)
89 {
90 	struct i2c_sbcon_context *context = dev->data;
91 
92 	return i2c_bitbang_transfer(&context->bitbang, msgs, num_msgs,
93 							slave_address);
94 }
95 
96 static const struct i2c_driver_api api = {
97 	.configure = i2c_sbcon_configure,
98 	.transfer = i2c_sbcon_transfer,
99 };
100 
i2c_sbcon_init(const struct device * dev)101 static int i2c_sbcon_init(const struct device *dev)
102 {
103 	struct i2c_sbcon_context *context = dev->data;
104 	const struct i2c_sbcon_config *config = dev->config;
105 
106 	i2c_bitbang_init(&context->bitbang, &io_fns, config->sbcon);
107 
108 	return 0;
109 }
110 
111 #define	DEFINE_I2C_SBCON(_num)						\
112 									\
113 static struct i2c_sbcon_context i2c_sbcon_dev_data_##_num;		\
114 									\
115 static const struct i2c_sbcon_config i2c_sbcon_dev_cfg_##_num = {	\
116 	.sbcon		= (void *)DT_INST_REG_ADDR(_num), \
117 };									\
118 									\
119 I2C_DEVICE_DT_INST_DEFINE(_num,						\
120 	    i2c_sbcon_init,						\
121 	    NULL,							\
122 	    &i2c_sbcon_dev_data_##_num,					\
123 	    &i2c_sbcon_dev_cfg_##_num,					\
124 	    PRE_KERNEL_2, CONFIG_I2C_INIT_PRIORITY, &api);
125 
126 DT_INST_FOREACH_STATUS_OKAY(DEFINE_I2C_SBCON)
127