Path: blob/master/drivers/i2c/busses/i2c-nforce2-s4985.c
15111 views
/*1* i2c-nforce2-s4985.c - i2c-nforce2 extras for the Tyan S4985 motherboard2*3* Copyright (C) 2008 Jean Delvare <[email protected]>4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation; either version 2 of the License, or8* (at your option) any later version.9*10* This program is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License16* along with this program; if not, write to the Free Software17* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.18*/1920/*21* We select the channels by sending commands to the Philips22* PCA9556 chip at I2C address 0x18. The main adapter is used for23* the non-multiplexed part of the bus, and 4 virtual adapters24* are defined for the multiplexed addresses: 0x50-0x53 (memory25* module EEPROM) located on channels 1-4. We define one virtual26* adapter per CPU, which corresponds to one multiplexed channel:27* CPU0: virtual adapter 1, channel 128* CPU1: virtual adapter 2, channel 229* CPU2: virtual adapter 3, channel 330* CPU3: virtual adapter 4, channel 431*/3233#include <linux/module.h>34#include <linux/kernel.h>35#include <linux/slab.h>36#include <linux/init.h>37#include <linux/i2c.h>38#include <linux/mutex.h>3940extern struct i2c_adapter *nforce2_smbus;4142static struct i2c_adapter *s4985_adapter;43static struct i2c_algorithm *s4985_algo;4445/* Wrapper access functions for multiplexed SMBus */46static DEFINE_MUTEX(nforce2_lock);4748static s32 nforce2_access_virt0(struct i2c_adapter *adap, u16 addr,49unsigned short flags, char read_write,50u8 command, int size,51union i2c_smbus_data *data)52{53int error;5455/* We exclude the multiplexed addresses */56if ((addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x3057|| addr == 0x18)58return -ENXIO;5960mutex_lock(&nforce2_lock);61error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,62command, size, data);63mutex_unlock(&nforce2_lock);6465return error;66}6768/* We remember the last used channels combination so as to only switch69channels when it is really needed. This greatly reduces the SMBus70overhead, but also assumes that nobody will be writing to the PCA955671in our back. */72static u8 last_channels;7374static inline s32 nforce2_access_channel(struct i2c_adapter *adap, u16 addr,75unsigned short flags, char read_write,76u8 command, int size,77union i2c_smbus_data *data,78u8 channels)79{80int error;8182/* We exclude the non-multiplexed addresses */83if ((addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)84return -ENXIO;8586mutex_lock(&nforce2_lock);87if (last_channels != channels) {88union i2c_smbus_data mplxdata;89mplxdata.byte = channels;9091error = nforce2_smbus->algo->smbus_xfer(adap, 0x18, 0,92I2C_SMBUS_WRITE, 0x01,93I2C_SMBUS_BYTE_DATA,94&mplxdata);95if (error)96goto UNLOCK;97last_channels = channels;98}99error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,100command, size, data);101102UNLOCK:103mutex_unlock(&nforce2_lock);104return error;105}106107static s32 nforce2_access_virt1(struct i2c_adapter *adap, u16 addr,108unsigned short flags, char read_write,109u8 command, int size,110union i2c_smbus_data *data)111{112/* CPU0: channel 1 enabled */113return nforce2_access_channel(adap, addr, flags, read_write, command,114size, data, 0x02);115}116117static s32 nforce2_access_virt2(struct i2c_adapter *adap, u16 addr,118unsigned short flags, char read_write,119u8 command, int size,120union i2c_smbus_data *data)121{122/* CPU1: channel 2 enabled */123return nforce2_access_channel(adap, addr, flags, read_write, command,124size, data, 0x04);125}126127static s32 nforce2_access_virt3(struct i2c_adapter *adap, u16 addr,128unsigned short flags, char read_write,129u8 command, int size,130union i2c_smbus_data *data)131{132/* CPU2: channel 3 enabled */133return nforce2_access_channel(adap, addr, flags, read_write, command,134size, data, 0x08);135}136137static s32 nforce2_access_virt4(struct i2c_adapter *adap, u16 addr,138unsigned short flags, char read_write,139u8 command, int size,140union i2c_smbus_data *data)141{142/* CPU3: channel 4 enabled */143return nforce2_access_channel(adap, addr, flags, read_write, command,144size, data, 0x10);145}146147static int __init nforce2_s4985_init(void)148{149int i, error;150union i2c_smbus_data ioconfig;151152if (!nforce2_smbus)153return -ENODEV;154155/* Configure the PCA9556 multiplexer */156ioconfig.byte = 0x00; /* All I/O to output mode */157error = i2c_smbus_xfer(nforce2_smbus, 0x18, 0, I2C_SMBUS_WRITE, 0x03,158I2C_SMBUS_BYTE_DATA, &ioconfig);159if (error) {160dev_err(&nforce2_smbus->dev, "PCA9556 configuration failed\n");161error = -EIO;162goto ERROR0;163}164165/* Unregister physical bus */166error = i2c_del_adapter(nforce2_smbus);167if (error) {168dev_err(&nforce2_smbus->dev, "Physical bus removal failed\n");169goto ERROR0;170}171172printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4985\n");173/* Define the 5 virtual adapters and algorithms structures */174s4985_adapter = kzalloc(5 * sizeof(struct i2c_adapter), GFP_KERNEL);175if (!s4985_adapter) {176error = -ENOMEM;177goto ERROR1;178}179s4985_algo = kzalloc(5 * sizeof(struct i2c_algorithm), GFP_KERNEL);180if (!s4985_algo) {181error = -ENOMEM;182goto ERROR2;183}184185/* Fill in the new structures */186s4985_algo[0] = *(nforce2_smbus->algo);187s4985_algo[0].smbus_xfer = nforce2_access_virt0;188s4985_adapter[0] = *nforce2_smbus;189s4985_adapter[0].algo = s4985_algo;190s4985_adapter[0].dev.parent = nforce2_smbus->dev.parent;191for (i = 1; i < 5; i++) {192s4985_algo[i] = *(nforce2_smbus->algo);193s4985_adapter[i] = *nforce2_smbus;194snprintf(s4985_adapter[i].name, sizeof(s4985_adapter[i].name),195"SMBus nForce2 adapter (CPU%d)", i - 1);196s4985_adapter[i].algo = s4985_algo + i;197s4985_adapter[i].dev.parent = nforce2_smbus->dev.parent;198}199s4985_algo[1].smbus_xfer = nforce2_access_virt1;200s4985_algo[2].smbus_xfer = nforce2_access_virt2;201s4985_algo[3].smbus_xfer = nforce2_access_virt3;202s4985_algo[4].smbus_xfer = nforce2_access_virt4;203204/* Register virtual adapters */205for (i = 0; i < 5; i++) {206error = i2c_add_adapter(s4985_adapter + i);207if (error) {208printk(KERN_ERR "i2c-nforce2-s4985: "209"Virtual adapter %d registration "210"failed, module not inserted\n", i);211for (i--; i >= 0; i--)212i2c_del_adapter(s4985_adapter + i);213goto ERROR3;214}215}216217return 0;218219ERROR3:220kfree(s4985_algo);221s4985_algo = NULL;222ERROR2:223kfree(s4985_adapter);224s4985_adapter = NULL;225ERROR1:226/* Restore physical bus */227i2c_add_adapter(nforce2_smbus);228ERROR0:229return error;230}231232static void __exit nforce2_s4985_exit(void)233{234if (s4985_adapter) {235int i;236237for (i = 0; i < 5; i++)238i2c_del_adapter(s4985_adapter+i);239kfree(s4985_adapter);240s4985_adapter = NULL;241}242kfree(s4985_algo);243s4985_algo = NULL;244245/* Restore physical bus */246if (i2c_add_adapter(nforce2_smbus))247printk(KERN_ERR "i2c-nforce2-s4985: "248"Physical bus restoration failed\n");249}250251MODULE_AUTHOR("Jean Delvare <[email protected]>");252MODULE_DESCRIPTION("S4985 SMBus multiplexing");253MODULE_LICENSE("GPL");254255module_init(nforce2_s4985_init);256module_exit(nforce2_s4985_exit);257258259