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#include <linux/module.h>
37#include <linux/kernel.h>
38#include <linux/slab.h>
39#include <linux/init.h>
40#include <linux/i2c.h>
41#include <linux/mutex.h>
42
43extern struct i2c_adapter amd756_smbus;
44
45static struct i2c_adapter *s4882_adapter;
46static struct i2c_algorithm *s4882_algo;
47
48
49static DEFINE_MUTEX(amd756_lock);
50
51static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr,
52 unsigned short flags, char read_write,
53 u8 command, int size,
54 union i2c_smbus_data * data)
55{
56 int error;
57
58
59 if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
60 || addr == 0x18)
61 return -ENXIO;
62
63 mutex_lock(&amd756_lock);
64
65 error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
66 command, size, data);
67
68 mutex_unlock(&amd756_lock);
69
70 return error;
71}
72
73
74
75
76
77static u8 last_channels;
78
79static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr,
80 unsigned short flags, char read_write,
81 u8 command, int size,
82 union i2c_smbus_data * data,
83 u8 channels)
84{
85 int error;
86
87
88 if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
89 return -ENXIO;
90
91 mutex_lock(&amd756_lock);
92
93 if (last_channels != channels) {
94 union i2c_smbus_data mplxdata;
95 mplxdata.byte = channels;
96
97 error = amd756_smbus.algo->smbus_xfer(adap, 0x18, 0,
98 I2C_SMBUS_WRITE, 0x01,
99 I2C_SMBUS_BYTE_DATA,
100 &mplxdata);
101 if (error)
102 goto UNLOCK;
103 last_channels = channels;
104 }
105 error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
106 command, size, data);
107
108UNLOCK:
109 mutex_unlock(&amd756_lock);
110 return error;
111}
112
113static s32 amd756_access_virt1(struct i2c_adapter * adap, u16 addr,
114 unsigned short flags, char read_write,
115 u8 command, int size,
116 union i2c_smbus_data * data)
117{
118
119 return amd756_access_channel(adap, addr, flags, read_write, command,
120 size, data, 0x03);
121}
122
123static s32 amd756_access_virt2(struct i2c_adapter * adap, u16 addr,
124 unsigned short flags, char read_write,
125 u8 command, int size,
126 union i2c_smbus_data * data)
127{
128
129 return amd756_access_channel(adap, addr, flags, read_write, command,
130 size, data, 0x24);
131}
132
133static s32 amd756_access_virt3(struct i2c_adapter * adap, u16 addr,
134 unsigned short flags, char read_write,
135 u8 command, int size,
136 union i2c_smbus_data * data)
137{
138
139 return amd756_access_channel(adap, addr, flags, read_write, command,
140 size, data, 0x48);
141}
142
143static s32 amd756_access_virt4(struct i2c_adapter * adap, u16 addr,
144 unsigned short flags, char read_write,
145 u8 command, int size,
146 union i2c_smbus_data * data)
147{
148
149 return amd756_access_channel(adap, addr, flags, read_write, command,
150 size, data, 0x90);
151}
152
153static int __init amd756_s4882_init(void)
154{
155 int i, error;
156 union i2c_smbus_data ioconfig;
157
158 if (!amd756_smbus.dev.parent)
159 return -ENODEV;
160
161
162 ioconfig.byte = 0x00;
163 error = i2c_smbus_xfer(&amd756_smbus, 0x18, 0, I2C_SMBUS_WRITE, 0x03,
164 I2C_SMBUS_BYTE_DATA, &ioconfig);
165 if (error) {
166 dev_err(&amd756_smbus.dev, "PCA9556 configuration failed\n");
167 error = -EIO;
168 goto ERROR0;
169 }
170
171
172 i2c_del_adapter(&amd756_smbus);
173
174 printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n");
175
176 if (!(s4882_adapter = kzalloc(5 * sizeof(struct i2c_adapter),
177 GFP_KERNEL))) {
178 error = -ENOMEM;
179 goto ERROR1;
180 }
181 if (!(s4882_algo = kzalloc(5 * sizeof(struct i2c_algorithm),
182 GFP_KERNEL))) {
183 error = -ENOMEM;
184 goto ERROR2;
185 }
186
187
188 s4882_algo[0] = *(amd756_smbus.algo);
189 s4882_algo[0].smbus_xfer = amd756_access_virt0;
190 s4882_adapter[0] = amd756_smbus;
191 s4882_adapter[0].algo = s4882_algo;
192 s4882_adapter[0].dev.parent = amd756_smbus.dev.parent;
193 for (i = 1; i < 5; i++) {
194 s4882_algo[i] = *(amd756_smbus.algo);
195 s4882_adapter[i] = amd756_smbus;
196 snprintf(s4882_adapter[i].name, sizeof(s4882_adapter[i].name),
197 "SMBus 8111 adapter (CPU%d)", i-1);
198 s4882_adapter[i].algo = s4882_algo+i;
199 s4882_adapter[i].dev.parent = amd756_smbus.dev.parent;
200 }
201 s4882_algo[1].smbus_xfer = amd756_access_virt1;
202 s4882_algo[2].smbus_xfer = amd756_access_virt2;
203 s4882_algo[3].smbus_xfer = amd756_access_virt3;
204 s4882_algo[4].smbus_xfer = amd756_access_virt4;
205
206
207 for (i = 0; i < 5; i++) {
208 error = i2c_add_adapter(s4882_adapter+i);
209 if (error) {
210 printk(KERN_ERR "i2c-amd756-s4882: "
211 "Virtual adapter %d registration "
212 "failed, module not inserted\n", i);
213 for (i--; i >= 0; i--)
214 i2c_del_adapter(s4882_adapter+i);
215 goto ERROR3;
216 }
217 }
218
219 return 0;
220
221ERROR3:
222 kfree(s4882_algo);
223 s4882_algo = NULL;
224ERROR2:
225 kfree(s4882_adapter);
226 s4882_adapter = NULL;
227ERROR1:
228
229 i2c_add_adapter(&amd756_smbus);
230ERROR0:
231 return error;
232}
233
234static void __exit amd756_s4882_exit(void)
235{
236 if (s4882_adapter) {
237 int i;
238
239 for (i = 0; i < 5; i++)
240 i2c_del_adapter(s4882_adapter+i);
241 kfree(s4882_adapter);
242 s4882_adapter = NULL;
243 }
244 kfree(s4882_algo);
245 s4882_algo = NULL;
246
247
248 if (i2c_add_adapter(&amd756_smbus))
249 printk(KERN_ERR "i2c-amd756-s4882: "
250 "Physical bus restoration failed\n");
251}
252
253MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
254MODULE_DESCRIPTION("S4882 SMBus multiplexing");
255MODULE_LICENSE("GPL");
256
257module_init(amd756_s4882_init);
258module_exit(amd756_s4882_exit);
259