1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> |
4 | * |
5 | * Original author: |
6 | * Ben Collins <bcollins@ubuntu.com> |
7 | * |
8 | * Additional work by: |
9 | * John Brooks <john.brooks@bluecherry.net> |
10 | */ |
11 | |
12 | /* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c |
13 | * channel. The bus can only handle one i2c event at a time. The below handles |
14 | * this all wrong. We should be using the status registers to see if the bus |
15 | * is in use, and have a global lock to check the status register. Also, |
16 | * the bulk of the work should be handled out-of-interrupt. The ugly loops |
17 | * that occur during interrupt scare me. The ISR should merely signal |
18 | * thread context, ACK the interrupt, and move on. -- BenC */ |
19 | |
20 | #include <linux/kernel.h> |
21 | #include <linux/sched/signal.h> |
22 | |
23 | #include "solo6x10.h" |
24 | |
25 | u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) |
26 | { |
27 | struct i2c_msg msgs[2]; |
28 | u8 data; |
29 | |
30 | msgs[0].flags = 0; |
31 | msgs[0].addr = addr; |
32 | msgs[0].len = 1; |
33 | msgs[0].buf = &off; |
34 | |
35 | msgs[1].flags = I2C_M_RD; |
36 | msgs[1].addr = addr; |
37 | msgs[1].len = 1; |
38 | msgs[1].buf = &data; |
39 | |
40 | i2c_transfer(adap: &solo_dev->i2c_adap[id], msgs, num: 2); |
41 | |
42 | return data; |
43 | } |
44 | |
45 | void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, |
46 | u8 off, u8 data) |
47 | { |
48 | struct i2c_msg msgs; |
49 | u8 buf[2]; |
50 | |
51 | buf[0] = off; |
52 | buf[1] = data; |
53 | msgs.flags = 0; |
54 | msgs.addr = addr; |
55 | msgs.len = 2; |
56 | msgs.buf = buf; |
57 | |
58 | i2c_transfer(adap: &solo_dev->i2c_adap[id], msgs: &msgs, num: 1); |
59 | } |
60 | |
61 | static void solo_i2c_flush(struct solo_dev *solo_dev, int wr) |
62 | { |
63 | u32 ctrl; |
64 | |
65 | ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); |
66 | |
67 | if (solo_dev->i2c_state == IIC_STATE_START) |
68 | ctrl |= SOLO_IIC_START; |
69 | |
70 | if (wr) { |
71 | ctrl |= SOLO_IIC_WRITE; |
72 | } else { |
73 | ctrl |= SOLO_IIC_READ; |
74 | if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) |
75 | ctrl |= SOLO_IIC_ACK_EN; |
76 | } |
77 | |
78 | if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) |
79 | ctrl |= SOLO_IIC_STOP; |
80 | |
81 | solo_reg_write(solo_dev, SOLO_IIC_CTRL, data: ctrl); |
82 | } |
83 | |
84 | static void solo_i2c_start(struct solo_dev *solo_dev) |
85 | { |
86 | u32 addr = solo_dev->i2c_msg->addr << 1; |
87 | |
88 | if (solo_dev->i2c_msg->flags & I2C_M_RD) |
89 | addr |= 1; |
90 | |
91 | solo_dev->i2c_state = IIC_STATE_START; |
92 | solo_reg_write(solo_dev, SOLO_IIC_TXD, data: addr); |
93 | solo_i2c_flush(solo_dev, wr: 1); |
94 | } |
95 | |
96 | static void solo_i2c_stop(struct solo_dev *solo_dev) |
97 | { |
98 | solo_irq_off(dev: solo_dev, SOLO_IRQ_IIC); |
99 | solo_reg_write(solo_dev, SOLO_IIC_CTRL, data: 0); |
100 | solo_dev->i2c_state = IIC_STATE_STOP; |
101 | wake_up(&solo_dev->i2c_wait); |
102 | } |
103 | |
104 | static int solo_i2c_handle_read(struct solo_dev *solo_dev) |
105 | { |
106 | prepare_read: |
107 | if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { |
108 | solo_i2c_flush(solo_dev, wr: 0); |
109 | return 0; |
110 | } |
111 | |
112 | solo_dev->i2c_msg_ptr = 0; |
113 | solo_dev->i2c_msg++; |
114 | solo_dev->i2c_msg_num--; |
115 | |
116 | if (solo_dev->i2c_msg_num == 0) { |
117 | solo_i2c_stop(solo_dev); |
118 | return 0; |
119 | } |
120 | |
121 | if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { |
122 | solo_i2c_start(solo_dev); |
123 | } else { |
124 | if (solo_dev->i2c_msg->flags & I2C_M_RD) |
125 | goto prepare_read; |
126 | else |
127 | solo_i2c_stop(solo_dev); |
128 | } |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | static int solo_i2c_handle_write(struct solo_dev *solo_dev) |
134 | { |
135 | retry_write: |
136 | if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { |
137 | solo_reg_write(solo_dev, SOLO_IIC_TXD, |
138 | data: solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); |
139 | solo_dev->i2c_msg_ptr++; |
140 | solo_i2c_flush(solo_dev, wr: 1); |
141 | return 0; |
142 | } |
143 | |
144 | solo_dev->i2c_msg_ptr = 0; |
145 | solo_dev->i2c_msg++; |
146 | solo_dev->i2c_msg_num--; |
147 | |
148 | if (solo_dev->i2c_msg_num == 0) { |
149 | solo_i2c_stop(solo_dev); |
150 | return 0; |
151 | } |
152 | |
153 | if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { |
154 | solo_i2c_start(solo_dev); |
155 | } else { |
156 | if (solo_dev->i2c_msg->flags & I2C_M_RD) |
157 | solo_i2c_stop(solo_dev); |
158 | else |
159 | goto retry_write; |
160 | } |
161 | |
162 | return 0; |
163 | } |
164 | |
165 | int solo_i2c_isr(struct solo_dev *solo_dev) |
166 | { |
167 | u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); |
168 | int ret = -EINVAL; |
169 | |
170 | |
171 | if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) |
172 | || solo_dev->i2c_id < 0) { |
173 | solo_i2c_stop(solo_dev); |
174 | return -ENXIO; |
175 | } |
176 | |
177 | switch (solo_dev->i2c_state) { |
178 | case IIC_STATE_START: |
179 | if (solo_dev->i2c_msg->flags & I2C_M_RD) { |
180 | solo_dev->i2c_state = IIC_STATE_READ; |
181 | ret = solo_i2c_handle_read(solo_dev); |
182 | break; |
183 | } |
184 | |
185 | solo_dev->i2c_state = IIC_STATE_WRITE; |
186 | fallthrough; |
187 | case IIC_STATE_WRITE: |
188 | ret = solo_i2c_handle_write(solo_dev); |
189 | break; |
190 | |
191 | case IIC_STATE_READ: |
192 | solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = |
193 | solo_reg_read(solo_dev, SOLO_IIC_RXD); |
194 | solo_dev->i2c_msg_ptr++; |
195 | |
196 | ret = solo_i2c_handle_read(solo_dev); |
197 | break; |
198 | |
199 | default: |
200 | solo_i2c_stop(solo_dev); |
201 | } |
202 | |
203 | return ret; |
204 | } |
205 | |
206 | static int solo_i2c_master_xfer(struct i2c_adapter *adap, |
207 | struct i2c_msg msgs[], int num) |
208 | { |
209 | struct solo_dev *solo_dev = adap->algo_data; |
210 | unsigned long timeout; |
211 | int ret; |
212 | int i; |
213 | DEFINE_WAIT(wait); |
214 | |
215 | for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { |
216 | if (&solo_dev->i2c_adap[i] == adap) |
217 | break; |
218 | } |
219 | |
220 | if (i == SOLO_I2C_ADAPTERS) |
221 | return num; /* XXX Right return value for failure? */ |
222 | |
223 | mutex_lock(&solo_dev->i2c_mutex); |
224 | solo_dev->i2c_id = i; |
225 | solo_dev->i2c_msg = msgs; |
226 | solo_dev->i2c_msg_num = num; |
227 | solo_dev->i2c_msg_ptr = 0; |
228 | |
229 | solo_reg_write(solo_dev, SOLO_IIC_CTRL, data: 0); |
230 | solo_irq_on(dev: solo_dev, SOLO_IRQ_IIC); |
231 | solo_i2c_start(solo_dev); |
232 | |
233 | timeout = HZ / 2; |
234 | |
235 | for (;;) { |
236 | prepare_to_wait(wq_head: &solo_dev->i2c_wait, wq_entry: &wait, |
237 | TASK_INTERRUPTIBLE); |
238 | |
239 | if (solo_dev->i2c_state == IIC_STATE_STOP) |
240 | break; |
241 | |
242 | timeout = schedule_timeout(timeout); |
243 | if (!timeout) |
244 | break; |
245 | |
246 | if (signal_pending(current)) |
247 | break; |
248 | } |
249 | |
250 | finish_wait(wq_head: &solo_dev->i2c_wait, wq_entry: &wait); |
251 | ret = num - solo_dev->i2c_msg_num; |
252 | solo_dev->i2c_state = IIC_STATE_IDLE; |
253 | solo_dev->i2c_id = -1; |
254 | |
255 | mutex_unlock(lock: &solo_dev->i2c_mutex); |
256 | |
257 | return ret; |
258 | } |
259 | |
260 | static u32 solo_i2c_functionality(struct i2c_adapter *adap) |
261 | { |
262 | return I2C_FUNC_I2C; |
263 | } |
264 | |
265 | static const struct i2c_algorithm solo_i2c_algo = { |
266 | .master_xfer = solo_i2c_master_xfer, |
267 | .functionality = solo_i2c_functionality, |
268 | }; |
269 | |
270 | int solo_i2c_init(struct solo_dev *solo_dev) |
271 | { |
272 | int i; |
273 | int ret; |
274 | |
275 | solo_reg_write(solo_dev, SOLO_IIC_CFG, |
276 | SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); |
277 | |
278 | solo_dev->i2c_id = -1; |
279 | solo_dev->i2c_state = IIC_STATE_IDLE; |
280 | init_waitqueue_head(&solo_dev->i2c_wait); |
281 | mutex_init(&solo_dev->i2c_mutex); |
282 | |
283 | for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { |
284 | struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; |
285 | |
286 | snprintf(buf: adap->name, I2C_NAME_SIZE, fmt: "%s I2C %d" , |
287 | SOLO6X10_NAME, i); |
288 | adap->algo = &solo_i2c_algo; |
289 | adap->algo_data = solo_dev; |
290 | adap->retries = 1; |
291 | adap->dev.parent = &solo_dev->pdev->dev; |
292 | |
293 | ret = i2c_add_adapter(adap); |
294 | if (ret) { |
295 | adap->algo_data = NULL; |
296 | break; |
297 | } |
298 | } |
299 | |
300 | if (ret) { |
301 | for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { |
302 | if (!solo_dev->i2c_adap[i].algo_data) |
303 | break; |
304 | i2c_del_adapter(adap: &solo_dev->i2c_adap[i]); |
305 | solo_dev->i2c_adap[i].algo_data = NULL; |
306 | } |
307 | return ret; |
308 | } |
309 | |
310 | return 0; |
311 | } |
312 | |
313 | void solo_i2c_exit(struct solo_dev *solo_dev) |
314 | { |
315 | int i; |
316 | |
317 | for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { |
318 | if (!solo_dev->i2c_adap[i].algo_data) |
319 | continue; |
320 | i2c_del_adapter(adap: &solo_dev->i2c_adap[i]); |
321 | solo_dev->i2c_adap[i].algo_data = NULL; |
322 | } |
323 | } |
324 | |