More on SMBus multiplexing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I disagree.
I think the original proposal, dating back several months, is still valid.
The proposal, briefly, elaborates on the 'i2c-virtual' work done previously
which recognizes the PCA955x chip and generates the additional i2c busses,
with locking. This requires an adapter/algorithm pair of drivers and changes
in i2c-core for the locking. I'm sure you already have the email threads
where we've discussed previously.
Your proposal looks less general, more board-specific.
Still catching up, been out of town for a while...
I'll continue to review your emails and code from the past week,
perhaps we can disuss on IRC this weekend.
mds

Jean Delvare wrote:
> Hi Greg,
> 
> Would you accept such a patch? This adds Tyan S4882-specific SMBus
> multiplexing support, in the form of a separate driver, as per Mark
> Hoffman's suggestion.
> 
> Changes to the original driver (i2c-amd756) are kept to a minimum:
> 
> 1* The i2c_adapter structure is exported (some noise due to a struct
> name change, I didn't want to export something as vague as
> "amd756_adapter").
> 
> 2* Autoload of the new driver (i2c-amd756-s4882) when needed. This can
> go away if you don't like it, but I thought it'd be more convenient for
> the end user. BTW, what will happen if the i2c-amd756-s4882 module
> doesn't exist? Should I add some #ifdef magic in the i2c-amd756 driver
> so that the autoload will not happen if the i2c-amd756-s4882 module was
> not selected?
> 
> The i2c-amd756-s4882 driver follows Mark Hoffman's suggestion, but in a
> simplified way. On load:
> 
> 1* Remove the physical adapter from the main i2c adapters list. As a
> side effect, any client gets kicked off.
> 
> 2* Register 5 virtual busses using to wrappers pointing to the original
> SMBus code. Clients can connect to the new adapters where it makes
> sense.
> 
> On unload, same thing but the other way around.
> 
> This approach has a drawback in the case several client drivers are
> already loaded (they will get attached, detached and attached again in a
> raw), but I don't think this is really a problem:
> 
> 1* In most cases bus drivers are loaded before clients anyway.
> 
> 2* SMBus multiplexing is quite rare, and I don't care if people with
> such specific boards experience drawbacks at init time in uncommon
> cases.
> 
> Advantages of the approach, however, are great. Multiplexing code
> belongs to a different driver, which improves maintainability as Mark
> pointed out, and means no impact on module size nor performance for
> regular users. The whole thing is also almost transparent for userspace
> and client drivers, requiring very very little change, if any at all.
> 
> Greg, anyone, comments on the approach and code are welcome. If
> everyone's OK, we'll go that way for both the 2.6 kernel and lm_sensors
> (for 2.4).
> 
> Thanks.
> 
> diff -ruN linux-2.6.9-rc4-bk1/drivers/i2c/busses.orig/Kconfig linux-2.6.9-rc4-bk1/drivers/i2c/busses/Kconfig
> --- linux-2.6.9-rc4-bk1/drivers/i2c/busses.orig/Kconfig	2004-10-16 19:37:33.000000000 +0200
> +++ linux-2.6.9-rc4-bk1/drivers/i2c/busses/Kconfig	2004-10-24 18:46:53.000000000 +0200
> @@ -51,6 +51,17 @@
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called i2c-amd756.
>  
> +config I2C_AMD756_S4882
> +	tristate "SMBus multiplexing on the Tyan S4882"
> +	depends on I2C_AMD756 && EXPERIMENTAL
> +	default y
> +	help
> +	  Enabling this option will add specific SMBus support for the Tyan
> +	  S4882 motherboard.  On this 4-CPU board, the SMBus is multiplexed
> +	  over 8 different channels, where the various memory module EEPROMs
> +	  and temperature sensors live.  Saying yes here will give you access
> +	  to these in addition to the trunk.
> +
>  config I2C_AMD8111
>  	tristate "AMD 8111"
>  	depends on I2C && PCI && EXPERIMENTAL
> diff -ruN linux-2.6.9-rc4-bk1/drivers/i2c/busses.orig/Makefile linux-2.6.9-rc4-bk1/drivers/i2c/busses/Makefile
> --- linux-2.6.9-rc4-bk1/drivers/i2c/busses.orig/Makefile	2004-10-12 19:44:25.000000000 +0200
> +++ linux-2.6.9-rc4-bk1/drivers/i2c/busses/Makefile	2004-10-23 22:55:45.000000000 +0200
> @@ -6,6 +6,7 @@
>  obj-$(CONFIG_I2C_ALI1563)	+= i2c-ali1563.o
>  obj-$(CONFIG_I2C_ALI15X3)	+= i2c-ali15x3.o
>  obj-$(CONFIG_I2C_AMD756)	+= i2c-amd756.o
> +obj-$(CONFIG_I2C_AMD756_S4882)	+= i2c-amd756-s4882.o
>  obj-$(CONFIG_I2C_AMD8111)	+= i2c-amd8111.o
>  obj-$(CONFIG_I2C_ELEKTOR)	+= i2c-elektor.o
>  obj-$(CONFIG_I2C_HYDRA)		+= i2c-hydra.o
> diff -ruN linux-2.6.9-rc4-bk1/drivers/i2c/busses.orig/i2c-amd756-s4882.c linux-2.6.9-rc4-bk1/drivers/i2c/busses/i2c-amd756-s4882.c
> --- linux-2.6.9-rc4-bk1/drivers/i2c/busses.orig/i2c-amd756-s4882.c	1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.9-rc4-bk1/drivers/i2c/busses/i2c-amd756-s4882.c	2004-10-24 18:42:12.000000000 +0200
> @@ -0,0 +1,266 @@
> +/*
> + * i2c-amd756-s4882.c - i2c-amd756 extras for the Tyan S4882 motherboard
> + *
> + * Copyright (C) 2004 Jean Delvare <khali at linux-fr.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> + 
> +/*
> + * We select the channels by sending commands to the Philips
> + * PCA9556 chip at I2C address 0x18. The main adapter is used for
> + * the non-multiplexed part of the bus, and 4 virtual adapters
> + * are defined for the multiplexed addresses: 0x50-0x53 (memory
> + * module EEPROM) located on channels 1-4, and 0x4c (LM63)
> + * located on multiplexed channels 0 and 5-7. We define one
> + * virtual adapter per CPU, which corresponds to two multiplexed
> + * channels:
> + *   CPU0: virtual adapter 1, channels 1 and 0
> + *   CPU1: virtual adapter 2, channels 2 and 5
> + *   CPU2: virtual adapter 3, channels 3 and 6
> + *   CPU3: virtual adapter 4, channels 4 and 7
> + */
> +
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/i2c.h>
> +
> +extern struct i2c_adapter amd756_smbus;
> +
> +static struct i2c_adapter *s4882_adapter;
> +static struct i2c_algorithm *s4882_algo;
> +
> +/* Wrapper access functions for multiplexed SMBus */
> +static struct semaphore amd756_lock;
> +
> +static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr,
> +			       unsigned short flags, char read_write,
> +			       u8 command, int size,
> +			       union i2c_smbus_data * data)
> +{
> +	int error;
> +
> +	/* We exclude the multiplexed addresses */
> +	if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
> +	 || addr == 0x18)
> +		return -1;
> +
> +	down(&amd756_lock);
> +
> +	error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
> +					      command, size, data);
> +
> +	up(&amd756_lock);
> +
> +	return error;
> +}
> +
> +/* We remember the last used channels combination so as to only switch
> +   channels when it is really needed. This greatly reduces the SMBus
> +   overhead, but also assumes that nobody will be writing to the PCA9556
> +   in our back. */
> +static u8 last_channels;
> +
> +static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr,
> +					unsigned short flags, char read_write,
> +					u8 command, int size,
> +					union i2c_smbus_data * data,
> +					u8 channels)
> +{
> +	int error;
> +
> +	/* We exclude the non-multiplexed addresses */
> +	if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
> +		return -1;
> +
> +	down(&amd756_lock);
> +
> +	if (last_channels != channels) {
> +		union i2c_smbus_data mplxdata;
> +		mplxdata.byte = channels;
> +
> +		error = amd756_smbus.algo->smbus_xfer(adap, 0x18, 0,
> +						      I2C_SMBUS_WRITE, 0x01,
> +						      I2C_SMBUS_BYTE_DATA,
> +						      &mplxdata);
> +		if (error)
> +			goto UNLOCK;
> +		last_channels = channels;
> +	}
> +	error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
> +					      command, size, data);
> +
> +UNLOCK:
> +	up(&amd756_lock);
> +	return error;
> +}
> +
> +static s32 amd756_access_virt1(struct i2c_adapter * adap, u16 addr,
> +			       unsigned short flags, char read_write,
> +			       u8 command, int size,
> +			       union i2c_smbus_data * data)
> +{
> +	/* CPU0: channels 1 and 0 enabled */
> +	return amd756_access_channel(adap, addr, flags, read_write, command,
> +				     size, data, 0x03);
> +}
> +
> +static s32 amd756_access_virt2(struct i2c_adapter * adap, u16 addr,
> +			       unsigned short flags, char read_write,
> +			       u8 command, int size,
> +			       union i2c_smbus_data * data)
> +{
> +	/* CPU1: channels 2 and 5 enabled */
> +	return amd756_access_channel(adap, addr, flags, read_write, command,
> +				     size, data, 0x24);
> +}
> +
> +static s32 amd756_access_virt3(struct i2c_adapter * adap, u16 addr,
> +			       unsigned short flags, char read_write,
> +			       u8 command, int size,
> +			       union i2c_smbus_data * data)
> +{
> +	/* CPU2: channels 3 and 6 enabled */
> +	return amd756_access_channel(adap, addr, flags, read_write, command,
> +				     size, data, 0x48);
> +}
> +
> +static s32 amd756_access_virt4(struct i2c_adapter * adap, u16 addr,
> +			       unsigned short flags, char read_write,
> +			       u8 command, int size,
> +			       union i2c_smbus_data * data)
> +{
> +	/* CPU3: channels 4 and 7 enabled */
> +	return amd756_access_channel(adap, addr, flags, read_write, command,
> +				     size, data, 0x90);
> +}
> +
> +static int __init amd756_s4882_init(void)
> +{
> +	int i, error;
> +	union i2c_smbus_data ioconfig;
> +
> +	if (pci_find_subsys(PCI_VENDOR_ID_AMD, 0x746B, PCI_VENDOR_ID_AMD,
> +			    0x36C0, NULL) == NULL)
> +		return -ENODEV;
> +
> +	printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n");
> +
> +	init_MUTEX(&amd756_lock);
> +
> +	/* Unregister physical bus */
> +	error = i2c_del_adapter(&amd756_smbus);
> +	if (error) {
> +		dev_err(&amd756_smbus.dev, "Physical bus removal "
> +			"failed\n");
> +		goto ERROR0;
> +	}
> +
> +	/* Define the 5 virtual adapters and algorithms structures */
> +	if (!(s4882_adapter = kmalloc(5 * sizeof(struct i2c_adapter),
> +				      GFP_KERNEL))) {
> +		error = -ENOMEM;
> +		goto ERROR1;
> +	}
> +	if (!(s4882_algo = kmalloc(5 * sizeof(struct i2c_algorithm),
> +				   GFP_KERNEL))) {
> +		error = -ENOMEM;
> +		goto ERROR2;
> +	}
> +
> +	/* Fill in the new structures */
> +	s4882_algo[0] = *(amd756_smbus.algo);
> +	s4882_algo[0].smbus_xfer = amd756_access_virt0;
> +	s4882_adapter[0] = amd756_smbus;
> +	s4882_adapter[0].algo = s4882_algo;
> +	for (i = 1; i < 5; i++) {
> +		s4882_algo[i] = *(amd756_smbus.algo);
> +		s4882_adapter[i] = amd756_smbus;
> +		sprintf(s4882_adapter[i].name,
> +			"SMBus 8111 adapter (CPU%d)", i-1);
> +		s4882_adapter[i].algo = s4882_algo+i;
> +	}
> +	s4882_algo[1].smbus_xfer = amd756_access_virt1;
> +	s4882_algo[2].smbus_xfer = amd756_access_virt2;
> +	s4882_algo[3].smbus_xfer = amd756_access_virt3;
> +	s4882_algo[4].smbus_xfer = amd756_access_virt4;
> +
> +	/* Configure the PCA9556 multiplexer */
> +	ioconfig.byte = 0x00; /* All I/O to output mode */
> +	error = amd756_smbus.algo->smbus_xfer(&amd756_smbus, 0x18, 0,
> +					      I2C_SMBUS_WRITE, 0x03,
> +					      I2C_SMBUS_BYTE_DATA, &ioconfig);
> +	if (error) {
> +		dev_dbg(&amd756_smbus.dev, "PCA9556 configuration failed\n");
> +		error = -EIO;
> +		goto ERROR3;
> +	}
> +
> +	/* Register virtual adapters */
> +	for (i = 0; i < 5; i++) {
> +		error = i2c_add_adapter(s4882_adapter+i);
> +		if (error) {
> +			dev_err(&amd756_smbus.dev,
> +			       "Virtual adapter %d registration "
> +			       "failed, module not inserted\n", i);
> +			for (i--; i >= 0; i--)
> +				i2c_del_adapter(s4882_adapter+i);
> +			goto ERROR3;
> +		}
> +	}
> +
> +	return 0;
> +
> +ERROR3:
> +	kfree(s4882_algo);
> +	s4882_algo = NULL;
> +ERROR2:
> +	kfree(s4882_adapter);
> +	s4882_adapter = NULL;
> +ERROR1:
> +	i2c_del_adapter(&amd756_smbus);
> +ERROR0:
> +	return error;
> +}
> +
> +static void __exit amd756_s4882_exit(void)
> +{
> +	if (s4882_adapter) {
> +		int i;
> +
> +		for (i = 0; i < 5; i++)
> +			i2c_del_adapter(s4882_adapter+i);
> +		kfree(s4882_adapter);
> +		s4882_adapter = NULL;
> +	}
> +	if (s4882_algo) {
> +		kfree(s4882_algo);
> +		s4882_algo = NULL;
> +	}
> +
> +	/* Restore physical bus */
> +	if (i2c_add_adapter(&amd756_smbus))
> +		dev_err(&amd756_smbus.dev, "Physical bus restoration "
> +			"failed\n");
> +}
> +
> +MODULE_AUTHOR("Jean Delvare <khali at linux-fr.org>");
> +MODULE_DESCRIPTION("S4882 SMBus multiplexing");
> +MODULE_LICENSE("GPL");
> +
> +module_init(amd756_s4882_init);
> +module_exit(amd756_s4882_exit);
> diff -ruN linux-2.6.9-rc4-bk1/drivers/i2c/busses.orig/i2c-amd756.c linux-2.6.9-rc4-bk1/drivers/i2c/busses/i2c-amd756.c
> --- linux-2.6.9-rc4-bk1/drivers/i2c/busses.orig/i2c-amd756.c	2004-10-16 21:13:45.000000000 +0200
> +++ linux-2.6.9-rc4-bk1/drivers/i2c/busses/i2c-amd756.c	2004-10-24 18:51:52.000000000 +0200
> @@ -47,6 +47,7 @@
>  #include <linux/ioport.h>
>  #include <linux/i2c.h>
>  #include <linux/init.h>
> +#include <linux/kmod.h>
>  #include <asm/io.h>
>  
>  /* AMD756 SMBus address offsets */
> @@ -302,23 +303,24 @@
>  	.functionality	= amd756_func,
>  };
>  
> -static struct i2c_adapter amd756_adapter = {
> +struct i2c_adapter amd756_smbus = {
>  	.owner		= THIS_MODULE,
>  	.class          = I2C_CLASS_HWMON,
>  	.algo		= &smbus_algorithm,
>  	.name		= "unset",
>  };
>  
> -enum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111 };
> +enum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111, S4882 };
>  static const char* chipname[] = {
>  	"AMD756", "AMD766", "AMD768",
> -	"nVidia nForce", "AMD8111",
> +	"nVidia nForce", "AMD8111", "AMD8111",
>  };
>  
>  static struct pci_device_id amd756_ids[] = {
>  	{PCI_VENDOR_ID_AMD, 0x740B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD756 },
>  	{PCI_VENDOR_ID_AMD, 0x7413, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD766 },
>  	{PCI_VENDOR_ID_AMD, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD768 },
> +	{PCI_VENDOR_ID_AMD, 0x746B, PCI_VENDOR_ID_AMD, 0x36C0, 0, 0, S4882 },
>  	{PCI_VENDOR_ID_AMD, 0x746B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD8111 },
>  	{PCI_VENDOR_ID_NVIDIA, 0x01B4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE },
>  	{ 0, }
> @@ -330,6 +332,7 @@
>  				  const struct pci_device_id *id)
>  {
>  	int nforce = (id->driver_data == NFORCE);
> +	int s4882 = (id->driver_data == S4882);
>  	int error;
>  	u8 temp;
>  	
> @@ -374,18 +377,21 @@
>  	dev_dbg(&pdev->dev, "AMD756_smba = 0x%X\n", amd756_ioport);
>  
>  	/* set up the driverfs linkage to our parent device */
> -	amd756_adapter.dev.parent = &pdev->dev;
> +	amd756_smbus.dev.parent = &pdev->dev;
>  
> -	sprintf(amd756_adapter.name, "SMBus %s adapter at %04x",
> +	sprintf(amd756_smbus.name, "SMBus %s adapter at %04x",
>  		chipname[id->driver_data], amd756_ioport);
>  
> -	error = i2c_add_adapter(&amd756_adapter);
> +	error = i2c_add_adapter(&amd756_smbus);
>  	if (error) {
>  		dev_err(&pdev->dev,
>  			"Adapter registration failed, module not inserted\n");
>  		goto out_err;
>  	}
>  
> +	if (s4882)
> +		request_module("i2c-amd756-s4882");
> +
>  	return 0;
>  
>   out_err:
> @@ -395,7 +401,7 @@
>  
>  static void __devexit amd756_remove(struct pci_dev *dev)
>  {
> -	i2c_del_adapter(&amd756_adapter);
> +	i2c_del_adapter(&amd756_smbus);
>  	release_region(amd756_ioport, SMB_IOSIZE);
>  }
>  
> @@ -420,5 +426,7 @@
>  MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver");
>  MODULE_LICENSE("GPL");
>  
> +EXPORT_SYMBOL(amd756_smbus);
> +
>  module_init(amd756_init)
>  module_exit(amd756_exit)
> 
> 




[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux