SPI <-> Parport (light) bridge code (Re: Stuck creating sysfs hooksfor a driver..)
From: Kaiwan N Billimoria
Date: Fri Mar 31 2006 - 00:57:15 EST
Jean Delvare wrote:
Hi Kaiwan,
--snip--
You must stay away from writing a driver for the board itself. What you
must write is in fact two different drivers:
1* A driver for the SPI interface of your board (basically a parallel
port <-> SPI bridge). This driver will expose the device as an SPI bus
to the rest of the kernel. This driver doesn't care about what chip is
plugged on it.
Hi Jean,
1. Yes, i know, long time since the above message..yet i'm happy to say
that i have built a (lightweight version) of the
SPI<->parport bridge (file pasted below), which is based on your
i2c-parport driver bridge code.
It is built as a header file: the driver developer basically appends
his/her adapter entry into the adapter_parm[] data structure and
includes this file in the device driver.
2* A driver for the LM70 temperature sensor chip, which doesn't care
about the chip location. This driver will use generic SPI commands as
offered by the spi kernel interface.
This modular approach makes it possible to then reuse each of the
drivers. If you later have a similar board for a different chip, the
first driver will still work (assuming the new board uses SPI and the
same wiring conventions). If you later have an LM70 chip on a different
physical interface, the second driver will still work.
2. I have also done this; i now have a working driver for a specific
chip (the NS lm70CILD-3 eval board) based on the (above mentioned)
SPi<->parport bridge. Basically, the "type 0" entry in the
adapter_parm[] data structure is the lm70CILD-3 entry. The driver
handles the other things, including creation of a sysfs hook (used to
query the temperature from userspace).
No, just do what every other hardware monitoring chip does, so that
support can be added for the lm70 chip in libsensors - then you win
instant support in all hardware monitoring application which rely on
libsensors, and even a few which do not.
It's really not a matter of how many features a chip has. Look at the
lm75 or w83l785ts driver, you'll see they have very few features as
well. It's a matter of having a common standard for exporting the
values to user-space, so that the same library or application can
handle all sources with minimum effort.
Thanks,
3. I still have to work on integrating the userspace conversion into
libsensors (as recommended by yourself & others).
4. My intention at this point of time is for you (and others) to take a
look at the spi-parport-light.h code and pl give me your feedback. I'm
not posting a patch at this time..
5. Also, because it's a header, am not sure what approach to take when
patching into the kernel; i mean, i can place it under the spi tree
(either drivers/spi or include/linux/spi) but can't add the usual
"obj-$(CONFIG_xxx) += xxx.o"
line (as it's a header). Or can I do something like this?? i'm really
quite uncertain, forgive me.. any suggestions on how i should go about this?
Thanks very much,
Kaiwan.
PS> If you'd like to see a usage example, i can post the lm70CILD3.c
driver code..
+++++++++++++++++++++ File spi-parport-light.h
+++++++++++++++++++++++++++++++++++++++
---
/*
------------------------------------------------------------------------ *
*
spi-parport-light.h *
* SPI bus over parallel
port *
*
------------------------------------------------------------------------ *
Copyright (C) 2006 Kaiwan N Billimoria <kaiwan@xxxxxxxxxxxxxxxxxxx>
Heavily based on i2c-parport-light.c
Copyright (C) 2003-2004 Jean Delvare <khali@xxxxxxxxxxxx>
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.
*
------------------------------------------------------------------------ */
#ifdef DATA
#undef DATA
#endif
#define DATA 0
#define STAT 1
#define CTRL 2
struct lineop {
u8 val;
u8 port;
u8 inverted;
};
struct adapter_parm {
char *name;
struct lineop setsda;
struct lineop setscl;
struct lineop setcsl; // chip select
struct lineop getsda;
struct lineop getscl;
struct lineop init;
struct spi_device *spidev;
};
static struct adapter_parm adapter_parm[] = {
/* type 0: National Semiconductor LM70CILD-3 evaluation board */
{
.name = "lm70CILD-3",
.setscl = { 0x40, DATA, 0 },
.setcsl = { 0x20, DATA, 1 },
.getsda = { 0x10, STAT, 0 }, // SI/O
.init = { 0xFE, DATA, 0 },
},
/* add adapters here */
};
/*----------------Module parameters-----------------------------*/
static int type;
module_param(type, int, 0);
MODULE_PARM_DESC(type,
"Type of adapter: (defaults to 0)\n"
" 0 = LM70CILD-3 (National Semiconductor) evaluation board\n");
/* add new type(s) here */
static u16 base;
module_param(base, ushort, 0);
MODULE_PARM_DESC(base, "Parport Base I/O address");
/*--------------------------------------------------------------*/
#include <linux/ioport.h>
#include <linux/delay.h>
#include <asm/io.h>
/* ----- Low-level parallel port access
----------------------------------- */
#define DEFAULT_BASE 0x378
static inline void port_write(unsigned char p, unsigned char d)
{
outb(d, base+p);
}
static inline unsigned char port_read(unsigned char p)
{
return inb(base+p);
}
/* ----- Unified line operation functions
--------------------------------- */
static inline void line_set(int state, const struct lineop *op)
{
u8 oldval = port_read(op->port);
/* Touch only the bit(s) needed */
if ((op->inverted && !state) || (!op->inverted && state))
port_write(op->port, oldval | op->val);
else
port_write(op->port, oldval & ~op->val);
}
static inline int line_get(const struct lineop *op)
{
u8 oldval = port_read(op->port);
return ((op->inverted && (oldval & op->val) != op->val)
|| (!op->inverted && (oldval & op->val) == op->val));
}
/* ----- SPI call-back functions and structures ----------------- */
static void parport_setscl(void *data, int state)
{
line_set(state, &adapter_parm[type].setscl);
}
static void parport_setsda(void *data, int state)
{
line_set(state, &adapter_parm[type].setsda);
}
static int parport_getscl(void *data)
{
return line_get(&adapter_parm[type].getscl);
}
static int parport_getsda(void *data)
{
return line_get(&adapter_parm[type].getsda);
}
static void parport_setcsl(void *data, int state)
{
line_set(state, &adapter_parm[type].setcsl);
}
/* ----- Module init and
exit---------------------------------------------- */
/* Module init : to be called from the specific driver init routine */
static int spi_parport_init(char *name)
{
/* adapter_parm set in the spi-parport-light.h header; type is a
module parameter */
if (type < 0 || type >= ARRAY_SIZE(adapter_parm)) {
printk(KERN_WARNING "%s: invalid parameter \"type\" (%d)\n\
defaulting to type 0\n", name, type);
type = 0;
}
if (base == 0)
base = DEFAULT_BASE;
if (!request_region(base, 3, "spi-parport-light"))
return -EBUSY;
/* Reset hardware to a sane state (SCL and SDA high) */
parport_setsda(NULL, 1);
parport_setscl(NULL, 1);
/* Other init if needed (power on...) */
if (adapter_parm[type].init.val)
line_set(1, &adapter_parm[type].init);
/* CS deselect, if necessary.. */
if (adapter_parm[type].setcsl.val)
line_set(0, &adapter_parm[type].setcsl);
/* Memory for the spi_device struct for this adapter */
adapter_parm[type].spidev = kzalloc(sizeof(struct spi_device),
GFP_KERNEL);
if (!adapter_parm[type].spidev) {
printk(KERN_ERR "%s: out of memory\n", name);
release_region(base, 3);
return -ENOMEM;
}
printk(KERN_INFO "%s loaded: adapter type %d (device '%s'), using
base address 0x%x\n",
name, type, adapter_parm[type].name, base);
return 0;
}
/* Module exit : to be called from the specific driver cleanup routine */
static void spi_parport_exit(char *name)
{
if (adapter_parm[type].spidev)
kfree (adapter_parm[type].spidev);
/* Un-init if needed (power off...) */
if (adapter_parm[type].init.val)
line_set(0, &adapter_parm[type].init);
release_region(base, 3);
printk(KERN_INFO "%s unloaded.\n", name);
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/