linux/drivers/i2c/busses/i2c-amd756-s4882.c
<<
>>
Prefs
   1/*
   2 * i2c-amd756-s4882.c - i2c-amd756 extras for the Tyan S4882 motherboard
   3 *
   4 * Copyright (C) 2004, 2008 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 <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/* Wrapper access functions for multiplexed SMBus */
  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        /* We exclude the multiplexed addresses */
  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/* We remember the last used channels combination so as to only switch
  74   channels when it is really needed. This greatly reduces the SMBus
  75   overhead, but also assumes that nobody will be writing to the PCA9556
  76   in our back. */
  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        /* We exclude the non-multiplexed addresses */
  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        /* CPU0: channels 1 and 0 enabled */
 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        /* CPU1: channels 2 and 5 enabled */
 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        /* CPU2: channels 3 and 6 enabled */
 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        /* CPU3: channels 4 and 7 enabled */
 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        /* Configure the PCA9556 multiplexer */
 162        ioconfig.byte = 0x00; /* All I/O to output mode */
 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        /* Unregister physical bus */
 172        i2c_del_adapter(&amd756_smbus);
 173
 174        printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n");
 175        /* Define the 5 virtual adapters and algorithms structures */
 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        /* Fill in the new structures */
 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        /* Register virtual adapters */
 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        /* Restore physical bus */
 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        /* Restore physical bus */
 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