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
21 #include <zephyr/logging/log.h>
22 LOG_MODULE_REGISTER(i2c_sbcon, CONFIG_I2C_LOG_LEVEL);
23
24 #include "i2c-priv.h"
25 #include "i2c_bitbang.h"
26
27 /* SBCon hardware registers layout */
28 struct sbcon {
29 union {
30 volatile uint32_t SB_CONTROLS; /* Write to set pins high */
31 volatile uint32_t SB_CONTROL; /* Read for state of pins */
32 };
33 volatile uint32_t SB_CONTROLC; /* Write to set pins low */
34 };
35
36 /* Bits values for SCL and SDA lines in struct sbcon registers */
37 #define SCL BIT(0)
38 #define SDA BIT(1)
39
40 /* Driver config */
41 struct i2c_sbcon_config {
42 struct sbcon *sbcon; /* Address of hardware registers */
43 uint32_t bitrate; /* I2C bus speed in Hz */
44 };
45
46 /* Driver instance data */
47 struct i2c_sbcon_context {
48 struct i2c_bitbang bitbang; /* Bit-bang library data */
49 };
50
i2c_sbcon_set_scl(void * io_context,int state)51 static void i2c_sbcon_set_scl(void *io_context, int state)
52 {
53 struct sbcon *sbcon = io_context;
54
55 if (state) {
56 sbcon->SB_CONTROLS = SCL;
57 } else {
58 sbcon->SB_CONTROLC = SCL;
59 }
60 }
61
i2c_sbcon_set_sda(void * io_context,int state)62 static void i2c_sbcon_set_sda(void *io_context, int state)
63 {
64 struct sbcon *sbcon = io_context;
65
66 if (state) {
67 sbcon->SB_CONTROLS = SDA;
68 } else {
69 sbcon->SB_CONTROLC = SDA;
70 }
71 }
72
i2c_sbcon_get_sda(void * io_context)73 static int i2c_sbcon_get_sda(void *io_context)
74 {
75 struct sbcon *sbcon = io_context;
76
77 return sbcon->SB_CONTROL & SDA;
78 }
79
80 static const struct i2c_bitbang_io io_fns = {
81 .set_scl = &i2c_sbcon_set_scl,
82 .set_sda = &i2c_sbcon_set_sda,
83 .get_sda = &i2c_sbcon_get_sda,
84 };
85
i2c_sbcon_configure(const struct device * dev,uint32_t dev_config)86 static int i2c_sbcon_configure(const struct device *dev, uint32_t dev_config)
87 {
88 struct i2c_sbcon_context *context = dev->data;
89
90 return i2c_bitbang_configure(&context->bitbang, dev_config);
91 }
92
i2c_sbcon_get_config(const struct device * dev,uint32_t * config)93 static int i2c_sbcon_get_config(const struct device *dev, uint32_t *config)
94 {
95 struct i2c_sbcon_context *context = dev->data;
96
97 return i2c_bitbang_get_config(&context->bitbang, config);
98 }
99
i2c_sbcon_transfer(const struct device * dev,struct i2c_msg * msgs,uint8_t num_msgs,uint16_t slave_address)100 static int i2c_sbcon_transfer(const struct device *dev, struct i2c_msg *msgs,
101 uint8_t num_msgs, uint16_t slave_address)
102 {
103 struct i2c_sbcon_context *context = dev->data;
104
105 return i2c_bitbang_transfer(&context->bitbang, msgs, num_msgs,
106 slave_address);
107 }
108
i2c_sbcon_recover_bus(const struct device * dev)109 static int i2c_sbcon_recover_bus(const struct device *dev)
110 {
111 struct i2c_sbcon_context *context = dev->data;
112
113 return i2c_bitbang_recover_bus(&context->bitbang);
114 }
115
116 static DEVICE_API(i2c, api) = {
117 .configure = i2c_sbcon_configure,
118 .get_config = i2c_sbcon_get_config,
119 .transfer = i2c_sbcon_transfer,
120 .recover_bus = i2c_sbcon_recover_bus,
121 #ifdef CONFIG_I2C_RTIO
122 .iodev_submit = i2c_iodev_submit_fallback,
123 #endif
124 };
125
i2c_sbcon_init(const struct device * dev)126 static int i2c_sbcon_init(const struct device *dev)
127 {
128 struct i2c_sbcon_context *context = dev->data;
129 const struct i2c_sbcon_config *config = dev->config;
130 int ret;
131
132 i2c_bitbang_init(&context->bitbang, &io_fns, config->sbcon);
133
134 ret = i2c_bitbang_configure(&context->bitbang,
135 I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate));
136 if (ret != 0) {
137 LOG_ERR("failed to configure I2C bitbang: %d", ret);
138 }
139
140 return ret;
141 }
142
143 #define DEFINE_I2C_SBCON(_num) \
144 \
145 static struct i2c_sbcon_context i2c_sbcon_dev_data_##_num; \
146 \
147 static const struct i2c_sbcon_config i2c_sbcon_dev_cfg_##_num = { \
148 .sbcon = (void *)DT_INST_REG_ADDR(_num), \
149 .bitrate = DT_INST_PROP(_num, clock_frequency), \
150 }; \
151 \
152 I2C_DEVICE_DT_INST_DEFINE(_num, \
153 i2c_sbcon_init, \
154 NULL, \
155 &i2c_sbcon_dev_data_##_num, \
156 &i2c_sbcon_dev_cfg_##_num, \
157 PRE_KERNEL_2, CONFIG_I2C_INIT_PRIORITY, &api);
158
159 DT_INST_FOREACH_STATUS_OKAY(DEFINE_I2C_SBCON)
160