2
0
mirror of https://github.com/lm-sensors/lm-sensors synced 2025-08-30 22:05:11 +00:00

baseline checkin of virtual adapter code from Ken Harrenstien

<klh@google.com> received Sept. '04.
      No changes by me other than include file path fixups.
      Compiles, but will not insmod without i2c-core changes
      or stubs to add xxx_nolock() functions.

-----------------

Here you go.  I apologize for the fact this code is not integrated
into the lm_sensors package structure and thus will require you to
make various little updates.  But I really wanted to get this out
before I'm yanked away again, and am pretty sure you can do it much
faster than I can figure out what needs to be done!

BTW the kernel files are built against 2.4.x where x varies; 18 and 19
should work.

Feel free to clean up anything.

--Ken

On Sat, 18 Sep 2004, Mark Studebaker wrote:

>>> > On Tue, 7 Sep 2004, Mark Studebaker wrote:
>>
>>>> >>sure, send it over.
>>>> >>Sorry for delay, your mail got buried by other mail and just now spotted it.
>>>> >>mds
>>>> >>
>>>> >>Ken Harrenstien wrote:
>>>> >>
>>>
>>>>> >>>It turned out to be a while before I could get back to this, but
>>>>> >>>everything's been working for a while now and I'd like to send in the
>>>>> >>>new pca954x driver.
>>>>> >>>
>>>>> >>>I was looking at the list in the "new_drivers" file -- it *is* kind of
>>>>> >>>intimidating.  Since some of the changes affect core i2c files, I was
>>>>> >>>hoping that what I could do to start with is just send you a tar of
>>>>> >>>the modified files in their entirety, or a patch-diff of the changes
>>>>> >>>against the 2.8.7 distribution, so you can scope things out and verify
>>>>> >>>they're on the right track for acceptance.  The following files are
>>>>> >>>the ones I've changed so far:
>>>>> >>>
>>>>> >>>        New:
>>>>> >>>                Documentation/i2c/virtual_i2c
>>>>> >>>                drivers/i2c/i2c-virtual.c
>>>>> >>>                drivers/sensors/pca954x.c
>>>>> >>>                include/linux/i2c-virtual.h
>>>>> >>>        Patched:
>>>>> >>>                Documentation/Configure.help
>>>>> >>>                drivers/i2c/Makefile
>>>>> >>>                drivers/i2c/Config.in
>>>>> >>>                drivers/i2c/i2c-core.c
>>>>> >>>                drivers/sensors/Makefile
>>>>> >>>                drivers/sensors/Config.in
>>>>> >>>                include/linux/i2c-id.h
>>>>> >>>                include/linux/i2c.h
>>>>> >>>
>>>>> >>>
>>>>> >>>I plan to write some additional doc once I know that the code itself
>>>>> >>>is acceptable, and you then can tell me what other files need to be
>>>>> >>>updated.
>>>>> >>>
>>>>> >>>To answer a couple other issues you raised:
>>>>> >>>
>>>>> >>>On Thu, 24 Jun 2004, Mark Studebaker wrote:
>>>>> >>>
>>>>> >>>
>>>>> >>>
>>>>
>>>>>> >>>>(copying the mailing list, already forwarded the first mail to the list)
>>>>>> >>>>
>>>>>> >>>>First I want to ask whether you are working on kernel 2.4 or 2.6.
>>>>
>>>>> >>>
>>>>> >>>
>>>>> >>>2.4.
>>>>> >>>
>>>>> >>>
>>>>> >>>
>>>>
>>>>>> >>>>Third, you may not have realized that Khali recently did a (2.4 kernel) pca9540 driver.
>>>>>> >>>>It is in our sensors package. It however doesn't do any of the bootstrapping or
>>>>>> >>>>bus registering either. It may or may not help you.
>>>>
>>>>> >>>
>>>>> >>>
>>>>> >>>I saw it in the 2.8.7 distrib, and used parts of it as a template.
>>>>> >>>The pca954x driver will support that chip, although the external /proc
>>>>> >>>interface it presents is necessarily different.
>>>>> >>>
>>>>> >>>--Ken


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@2765 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
Mark D. Studebaker
2004-11-21 16:56:23 +00:00
parent 1c9a5c9337
commit 8f73f9beed
4 changed files with 1441 additions and 0 deletions

102
doc/busses/i2c-virtual Normal file
View File

@@ -0,0 +1,102 @@
Virtual i2c bus
Brian Kuschak <bkuschak@xxxxxxxxx>
I2C devices must have a unique 7 bit address on the bus to which they
are attached. Many I2C chips have one or more input lines which can be
used to configure one or more of their address bits. This allows the
hardware designer to include multiple chips on a single bus by selecting
different addresses for each one. However, some chips have fixed addresses
which present a problem if more than one of them are required on a board.
Also, sometimes there are more devices than there are selectable addresses.
A common solution is to break the i2c bus into disjoint segments, by using
a multiplexor or i2c bus switch. This electrically isolates the individual
bus segments. Then the devices with duplicate addresses are placed on
separate bus segments, to prevent address conflicts.
This seems to be a very common approach in certain types of applications,
such as bladed chassis, where many identical 'blades' or cards are inserted
into a common backplane. The I2C bus on each blade is then located behind
a mux or i2c bus switch. This solution works great from the hardware
perspective, but presents some complexities for software.
The 'virtual i2c adapter' driver is an attempt to provide a flexible software
abstraction to simiplify these issues, while minimizing the impact on
existing code and fitting nicely into the i2c driver framework.
What is a 'virtual i2c adapter'
===============================
Think of this adapter as a layer above the existing algorithm and adapter
drivers. The virtual i2c adapter is the handle by which you access a
specific multiplexed bus segment. Each disjoint bus segment becomes a new
virtual bus. The virtual adapter is associated with a 'real' adapter and a
set of multiplexor control variables.
For example: you have an i2c-0 bus with one multiplexor or i2c bus switch,
and the mux has four outputs which can be electrically connected to the
i2c-0 bus. You would register four virtual i2c busses, one for each position
of the mux. As part of the registration, you provide callback functions to
select and deselect the mux as well as the selector values to be passed to
these functions.
--------- <--------> i2c-1 virtual
| i2c mux | <--------> i2c-2 virtual
i2c-0 <-------> | | <--------> i2c-3 virtual
ibm_ocp | 0x74 | <--------> i2c-4 virtual
---------
^
|
+--- (or externally controlled)
The mux may be i2c-controlled, in which case the mux_addr is set to the
slave address (0x74) and the mux_val would be the port number to select (0-3).
Alternatively, the mux could be controlled by some fixed memory-mapped register
in which case only the mux_val would be needed. The callback functions
are hardware specific and written by the user.
Once the virtual busses are registered, you may talk to any devices behind
the mux simply the using the i2c-1, i2c-2, i2c-3, etc. adapters instead of
the i2c-0 adapter. This makes the mux essentially transparent to the
i2c_client.
The great advantage of course is that all the existing sensor drivers and
other i2c client drivers automatically work, and the mux becomes transparent
to them.
Additional capabilities
=======================
This driver also supports multiple levels of muxing, by allowing the
user to register a virtual bus which has a different virtual bus as its
'real' adapter. In addition, each virtual bus may have different kinds of
multiplexors, with each bus having its own set of callback functions.
This allows you to easily support some very complex topologies.
In addition to multiplexing, some systems require that you obtain hardware
access to the bus behind the mux before talking on it. (Again, bladed chassis
typically 'fence' other blades off the bus to prevent a crashed blade from
bringing down the whole backplane.) Before talking on this bus, you have to
arbitrate with the other blades to acquire exclusive access to the bus. This
requirement is met by the addition of two new function pointers to the
algorithm drivers, acquire_exclusive() and release_exclusive(). Traditional
adapters will not use these and they can safely be left NULL. If they are
non-null, the virtual bus driver will call the acquire_exclusive() function
before attempting any operations on that virtual bus. This function may block,
and this is done before acquiring the lock on the underlying 'real' bus to
allow transactions on the real bus to continue unimpeded while waiting for
arbitrartion. In addition the ownership is only held for as long as it takes
to complete the transfer.
Files
=====
drivers/i2c/i2c-virtual.c: The 'algorithm' and 'adapter' driver
include/linux/i2c-virtual.h: Header file with the mux control structure
and callback prototypes.

431
kernel/busses/i2c-virtual.c Normal file
View File

@@ -0,0 +1,431 @@
/*
* i2c-virtual.c - Virtual I2C bus driver.
*
* Simplifies access to complex multiplexed I2C bus topologies, by presenting
* each multiplexed bus segment as a virtual I2C adapter. Supports multi-level
* mux'ing (mux behind a mux), as well as arbitration for exclusive bus access
* for those systems which require it (some bladed chassis for example).
*
* Copyright (c) 2004 Google, Inc. (Ken Harrenstien)
*
* Based on:
* i2c-virtual.c from Brian Kuschak <bkuschak@yahoo.com>
* which was:
* Adapted from i2c-adap-ibm_ocp.c
* Original file Copyright 2000-2002 MontaVista Software Inc.
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include "i2c-virtual.h"
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#ifdef DEBUG
# define DBG(x) x
#else
# define DBG(x) {}
#endif
#define I2C_VIRT_PROCMAP 0 /* 1 for /proc/driver/i2c/virtual_i2c_map */
#define VIRT_TIMEOUT (HZ/2) /* 500msec */
#define VIRT_RETRIES 3
/* exclusive access to the bus */
#define I2C_LOCK(adap) down(&adap->bus)
#define I2C_UNLOCK(adap) up(&adap->bus)
#if I2C_VIRT_PROCMAP
/* pointer to the list of i2c_bus_mappings */
static LIST_HEAD(i2c_map_list);
struct i2c_bus_mapping {
struct i2c_adapter *parent;
struct i2c_adapter *virt;
unsigned long mux_addr;
unsigned long mux_val;
struct list_head list;
};
static void *i2c_virt_add_map(struct i2c_adapter *parent,
struct i2c_adapter *virt,
unsigned long mux_addr,
unsigned long mux_val);
#endif
/* First the I2C 'algorithm' driver code: */
/*
* Description: acquire exclusive access (if needed), select the mux,
* perform the transfer on the parent i2c adapter, deselect mux and drop
* exclusive access.
*/
static int
i2c_virt_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct i2c_virt_priv *priv = (struct i2c_virt_priv *)adap->data;
struct i2c_adapter *parent = priv->parent_adap;
int ret;
#ifdef I2C_REQUIRE_ARBITRATION
/* Acquire exclusive access to a shared I2C bus _before_ taking the
local I2C lock to prevent stalling local I2C transactions while
waiting for exclusive access.
*/
if(parent->algo->acquire_exclusive)
if ((ret = parent->algo->acquire_exclusive(parent)) < 0)
return ret;
#endif
/* Grab the lock for the parent adapter. We already hold the lock for
the virtual adapter. Then select the right mux port and perform
the transfer.
*/
DBG(printk(KERN_DEBUG "%s: Locking parent bus %0lX\n",
__FUNCTION__, (unsigned long)parent));
I2C_LOCK(parent);
if ((ret = priv->mux.select(parent, &priv->mux)) == 0) {
ret = parent->algo->master_xfer(parent, msgs, num);
}
(void) priv->mux.deselect(parent, &priv->mux);
I2C_UNLOCK(parent);
DBG(printk(KERN_DEBUG "%s: Unlocked parent bus %0lX\n",
__FUNCTION__, (unsigned long)parent));
#ifdef I2C_REQUIRE_ARBITRATION
{
int ret2;
/* Release exclusive bus access */
if(parent->algo->release_exclusive)
if ((ret2 = parent->algo->release_exclusive(parent)) < 0)
return ret2;
}
#endif
return ret;
}
static int
i2c_virt_smbus_xfer(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
struct i2c_virt_priv *priv = (struct i2c_virt_priv *)adap->data;
struct i2c_adapter *parent = priv->parent_adap;
int ret;
#ifdef I2C_REQUIRE_ARBITRATION
/* Acquire exclusive access to a shared I2C bus _before_ taking the
local I2C lock to prevent stalling local I2C transactions while
waiting for exclusive access.
*/
if(parent->algo->acquire_exclusive)
if ((ret = parent->algo->acquire_exclusive(parent)) < 0)
return ret;
#endif
/* Grab the lock for the parent adapter. We already hold the lock for
the virtual adapter. Then select the right mux port and perform
the transfer.
*/
DBG(printk(KERN_DEBUG "%s: Locking parent bus %0lX\n",
__FUNCTION__, (unsigned long)parent));
I2C_LOCK(parent);
if ((ret = priv->mux.select(parent, &priv->mux)) == 0) {
ret = parent->algo->smbus_xfer(parent, addr, flags,
read_write, command, size, data);
}
(void) priv->mux.deselect(parent, &priv->mux);
I2C_UNLOCK(parent);
DBG(printk(KERN_DEBUG "%s: Unlocked parent bus %0lX\n",
__FUNCTION__, (unsigned long)parent));
#ifdef I2C_REQUIRE_ARBITRATION
{
int ret2;
/* Release exclusive bus access */
if(parent->algo->release_exclusive)
if ((ret2 = parent->algo->release_exclusive(parent)) < 0)
return ret2;
}
#endif
return ret;
}
/*
* Description: Implements device specific ioctls.
* We don't support any yet.
*/
static int
i2c_virt_algo_control(struct i2c_adapter *adap, unsigned int cmd,
unsigned long arg)
{
return 0;
}
/* Virtual adapter functionality; returns functionality of parent adapter.
* OK if parent itself is virtual, as this will go all the way up to the
* real adapter.
*/
static u32
i2c_virt_functionality(struct i2c_adapter *adap)
{
struct i2c_virt_priv *priv = (struct i2c_virt_priv *)adap->data;
struct i2c_adapter *parent = priv->parent_adap;
return parent->algo->functionality(parent);
}
/* ===================================================================== */
/* The 'adapter' driver code: */
static int
i2c_virt_reg(struct i2c_client *client)
{
return 0;
}
static int
i2c_virt_unreg(struct i2c_client *client)
{
return 0;
}
/*
* Called to create a 'virtual' i2c bus which represents a multiplexed bus
* segment. Client and mux_val are passed to the select and deselect
* callback functions to perform hardware-specific mux control.
*/
struct i2c_adapter *i2c_virt_create_adapter(struct i2c_adapter *parent_adap,
struct i2c_client *client,
unsigned long mux_val,
void *select_cb,
void *deselect_cb)
{
struct i2c_adapter *adap;
struct i2c_virt_priv *priv;
struct i2c_algorithm *algo;
if (!(adap = kmalloc(sizeof (struct i2c_adapter)
+ sizeof(struct i2c_virt_priv)
+ sizeof(struct i2c_algorithm),
GFP_KERNEL))) {
printk(KERN_ERR "i2c_virt_register_adap: Failed allocation\n");
return NULL;
}
memset(adap, 0, sizeof(struct i2c_adapter)
+ sizeof(struct i2c_virt_priv)
+ sizeof(struct i2c_algorithm));
priv = (struct i2c_virt_priv *) (adap+1);
algo = (struct i2c_algorithm *) (priv+1);
/* Set up private adapter data */
priv->parent_adap = parent_adap;
priv->mux.client = client;
priv->mux.addr = client->addr;
priv->mux.value = mux_val;
priv->mux.select = select_cb;
priv->mux.deselect = deselect_cb;
/* Need to do algo dynamically because we don't know ahead
of time what sort of physical adapter we'll be dealing with.
*/
algo->owner = NULL;
algo->id = I2C_ALGO_VIRT;
strcpy(algo->name, "Virtual I2C algorithm driver");
algo->master_xfer = (parent_adap->algo->master_xfer
? i2c_virt_master_xfer : NULL);
algo->smbus_xfer = (parent_adap->algo->smbus_xfer
? i2c_virt_smbus_xfer : NULL);
algo->slave_send = NULL;
algo->slave_recv = NULL;
algo->algo_control = i2c_virt_algo_control;
algo->functionality = i2c_virt_functionality;
#ifdef I2C_REQUIRE_ARBITRATION
algo->acquire_exclusive = NULL;
algo->release_exclusive = NULL;
#endif
/* Now fill out new adapter structure */
adap->owner = NULL;
snprintf(adap->name, sizeof(adap->name),
"Virtual I2C (i2c-%d, mux %02lx:%02lx)",
i2c_adapter_id(parent_adap),
(unsigned long)client->addr, mux_val);
adap->id = I2C_HW_VIRT | algo->id;
adap->algo = algo;
adap->algo_data = NULL; /* XXX: Use this?? */
adap->client_register = i2c_virt_reg;
adap->client_unregister = i2c_virt_unreg;
adap->data = priv;
adap->flags = 0;
adap->timeout = VIRT_TIMEOUT;
adap->retries = VIRT_RETRIES;
DBG(printk(KERN_DEBUG "%s: Adding virt bus %0lX\n",
__FUNCTION__, (unsigned long)adap));
if (i2c_add_adapter_nolock(adap) < 0) {
kfree(adap);
return NULL;
}
printk(KERN_NOTICE "i2c%d: Virtual I2C bus "
"(Physical bus i2c%d, multiplexer %02lx port %ld)\n",
i2c_adapter_id(adap), i2c_adapter_id(parent_adap),
priv->mux.addr, mux_val);
#if I2C_VIRT_PROCMAP
i2c_virt_add_map(priv->parent_adap, adap,
priv->mux.addr, priv->value);
#endif
MOD_INC_USE_COUNT;
DBG(printk(KERN_DEBUG "%s: Added virt bus %0lX\n",
__FUNCTION__, (unsigned long)adap));
return adap;
}
int i2c_virt_remove_adapter(struct i2c_adapter *adap)
{
int ret;
DBG(printk(KERN_DEBUG "%s: Removing virt bus %0lX\n",
__FUNCTION__, (unsigned long)adap));
if ((ret = i2c_del_adapter_nolock(adap)) < 0)
return ret;
kfree(adap);
MOD_DEC_USE_COUNT;
return 0;
}
/* ------------------------------------------------------------------ */
#if I2C_VIRT_PROCMAP
/* Dubious stuff to implement a /proc/driver/i2c/virtual_i2c_map
* listing of all virtual busses.
*
* XXX: LOCKING???
*/
static int i2c_virt_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len;
char *buf = page;
struct list_head *l;
struct i2c_bus_mapping *pmap;
/* XXX: BUFFER BOUNDS CHECKING? */
buf += sprintf(buf, "i2c bus_mapping\n");
list_for_each(l, &i2c_map_list) {
pmap = list_entry(l, struct i2c_bus_mapping, list);
if(pmap)
buf += sprintf(buf, "base=iic%d mux_addr=%02hx, mux_val=%02hx => iic%d\n",
i2c_adapter_id(pmap->parent),
pmap->mux_addr, pmap->mux_val,
i2c_adapter_id(pmap->virt));
}
len = buf - page;
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
}
static void *i2c_virt_add_map(struct i2c_adapter *parent,
struct i2c_adapter *virt,
unsigned long mux_addr,
unsigned long mux_val)
{
struct i2c_bus_mapping *pmap;
if((pmap = kmalloc(sizeof(struct i2c_bus_mapping), GFP_KERNEL))
== NULL) {
printk(KERN_ERR "%s: Failed to allocate bus mapping\n",
__FUNCTION__);
return;
}
pmap->virt = virt;
pmap->parent = parent;
pmap->mux_addr = mux_addr;
pmap->mux_val = mux_val;
if (list_empty(&i2c_map_list)) {
/* If first time, ensure proc entry exists */
/* XXX: Maybe do this with module_init() */
create_proc_read_entry("driver/i2c/virtual_i2c_map", 0, NULL,
&i2c_virt_read_proc, NULL);
}
list_add_tail(&pmap->list, &i2c_map_list);
}
#if 0 /* Not used by anything, but kept for a while in case */
/* Called to find out which i2c bus to use to get to a specific bus segment.
*/
struct i2c_adapter *i2c_lookup_adapter(struct i2c_adapter *base,
unsigned long mux_addr,
unsigned long mux_val)
{
struct list_head *l;
struct i2c_bus_mapping *pmap;
list_for_each(l, &i2c_map_list) {
pmap = list_entry(l, struct i2c_bus_mapping, list);
if(pmap->parent == base
&& pmap->mux_addr == mux_addr
&& pmap->mux_val == mux_val)
return pmap->virt;
}
return NULL; /* none found */
}
EXPORT_SYMBOL(i2c_lookup_adapter);
#endif /* 0 */
#endif /* I2C_VIRT_PROCMAP */
EXPORT_SYMBOL(i2c_virt_create_adapter);
EXPORT_SYMBOL(i2c_virt_remove_adapter);
MODULE_AUTHOR("Ken Harrenstien (from Brian Kuschak)");
MODULE_DESCRIPTION("Virtual I2C driver for multiplexed I2C busses");
MODULE_LICENSE("GPL");

837
kernel/chips/pca954x.c Normal file
View File

@@ -0,0 +1,837 @@
/*
* pca954x.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* This module supports the PCA954x series of I2C multiplexer/switch chips
* made by Philips Semiconductors. This includes the
* PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, and PCA9548.
*
* Copyright (c) 2004 Google, Inc. (Ken Harrenstien)
*
* Based on:
* i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
* and
* pca9540.c from Jean Delvare <khali@linux-fr.org>, which was
* based on pcf8574.c from the same project by Frodo Looijaard,
* Philip Edelbrock, Dan Eaton and Aurelien Jarno.
*
* These chips are all controlled via the I2C bus itself, and all have a
* single 8-bit register (normally at 0x70). The upstream "parent" bus fans
* out to two, four, or eight downstream busses or channels; which of these
* are selected is determined by the chip type and register contents. A
* mux can select only one sub-bus at a time; a switch can select any
* combination simultaneously.
*
* See Documentation/i2c/virtual_i2c for details on the virtual bus/adapter
* mechanism.
*
*********************************************************************
*
* 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.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-proc.h>
#include <linux/init.h>
#include "i2c-virtual.h"
#include "version.h"
MODULE_LICENSE("GPL");
#define DEBUG 1 /* XXX: Comment out before release? */
#ifdef DEBUG
# define DBG(x) if (pca954x_debug > 0) { x; } else
#else
# define DBG(x) {}
#endif
#define PCA954X_NCHANS 8 /* Max # of channels (PCA9548) */
/* Addresses to scan */
static unsigned short normal_i2c[] = { /*0x70,*/ SENSORS_I2C_END };
static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
static unsigned int normal_isa[] = { SENSORS_ISA_END };
static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
/* Insmod/modprobe parameters */
/* Chip type must normally be specified using a parameter of the form
"force_pca9544=0,0x70"
The following declares the possible types.
*/
SENSORS_INSMOD_7(pca9540,pca9542,pca9543,pca9544,pca9545,pca9546,pca9548);
static int pca954x_debug;
MODULE_PARM(pca954x_debug, "i");
MODULE_PARM_DESC(pca954x_debug, "Debug output level (0 = none)");
#if 0 /* Considered adding this, but too many issues. See doc. */
static int pca954x_selmask[32]; /* Max # chips supported, sigh */
MODULE_PARM(pca954x_selmask, "1-32i");
MODULE_PARM_DESC(pca954x_selmask, "List of selmask settings for each mux/swi");
#endif
/* Provide specs for the PCA954x types we know about
*/
static struct pca954x_chipdef {
enum chips type;
const char *type_name;
int pcanum;
int nchans;
enum muxtype { pca954x_ismux=0, pca954x_isswi } muxtype;
} pca954x_chipdefs[] = {
{ pca9540, "pca9540", 9540, 2, pca954x_ismux },
{ pca9542, "pca9542", 9542, 2, pca954x_ismux },
{ pca9543, "pca9543", 9543, 2, pca954x_isswi },
{ pca9544, "pca9544", 9544, 4, pca954x_ismux },
{ pca9545, "pca9545", 9545, 4, pca954x_isswi },
{ pca9546, "pca9546", 9546, 4, pca954x_isswi },
{ pca9548, "pca9548", 9548, 8, pca954x_isswi },
};
#define PCA954X_MUX_ENA 0x04 /* Mux enable bit */
/* Each client has this additional data.
Note the two conventions for identifying a chip's channel (sub-bus):
"channel" 0 to N-1, where N is the number of channels supported
"bitmask" 1 to 1 << N-1, so bit 0 is channel 0.
This use of "channel" is consistent with that in the Philips PCA954X doc.
*/
struct pca954x_data {
struct i2c_client client;
int sysctl_id;
enum chips type;
enum muxtype muxtype;
int pcanum; /* PCA chip number */
int nchans; /* # of channels (busses) this chip controls */
int nchmask; /* Mask of all channels */
struct semaphore update_lock;
int selmask; /* Bitmask of chans OK to leave on (chn1 == bit0) */
int curmask; /* Bitmask of chans currently selected */
u8 regval; /* Current chip register value */
u8 origval; /* Original value at initialization */
struct i2c_adapter *virt_adapters[PCA954X_NCHANS];
};
static int pca954x_attach_adapter(struct i2c_adapter *adapter);
static int pca954x_detect(struct i2c_adapter *adapter, int address,
unsigned short flags, int kind);
static int pca954x_detach_client(struct i2c_client *client);
static void pca954x_pe_busses(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_pe_debug(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_pe_regval(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_pe_selbus(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_pe_selmask(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_pe_type(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_set_selmask(struct i2c_client *client, int mask);
static u8 pca954x_mask2val(struct i2c_client *client, int mask);
static int pca954x_val2mask(struct i2c_client *client, u8 val);
static int pca954x_select_mux(struct i2c_adapter *adap,
struct i2c_mux_ctrl *mux);
static int pca954x_deselect_mux(struct i2c_adapter *adap,
struct i2c_mux_ctrl *mux);
static int pca954x_select_chan(struct i2c_adapter *adap,
struct i2c_client *client, unsigned long chan);
static int pca954x_xfer(struct i2c_adapter *adap,
struct i2c_client *client,
int read_write, u8 *val);
/* This is the driver that will be inserted */
static struct i2c_driver pca954x_driver = {
.owner = THIS_MODULE,
.name = "PCA954X I2C mux/switch driver",
.flags = I2C_DF_NOTIFY,
.attach_adapter = pca954x_attach_adapter,
.detach_client = pca954x_detach_client,
};
/* -- SENSORS SYSCTL START -- */
#define PCA954X_SYSCTL_BUSSES 1000
#define PCA954X_SYSCTL_DEBUG 1001
#define PCA954X_SYSCTL_REGVAL 1002
#define PCA954X_SYSCTL_SELBUS 1003
#define PCA954X_SYSCTL_SELMASK 1004
#define PCA954X_SYSCTL_TYPE 1005
/* -- SENSORS SYSCTL END -- */
static ctl_table pca954x_dir_table_template[] = {
{PCA954X_SYSCTL_BUSSES, "busses", NULL, 0, 0444, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_busses},
{PCA954X_SYSCTL_DEBUG, "debug", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_debug},
{PCA954X_SYSCTL_REGVAL, "regval", NULL, 0, 0444, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_regval},
{PCA954X_SYSCTL_SELBUS, "selbus", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_selbus},
{PCA954X_SYSCTL_SELMASK, "selmask", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_selmask},
{PCA954X_SYSCTL_TYPE, "type", NULL, 0, 0444, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_type},
{0}
};
static int pca954x_id = 0;
static int __init pca954x_init(void)
{
printk("pca954x.o version %s (%s)\n", LM_VERSION, LM_DATE);
/* Because we've set I2C_DF_NOTIFY in our driver struct,
* this call results in calling pca954x_attach_adapter() for
* every adapter currently known, both real and virtual.
*/
return i2c_add_driver(&pca954x_driver);
}
static void __exit pca954x_exit(void)
{
i2c_del_driver(&pca954x_driver);
}
/* This function is called whenever a new bus/adapter is added, OR when
the driver itself is added (for all existing adapters), to see if
the driver can find a chip address it's responsible for.
*/
static int pca954x_attach_adapter(struct i2c_adapter *adapter)
{
DBG(printk(KERN_DEBUG "%s: %0lX\n",
__FUNCTION__, (unsigned long)adapter));
/* Apply standard lookup via parameters or probing.
"addr_data" is defined by the SENSORS_INSMOD_n macro.
*/
return i2c_detect(adapter, &addr_data, pca954x_detect);
}
/* This function is called by i2c_detect and must be of type
"i2c_found_addr_proc" as defined in i2c-proc.h.
For the PCA954x series this should only happen due to "force" parameters
given to insmod/modprobe, as there is no way to autodetect what flavor
of chip we have (and the differences matter!)
Thus, we should never get a "kind" of -1.
Note the unique potential for infinite recursion here because we are
creating new adapters (one for each muxed virtual bus). This will cause
our attach_adapter() function to be called twice for each new adapter,
because:
(1) The call to i2c_add_adapter results in calling the attach_adapter()
function of all known chip drivers, including this one.
(2) Because normally this is first called via i2c_add_driver calling
i2c_add_adapter, each new virtual bus/adapter created here will create
a new entry in i2c-core's master adapter list at a point that hasn't
yet been reached by i2c_add_driver's scan, and thus i2c_add_driver
will cause yet another call to our attach_adapter for each of these
new bus/adapters.
Each call to our attach_adapter() causes another call to i2c_detect,
which would detect the mux all over again if an actual probe was made.
This is prevented in two ways. The first line of defense is
i2c_check_addr() which is called by both i2c-proc.c:i2c_detect()
and i2c-core.c:i2c_probe() to make sure a bus/addr pair has not already
been seen; that function now knows how to verify this on all parent
busses as well.
The second line of defense is to never put anything in the "normal_i2c"
or "normal_i2c_range" tables that would induce i2c_detect() to probe
a PCA954x address.
*/
int pca954x_detect(struct i2c_adapter *adapter, int address,
unsigned short flags, int kind)
{
int i, n;
struct i2c_client *client;
struct pca954x_data *data;
const char *type_name = "";
int err = 0;
DBG(printk(KERN_DEBUG "%s: %0lX %0x %0x %d\n",
__FUNCTION__, (unsigned long)adapter,
address, flags, kind));
/* XXX: Remove this after verifying that no more recursion
is happening!
*/
if (adapter->algo->id == I2C_ALGO_VIRT) {
printk(KERN_ERR "%s: Avoiding recursion! %0lX %0x %0x %d\n",
__FUNCTION__, (unsigned long)adapter,
address, flags, kind);
goto ERROR0;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
goto ERROR0;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet. */
if (!(data = kmalloc(sizeof(struct pca954x_data), GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR0;
}
memset(data, 0, sizeof(struct pca954x_data));
client = &data->client;
client->addr = address;
client->data = data;
client->adapter = adapter;
client->driver = &pca954x_driver;
client->flags = 0;
/* The detection is very weak. In fact, we have no way of
* telling what kind of mux/swi is there, and the differences matter,
* so if asked to figure it out we just complain instead.
*/
if (kind < 0) {
printk(KERN_ERR "%s: Attempted ill-advised probe at addr %0x",
__FUNCTION__, address);
goto ERROR1;
}
/* Read the mux register at addr. This does two things: it verifies
that the mux is in fact present, and fetches its current
contents for possible use with a future deselect algorithm.
*/
if ((i = i2c_smbus_read_byte(client)) < 0) {
printk(KERN_WARNING
"i2c-%d: pca954x.o failed to read reg at %0x",
i2c_adapter_id(adapter), address);
err = -ENODEV;
goto ERROR1;
}
data->origval = i;
if (kind == any_chip) {
printk(KERN_WARNING
"i2c-%d: pca954x.o needs advice on chip type -"
" wildly guessing %0x is a PCA9540",
i2c_adapter_id(adapter), address);
kind = pca9540; /* Make "any" default to PCA9540 */
}
/* Look up in table */
for (i = sizeof(pca954x_chipdefs)/sizeof(pca954x_chipdefs[0]);
--i >= 0;) {
if (pca954x_chipdefs[i].type == kind)
break;
}
if (i < 0) {
printk(KERN_ERR "%s: Internal error: unknown kind (%d)",
__FUNCTION__, kind);
goto ERROR1;
}
data->type = kind;
type_name = pca954x_chipdefs[i].type_name;
data->pcanum = pca954x_chipdefs[i].pcanum;
data->nchans = pca954x_chipdefs[i].nchans;
data->muxtype = pca954x_chipdefs[i].muxtype;
data->nchmask = (1 << data->nchans) - 1;
/* Now that we know the mux/swi type, we can analyze the
pca954x_selmask parameter, if one was specified.
XXX: Not yet. Still uncertain whether there is a good way
to initialize selmask and still continue to detect other chips
correctly on the right busses, or whether the original reg
setting (origval) should be retained in case BIOS depends on it.
*/
/* Fill in remaining client fields and put it into the global list */
snprintf(client->name, sizeof(client->name),
"PCA%d I2C %d-chan %s chip",
data->pcanum, data->nchans,
(data->muxtype == pca954x_ismux ? "mux" : "switch"));
client->id = pca954x_id++;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(client)))
goto ERROR1;
/* Register new /proc directory entries with module sensors */
if ((i = i2c_register_entry(client, type_name,
pca954x_dir_table_template)) < 0) {
err = i;
goto ERROR2;
}
data->sysctl_id = i;
/* Initialize the PCA954X chip.
For now, force unselected. This had better happen BEFORE any
other sensors modules are loaded, or their chip addresses will
stop working if they were actually behind the mux chip.
*/
pca954x_set_selmask(client, 0);
/* Now create virtual busses and adapters for them */
#ifdef I2C_REQUIRE_ARBITRATION
/* This doesn't actually do anything yet; kept as a
placeholder from the original i2c-virtual_cb.c
*/
adapter->algo->acquire_exclusive = &acquire_shared_bus;
adapter->algo->release_exclusive = &release_shared_bus;
#endif
DBG(printk(KERN_DEBUG "%s: creating %d virt busses\n",
__FUNCTION__, data->nchans));
for (i = 0, n = 0; i < data->nchans; ++i) {
struct i2c_adapter *virt;
virt = i2c_virt_create_adapter(adapter, client, i,
&pca954x_select_mux,
&pca954x_deselect_mux);
if ((data->virt_adapters[i] = virt) != NULL)
++n;
}
printk("i2c-%d: Registered %d of %d virtual busses for I2C mux %s\n",
i2c_adapter_id(adapter), n, i, type_name);
return 0;
ERROR2:
i2c_detach_client(client);
ERROR1:
kfree(data);
ERROR0:
return err;
}
static int pca954x_detach_client(struct i2c_client *client)
{
struct pca954x_data *data = (struct pca954x_data *)(client->data);
int i, err;
DBG(printk(KERN_DEBUG "%s: %0lX\n",
__FUNCTION__, (unsigned long)client));
/* Must flush everything attached to our channels!
*/
for (i = 0; i < data->nchans; ++i) {
if (data->virt_adapters[i]) {
err = i2c_virt_remove_adapter(data->virt_adapters[i]);
if (err != 0) {
printk(KERN_ERR "pca954x.o: Bus deregistration failed, "
"client not detached.\n");
return err;
}
data->virt_adapters[i] = NULL;
}
}
i2c_deregister_entry(data->sysctl_id);
if ((err = i2c_detach_client(client))) {
printk(KERN_ERR "pca954x.o: Client deregistration failed, "
"client not detached.\n");
return err;
}
kfree(client->data);
return 0;
}
/* Implement /proc "regval" - the actual contents of the chip register.
* This is READ-ONLY.
*/
void pca954x_pe_regval(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct pca954x_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
#if 0 /* XXX: For now, always use cached value */
static void pca954x_update_client(struct i2c_client *client);
pca954x_update_client(client);
#endif
results[0] = data->regval;
*nrels_mag = 1;
}
}
/* Implement /proc "debug" - a non-zero value enables debug output, if
* the code was compiled with it. This actually applies to all mux chips,
* not just a single instance, but that's OK.
*/
void pca954x_pe_debug(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
results[0] = pca954x_debug;
*nrels_mag = 1;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
pca954x_debug = results[0];
}
}
}
/* Implement /proc "busses" - list of (virtual) adapter IDs that constitute
the busses that this chip connects to.
This is READ-ONLY.
*/
void pca954x_pe_busses(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct pca954x_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
int i, lasti;
if ((lasti = data->nchans) > *nrels_mag)
lasti = *nrels_mag;
for (i = 0; i < lasti; ++i) {
results[i] = i2c_adapter_id(data->virt_adapters[i]);
}
*nrels_mag = lasti;
}
}
/* Implement /proc "selmask" - the value specifies the current default
* channel mask. Sub-bus 0 == bit 0.
# A value of 0 means "none"
*/
void pca954x_pe_selmask(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct pca954x_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
results[0] = data->selmask;
*nrels_mag = 1;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
pca954x_set_selmask(client, results[0]);
}
}
}
/* Implement /proc "selbus" - the list of values is considered to be virtual
adapter IDs, as an alternative representation of the select mask.
No values at all means "none".
*/
void pca954x_pe_selbus(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct pca954x_data *data = client->data;
int i, nres;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
for (i = 0, nres = 0; i < data->nchans; ++i) {
if ((data->selmask & (1<<i)) && (nres < *nrels_mag)) {
results[nres] = i2c_adapter_id(data->virt_adapters[i]);
++nres;
}
}
*nrels_mag = nres;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
int newmask = 0;
for (nres = 0; nres < *nrels_mag; ++nres) {
/* Ugly, but shouldn't happen very often */
for (i = 0; i < data->nchans; ++i) {
if (results[nres]
== i2c_adapter_id(data->virt_adapters[i])) {
newmask |= (1<<i);
}
}
}
pca954x_set_selmask(client, newmask);
}
}
/* Implement /proc "type" - expose details of the mux type.
Due to limitations of the sensor package conventions, we can only provide
an integer array rather than strings:
[0] = chip number: 9540 for "PCA9540", etc.
[1] = mux type: 0 for mux, 1 for switch
[2] = # channels: 2, 4, or 8
*/
void pca954x_pe_type(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct pca954x_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
if (*nrels_mag >= 3) {
results[0] = data->pcanum;
results[1] = data->muxtype;
results[2] = data->nchans;
*nrels_mag = 3;
}
}
}
/* Set default select mask.
Since this can be executed while something else is using the mux or
one of its sub-busses, the main consideration is making sure we don't
mess up anything in progress.
This is accomplished by locking the parent bus that the mux resides on,
and then calling our internal write function.
It would not work to just call i2c_smbus_write_byte() because
by the time that returns, the values we're trying to update
could already be wrong. This can happen if something else grabs
the bus and writes to the mux (eg for doing I/O to a sub-bus
device), leading to a race condition for updating
selmask, regval, and curmask.
It doesn't help to use an update_lock for those values because
you still have to contend with other threads trying to select
sub-busses and would encounter deadlocks.
*/
static void pca954x_set_selmask(struct i2c_client *client, int mask)
{
struct pca954x_data *data = client->data;
DBG(printk(KERN_DEBUG
"%s: Locking bus %d mux %d (%0lX) "
"for write of selmask %0x\n",
__FUNCTION__, i2c_adapter_id(client->adapter),
client->id, (unsigned long)client, mask));
down(&client->adapter->bus);
DBG(printk(KERN_DEBUG "%s: Starting write.\n", __FUNCTION__));
data->selmask = (mask & data->nchmask);
(void) pca954x_select_chan(client->adapter, client, -1);
up(&client->adapter->bus);
DBG(printk(KERN_DEBUG "%s: Unlocked mux (%0lX) for write.\n",
__FUNCTION__, (unsigned long)client));
}
#if 0 /* Not needed at the moment; see pca954x_pe_regval() */
/* Read pca954x register into cached value
*/
static void pca954x_update_client(struct i2c_client *client)
{
struct pca954x_data *data = client->data;
DBG(printk(KERN_DEBUG
"%s: Locking bus %d mux %d (%0lX) for read\n",
__FUNCTION__, i2c_adapter_id(client->adapter),
client->id, (unsigned long)client));
down(&client->adapter->bus);
DBG(printk(KERN_DEBUG "%s: Starting read.\n", __FUNCTION__));
pca954x_xfer(client->adapter, client, I2C_SMBUS_READ, &data->regval);
data->curmask = pca954x_val2mask(client, data->regval);
up(&client->adapter->bus);
DBG(printk(KERN_DEBUG "%s: Unlocked mux (%0lX) for read.\n",
__FUNCTION__, (unsigned long)client));
}
#endif
static u8 pca954x_mask2val(struct i2c_client *client, int mask)
{
struct pca954x_data *data = (struct pca954x_data *)(client->data);
int i;
mask &= data->nchmask; /* Sanitize mask */
if (mask == 0)
return 0; /* Deselecting all, turn off */
if (data->muxtype == pca954x_isswi)
return mask; /* Switch, can enable all at once! */
/* Mux, can only pick one. Select lowest chan bit */
for (i=0; (mask & (1<<i)) == 0; ++i);
return PCA954X_MUX_ENA | i;
}
static int pca954x_val2mask(struct i2c_client *client, u8 val)
{
struct pca954x_data *data = (struct pca954x_data *)(client->data);
if (data->muxtype == pca954x_isswi)
return val & data->nchmask; /* Switch val is == mask */
/* Mux - mask off low bits to get 0-based chan # as bit shift */
if (val & PCA954X_MUX_ENA)
return 1 << (val & (data->nchans-1));
return 0;
}
/*****************************************************************************
* All of the following functions should only be called after the parent
* adapter bus semaphore (adapter->bus) has been acquired, so we cannot
* call the high-level i2c_smbus_write_byte() function -- it will deadlock
* attempting to acquire the same bus lock. Instead do directly what it
* would do, by means of pca954x_xfer(). Sigh!
*
* These functions are allowed to block.
*
*****************************************************************************/
static int pca954x_select_mux(struct i2c_adapter *adap,
struct i2c_mux_ctrl *mux)
{
return pca954x_select_chan(adap, mux->client, mux->value);
}
static int pca954x_deselect_mux(struct i2c_adapter *adap,
struct i2c_mux_ctrl *mux)
{
return pca954x_select_chan(adap, mux->client, -1);
}
/* Select channel.
* PCA954x I2C-controlled bus mux/switch chips mainly differ in
* whether the chip is a mux or a switch:
* Mux: value addressed, only 1 bus can be selected.
* Swi: bitmask selected, any/all busses can be selected.
*/
static int pca954x_select_chan(struct i2c_adapter *adap,
struct i2c_client *client,
unsigned long chan)
{
struct pca954x_data *data = (struct pca954x_data *)(client->data);
int maskbit;
u8 newregval;
int ret;
if (chan == -1) { /* Op: clever deselect (to selmask) */
if ((data->muxtype == pca954x_ismux)
&& (data->curmask & data->selmask)) {
/* If mux, and current chan allowed by selmask, do nothing */
newregval = data->regval; /* No change */
} else {
/* Anything else just reverts to selmask */
newregval = pca954x_mask2val(client, data->selmask);
}
} else { /* Op: select chan */
maskbit = 1 << chan;
if (maskbit & data->curmask) { /* If already selected, */
newregval = data->regval; /* no change */
} else if ((data->muxtype == pca954x_ismux)
|| !(maskbit & data->selmask)) {
newregval = pca954x_mask2val(client, maskbit);
} else
newregval = pca954x_mask2val(client, data->selmask);
}
/* Only clobber control reg when value changes, to avoid
unnecessary mux writes. For safety and initialization,
ALWAYS force the write if 0 is desired.
*/
ret = (!newregval || (data->regval != newregval));
DBG(printk(KERN_DEBUG
"%s: Mux %d: chan %d, val %02x - %s\n",
__FUNCTION__,
client->id, (int)chan, newregval,
(ret ? "writing" : "skipping" ) ));
if (ret) {
ret = pca954x_xfer(adap, client, I2C_SMBUS_WRITE, &newregval);
if (ret < 0) {
printk(KERN_ERR "%s: I2C mux %d select failed "
"(addr=%02x, val=%02x, err=%d)!\n",
__FUNCTION__, client->id,
client->addr, newregval, ret);
} else {
data->regval = newregval;
data->curmask = pca954x_val2mask(client, newregval);
}
}
return ret;
}
static int pca954x_xfer(struct i2c_adapter *adap,
struct i2c_client *client,
int read_write, u8 *val)
{
int ret;
if (adap->algo->master_xfer) {
/* Use I2C transfer */
struct i2c_msg msg;
char buf[1];
msg.addr = client->addr;
msg.flags = (read_write == I2C_SMBUS_READ ? I2C_M_RD : 0);
msg.len = 1;
buf[0] = *val;
msg.buf = buf;
ret = adap->algo->master_xfer(adap, &msg, 1);
if (!ret && (read_write == I2C_SMBUS_READ))
*val = buf[0];
} else if (adap->algo->smbus_xfer) {
/* Use SMBus transfer */
union i2c_smbus_data data;
ret = adap->algo->smbus_xfer(adap,
client->addr,
client->flags & I2C_M_TEN,
read_write,
*val,
I2C_SMBUS_BYTE, &data);
if (!ret && (read_write == I2C_SMBUS_READ))
*val = data.byte;
} else {
printk(KERN_ERR "%s: no smbus_xfer or master_xfer for "
"adapter %0lX, client %0lX\n",
__FUNCTION__, (unsigned long)adap, (unsigned long)client);
ret = -1;
}
return ret;
}
/* ------------------ */
MODULE_AUTHOR("Ken Harrenstien <klh@google.com>");
MODULE_DESCRIPTION("PCA954X I2C mux/switch driver");
module_init(pca954x_init);
module_exit(pca954x_exit);

View File

@@ -0,0 +1,71 @@
/*
* i2c-virtual.h - Header file for the 'virtual i2c' adapter driver.
*
* Copyright (c) 2004 Google, Inc.
*
* Based on:
* i2c-virtual.h from Brian Kuschak <bkuschak@yahoo.com>
*
* 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.
*/
#ifndef __I2C_VIRTUAL_H
#define __I2C_VIRTUAL_H
#include <linux/i2c.h>
struct i2c_mux_ctrl {
struct i2c_client *client; /* The mux chip/device */
unsigned long addr;
unsigned long value; /* Channel # */
/* fn which enables the mux */
int (*select)(struct i2c_adapter *adap, struct i2c_mux_ctrl *mux);
/* fn which disables the mux */
int (*deselect)(struct i2c_adapter *adap, struct i2c_mux_ctrl *mux);
};
/* This has to be exposed, since the code which assigns the callbacks must
use it.
*/
struct i2c_virt_priv {
struct i2c_adapter *parent_adap; /* pointer to parent adapter */
struct i2c_mux_ctrl mux; /* MUX settings for this adapter */
};
static inline struct i2c_adapter *i2c_virt_parent(struct i2c_adapter *adap)
{
if (adap->algo && (adap->algo->id == I2C_ALGO_VIRT)) {
return ((struct i2c_virt_priv *)(adap->data))->parent_adap;
}
return NULL;
}
/*
* Called to create a 'virtual' i2c bus which represents a multiplexed bus
* segment. The client and mux_val are passed to the select and deselect
* callback functions to perform hardware-specific mux control.
*/
struct i2c_adapter *i2c_virt_create_adapter(struct i2c_adapter *parent_adap,
struct i2c_client *client,
unsigned long mux_val,
void *select_cb,
void *deselect_cb);
int i2c_virt_remove_adapter(struct i2c_adapter *adap);
#endif /* __I2C_VIRTUAL_H */