1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Coral-P(A)/Lime I2C adapter driver |
4 | * |
5 | * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de> |
6 | */ |
7 | |
8 | #include <linux/fb.h> |
9 | #include <linux/i2c.h> |
10 | #include <linux/io.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/export.h> |
13 | |
14 | #include "mb862xxfb.h" |
15 | #include "mb862xx_reg.h" |
16 | |
17 | static int mb862xx_i2c_wait_event(struct i2c_adapter *adap) |
18 | { |
19 | struct mb862xxfb_par *par = adap->algo_data; |
20 | u32 reg; |
21 | |
22 | do { |
23 | udelay(10); |
24 | reg = inreg(i2c, GC_I2C_BCR); |
25 | if (reg & (I2C_INT | I2C_BER)) |
26 | break; |
27 | } while (1); |
28 | |
29 | return (reg & I2C_BER) ? 0 : 1; |
30 | } |
31 | |
32 | static int mb862xx_i2c_do_address(struct i2c_adapter *adap, int addr) |
33 | { |
34 | struct mb862xxfb_par *par = adap->algo_data; |
35 | |
36 | outreg(i2c, GC_I2C_DAR, addr); |
37 | outreg(i2c, GC_I2C_CCR, I2C_CLOCK_AND_ENABLE); |
38 | outreg(i2c, GC_I2C_BCR, par->i2c_rs ? I2C_REPEATED_START : I2C_START); |
39 | if (!mb862xx_i2c_wait_event(adap)) |
40 | return -EIO; |
41 | par->i2c_rs = !(inreg(i2c, GC_I2C_BSR) & I2C_LRB); |
42 | return par->i2c_rs; |
43 | } |
44 | |
45 | static int mb862xx_i2c_write_byte(struct i2c_adapter *adap, u8 byte) |
46 | { |
47 | struct mb862xxfb_par *par = adap->algo_data; |
48 | |
49 | outreg(i2c, GC_I2C_DAR, byte); |
50 | outreg(i2c, GC_I2C_BCR, I2C_START); |
51 | if (!mb862xx_i2c_wait_event(adap)) |
52 | return -EIO; |
53 | return !(inreg(i2c, GC_I2C_BSR) & I2C_LRB); |
54 | } |
55 | |
56 | static int mb862xx_i2c_read_byte(struct i2c_adapter *adap, u8 *byte, int last) |
57 | { |
58 | struct mb862xxfb_par *par = adap->algo_data; |
59 | |
60 | outreg(i2c, GC_I2C_BCR, I2C_START | (last ? 0 : I2C_ACK)); |
61 | if (!mb862xx_i2c_wait_event(adap)) |
62 | return 0; |
63 | *byte = inreg(i2c, GC_I2C_DAR); |
64 | return 1; |
65 | } |
66 | |
67 | static void mb862xx_i2c_stop(struct i2c_adapter *adap) |
68 | { |
69 | struct mb862xxfb_par *par = adap->algo_data; |
70 | |
71 | outreg(i2c, GC_I2C_BCR, I2C_STOP); |
72 | outreg(i2c, GC_I2C_CCR, I2C_DISABLE); |
73 | par->i2c_rs = 0; |
74 | } |
75 | |
76 | static int mb862xx_i2c_read(struct i2c_adapter *adap, struct i2c_msg *m) |
77 | { |
78 | int i, ret = 0; |
79 | int last = m->len - 1; |
80 | |
81 | for (i = 0; i < m->len; i++) { |
82 | if (!mb862xx_i2c_read_byte(adap, byte: &m->buf[i], last: i == last)) { |
83 | ret = -EIO; |
84 | break; |
85 | } |
86 | } |
87 | return ret; |
88 | } |
89 | |
90 | static int mb862xx_i2c_write(struct i2c_adapter *adap, struct i2c_msg *m) |
91 | { |
92 | int i, ret = 0; |
93 | |
94 | for (i = 0; i < m->len; i++) { |
95 | if (!mb862xx_i2c_write_byte(adap, byte: m->buf[i])) { |
96 | ret = -EIO; |
97 | break; |
98 | } |
99 | } |
100 | return ret; |
101 | } |
102 | |
103 | static int mb862xx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, |
104 | int num) |
105 | { |
106 | struct mb862xxfb_par *par = adap->algo_data; |
107 | struct i2c_msg *m; |
108 | int addr; |
109 | int i = 0, err = 0; |
110 | |
111 | dev_dbg(par->dev, "%s: %d msgs\n" , __func__, num); |
112 | |
113 | for (i = 0; i < num; i++) { |
114 | m = &msgs[i]; |
115 | if (!m->len) { |
116 | dev_dbg(par->dev, "%s: null msgs\n" , __func__); |
117 | continue; |
118 | } |
119 | addr = m->addr; |
120 | if (m->flags & I2C_M_RD) |
121 | addr |= 1; |
122 | |
123 | err = mb862xx_i2c_do_address(adap, addr); |
124 | if (err < 0) |
125 | break; |
126 | if (m->flags & I2C_M_RD) |
127 | err = mb862xx_i2c_read(adap, m); |
128 | else |
129 | err = mb862xx_i2c_write(adap, m); |
130 | } |
131 | |
132 | if (i) |
133 | mb862xx_i2c_stop(adap); |
134 | |
135 | return (err < 0) ? err : i; |
136 | } |
137 | |
138 | static u32 mb862xx_func(struct i2c_adapter *adap) |
139 | { |
140 | return I2C_FUNC_SMBUS_BYTE_DATA; |
141 | } |
142 | |
143 | static const struct i2c_algorithm mb862xx_algo = { |
144 | .master_xfer = mb862xx_xfer, |
145 | .functionality = mb862xx_func, |
146 | }; |
147 | |
148 | static struct i2c_adapter mb862xx_i2c_adapter = { |
149 | .name = "MB862xx I2C adapter" , |
150 | .algo = &mb862xx_algo, |
151 | .owner = THIS_MODULE, |
152 | }; |
153 | |
154 | int mb862xx_i2c_init(struct mb862xxfb_par *par) |
155 | { |
156 | mb862xx_i2c_adapter.algo_data = par; |
157 | par->adap = &mb862xx_i2c_adapter; |
158 | |
159 | return i2c_add_adapter(adap: par->adap); |
160 | } |
161 | |
162 | void mb862xx_i2c_exit(struct mb862xxfb_par *par) |
163 | { |
164 | if (par->adap) { |
165 | i2c_del_adapter(adap: par->adap); |
166 | par->adap = NULL; |
167 | } |
168 | } |
169 | |