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 <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
  39extern struct i2c_adapter amd756_smbus;
  40
  41static struct i2c_adapter *s4882_adapter;
  42static struct i2c_algorithm *s4882_algo;
  43
  44/* Wrapper access functions for multiplexed SMBus */
  45static DEFINE_MUTEX(amd756_lock);
  46
  47static 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. */
  73static u8 last_channels;
  74
  75static 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
 104UNLOCK:
 105        mutex_unlock(&amd756_lock);
 106        return error;
 107}
 108
 109static 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
 119static 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
 129static 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
 139static 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
 149static 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 = kzalloc(5 * sizeof(struct i2c_adapter),
 173                                      GFP_KERNEL))) {
 174                error = -ENOMEM;
 175                goto ERROR1;
 176        }
 177        if (!(s4882_algo = kzalloc(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
 217ERROR3:
 218        kfree(s4882_algo);
 219        s4882_algo = NULL;
 220ERROR2:
 221        kfree(s4882_adapter);
 222        s4882_adapter = NULL;
 223ERROR1:
 224        /* Restore physical bus */
 225        i2c_add_adapter(&amd756_smbus);
 226ERROR0:
 227        return error;
 228}
 229
 230static 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
 249MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
 250MODULE_DESCRIPTION("S4882 SMBus multiplexing");
 251MODULE_LICENSE("GPL");
 252
 253module_init(amd756_s4882_init);
 254module_exit(amd756_s4882_exit);
 255