1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 starfivetech.com
*
* Authors:
* Minda Chen <minda.chen@starfivetech.com>
*/
#include <sbi/riscv_io.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_timer.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_string.h>
#include <sbi/sbi_bitops.h>
#include <sbi_utils/i2c/dw_i2c.h>
#define DW_IC_CON 0x00
#define DW_IC_TAR 0x04
#define DW_IC_SAR 0x08
#define DW_IC_DATA_CMD 0x10
#define DW_IC_SS_SCL_HCNT 0x14
#define DW_IC_SS_SCL_LCNT 0x18
#define DW_IC_FS_SCL_HCNT 0x1c
#define DW_IC_FS_SCL_LCNT 0x20
#define DW_IC_HS_SCL_HCNT 0x24
#define DW_IC_HS_SCL_LCNT 0x28
#define DW_IC_INTR_STAT 0x2c
#define DW_IC_INTR_MASK 0x30
#define DW_IC_RAW_INTR_STAT 0x34
#define DW_IC_RX_TL 0x38
#define DW_IC_TX_TL 0x3c
#define DW_IC_CLR_INTR 0x40
#define DW_IC_CLR_RX_UNDER 0x44
#define DW_IC_CLR_RX_OVER 0x48
#define DW_IC_CLR_TX_OVER 0x4c
#define DW_IC_CLR_RD_REQ 0x50
#define DW_IC_CLR_TX_ABRT 0x54
#define DW_IC_CLR_RX_DONE 0x58
#define DW_IC_CLR_ACTIVITY 0x5c
#define DW_IC_CLR_STOP_DET 0x60
#define DW_IC_CLR_START_DET 0x64
#define DW_IC_CLR_GEN_CALL 0x68
#define DW_IC_ENABLE 0x6c
#define DW_IC_STATUS 0x70
#define DW_IC_TXFLR 0x74
#define DW_IC_RXFLR 0x78
#define DW_IC_SDA_HOLD 0x7c
#define DW_IC_TX_ABRT_SOURCE 0x80
#define DW_IC_ENABLE_STATUS 0x9c
#define DW_IC_CLR_RESTART_DET 0xa8
#define DW_IC_COMP_PARAM_1 0xf4
#define DW_IC_COMP_VERSION 0xf8
#define DW_I2C_STATUS_TXFIFO_EMPTY BIT(2)
#define DW_I2C_STATUS_RXFIFO_NOT_EMPTY BIT(3)
#define IC_DATA_CMD_READ BIT(8)
#define IC_DATA_CMD_STOP BIT(9)
#define IC_DATA_CMD_RESTART BIT(10)
#define IC_INT_STATUS_STOPDET BIT(9)
static inline void dw_i2c_setreg(struct dw_i2c_adapter *adap,
u8 reg, u32 value)
{
writel(value, (void *)adap->addr + reg);
}
static inline u32 dw_i2c_getreg(struct dw_i2c_adapter *adap,
u32 reg)
{
return readl((void *)adap->addr + reg);
}
static int dw_i2c_adapter_poll(struct dw_i2c_adapter *adap,
u32 mask, u32 addr,
bool inverted)
{
unsigned int timeout = 10; /* msec */
int count = 0;
u32 val;
do {
val = dw_i2c_getreg(adap, addr);
if (inverted) {
if (!(val & mask))
return 0;
} else {
if (val & mask)
return 0;
}
sbi_timer_udelay(2);
count += 1;
if (count == (timeout * 1000))
return SBI_ETIMEDOUT;
} while (1);
}
#define dw_i2c_adapter_poll_rxrdy(adap) \
dw_i2c_adapter_poll(adap, DW_I2C_STATUS_RXFIFO_NOT_EMPTY, DW_IC_STATUS, 0)
#define dw_i2c_adapter_poll_txfifo_ready(adap) \
dw_i2c_adapter_poll(adap, DW_I2C_STATUS_TXFIFO_EMPTY, DW_IC_STATUS, 0)
static int dw_i2c_write_addr(struct dw_i2c_adapter *adap, u8 addr)
{
dw_i2c_setreg(adap, DW_IC_ENABLE, 0);
dw_i2c_setreg(adap, DW_IC_TAR, addr);
dw_i2c_setreg(adap, DW_IC_ENABLE, 1);
return 0;
}
static int dw_i2c_adapter_read(struct i2c_adapter *ia, u8 addr,
u8 reg, u8 *buffer, int len)
{
struct dw_i2c_adapter *adap =
container_of(ia, struct dw_i2c_adapter, adapter);
int rc;
dw_i2c_write_addr(adap, addr);
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
if (rc)
return rc;
/* set register address */
dw_i2c_setreg(adap, DW_IC_DATA_CMD, reg);
/* set value */
while (len) {
if (len == 1)
dw_i2c_setreg(adap, DW_IC_DATA_CMD,
IC_DATA_CMD_READ | IC_DATA_CMD_STOP);
else
dw_i2c_setreg(adap, DW_IC_DATA_CMD, IC_DATA_CMD_READ);
rc = dw_i2c_adapter_poll_rxrdy(adap);
if (rc)
return rc;
*buffer = dw_i2c_getreg(adap, DW_IC_DATA_CMD) & 0xff;
buffer++;
len--;
}
return 0;
}
static int dw_i2c_adapter_write(struct i2c_adapter *ia, u8 addr,
u8 reg, u8 *buffer, int len)
{
struct dw_i2c_adapter *adap =
container_of(ia, struct dw_i2c_adapter, adapter);
int rc;
dw_i2c_write_addr(adap, addr);
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
if (rc)
return rc;
/* set register address */
dw_i2c_setreg(adap, DW_IC_DATA_CMD, reg);
while (len) {
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
if (rc)
return rc;
if (len == 1)
dw_i2c_setreg(adap, DW_IC_DATA_CMD, *buffer | IC_DATA_CMD_STOP);
else
dw_i2c_setreg(adap, DW_IC_DATA_CMD, *buffer);
buffer++;
len--;
}
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
return rc;
}
int dw_i2c_init(struct i2c_adapter *adapter, int nodeoff)
{
adapter->id = nodeoff;
adapter->write = dw_i2c_adapter_write;
adapter->read = dw_i2c_adapter_read;
return i2c_adapter_add(adapter);
}
|