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:
102
doc/busses/i2c-virtual
Normal file
102
doc/busses/i2c-virtual
Normal 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
431
kernel/busses/i2c-virtual.c
Normal 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
837
kernel/chips/pca954x.c
Normal 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);
|
71
kernel/include/i2c-virtual.h
Normal file
71
kernel/include/i2c-virtual.h
Normal 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 */
|
Reference in New Issue
Block a user