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