mirror of
https://github.com/lm-sensors/lm-sensors
synced 2025-09-01 14:55:27 +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