linux/drivers/i2c/busses/i2c-nforce2-s4985.c
<<
>>
Prefs
   1/*
   2 * i2c-nforce2-s4985.c - i2c-nforce2 extras for the Tyan S4985 motherboard
   3 *
   4 * Copyright (C) 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. We define one virtual
  27 * adapter per CPU, which corresponds to one multiplexed channel:
  28 *   CPU0: virtual adapter 1, channel 1
  29 *   CPU1: virtual adapter 2, channel 2
  30 *   CPU2: virtual adapter 3, channel 3
  31 *   CPU3: virtual adapter 4, channel 4
  32 */
  33
  34#include <linux/module.h>
  35#include <linux/kernel.h>
  36#include <linux/slab.h>
  37#include <linux/init.h>
  38#include <linux/i2c.h>
  39#include <linux/mutex.h>
  40
  41extern struct i2c_adapter *nforce2_smbus;
  42
  43static struct i2c_adapter *s4985_adapter;
  44static struct i2c_algorithm *s4985_algo;
  45
  46/* Wrapper access functions for multiplexed SMBus */
  47static DEFINE_MUTEX(nforce2_lock);
  48
  49static s32 nforce2_access_virt0(struct i2c_adapter *adap, u16 addr,
  50                                unsigned short flags, char read_write,
  51                                u8 command, int size,
  52                                union i2c_smbus_data *data)
  53{
  54        int error;
  55
  56        /* We exclude the multiplexed addresses */
  57        if ((addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
  58         || addr == 0x18)
  59                return -ENXIO;
  60
  61        mutex_lock(&nforce2_lock);
  62        error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,
  63                                                command, size, data);
  64        mutex_unlock(&nforce2_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. */
  73static u8 last_channels;
  74
  75static inline s32 nforce2_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 & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
  85                return -ENXIO;
  86
  87        mutex_lock(&nforce2_lock);
  88        if (last_channels != channels) {
  89                union i2c_smbus_data mplxdata;
  90                mplxdata.byte = channels;
  91
  92                error = nforce2_smbus->algo->smbus_xfer(adap, 0x18, 0,
  93                                                        I2C_SMBUS_WRITE, 0x01,
  94                                                        I2C_SMBUS_BYTE_DATA,
  95                                                        &mplxdata);
  96                if (error)
  97                        goto UNLOCK;
  98                last_channels = channels;
  99        }
 100        error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,
 101                                                command, size, data);
 102
 103UNLOCK:
 104        mutex_unlock(&nforce2_lock);
 105        return error;
 106}
 107
 108static s32 nforce2_access_virt1(struct i2c_adapter *adap, u16 addr,
 109                                unsigned short flags, char read_write,
 110                                u8 command, int size,
 111                                union i2c_smbus_data *data)
 112{
 113        /* CPU0: channel 1 enabled */
 114        return nforce2_access_channel(adap, addr, flags, read_write, command,
 115                                      size, data, 0x02);
 116}
 117
 118static s32 nforce2_access_virt2(struct i2c_adapter *adap, u16 addr,
 119                                unsigned short flags, char read_write,
 120                                u8 command, int size,
 121                                union i2c_smbus_data *data)
 122{
 123        /* CPU1: channel 2 enabled */
 124        return nforce2_access_channel(adap, addr, flags, read_write, command,
 125                                      size, data, 0x04);
 126}
 127
 128static s32 nforce2_access_virt3(struct i2c_adapter *adap, u16 addr,
 129                                unsigned short flags, char read_write,
 130                                u8 command, int size,
 131                                union i2c_smbus_data *data)
 132{
 133        /* CPU2: channel 3 enabled */
 134        return nforce2_access_channel(adap, addr, flags, read_write, command,
 135                                      size, data, 0x08);
 136}
 137
 138static s32 nforce2_access_virt4(struct i2c_adapter *adap, u16 addr,
 139                                unsigned short flags, char read_write,
 140                                u8 command, int size,
 141                                union i2c_smbus_data *data)
 142{
 143        /* CPU3: channel 4 enabled */
 144        return nforce2_access_channel(adap, addr, flags, read_write, command,
 145                                      size, data, 0x10);
 146}
 147
 148static int __init nforce2_s4985_init(void)
 149{
 150        int i, error;
 151        union i2c_smbus_data ioconfig;
 152
 153        if (!nforce2_smbus)
 154                return -ENODEV;
 155
 156        /* Configure the PCA9556 multiplexer */
 157        ioconfig.byte = 0x00; /* All I/O to output mode */
 158        error = i2c_smbus_xfer(nforce2_smbus, 0x18, 0, I2C_SMBUS_WRITE, 0x03,
 159                               I2C_SMBUS_BYTE_DATA, &ioconfig);
 160        if (error) {
 161                dev_err(&nforce2_smbus->dev, "PCA9556 configuration failed\n");
 162                error = -EIO;
 163                goto ERROR0;
 164        }
 165
 166        /* Unregister physical bus */
 167        error = i2c_del_adapter(nforce2_smbus);
 168        if (error) {
 169                dev_err(&nforce2_smbus->dev, "Physical bus removal failed\n");
 170                goto ERROR0;
 171        }
 172
 173        printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4985\n");
 174        /* Define the 5 virtual adapters and algorithms structures */
 175        s4985_adapter = kzalloc(5 * sizeof(struct i2c_adapter), GFP_KERNEL);
 176        if (!s4985_adapter) {
 177                error = -ENOMEM;
 178                goto ERROR1;
 179        }
 180        s4985_algo = kzalloc(5 * sizeof(struct i2c_algorithm), GFP_KERNEL);
 181        if (!s4985_algo) {
 182                error = -ENOMEM;
 183                goto ERROR2;
 184        }
 185
 186        /* Fill in the new structures */
 187        s4985_algo[0] = *(nforce2_smbus->algo);
 188        s4985_algo[0].smbus_xfer = nforce2_access_virt0;
 189        s4985_adapter[0] = *nforce2_smbus;
 190        s4985_adapter[0].algo = s4985_algo;
 191        s4985_adapter[0].dev.parent = nforce2_smbus->dev.parent;
 192        for (i = 1; i < 5; i++) {
 193                s4985_algo[i] = *(nforce2_smbus->algo);
 194                s4985_adapter[i] = *nforce2_smbus;
 195                snprintf(s4985_adapter[i].name, sizeof(s4985_adapter[i].name),
 196                         "SMBus nForce2 adapter (CPU%d)", i - 1);
 197                s4985_adapter[i].algo = s4985_algo + i;
 198                s4985_adapter[i].dev.parent = nforce2_smbus->dev.parent;
 199        }
 200        s4985_algo[1].smbus_xfer = nforce2_access_virt1;
 201        s4985_algo[2].smbus_xfer = nforce2_access_virt2;
 202        s4985_algo[3].smbus_xfer = nforce2_access_virt3;
 203        s4985_algo[4].smbus_xfer = nforce2_access_virt4;
 204
 205        /* Register virtual adapters */
 206        for (i = 0; i < 5; i++) {
 207                error = i2c_add_adapter(s4985_adapter + i);
 208                if (error) {
 209                        printk(KERN_ERR "i2c-nforce2-s4985: "
 210                               "Virtual adapter %d registration "
 211                               "failed, module not inserted\n", i);
 212                        for (i--; i >= 0; i--)
 213                                i2c_del_adapter(s4985_adapter + i);
 214                        goto ERROR3;
 215                }
 216        }
 217
 218        return 0;
 219
 220ERROR3:
 221        kfree(s4985_algo);
 222        s4985_algo = NULL;
 223ERROR2:
 224        kfree(s4985_adapter);
 225        s4985_adapter = NULL;
 226ERROR1:
 227        /* Restore physical bus */
 228        i2c_add_adapter(nforce2_smbus);
 229ERROR0:
 230        return error;
 231}
 232
 233static void __exit nforce2_s4985_exit(void)
 234{
 235        if (s4985_adapter) {
 236                int i;
 237
 238                for (i = 0; i < 5; i++)
 239                        i2c_del_adapter(s4985_adapter+i);
 240                kfree(s4985_adapter);
 241                s4985_adapter = NULL;
 242        }
 243        kfree(s4985_algo);
 244        s4985_algo = NULL;
 245
 246        /* Restore physical bus */
 247        if (i2c_add_adapter(nforce2_smbus))
 248                printk(KERN_ERR "i2c-nforce2-s4985: "
 249                       "Physical bus restoration failed\n");
 250}
 251
 252MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
 253MODULE_DESCRIPTION("S4985 SMBus multiplexing");
 254MODULE_LICENSE("GPL");
 255
 256module_init(nforce2_s4985_init);
 257module_exit(nforce2_s4985_exit);
 258