1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. |
4 | * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. |
5 | |
6 | */ |
7 | |
8 | #include <linux/platform_device.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/spinlock.h> |
11 | #include <linux/module.h> |
12 | #include <linux/via-core.h> |
13 | #include <linux/via_i2c.h> |
14 | |
15 | /* |
16 | * There can only be one set of these, so there's no point in having |
17 | * them be dynamically allocated... |
18 | */ |
19 | #define VIAFB_NUM_I2C 5 |
20 | static struct via_i2c_stuff via_i2c_par[VIAFB_NUM_I2C]; |
21 | static struct viafb_dev *i2c_vdev; /* Passed in from core */ |
22 | |
23 | static void via_i2c_setscl(void *data, int state) |
24 | { |
25 | u8 val; |
26 | struct via_port_cfg *adap_data = data; |
27 | unsigned long flags; |
28 | |
29 | spin_lock_irqsave(&i2c_vdev->reg_lock, flags); |
30 | val = via_read_reg(port: adap_data->io_port, index: adap_data->ioport_index) & 0xF0; |
31 | if (state) |
32 | val |= 0x20; |
33 | else |
34 | val &= ~0x20; |
35 | switch (adap_data->type) { |
36 | case VIA_PORT_I2C: |
37 | val |= 0x01; |
38 | break; |
39 | case VIA_PORT_GPIO: |
40 | val |= 0x82; |
41 | break; |
42 | default: |
43 | printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n" ); |
44 | } |
45 | via_write_reg(port: adap_data->io_port, index: adap_data->ioport_index, data: val); |
46 | spin_unlock_irqrestore(lock: &i2c_vdev->reg_lock, flags); |
47 | } |
48 | |
49 | static int via_i2c_getscl(void *data) |
50 | { |
51 | struct via_port_cfg *adap_data = data; |
52 | unsigned long flags; |
53 | int ret = 0; |
54 | |
55 | spin_lock_irqsave(&i2c_vdev->reg_lock, flags); |
56 | if (adap_data->type == VIA_PORT_GPIO) |
57 | via_write_reg_mask(port: adap_data->io_port, index: adap_data->ioport_index, |
58 | data: 0, mask: 0x80); |
59 | if (via_read_reg(port: adap_data->io_port, index: adap_data->ioport_index) & 0x08) |
60 | ret = 1; |
61 | spin_unlock_irqrestore(lock: &i2c_vdev->reg_lock, flags); |
62 | return ret; |
63 | } |
64 | |
65 | static int via_i2c_getsda(void *data) |
66 | { |
67 | struct via_port_cfg *adap_data = data; |
68 | unsigned long flags; |
69 | int ret = 0; |
70 | |
71 | spin_lock_irqsave(&i2c_vdev->reg_lock, flags); |
72 | if (adap_data->type == VIA_PORT_GPIO) |
73 | via_write_reg_mask(port: adap_data->io_port, index: adap_data->ioport_index, |
74 | data: 0, mask: 0x40); |
75 | if (via_read_reg(port: adap_data->io_port, index: adap_data->ioport_index) & 0x04) |
76 | ret = 1; |
77 | spin_unlock_irqrestore(lock: &i2c_vdev->reg_lock, flags); |
78 | return ret; |
79 | } |
80 | |
81 | static void via_i2c_setsda(void *data, int state) |
82 | { |
83 | u8 val; |
84 | struct via_port_cfg *adap_data = data; |
85 | unsigned long flags; |
86 | |
87 | spin_lock_irqsave(&i2c_vdev->reg_lock, flags); |
88 | val = via_read_reg(port: adap_data->io_port, index: adap_data->ioport_index) & 0xF0; |
89 | if (state) |
90 | val |= 0x10; |
91 | else |
92 | val &= ~0x10; |
93 | switch (adap_data->type) { |
94 | case VIA_PORT_I2C: |
95 | val |= 0x01; |
96 | break; |
97 | case VIA_PORT_GPIO: |
98 | val |= 0x42; |
99 | break; |
100 | default: |
101 | printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n" ); |
102 | } |
103 | via_write_reg(port: adap_data->io_port, index: adap_data->ioport_index, data: val); |
104 | spin_unlock_irqrestore(lock: &i2c_vdev->reg_lock, flags); |
105 | } |
106 | |
107 | int viafb_i2c_readbyte(u8 adap, u8 slave_addr, u8 index, u8 *pdata) |
108 | { |
109 | int ret; |
110 | u8 mm1[] = {0x00}; |
111 | struct i2c_msg msgs[2]; |
112 | |
113 | if (!via_i2c_par[adap].is_active) |
114 | return -ENODEV; |
115 | *pdata = 0; |
116 | msgs[0].flags = 0; |
117 | msgs[1].flags = I2C_M_RD; |
118 | msgs[0].addr = msgs[1].addr = slave_addr / 2; |
119 | mm1[0] = index; |
120 | msgs[0].len = 1; msgs[1].len = 1; |
121 | msgs[0].buf = mm1; msgs[1].buf = pdata; |
122 | ret = i2c_transfer(adap: &via_i2c_par[adap].adapter, msgs, num: 2); |
123 | if (ret == 2) |
124 | ret = 0; |
125 | else if (ret >= 0) |
126 | ret = -EIO; |
127 | |
128 | return ret; |
129 | } |
130 | |
131 | int viafb_i2c_writebyte(u8 adap, u8 slave_addr, u8 index, u8 data) |
132 | { |
133 | int ret; |
134 | u8 msg[2] = { index, data }; |
135 | struct i2c_msg msgs; |
136 | |
137 | if (!via_i2c_par[adap].is_active) |
138 | return -ENODEV; |
139 | msgs.flags = 0; |
140 | msgs.addr = slave_addr / 2; |
141 | msgs.len = 2; |
142 | msgs.buf = msg; |
143 | ret = i2c_transfer(adap: &via_i2c_par[adap].adapter, msgs: &msgs, num: 1); |
144 | if (ret == 1) |
145 | ret = 0; |
146 | else if (ret >= 0) |
147 | ret = -EIO; |
148 | |
149 | return ret; |
150 | } |
151 | |
152 | int viafb_i2c_readbytes(u8 adap, u8 slave_addr, u8 index, u8 *buff, int buff_len) |
153 | { |
154 | int ret; |
155 | u8 mm1[] = {0x00}; |
156 | struct i2c_msg msgs[2]; |
157 | |
158 | if (!via_i2c_par[adap].is_active) |
159 | return -ENODEV; |
160 | msgs[0].flags = 0; |
161 | msgs[1].flags = I2C_M_RD; |
162 | msgs[0].addr = msgs[1].addr = slave_addr / 2; |
163 | mm1[0] = index; |
164 | msgs[0].len = 1; msgs[1].len = buff_len; |
165 | msgs[0].buf = mm1; msgs[1].buf = buff; |
166 | ret = i2c_transfer(adap: &via_i2c_par[adap].adapter, msgs, num: 2); |
167 | if (ret == 2) |
168 | ret = 0; |
169 | else if (ret >= 0) |
170 | ret = -EIO; |
171 | |
172 | return ret; |
173 | } |
174 | |
175 | /* |
176 | * Allow other viafb subdevices to look up a specific adapter |
177 | * by port name. |
178 | */ |
179 | struct i2c_adapter *viafb_find_i2c_adapter(enum viafb_i2c_adap which) |
180 | { |
181 | struct via_i2c_stuff *stuff = &via_i2c_par[which]; |
182 | |
183 | return &stuff->adapter; |
184 | } |
185 | EXPORT_SYMBOL_GPL(viafb_find_i2c_adapter); |
186 | |
187 | |
188 | static int create_i2c_bus(struct i2c_adapter *adapter, |
189 | struct i2c_algo_bit_data *algo, |
190 | struct via_port_cfg *adap_cfg, |
191 | struct pci_dev *pdev) |
192 | { |
193 | algo->setsda = via_i2c_setsda; |
194 | algo->setscl = via_i2c_setscl; |
195 | algo->getsda = via_i2c_getsda; |
196 | algo->getscl = via_i2c_getscl; |
197 | algo->udelay = 10; |
198 | algo->timeout = 2; |
199 | algo->data = adap_cfg; |
200 | |
201 | sprintf(buf: adapter->name, fmt: "viafb i2c io_port idx 0x%02x" , |
202 | adap_cfg->ioport_index); |
203 | adapter->owner = THIS_MODULE; |
204 | adapter->algo_data = algo; |
205 | if (pdev) |
206 | adapter->dev.parent = &pdev->dev; |
207 | else |
208 | adapter->dev.parent = NULL; |
209 | /* i2c_set_adapdata(adapter, adap_cfg); */ |
210 | |
211 | /* Raise SCL and SDA */ |
212 | via_i2c_setsda(data: adap_cfg, state: 1); |
213 | via_i2c_setscl(data: adap_cfg, state: 1); |
214 | udelay(20); |
215 | |
216 | return i2c_bit_add_bus(adapter); |
217 | } |
218 | |
219 | static int viafb_i2c_probe(struct platform_device *platdev) |
220 | { |
221 | int i, ret; |
222 | struct via_port_cfg *configs; |
223 | |
224 | i2c_vdev = platdev->dev.platform_data; |
225 | configs = i2c_vdev->port_cfg; |
226 | |
227 | for (i = 0; i < VIAFB_NUM_PORTS; i++) { |
228 | struct via_port_cfg *adap_cfg = configs++; |
229 | struct via_i2c_stuff *i2c_stuff = &via_i2c_par[i]; |
230 | |
231 | i2c_stuff->is_active = 0; |
232 | if (adap_cfg->type == 0 || adap_cfg->mode != VIA_MODE_I2C) |
233 | continue; |
234 | ret = create_i2c_bus(adapter: &i2c_stuff->adapter, |
235 | algo: &i2c_stuff->algo, adap_cfg, |
236 | NULL); /* FIXME: PCIDEV */ |
237 | if (ret < 0) { |
238 | printk(KERN_ERR "viafb: cannot create i2c bus %u:%d\n" , |
239 | i, ret); |
240 | continue; /* Still try to make the rest */ |
241 | } |
242 | i2c_stuff->is_active = 1; |
243 | } |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | static void viafb_i2c_remove(struct platform_device *platdev) |
249 | { |
250 | int i; |
251 | |
252 | for (i = 0; i < VIAFB_NUM_PORTS; i++) { |
253 | struct via_i2c_stuff *i2c_stuff = &via_i2c_par[i]; |
254 | /* |
255 | * Only remove those entries in the array that we've |
256 | * actually used (and thus initialized algo_data) |
257 | */ |
258 | if (i2c_stuff->is_active) |
259 | i2c_del_adapter(adap: &i2c_stuff->adapter); |
260 | } |
261 | } |
262 | |
263 | static struct platform_driver via_i2c_driver = { |
264 | .driver = { |
265 | .name = "viafb-i2c" , |
266 | }, |
267 | .probe = viafb_i2c_probe, |
268 | .remove_new = viafb_i2c_remove, |
269 | }; |
270 | |
271 | int viafb_i2c_init(void) |
272 | { |
273 | return platform_driver_register(&via_i2c_driver); |
274 | } |
275 | |
276 | void viafb_i2c_exit(void) |
277 | { |
278 | platform_driver_unregister(&via_i2c_driver); |
279 | } |
280 | |