a2f5f992af7aa7e21d70294d302891c08054f4da
[sfrench/cifs-2.6.git] / drivers / i2c / busses / i2c-amd756-s4882.c
1 /*
2  * i2c-amd756-s4882.c - i2c-amd756 extras for the Tyan S4882 motherboard
3  *
4  * Copyright (C) 2004, 2008 Jean Delvare <jdelvare@suse.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16  
17 /*
18  * We select the channels by sending commands to the Philips
19  * PCA9556 chip at I2C address 0x18. The main adapter is used for
20  * the non-multiplexed part of the bus, and 4 virtual adapters
21  * are defined for the multiplexed addresses: 0x50-0x53 (memory
22  * module EEPROM) located on channels 1-4, and 0x4c (LM63)
23  * located on multiplexed channels 0 and 5-7. We define one
24  * virtual adapter per CPU, which corresponds to two multiplexed
25  * channels:
26  *   CPU0: virtual adapter 1, channels 1 and 0
27  *   CPU1: virtual adapter 2, channels 2 and 5
28  *   CPU2: virtual adapter 3, channels 3 and 6
29  *   CPU3: virtual adapter 4, channels 4 and 7
30  */
31
32 #include <linux/module.h>
33 #include <linux/kernel.h>
34 #include <linux/slab.h>
35 #include <linux/init.h>
36 #include <linux/i2c.h>
37 #include <linux/mutex.h>
38
39 extern struct i2c_adapter amd756_smbus;
40
41 static struct i2c_adapter *s4882_adapter;
42 static struct i2c_algorithm *s4882_algo;
43
44 /* Wrapper access functions for multiplexed SMBus */
45 static DEFINE_MUTEX(amd756_lock);
46
47 static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr,
48                                unsigned short flags, char read_write,
49                                u8 command, int size,
50                                union i2c_smbus_data * data)
51 {
52         int error;
53
54         /* We exclude the multiplexed addresses */
55         if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
56          || addr == 0x18)
57                 return -ENXIO;
58
59         mutex_lock(&amd756_lock);
60
61         error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
62                                               command, size, data);
63
64         mutex_unlock(&amd756_lock);
65
66         return error;
67 }
68
69 /* We remember the last used channels combination so as to only switch
70    channels when it is really needed. This greatly reduces the SMBus
71    overhead, but also assumes that nobody will be writing to the PCA9556
72    in our back. */
73 static u8 last_channels;
74
75 static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr,
76                                         unsigned short flags, char read_write,
77                                         u8 command, int size,
78                                         union i2c_smbus_data * data,
79                                         u8 channels)
80 {
81         int error;
82
83         /* We exclude the non-multiplexed addresses */
84         if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
85                 return -ENXIO;
86
87         mutex_lock(&amd756_lock);
88
89         if (last_channels != channels) {
90                 union i2c_smbus_data mplxdata;
91                 mplxdata.byte = channels;
92
93                 error = amd756_smbus.algo->smbus_xfer(adap, 0x18, 0,
94                                                       I2C_SMBUS_WRITE, 0x01,
95                                                       I2C_SMBUS_BYTE_DATA,
96                                                       &mplxdata);
97                 if (error)
98                         goto UNLOCK;
99                 last_channels = channels;
100         }
101         error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
102                                               command, size, data);
103
104 UNLOCK:
105         mutex_unlock(&amd756_lock);
106         return error;
107 }
108
109 static s32 amd756_access_virt1(struct i2c_adapter * adap, u16 addr,
110                                unsigned short flags, char read_write,
111                                u8 command, int size,
112                                union i2c_smbus_data * data)
113 {
114         /* CPU0: channels 1 and 0 enabled */
115         return amd756_access_channel(adap, addr, flags, read_write, command,
116                                      size, data, 0x03);
117 }
118
119 static s32 amd756_access_virt2(struct i2c_adapter * adap, u16 addr,
120                                unsigned short flags, char read_write,
121                                u8 command, int size,
122                                union i2c_smbus_data * data)
123 {
124         /* CPU1: channels 2 and 5 enabled */
125         return amd756_access_channel(adap, addr, flags, read_write, command,
126                                      size, data, 0x24);
127 }
128
129 static s32 amd756_access_virt3(struct i2c_adapter * adap, u16 addr,
130                                unsigned short flags, char read_write,
131                                u8 command, int size,
132                                union i2c_smbus_data * data)
133 {
134         /* CPU2: channels 3 and 6 enabled */
135         return amd756_access_channel(adap, addr, flags, read_write, command,
136                                      size, data, 0x48);
137 }
138
139 static s32 amd756_access_virt4(struct i2c_adapter * adap, u16 addr,
140                                unsigned short flags, char read_write,
141                                u8 command, int size,
142                                union i2c_smbus_data * data)
143 {
144         /* CPU3: channels 4 and 7 enabled */
145         return amd756_access_channel(adap, addr, flags, read_write, command,
146                                      size, data, 0x90);
147 }
148
149 static int __init amd756_s4882_init(void)
150 {
151         int i, error;
152         union i2c_smbus_data ioconfig;
153
154         if (!amd756_smbus.dev.parent)
155                 return -ENODEV;
156
157         /* Configure the PCA9556 multiplexer */
158         ioconfig.byte = 0x00; /* All I/O to output mode */
159         error = i2c_smbus_xfer(&amd756_smbus, 0x18, 0, I2C_SMBUS_WRITE, 0x03,
160                                I2C_SMBUS_BYTE_DATA, &ioconfig);
161         if (error) {
162                 dev_err(&amd756_smbus.dev, "PCA9556 configuration failed\n");
163                 error = -EIO;
164                 goto ERROR0;
165         }
166
167         /* Unregister physical bus */
168         i2c_del_adapter(&amd756_smbus);
169
170         printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n");
171         /* Define the 5 virtual adapters and algorithms structures */
172         if (!(s4882_adapter = kcalloc(5, sizeof(struct i2c_adapter),
173                                       GFP_KERNEL))) {
174                 error = -ENOMEM;
175                 goto ERROR1;
176         }
177         if (!(s4882_algo = kcalloc(5, sizeof(struct i2c_algorithm),
178                                    GFP_KERNEL))) {
179                 error = -ENOMEM;
180                 goto ERROR2;
181         }
182
183         /* Fill in the new structures */
184         s4882_algo[0] = *(amd756_smbus.algo);
185         s4882_algo[0].smbus_xfer = amd756_access_virt0;
186         s4882_adapter[0] = amd756_smbus;
187         s4882_adapter[0].algo = s4882_algo;
188         s4882_adapter[0].dev.parent = amd756_smbus.dev.parent;
189         for (i = 1; i < 5; i++) {
190                 s4882_algo[i] = *(amd756_smbus.algo);
191                 s4882_adapter[i] = amd756_smbus;
192                 snprintf(s4882_adapter[i].name, sizeof(s4882_adapter[i].name),
193                          "SMBus 8111 adapter (CPU%d)", i-1);
194                 s4882_adapter[i].algo = s4882_algo+i;
195                 s4882_adapter[i].dev.parent = amd756_smbus.dev.parent;
196         }
197         s4882_algo[1].smbus_xfer = amd756_access_virt1;
198         s4882_algo[2].smbus_xfer = amd756_access_virt2;
199         s4882_algo[3].smbus_xfer = amd756_access_virt3;
200         s4882_algo[4].smbus_xfer = amd756_access_virt4;
201
202         /* Register virtual adapters */
203         for (i = 0; i < 5; i++) {
204                 error = i2c_add_adapter(s4882_adapter+i);
205                 if (error) {
206                         printk(KERN_ERR "i2c-amd756-s4882: "
207                                "Virtual adapter %d registration "
208                                "failed, module not inserted\n", i);
209                         for (i--; i >= 0; i--)
210                                 i2c_del_adapter(s4882_adapter+i);
211                         goto ERROR3;
212                 }
213         }
214
215         return 0;
216
217 ERROR3:
218         kfree(s4882_algo);
219         s4882_algo = NULL;
220 ERROR2:
221         kfree(s4882_adapter);
222         s4882_adapter = NULL;
223 ERROR1:
224         /* Restore physical bus */
225         i2c_add_adapter(&amd756_smbus);
226 ERROR0:
227         return error;
228 }
229
230 static void __exit amd756_s4882_exit(void)
231 {
232         if (s4882_adapter) {
233                 int i;
234
235                 for (i = 0; i < 5; i++)
236                         i2c_del_adapter(s4882_adapter+i);
237                 kfree(s4882_adapter);
238                 s4882_adapter = NULL;
239         }
240         kfree(s4882_algo);
241         s4882_algo = NULL;
242
243         /* Restore physical bus */
244         if (i2c_add_adapter(&amd756_smbus))
245                 printk(KERN_ERR "i2c-amd756-s4882: "
246                        "Physical bus restoration failed\n");
247 }
248
249 MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
250 MODULE_DESCRIPTION("S4882 SMBus multiplexing");
251 MODULE_LICENSE("GPL");
252
253 module_init(amd756_s4882_init);
254 module_exit(amd756_s4882_exit);