root/lm-sensors/trunk/kernel/busses/i2c-amd756-s4882.c @ 3251

Revision 3251, 7.4 KB (checked in by khali, 9 years ago)

Add missing include for struct semaphore.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * i2c-amd756-s4882.c - i2c-amd756 extras for the Tyan S4882 motherboard
3 *
4 * Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
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 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21/*
22 * We select the channels by sending commands to the Philips
23 * PCA9556 chip at I2C address 0x18. The main adapter is used for
24 * the non-multiplexed part of the bus, and 4 virtual adapters
25 * are defined for the multiplexed addresses: 0x50-0x53 (memory
26 * module EEPROM) located on channels 1-4, and 0x4c (LM63)
27 * located on multiplexed channels 0 and 5-7. We define one
28 * virtual adapter per CPU, which corresponds to two multiplexed
29 * channels:
30 *   CPU0: virtual adapter 1, channels 1 and 0
31 *   CPU1: virtual adapter 2, channels 2 and 5
32 *   CPU2: virtual adapter 3, channels 3 and 6
33 *   CPU3: virtual adapter 4, channels 4 and 7
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 <asm/semaphore.h>
42
43#define DRV_NAME        "i2c-amd756-s4882"
44
45extern struct i2c_adapter amd756_smbus;
46
47static struct i2c_adapter *s4882_adapter;
48static struct i2c_algorithm *s4882_algo;
49
50/* Wrapper access functions for multiplexed SMBus */
51static struct semaphore amd756_lock;
52
53static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr,
54                               unsigned short flags, char read_write,
55                               u8 command, int size,
56                               union i2c_smbus_data * data)
57{
58        int error;
59
60        /* We exclude the multiplexed addresses */
61        if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
62         || addr == 0x18)
63                return -1;
64
65        down(&amd756_lock);
66
67        error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
68                                              command, size, data);
69
70        up(&amd756_lock);
71
72        return error;
73}
74
75/* We remember the last used channels combination so as to only switch
76   channels when it is really needed. This greatly reduces the SMBus
77   overhead, but also assumes that nobody will be writing to the PCA9556
78   in our back. */
79static u8 last_channels;
80
81static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr,
82                                        unsigned short flags, char read_write,
83                                        u8 command, int size,
84                                        union i2c_smbus_data * data,
85                                        u8 channels)
86{
87        int error;
88
89        /* We exclude the non-multiplexed addresses */
90        if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
91                return -1;
92
93        down(&amd756_lock);
94
95        if (last_channels != channels) {
96                union i2c_smbus_data mplxdata;
97                mplxdata.byte = channels;
98
99                error = amd756_smbus.algo->smbus_xfer(adap, 0x18, 0,
100                                                      I2C_SMBUS_WRITE, 0x01,
101                                                      I2C_SMBUS_BYTE_DATA,
102                                                      &mplxdata);
103                if (error)
104                        goto UNLOCK;
105                last_channels = channels;
106        }
107        error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
108                                              command, size, data);
109
110UNLOCK:
111        up(&amd756_lock);
112        return error;
113}
114
115static s32 amd756_access_virt1(struct i2c_adapter * adap, u16 addr,
116                               unsigned short flags, char read_write,
117                               u8 command, int size,
118                               union i2c_smbus_data * data)
119{
120        /* CPU0: channels 1 and 0 enabled */
121        return amd756_access_channel(adap, addr, flags, read_write, command,
122                                     size, data, 0x03);
123}
124
125static s32 amd756_access_virt2(struct i2c_adapter * adap, u16 addr,
126                               unsigned short flags, char read_write,
127                               u8 command, int size,
128                               union i2c_smbus_data * data)
129{
130        /* CPU1: channels 2 and 5 enabled */
131        return amd756_access_channel(adap, addr, flags, read_write, command,
132                                     size, data, 0x24);
133}
134
135static s32 amd756_access_virt3(struct i2c_adapter * adap, u16 addr,
136                               unsigned short flags, char read_write,
137                               u8 command, int size,
138                               union i2c_smbus_data * data)
139{
140        /* CPU2: channels 3 and 6 enabled */
141        return amd756_access_channel(adap, addr, flags, read_write, command,
142                                     size, data, 0x48);
143}
144
145static s32 amd756_access_virt4(struct i2c_adapter * adap, u16 addr,
146                               unsigned short flags, char read_write,
147                               u8 command, int size,
148                               union i2c_smbus_data * data)
149{
150        /* CPU3: channels 4 and 7 enabled */
151        return amd756_access_channel(adap, addr, flags, read_write, command,
152                                     size, data, 0x90);
153}
154
155static int __init amd756_s4882_init(void)
156{
157        int i, error;
158        union i2c_smbus_data ioconfig;
159
160        /* Unregister physical bus */
161        error = i2c_del_adapter(&amd756_smbus);
162        if (error) {
163                if (error != -ENODEV)
164                        printk(KERN_ERR DRV_NAME ": Physical bus removal "
165                               "failed\n");
166                goto ERROR0;
167        }
168
169        printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n");
170        init_MUTEX(&amd756_lock);
171
172        /* Define the 5 virtual adapters and algorithms structures */
173        if (!(s4882_adapter = kmalloc(5 * sizeof(struct i2c_adapter),
174                                      GFP_KERNEL))) {
175                error = -ENOMEM;
176                goto ERROR1;
177        }
178        if (!(s4882_algo = kmalloc(5 * sizeof(struct i2c_algorithm),
179                                   GFP_KERNEL))) {
180                error = -ENOMEM;
181                goto ERROR2;
182        }
183
184        /* Fill in the new structures */
185        s4882_algo[0] = *(amd756_smbus.algo);
186        s4882_algo[0].smbus_xfer = amd756_access_virt0;
187        s4882_adapter[0] = amd756_smbus;
188        s4882_adapter[0].algo = s4882_algo;
189        for (i = 1; i < 5; i++) {
190                s4882_algo[i] = *(amd756_smbus.algo);
191                s4882_adapter[i] = amd756_smbus;
192                sprintf(s4882_adapter[i].name,
193                        "SMBus 8111 adapter (CPU%d)", i-1);
194                s4882_adapter[i].algo = s4882_algo+i;
195        }
196        s4882_algo[1].smbus_xfer = amd756_access_virt1;
197        s4882_algo[2].smbus_xfer = amd756_access_virt2;
198        s4882_algo[3].smbus_xfer = amd756_access_virt3;
199        s4882_algo[4].smbus_xfer = amd756_access_virt4;
200
201        /* Configure the PCA9556 multiplexer */
202        ioconfig.byte = 0x00; /* All I/O to output mode */
203        error = amd756_smbus.algo->smbus_xfer(&amd756_smbus, 0x18, 0,
204                                              I2C_SMBUS_WRITE, 0x03,
205                                              I2C_SMBUS_BYTE_DATA, &ioconfig);
206        if (error) {
207                printk(KERN_ERR DRV_NAME ": PCA9556 configuration failed\n");
208                error = -EIO;
209                goto ERROR3;
210        }
211
212        /* Register virtual adapters */
213        for (i = 0; i < 5; i++) {
214                error = i2c_add_adapter(s4882_adapter+i);
215                if (error) {
216                        printk(KERN_ERR DRV_NAME
217                               ": Virtual adapter %d registration "
218                               "failed, module not inserted\n", i);
219                        for (i--; i >= 0; i--)
220                                i2c_del_adapter(s4882_adapter+i);
221                        goto ERROR3;
222                }
223        }
224
225        return 0;
226
227ERROR3:
228        kfree(s4882_algo);
229        s4882_algo = NULL;
230ERROR2:
231        kfree(s4882_adapter);
232        s4882_adapter = NULL;
233ERROR1:
234        i2c_del_adapter(&amd756_smbus);
235ERROR0:
236        return error;
237}
238
239static void __exit amd756_s4882_exit(void)
240{
241        if (s4882_adapter) {
242                int i;
243
244                for (i = 0; i < 5; i++)
245                        i2c_del_adapter(s4882_adapter+i);
246                kfree(s4882_adapter);
247                s4882_adapter = NULL;
248        }
249        if (s4882_algo) {
250                kfree(s4882_algo);
251                s4882_algo = NULL;
252        }
253
254        /* Restore physical bus */
255        if (i2c_add_adapter(&amd756_smbus))
256                printk(KERN_ERR DRV_NAME ": Physical bus restoration "
257                       "failed\n");
258}
259
260MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
261MODULE_DESCRIPTION("S4882 SMBus multiplexing");
262MODULE_LICENSE("GPL");
263
264module_init(amd756_s4882_init);
265module_exit(amd756_s4882_exit);
Note: See TracBrowser for help on using the browser.