Commit | Line | Data |
---|---|---|
4771d831 | 1 | // SPDX-License-Identifier: GPL-2.0 |
a96e5ab8 | 2 | /* |
22e74389 | 3 | * ddbridge-i2c.c: Digital Devices bridge i2c driver |
a96e5ab8 | 4 | * |
22e74389 DS |
5 | * Copyright (C) 2010-2017 Digital Devices GmbH |
6 | * Ralph Metzler <rjkm@metzlerbros.de> | |
7 | * Marcus Metzler <mocm@metzlerbros.de> | |
a96e5ab8 DS |
8 | * |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License | |
11 | * version 2 only, as published by the Free Software Foundation. | |
12 | * | |
a96e5ab8 DS |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
a96e5ab8 DS |
17 | */ |
18 | ||
a96e5ab8 DS |
19 | #include <linux/module.h> |
20 | #include <linux/init.h> | |
21 | #include <linux/interrupt.h> | |
22 | #include <linux/delay.h> | |
23 | #include <linux/slab.h> | |
24 | #include <linux/poll.h> | |
25 | #include <linux/io.h> | |
26 | #include <linux/pci.h> | |
27 | #include <linux/pci_ids.h> | |
28 | #include <linux/timer.h> | |
29 | #include <linux/i2c.h> | |
30 | #include <linux/swab.h> | |
31 | #include <linux/vmalloc.h> | |
32 | ||
33 | #include "ddbridge.h" | |
a96e5ab8 | 34 | #include "ddbridge-i2c.h" |
22e74389 | 35 | #include "ddbridge-regs.h" |
14e27a10 | 36 | #include "ddbridge-io.h" |
a96e5ab8 DS |
37 | |
38 | /******************************************************************************/ | |
39 | ||
40 | static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd) | |
41 | { | |
42 | struct ddb *dev = i2c->dev; | |
22e74389 | 43 | unsigned long stat; |
a96e5ab8 DS |
44 | u32 val; |
45 | ||
22e74389 DS |
46 | ddbwritel(dev, (adr << 9) | cmd, i2c->regs + I2C_COMMAND); |
47 | stat = wait_for_completion_timeout(&i2c->completion, HZ); | |
48 | val = ddbreadl(dev, i2c->regs + I2C_COMMAND); | |
a96e5ab8 | 49 | if (stat == 0) { |
22e74389 DS |
50 | dev_err(dev->dev, "I2C timeout, card %d, port %d, link %u\n", |
51 | dev->nr, i2c->nr, i2c->link); | |
52 | { | |
53 | u32 istat = ddbreadl(dev, INTERRUPT_STATUS); | |
54 | ||
55 | dev_err(dev->dev, "DDBridge IRS %08x\n", istat); | |
56 | if (i2c->link) { | |
57 | u32 listat = ddbreadl(dev, | |
58 | DDB_LINK_TAG(i2c->link) | | |
59 | INTERRUPT_STATUS); | |
60 | ||
61 | dev_err(dev->dev, "DDBridge link %u IRS %08x\n", | |
62 | i2c->link, listat); | |
63 | } | |
64 | if (istat & 1) { | |
65 | ddbwritel(dev, istat & 1, INTERRUPT_ACK); | |
66 | } else { | |
67 | u32 mon = ddbreadl(dev, | |
68 | i2c->regs + I2C_MONITOR); | |
69 | ||
70 | dev_err(dev->dev, "I2C cmd=%08x mon=%08x\n", | |
71 | val, mon); | |
72 | } | |
a96e5ab8 DS |
73 | } |
74 | return -EIO; | |
75 | } | |
d96eeee5 DS |
76 | val &= 0x70000; |
77 | if (val == 0x20000) | |
78 | dev_err(dev->dev, "I2C bus error\n"); | |
79 | if (val) | |
a96e5ab8 DS |
80 | return -EIO; |
81 | return 0; | |
82 | } | |
83 | ||
84 | static int ddb_i2c_master_xfer(struct i2c_adapter *adapter, | |
85 | struct i2c_msg msg[], int num) | |
86 | { | |
757d78d3 | 87 | struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter); |
a96e5ab8 DS |
88 | struct ddb *dev = i2c->dev; |
89 | u8 addr = 0; | |
90 | ||
22e74389 DS |
91 | addr = msg[0].addr; |
92 | if (msg[0].len > i2c->bsize) | |
93 | return -EIO; | |
94 | switch (num) { | |
95 | case 1: | |
96 | if (msg[0].flags & I2C_M_RD) { | |
97 | ddbwritel(dev, msg[0].len << 16, | |
98 | i2c->regs + I2C_TASKLENGTH); | |
99 | if (ddb_i2c_cmd(i2c, addr, 3)) | |
100 | break; | |
101 | ddbcpyfrom(dev, msg[0].buf, | |
102 | i2c->rbuf, msg[0].len); | |
a96e5ab8 DS |
103 | return num; |
104 | } | |
22e74389 DS |
105 | ddbcpyto(dev, i2c->wbuf, msg[0].buf, msg[0].len); |
106 | ddbwritel(dev, msg[0].len, i2c->regs + I2C_TASKLENGTH); | |
107 | if (ddb_i2c_cmd(i2c, addr, 2)) | |
108 | break; | |
109 | return num; | |
110 | case 2: | |
111 | if ((msg[0].flags & I2C_M_RD) == I2C_M_RD) | |
112 | break; | |
113 | if ((msg[1].flags & I2C_M_RD) != I2C_M_RD) | |
114 | break; | |
115 | if (msg[1].len > i2c->bsize) | |
116 | break; | |
117 | ddbcpyto(dev, i2c->wbuf, msg[0].buf, msg[0].len); | |
118 | ddbwritel(dev, msg[0].len | (msg[1].len << 16), | |
119 | i2c->regs + I2C_TASKLENGTH); | |
120 | if (ddb_i2c_cmd(i2c, addr, 1)) | |
121 | break; | |
122 | ddbcpyfrom(dev, msg[1].buf, | |
123 | i2c->rbuf, | |
124 | msg[1].len); | |
125 | return num; | |
126 | default: | |
127 | break; | |
a96e5ab8 DS |
128 | } |
129 | return -EIO; | |
130 | } | |
131 | ||
a96e5ab8 DS |
132 | static u32 ddb_i2c_functionality(struct i2c_adapter *adap) |
133 | { | |
22e74389 | 134 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; |
a96e5ab8 DS |
135 | } |
136 | ||
137 | static const struct i2c_algorithm ddb_i2c_algo = { | |
138 | .master_xfer = ddb_i2c_master_xfer, | |
139 | .functionality = ddb_i2c_functionality, | |
140 | }; | |
141 | ||
142 | void ddb_i2c_release(struct ddb *dev) | |
143 | { | |
144 | int i; | |
145 | struct ddb_i2c *i2c; | |
a96e5ab8 | 146 | |
22e74389 | 147 | for (i = 0; i < dev->i2c_num; i++) { |
a96e5ab8 | 148 | i2c = &dev->i2c[i]; |
22e74389 | 149 | i2c_del_adapter(&i2c->adap); |
a96e5ab8 DS |
150 | } |
151 | } | |
152 | ||
1dda87ac | 153 | static void i2c_handler(void *priv) |
22e74389 | 154 | { |
757d78d3 | 155 | struct ddb_i2c *i2c = (struct ddb_i2c *)priv; |
22e74389 DS |
156 | |
157 | complete(&i2c->completion); | |
158 | } | |
159 | ||
160 | static int ddb_i2c_add(struct ddb *dev, struct ddb_i2c *i2c, | |
0937e7e7 DS |
161 | const struct ddb_regmap *regmap, int link, |
162 | int i, int num) | |
a96e5ab8 | 163 | { |
a96e5ab8 DS |
164 | struct i2c_adapter *adap; |
165 | ||
22e74389 DS |
166 | i2c->nr = i; |
167 | i2c->dev = dev; | |
168 | i2c->link = link; | |
169 | i2c->bsize = regmap->i2c_buf->size; | |
170 | i2c->wbuf = DDB_LINK_TAG(link) | | |
171 | (regmap->i2c_buf->base + i2c->bsize * i); | |
172 | i2c->rbuf = i2c->wbuf; /* + i2c->bsize / 2 */ | |
173 | i2c->regs = DDB_LINK_TAG(link) | | |
174 | (regmap->i2c->base + regmap->i2c->size * i); | |
175 | ddbwritel(dev, I2C_SPEED_100, i2c->regs + I2C_TIMING); | |
176 | ddbwritel(dev, ((i2c->rbuf & 0xffff) << 16) | (i2c->wbuf & 0xffff), | |
757d78d3 | 177 | i2c->regs + I2C_TASKADDRESS); |
22e74389 DS |
178 | init_completion(&i2c->completion); |
179 | ||
180 | adap = &i2c->adap; | |
181 | i2c_set_adapdata(adap, i2c); | |
a96e5ab8 | 182 | #ifdef I2C_ADAP_CLASS_TV_DIGITAL |
757d78d3 | 183 | adap->class = I2C_ADAP_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG; |
a96e5ab8 DS |
184 | #else |
185 | #ifdef I2C_CLASS_TV_ANALOG | |
22e74389 | 186 | adap->class = I2C_CLASS_TV_ANALOG; |
a96e5ab8 DS |
187 | #endif |
188 | #endif | |
22e74389 | 189 | snprintf(adap->name, I2C_NAME_SIZE, "ddbridge_%02x.%x.%x", |
757d78d3 | 190 | dev->nr, i2c->link, i); |
22e74389 DS |
191 | adap->algo = &ddb_i2c_algo; |
192 | adap->algo_data = (void *)i2c; | |
193 | adap->dev.parent = dev->dev; | |
194 | return i2c_add_adapter(adap); | |
195 | } | |
196 | ||
197 | int ddb_i2c_init(struct ddb *dev) | |
198 | { | |
199 | int stat = 0; | |
200 | u32 i, j, num = 0, l, base; | |
201 | struct ddb_i2c *i2c; | |
202 | struct i2c_adapter *adap; | |
0937e7e7 | 203 | const struct ddb_regmap *regmap; |
22e74389 DS |
204 | |
205 | for (l = 0; l < DDB_MAX_LINK; l++) { | |
206 | if (!dev->link[l].info) | |
207 | continue; | |
208 | regmap = dev->link[l].info->regmap; | |
209 | if (!regmap || !regmap->i2c) | |
210 | continue; | |
211 | base = regmap->irq_base_i2c; | |
212 | for (i = 0; i < regmap->i2c->num; i++) { | |
213 | if (!(dev->link[l].info->i2c_mask & (1 << i))) | |
214 | continue; | |
215 | i2c = &dev->i2c[num]; | |
1dda87ac | 216 | ddb_irq_set(dev, l, i + base, i2c_handler, i2c); |
22e74389 DS |
217 | stat = ddb_i2c_add(dev, i2c, regmap, l, i, num); |
218 | if (stat) | |
219 | break; | |
220 | num++; | |
221 | } | |
a96e5ab8 | 222 | } |
22e74389 DS |
223 | if (stat) { |
224 | for (j = 0; j < num; j++) { | |
a96e5ab8 DS |
225 | i2c = &dev->i2c[j]; |
226 | adap = &i2c->adap; | |
227 | i2c_del_adapter(adap); | |
228 | } | |
757d78d3 | 229 | } else { |
22e74389 | 230 | dev->i2c_num = num; |
757d78d3 DS |
231 | } |
232 | ||
a96e5ab8 DS |
233 | return stat; |
234 | } |