Commit a6a86725 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge kroah.com:/home/greg/linux/BK/bleed-2.6

into kroah.com:/home/greg/linux/BK/i2c-2.6
parents 1ade820c 41e1a1ee
==================
i2c-parport driver
==================
2004-07-06, Jean Delvare
This is a unified driver for several i2c-over-parallel-port adapters,
such as the ones made by Philips, Velleman or ELV. This driver is
meant as a replacement for the older, individual drivers:
* i2c-philips-par
* i2c-elv
* i2c-velleman
* video/i2c-parport (NOT the same as this one, dedicated to home brew
teletext adapters)
It currently supports the following devices:
* Philips adapter
* home brew teletext adapter
* Velleman K8000 adapter
* ELV adapter
* Analog Devices evaluation boards (ADM1025, ADM1030, ADM1031, ADM1032)
These devices use different pinout configurations, so you have to tell
the driver what you have, using the type module parameter. There is no
way to autodetect the devices. Support for different pinout configurations
can be easily added when needed.
Building your own adapter
-------------------------
If you want to build you own i2c-over-parallel-port adapter, here is
a sample electronics schema (credits go to Sylvain Munaut):
Device PC
Side ___________________Vdd (+) Side
| | |
--- --- ---
| | | | | |
|R| |R| |R|
| | | | | |
--- --- ---
| | |
| | /| |
SCL ----------x--------o |-----------x------------------- pin 2
| \| | |
| | |
| |\ | |
SDA ----------x----x---| o---x--------------------------- pin 13
| |/ |
| |
| /| |
---------o |----------------x-------------- pin 3
\| | |
| |
--- ---
| | | |
|R| |R|
| | | |
--- ---
| |
### ###
GND GND
Remarks:
- This is the exact pinout and electronics used on the Analog Devices
evaluation boards.
/|
- All inverters -o |- must be 74HC05, they must be open collector output.
\|
- All resitors are 10k.
- Pins 18-25 of the parallel port connected to GND.
- Pins 4-9 (D2-D7) could be used as VDD is the driver drives them high.
The ADM1032 evaluation board uses D4-D7. Beware that the amount of
current you can draw from the parallel port is limited. Also note that
all connected lines MUST BE driven at the same state, else you'll short
circuit the output buffers! So plugging the I2C adapter after loading
the i2c-parport module might be a good safety since data line state
prior to init may be unknown.
- This is 5V!
- Obviously you cannot read SCL (so it's not really standard-compliant).
Pretty easy to add, just copy the SDA part and use another input pin.
That would give (ELV compatible pinout):
Device PC
Side ______________________________Vdd (+) Side
| | | |
--- --- --- ---
| | | | | | | |
|R| |R| |R| |R|
| | | | | | | |
--- --- --- ---
| | | |
| | |\ | |
SCL ----------x--------x--| o---x------------------------ pin 15
| | |/ |
| | |
| | /| |
| ---o |-------------x-------------- pin 2
| \| | |
| | |
| | |
| |\ | |
SDA ---------------x---x--| o--------x------------------- pin 10
| |/ |
| |
| /| |
---o |------------------x--------- pin 3
\| | |
| |
--- ---
| | | |
|R| |R|
| | | |
--- ---
| |
### ###
GND GND
If possible, you should use the same pinout configuration as existing
adapters do, so you won't even have to change the code.
Similar (but different) drivers
-------------------------------
This driver is NOT the same as the i2c-pport driver found in the i2c package.
The i2c-pport driver makes use of modern parallel port features so that
you don't need additional electronics. It has other restrictions however, and
was not ported to Linux 2.6 (yet).
This driver is also NOT the same as the i2c-pcf-epp driver found in the
lm_sensors package. The i2c-pcf-epp driver doesn't use the parallel port
as an I2C bus directly. Instead, it uses it to control an external I2C bus
master. That driver was not ported to Linux 2.6 (yet) either.
Legacy documentation for Velleman adapter
-----------------------------------------
Useful links:
Velleman http://www.velleman.be/
Velleman K8000 Howto http://howto.htlw16.ac.at/k8000-howto.html
The project has lead to new libs for the Velleman K8000 and K8005:
LIBK8000 v1.99.1 and LIBK8005 v0.21
With these libs, you can control the K8000 interface card and the K8005
stepper motor card with the simple commands which are in the original
Velleman software, like SetIOchannel, ReadADchannel, SendStepCCWFull and
many more, using /dev/velleman.
http://home.wanadoo.nl/hihihi/libk8000.htm
http://home.wanadoo.nl/hihihi/libk8005.htm
http://struyve.mine.nu:8080/index.php?block=k8000
http://sourceforge.net/projects/libk8005/
Primitive parallel port is driver for i2c bus, which exploits
features of modern bidirectional parallel ports.
Bidirectional ports have particular bits connected in following way:
|
/-----| R
--o| |-----|
read \-----| /------- Out pin
|/
- -|\
write V
|
---
It means when output is set to 1 we can read the port. Therefore
we can use 2 pins of parallel port as SDA and SCL for i2c bus. It
is not necessary to add any external - additional parts, we can
read and write the same port simultaneously.
I only use register base+2 so it is possible to use all
8 data bits of parallel port for other applications (I have
connected EEPROM and LCD display). I do not use bit Enable Bi-directional
Port. The only disadvantage is we can only support 5V chips.
Layout:
Cannon 25 pin
SDA - connect to pin 14 (Auto Linefeed)
SCL - connect to pin 16 (Initialize Printer)
GND - connect to pin 18-25
+5V - use external supply (I use 5V from 3.5" floppy connector)
no pullups requied
Module parameters:
base = 0xXXX
XXX - 278 or 378
That's all.
Daniel Smolik
marvin@sitour.cz
i2c-velleman driver
-------------------
This is a driver for i2c-hw access for Velleman K8000 and other adapters.
Useful links
------------
Velleman:
http://www.velleman.be/
Velleman K8000 Howto:
http://howto.htlw16.ac.at/k8000-howto.html
K8000 and K8005 libraries
-------------------------
The project has lead to new libs for the Velleman K8000 and K8005:
LIBK8000 v1.99.1 and LIBK8005 v0.21
With these libs, you can control the K8000 interface card and the K8005
stepper motor card with the simple commands which are in the original
Velleman software, like SetIOchannel, ReadADchannel, SendStepCCWFull and
many more, using /dev/velleman.
The libs can be found on http://groups.yahoo.com/group/k8000/files/linux/
......@@ -2374,6 +2374,12 @@ P: Gerd Knorr
M: kraxel@bytesex.org
S: Maintained
W1 DALLAS'S 1-WIRE BUS
P: Evgeniy Polyakov
M: johnpol@2ka.mipt.ru
L: sensors@stimpy.netroedge.com
S: Maintained
WAN ROUTER & SANGOMA WANPIPE DRIVERS & API (X.25, FRAME RELAY, PPP, CISCO HDLC)
P: Nenad Corbic
M: ncorbic@sangoma.com
......
......@@ -42,6 +42,8 @@ source "drivers/char/Kconfig"
source "drivers/i2c/Kconfig"
source "drivers/w1/Kconfig"
source "drivers/misc/Kconfig"
source "drivers/media/Kconfig"
......
......@@ -42,6 +42,7 @@ obj-$(CONFIG_GAMEPORT) += input/gameport/
obj-$(CONFIG_SERIO) += input/serio/
obj-$(CONFIG_I2O) += message/
obj-$(CONFIG_I2C) += i2c/
obj-$(CONFIG_W1) += w1/
obj-$(CONFIG_PHONE) += telephony/
obj-$(CONFIG_MD) += md/
obj-$(CONFIG_BT) += bluetooth/
......
......@@ -143,7 +143,7 @@ static int pcf_isa_init(void)
}
}
if (irq > 0) {
if (request_irq(irq, pcf_isa_handler, 0, "PCF8584", 0) < 0) {
if (request_irq(irq, pcf_isa_handler, 0, "PCF8584", NULL) < 0) {
printk(KERN_ERR "i2c-elektor: Request irq%d failed\n", irq);
irq = 0;
} else
......@@ -244,7 +244,7 @@ static int __init i2c_pcfisa_init(void)
fail:
if (irq > 0) {
disable_irq(irq);
free_irq(irq, 0);
free_irq(irq, NULL);
}
if (!mmapped)
......@@ -258,7 +258,7 @@ static void i2c_pcfisa_exit(void)
if (irq > 0) {
disable_irq(irq);
free_irq(irq, 0);
free_irq(irq, NULL);
}
if (!mmapped)
......
......@@ -45,7 +45,7 @@
#include "i2c-ibm_iic.h"
#define DRIVER_VERSION "2.01"
#define DRIVER_VERSION "2.1"
MODULE_DESCRIPTION("IBM IIC driver v" DRIVER_VERSION);
MODULE_LICENSE("GPL");
......@@ -96,6 +96,31 @@ static void dump_iic_regs(const char* header, struct ibm_iic_private* dev)
# define DUMP_REGS(h,dev) ((void)0)
#endif
/* Bus timings (in ns) for bit-banging */
static struct i2c_timings {
unsigned int hd_sta;
unsigned int su_sto;
unsigned int low;
unsigned int high;
unsigned int buf;
} timings [] = {
/* Standard mode (100 KHz) */
{
.hd_sta = 4000,
.su_sto = 4000,
.low = 4700,
.high = 4000,
.buf = 4700,
},
/* Fast mode (400 KHz) */
{
.hd_sta = 600,
.su_sto = 600,
.low = 1300,
.high = 600,
.buf = 1300,
}};
/* Enable/disable interrupt generation */
static inline void iic_interrupt_mode(struct ibm_iic_private* dev, int enable)
{
......@@ -195,6 +220,104 @@ static void iic_dev_reset(struct ibm_iic_private* dev)
iic_dev_init(dev);
}
/*
* Do 0-length transaction using bit-banging through IIC_DIRECTCNTL register.
*/
/* Wait for SCL and/or SDA to be high */
static int iic_dc_wait(volatile struct iic_regs *iic, u8 mask)
{
unsigned long x = jiffies + HZ / 28 + 2;
while ((in_8(&iic->directcntl) & mask) != mask){
if (unlikely(time_after(jiffies, x)))
return -1;
cond_resched();
}
return 0;
}
static int iic_smbus_quick(struct ibm_iic_private* dev, const struct i2c_msg* p)
{
volatile struct iic_regs* iic = dev->vaddr;
const struct i2c_timings* t = &timings[dev->fast_mode ? 1 : 0];
u8 mask, v, sda;
int i, res;
/* Only 7-bit addresses are supported */
if (unlikely(p->flags & I2C_M_TEN)){
DBG("%d: smbus_quick - 10 bit addresses are not supported\n",
dev->idx);
return -EINVAL;
}
DBG("%d: smbus_quick(0x%02x)\n", dev->idx, p->addr);
/* Reset IIC interface */
out_8(&iic->xtcntlss, XTCNTLSS_SRST);
/* Wait for bus to become free */
out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC);
if (unlikely(iic_dc_wait(iic, DIRCNTL_MSDA | DIRCNTL_MSC)))
goto err;
ndelay(t->buf);
/* START */
out_8(&iic->directcntl, DIRCNTL_SCC);
sda = 0;
ndelay(t->hd_sta);
/* Send address */
v = (u8)((p->addr << 1) | ((p->flags & I2C_M_RD) ? 1 : 0));
for (i = 0, mask = 0x80; i < 8; ++i, mask >>= 1){
out_8(&iic->directcntl, sda);
ndelay(t->low / 2);
sda = (v & mask) ? DIRCNTL_SDAC : 0;
out_8(&iic->directcntl, sda);
ndelay(t->low / 2);
out_8(&iic->directcntl, DIRCNTL_SCC | sda);
if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC)))
goto err;
ndelay(t->high);
}
/* ACK */
out_8(&iic->directcntl, sda);
ndelay(t->low / 2);
out_8(&iic->directcntl, DIRCNTL_SDAC);
ndelay(t->low / 2);
out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC);
if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC)))
goto err;
res = (in_8(&iic->directcntl) & DIRCNTL_MSDA) ? -EREMOTEIO : 1;
ndelay(t->high);
/* STOP */
out_8(&iic->directcntl, 0);
ndelay(t->low);
out_8(&iic->directcntl, DIRCNTL_SCC);
if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC)))
goto err;
ndelay(t->su_sto);
out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC);
ndelay(t->buf);
DBG("%d: smbus_quick -> %s\n", dev->idx, res ? "NACK" : "ACK");
out:
/* Remove reset */
out_8(&iic->xtcntlss, 0);
/* Reinitialize interface */
iic_dev_init(dev);
return res;
err:
DBG("%d: smbus_quick - bus is stuck\n", dev->idx);
res = -EREMOTEIO;
goto out;
}
/*
* IIC interrupt handler
*/
......@@ -457,13 +580,10 @@ static int iic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
if (unlikely(msgs[i].len <= 0)){
if (num == 1 && !msgs[0].len){
/* Special case for I2C_SMBUS_QUICK emulation.
* Although this logic is FAR FROM PERFECT, this
* is what previous driver version did.
* IBM IIC doesn't support 0-length transactions
* (except bit-banging through IICx_DIRECTCNTL).
* so we have to emulate them using bit-banging.
*/
DBG("%d: zero-length msg kludge\n", dev->idx);
return 0;
return iic_smbus_quick(dev, &msgs[0]);
}
DBG("%d: invalid len %d in msg[%d]\n", dev->idx,
msgs[i].len, i);
......
......@@ -458,6 +458,7 @@ static int __init scx200_acb_create(int base, int index)
adapter->owner = THIS_MODULE;
adapter->id = I2C_ALGO_SMBUS;
adapter->algo = &scx200_acb_algorithm;
adapter->class = I2C_CLASS_HWMON;
init_MUTEX(&iface->sem);
......
......@@ -22,6 +22,26 @@ config SENSORS_ADM1021
This driver can also be built as a module. If so, the module
will be called adm1021.
config SENSORS_ADM1025
tristate "Analog Devices ADM1025 and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Analog Devices ADM1025
and Philips NE1619 sensor chips.
This driver can also be built as a module. If so, the module
will be called adm1025.
config SENSORS_ADM1031
tristate "Analog Devices ADM1031 and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Analog Devices ADM1031
and ADM1030 sensor chips.
This driver can also be built as a module. If so, the module
will be called adm1031.
config SENSORS_ASB100
tristate "Asus ASB100 Bach"
depends on I2C && EXPERIMENTAL
......@@ -83,12 +103,27 @@ config SENSORS_LM75
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor LM75
sensor chips and clones: Dallas Semi DS75 and DS1775, TelCon
TCN75, and National Semiconductor LM77.
sensor chips and clones: Dallas Semiconductor DS75 and DS1775 (in
9-bit precision mode), and TelCom (now Microchip) TCN75.
The DS75 and DS1775 in 10- to 12-bit precision modes will require
a force module parameter. The driver will not handle the extra
precision anyhow.
This driver can also be built as a module. If so, the module
will be called lm75.
config SENSORS_LM77
tristate "National Semiconductor LM77"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor LM77
sensor chips.
This driver can also be built as a module. If so, the module
will be called lm77.
config SENSORS_LM78
tristate "National Semiconductor LM78 and compatibles"
depends on I2C && EXPERIMENTAL
......@@ -140,7 +175,8 @@ config SENSORS_LM90
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor LM90,
LM89 and LM99, and Analog Devices ADM1032 sensor chips.
LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657 and
MAX6658 sensor chips.
This driver can also be built as a module. If so, the module
will be called lm90.
......
......@@ -8,12 +8,15 @@ obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o
obj-$(CONFIG_SENSORS_FSCHER) += fscher.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_LM75) += lm75.o
obj-$(CONFIG_SENSORS_LM77) += lm77.o
obj-$(CONFIG_SENSORS_LM78) += lm78.o
obj-$(CONFIG_SENSORS_LM80) += lm80.o
obj-$(CONFIG_SENSORS_LM83) += lm83.o
......
This diff is collapsed.
This diff is collapsed.
......@@ -113,7 +113,7 @@ static int lm75_attach_adapter(struct i2c_adapter *adapter)
/* This function is called by i2c_detect */
static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i, cur, conf, hyst, os;
int i;
struct i2c_client *new_client;
struct lm75_data *data;
int err = 0;
......@@ -149,16 +149,41 @@ static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
new_client->driver = &lm75_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. It is lousy. */
/* Now, we do the remaining detection. There is no identification-
dedicated register so we have to rely on several tricks:
unused bits, registers cycling over 8-address boundaries,
addresses 0x04-0x07 returning the last read value.
The cycling+unused addresses combination is not tested,
since it would significantly slow the detection down and would
hardly add any value. */
if (kind < 0) {
int cur, conf, hyst, os;
/* Unused addresses */
cur = i2c_smbus_read_word_data(new_client, 0);
conf = i2c_smbus_read_byte_data(new_client, 1);
hyst = i2c_smbus_read_word_data(new_client, 2);
if (i2c_smbus_read_word_data(new_client, 4) != hyst
|| i2c_smbus_read_word_data(new_client, 5) != hyst
|| i2c_smbus_read_word_data(new_client, 6) != hyst
|| i2c_smbus_read_word_data(new_client, 7) != hyst)
goto exit_free;
os = i2c_smbus_read_word_data(new_client, 3);
for (i = 0; i <= 0x1f; i++)
if ((i2c_smbus_read_byte_data(new_client, i * 8 + 1) != conf) ||
(i2c_smbus_read_word_data(new_client, i * 8 + 2) != hyst) ||
(i2c_smbus_read_word_data(new_client, i * 8 + 3) != os))
if (i2c_smbus_read_word_data(new_client, 4) != os
|| i2c_smbus_read_word_data(new_client, 5) != os
|| i2c_smbus_read_word_data(new_client, 6) != os
|| i2c_smbus_read_word_data(new_client, 7) != os)
goto exit_free;
/* Unused bits */
if (conf & 0xe0)
goto exit_free;
/* Addresses cycling */
for (i = 8; i < 0xff; i += 8)
if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
|| i2c_smbus_read_word_data(new_client, i + 2) != hyst
|| i2c_smbus_read_word_data(new_client, i + 3) != os)
goto exit_free;
}
......
This diff is collapsed.
......@@ -123,55 +123,6 @@ static inline u8 DIV_TO_REG(int val)
}
#define DIV_FROM_REG(val) (1 << (val))
/* Initial limits. To keep them sane, we use the 'standard' translation as
specified in the LM78 sheet. Use the config file to set better limits. */
#define LM78_INIT_IN_0(vid) ((vid)==3500 ? 2800 : (vid))
#define LM78_INIT_IN_1(vid) ((vid)==3500 ? 2800 : (vid))
#define LM78_INIT_IN_2 3300
#define LM78_INIT_IN_3 (((5000) * 100)/168)
#define LM78_INIT_IN_4 (((12000) * 10)/38)
#define LM78_INIT_IN_5 (((-12000) * -604)/2100)
#define LM78_INIT_IN_6 (((-5000) * -604)/909)
#define LM78_INIT_IN_PERCENTAGE 10
#define LM78_INIT_IN_MIN_0(vid) (LM78_INIT_IN_0(vid) - \
LM78_INIT_IN_0(vid) * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_0(vid) (LM78_INIT_IN_0(vid) + \
LM78_INIT_IN_0(vid) * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_1(vid) (LM78_INIT_IN_1(vid) - \
LM78_INIT_IN_1(vid) * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_1(vid) (LM78_INIT_IN_1(vid) + \
LM78_INIT_IN_1(vid) * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_2 \
(LM78_INIT_IN_2 - LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_2 \
(LM78_INIT_IN_2 + LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_3 \
(LM78_INIT_IN_3 - LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_3 \
(LM78_INIT_IN_3 + LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_4 \
(LM78_INIT_IN_4 - LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_4 \
(LM78_INIT_IN_4 + LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_5 \
(LM78_INIT_IN_5 - LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_5 \
(LM78_INIT_IN_5 + LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_6 \
(LM78_INIT_IN_6 - LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_6 \
(LM78_INIT_IN_6 + LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_FAN_MIN_1 3000
#define LM78_INIT_FAN_MIN_2 3000
#define LM78_INIT_FAN_MIN_3 3000
#define LM78_INIT_TEMP_OVER 60000
#define LM78_INIT_TEMP_HYST 50000
/* There are some complications in a module like this. First off, LM78 chips
may be both present on the SMBus and the ISA bus, and we have to handle
those cases separately at some places. Second, there might be several
......@@ -756,45 +707,6 @@ static void lm78_init_client(struct i2c_client *client)
vid |= 0x10;
vid = VID_FROM_REG(vid);
lm78_write_value(client, LM78_REG_IN_MIN(0),
IN_TO_REG(LM78_INIT_IN_MIN_0(vid)));
lm78_write_value(client, LM78_REG_IN_MAX(0),
IN_TO_REG(LM78_INIT_IN_MAX_0(vid)));
lm78_write_value(client, LM78_REG_IN_MIN(1),
IN_TO_REG(LM78_INIT_IN_MIN_1(vid)));
lm78_write_value(client, LM78_REG_IN_MAX(1),
IN_TO_REG(LM78_INIT_IN_MAX_1(vid)));
lm78_write_value(client, LM78_REG_IN_MIN(2),
IN_TO_REG(LM78_INIT_IN_MIN_2));
lm78_write_value(client, LM78_REG_IN_MAX(2),
IN_TO_REG(LM78_INIT_IN_MAX_2));
lm78_write_value(client, LM78_REG_IN_MIN(3),
IN_TO_REG(LM78_INIT_IN_MIN_3));
lm78_write_value(client, LM78_REG_IN_MAX(3),
IN_TO_REG(LM78_INIT_IN_MAX_3));
lm78_write_value(client, LM78_REG_IN_MIN(4),
IN_TO_REG(LM78_INIT_IN_MIN_4));
lm78_write_value(client, LM78_REG_IN_MAX(4),
IN_TO_REG(LM78_INIT_IN_MAX_4));
lm78_write_value(client, LM78_REG_IN_MIN(5),
IN_TO_REG(LM78_INIT_IN_MIN_5));
lm78_write_value(client, LM78_REG_IN_MAX(5),
IN_TO_REG(LM78_INIT_IN_MAX_5));
lm78_write_value(client, LM78_REG_IN_MIN(6),
IN_TO_REG(LM78_INIT_IN_MIN_6));
lm78_write_value(client, LM78_REG_IN_MAX(6),
IN_TO_REG(LM78_INIT_IN_MAX_6));
lm78_write_value(client, LM78_REG_FAN_MIN(0),
FAN_TO_REG(LM78_INIT_FAN_MIN_1, 2));
lm78_write_value(client, LM78_REG_FAN_MIN(1),
FAN_TO_REG(LM78_INIT_FAN_MIN_2, 2));
lm78_write_value(client, LM78_REG_FAN_MIN(2),
FAN_TO_REG(LM78_INIT_FAN_MIN_3, 2));
lm78_write_value(client, LM78_REG_TEMP_OVER,
TEMP_TO_REG(LM78_INIT_TEMP_OVER));
lm78_write_value(client, LM78_REG_TEMP_HYST,
TEMP_TO_REG(LM78_INIT_TEMP_HYST));
/* Start monitoring */
lm78_write_value(client, LM78_REG_CONFIG,
(lm78_read_value(client, LM78_REG_CONFIG) & 0xf7)
......
......@@ -21,11 +21,26 @@
* http://www.national.com/pf/LM/LM99.html
* Note that there is no way to differenciate between both chips.
*
* This driver also supports the LM86, another sensor chip made by
* National Semiconductor. It is exactly similar to the LM90 except it
* has a higher accuracy.
* Complete datasheet can be obtained from National's website at:
* http://www.national.com/pf/LM/LM86.html
*
* This driver also supports the ADM1032, a sensor chip made by Analog
* Devices. That chip is similar to the LM90, with a few differences
* that are not handled by this driver. Complete datasheet can be
* obtained from Analog's website at:
* http://products.analog.com/products/info.asp?product=ADM1032
* Among others, it has a higher accuracy than the LM90, much like the
* LM86 does.
*
* This driver also supports the MAX6657 and MAX6658, sensor chips made
* by Maxim. These chips are similar to the LM86. Complete datasheet
* can be obtained at Maxim's website at:
* http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578
* Note that there is no way to differenciate between both chips (but
* no need either).
*
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
......@@ -56,7 +71,7 @@
/*
* Addresses to scan
* Address is fully defined internally and cannot be changed.
* LM89, LM90, LM99 and ADM1032 have address 0x4c.
* LM86, LM89, LM90, LM99, ADM1032, MAX6657 and MAX6658 have address 0x4c.
* LM89-1, and LM99-1 have address 0x4d.
*/
......@@ -69,7 +84,7 @@ static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
* Insmod parameters
*/
SENSORS_INSMOD_3(lm90, adm1032, lm99);
SENSORS_INSMOD_5(lm90, adm1032, lm99, lm86, max6657);
/*
* The LM90 registers
......@@ -289,7 +304,6 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
struct lm90_data *data;
int err = 0;
const char *name = "";
u8 reg_config1=0, reg_convrate=0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
......@@ -319,28 +333,22 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
* requested, so both the detection and the identification steps
* are skipped.
*/
if (kind < 0) { /* detection */
reg_config1 = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONFIG1);
reg_convrate = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONVRATE);
if ((reg_config1 & 0x2A) != 0x00
|| reg_convrate > 0x0A) {
dev_dbg(&adapter->dev,
"LM90 detection failed at 0x%02x.\n",
address);
goto exit_free;
}
}
/* Default to an LM90 if forced */
if (kind == 0)
kind = lm90;
if (kind <= 0) { /* identification */
u8 man_id, chip_id;
if (kind < 0) { /* detection and identification */
u8 man_id, chip_id, reg_config1, reg_convrate;
man_id = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_MAN_ID);
chip_id = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CHIP_ID);
reg_config1 = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONFIG1);
reg_convrate = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONVRATE);
if (man_id == 0x01) { /* National Semiconductor */
u8 reg_config2;
......@@ -348,25 +356,36 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
reg_config2 = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONFIG2);
if (kind == 0 /* skip detection */
|| ((reg_config2 & 0xF8) == 0x00
&& reg_convrate <= 0x09)) {
if ((reg_config1 & 0x2A) == 0x00
&& (reg_config2 & 0xF8) == 0x00
&& reg_convrate <= 0x09) {
if (address == 0x4C
&& (chip_id & 0xF0) == 0x20) { /* LM90 */
kind = lm90;
} else
if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */
kind = lm99;
} else
if (address == 0x4C
&& (chip_id & 0xF0) == 0x10) { /* LM86 */
kind = lm86;
}
}
} else
if (man_id == 0x41) { /* Analog Devices */
if (address == 0x4C
&& (chip_id & 0xF0) == 0x40 /* ADM1032 */
&& (kind == 0 /* skip detection */
|| (reg_config1 & 0x3F) == 0x00)) {
&& (reg_config1 & 0x3F) == 0x00
&& reg_convrate <= 0x0A) {
kind = adm1032;
}
} else
if (man_id == 0x4D) { /* Maxim */
if (address == 0x4C
&& (reg_config1 & 0x1F) == 0
&& reg_convrate <= 0x09) {
kind = max6657;
}
}
if (kind <= 0) { /* identification failed */
......@@ -383,6 +402,10 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
name = "adm1032";
} else if (kind == lm99) {
name = "lm99";
} else if (kind == lm86) {
name = "lm86";
} else if (kind == max6657) {
name = "max6657";
}
/* We can fill in the remaining client fields */
......
......@@ -518,20 +518,29 @@ static int __init i2c_dev_init(void)
printk(KERN_INFO "i2c /dev entries driver\n");
if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) {
printk(KERN_ERR "i2c-dev.o: unable to get major %d for i2c bus\n",
I2C_MAJOR);
return -EIO;
}
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
if (res)
goto out;
res = class_register(&i2c_dev_class);
if (res)
goto out_unreg_chrdev;
res = i2c_add_driver(&i2cdev_driver);
if (res)
goto out_unreg_class;
devfs_mk_dir("i2c");
class_register(&i2c_dev_class);
if ((res = i2c_add_driver(&i2cdev_driver))) {
printk(KERN_ERR "i2c-dev.o: Driver registration failed, module not inserted.\n");
devfs_remove("i2c");
unregister_chrdev(I2C_MAJOR,"i2c");
return res;
}
return 0;
out_unreg_class:
class_unregister(&i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev(I2C_MAJOR, "i2c");
out:
printk(KERN_ERR "%s: Driver Initialisation failed", __FILE__);
return res;
}
static void __exit i2c_dev_exit(void)
......
menu "Dallas's 1-wire bus"
config W1
tristate "Dallas's 1-wire support"
---help---
Dallas's 1-wire bus is usefull to connect slow 1-pin devices
such as iButtons and thermal sensors.
If you want W1 support, you should say Y here.
This W1 support can also be built as a module. If so, the module
will be called wire.ko.
config W1_MATROX
tristate "Matrox G400 transport layer for 1-wire"
depends on W1
help
Say Y here if you want to communicate with your 1-wire devices
using Matrox's G400 GPIO pins.
This support is also available as a module. If so, the module
will be called matrox_w1.ko.
config W1_THERM
tristate "Thermal family implementation"
depends on W1
help
Say Y here if you want to connect 1-wire thermal sensors to you
wire.
endmenu
#
# Makefile for the Dallas's 1-wire bus.
#
obj-$(CONFIG_W1) += wire.o
wire-objs := w1.o w1_int.o w1_family.o w1_netlink.o w1_io.o
obj-$(CONFIG_W1_MATROX) += matrox_w1.o
obj-$(CONFIG_W1_THERM) += w1_therm.o
/*
* matrox_w1.c
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <asm/atomic.h>
#include <asm/types.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/pci_ids.h>
#include <linux/pci.h>
#include <linux/timer.h>
#include "w1.h"
#include "w1_int.h"
#include "w1_log.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
MODULE_DESCRIPTION("Driver for transport(Dallas 1-wire prtocol) over VGA DDC(matrox gpio).");
static struct pci_device_id matrox_w1_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400) },
{ },
};
MODULE_DEVICE_TABLE(pci, matrox_w1_tbl);
static int __devinit matrox_w1_probe(struct pci_dev *, const struct pci_device_id *);
static void __devexit matrox_w1_remove(struct pci_dev *);
static struct pci_driver matrox_w1_pci_driver = {
.name = "matrox_w1",
.id_table = matrox_w1_tbl,
.probe = matrox_w1_probe,
.remove = __devexit_p(matrox_w1_remove),
};
/*
* Matrox G400 DDC registers.
*/
#define MATROX_G400_DDC_CLK (1<<4)
#define MATROX_G400_DDC_DATA (1<<1)
#define MATROX_BASE 0x3C00
#define MATROX_STATUS 0x1e14
#define MATROX_PORT_INDEX_OFFSET 0x00
#define MATROX_PORT_DATA_OFFSET 0x0A
#define MATROX_GET_CONTROL 0x2A
#define MATROX_GET_DATA 0x2B
#define MATROX_CURSOR_CTL 0x06
struct matrox_device
{
unsigned long base_addr;
unsigned long port_index, port_data;
u8 data_mask;
unsigned long phys_addr, virt_addr;
unsigned long found;
struct w1_bus_master *bus_master;
};
static u8 matrox_w1_read_ddc_bit(unsigned long);
static void matrox_w1_write_ddc_bit(unsigned long, u8);
/*
* These functions read and write DDC Data bit.
*
* Using tristate pins, since i can't fin any open-drain pin in whole motherboard.
* Unfortunately we can't connect to Intel's 82801xx IO controller
* since we don't know motherboard schema, wich has pretty unused(may be not) GPIO.
*
* I've heard that PIIX also has open drain pin.
*
* Port mapping.
*/
static __inline__ u8 matrox_w1_read_reg(struct matrox_device *dev, u8 reg)
{
u8 ret;
writeb(reg, dev->port_index);
ret = readb(dev->port_data);
barrier();
return ret;
}
static __inline__ void matrox_w1_write_reg(struct matrox_device *dev, u8 reg, u8 val)
{
writeb(reg, dev->port_index);
writeb(val, dev->port_data);
wmb();
}
static void matrox_w1_write_ddc_bit(unsigned long data, u8 bit)
{
u8 ret;
struct matrox_device *dev = (struct matrox_device *) data;
if (bit)
bit = 0;
else
bit = dev->data_mask;
ret = matrox_w1_read_reg(dev, MATROX_GET_CONTROL);
matrox_w1_write_reg(dev, MATROX_GET_CONTROL, ((ret & ~dev->data_mask) | bit));
matrox_w1_write_reg(dev, MATROX_GET_DATA, 0x00);
}
static u8 matrox_w1_read_ddc_bit(unsigned long data)
{
u8 ret;
struct matrox_device *dev = (struct matrox_device *) data;
ret = matrox_w1_read_reg(dev, MATROX_GET_DATA);
return ret;
}
static void matrox_w1_hw_init(struct matrox_device *dev)
{
matrox_w1_write_reg(dev, MATROX_GET_DATA, 0xFF);
matrox_w1_write_reg(dev, MATROX_GET_CONTROL, 0x00);
}
static int __devinit matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct matrox_device *dev;
int err;
assert(pdev != NULL);
assert(ent != NULL);
if (pdev->vendor != PCI_VENDOR_ID_MATROX || pdev->device != PCI_DEVICE_ID_MATROX_G400)
return -ENODEV;
dev = kmalloc(sizeof(struct matrox_device) +
sizeof(struct w1_bus_master), GFP_KERNEL);
if (!dev) {
dev_err(&pdev->dev,
"%s: Failed to create new matrox_device object.\n",
__func__);
return -ENOMEM;
}
memset(dev, 0, sizeof(struct matrox_device) + sizeof(struct w1_bus_master));
dev->bus_master = (struct w1_bus_master *)(dev + 1);
/*
* True for G400, for some other we need resource 0, see drivers/video/matrox/matroxfb_base.c
*/
dev->phys_addr = pci_resource_start(pdev, 1);
dev->virt_addr =
(unsigned long) ioremap_nocache(dev->phys_addr, 16384);
if (!dev->virt_addr) {
dev_err(&pdev->dev, "%s: failed to ioremap(0x%lx, %d).\n",
__func__, dev->phys_addr, 16384);
err = -EIO;
goto err_out_free_device;
}
dev->base_addr = dev->virt_addr + MATROX_BASE;
dev->port_index = dev->base_addr + MATROX_PORT_INDEX_OFFSET;
dev->port_data = dev->base_addr + MATROX_PORT_DATA_OFFSET;
dev->data_mask = (MATROX_G400_DDC_DATA);
matrox_w1_hw_init(dev);
dev->bus_master->data = (unsigned long) dev;
dev->bus_master->read_bit = &matrox_w1_read_ddc_bit;
dev->bus_master->write_bit = &matrox_w1_write_ddc_bit;
err = w1_add_master_device(dev->bus_master);
if (err)
goto err_out_free_device;
pci_set_drvdata(pdev, dev);
dev->found = 1;
dev_info(&pdev->dev, "Matrox G400 GPIO transport layer for 1-wire.\n");
return 0;
err_out_free_device:
kfree(dev);
return err;
}
static void __devexit matrox_w1_remove(struct pci_dev *pdev)
{
struct matrox_device *dev = pci_get_drvdata(pdev);
assert(dev != NULL);
if (dev->found) {
w1_remove_master_device(dev->bus_master);
iounmap((void *) dev->virt_addr);
}
kfree(dev);
}
static int __init matrox_w1_init(void)
{
return pci_module_init(&matrox_w1_pci_driver);
}
static void __exit matrox_w1_fini(void)
{
pci_unregister_driver(&matrox_w1_pci_driver);
}
module_init(matrox_w1_init);
module_exit(matrox_w1_fini);
This diff is collapsed.
/*
* w1.h
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __W1_H
#define __W1_H
struct w1_reg_num
{
__u64 family:8,
id:48,
crc:8;
};
#ifdef __KERNEL__
#include <linux/completion.h>
#include <linux/device.h>
#include <net/sock.h>
#include <asm/semaphore.h>
#include "w1_family.h"
#define W1_MAXNAMELEN 32
#define W1_SLAVE_DATA_SIZE 128
#define W1_SEARCH 0xF0
#define W1_CONDITIONAL_SEARCH 0xEC
#define W1_CONVERT_TEMP 0x44
#define W1_SKIP_ROM 0xCC
#define W1_READ_SCRATCHPAD 0xBE
#define W1_READ_ROM 0x33
#define W1_READ_PSUPPLY 0xB4
#define W1_MATCH_ROM 0x55
struct w1_slave
{
struct module *owner;
unsigned char name[W1_MAXNAMELEN];
struct list_head w1_slave_entry;
struct w1_reg_num reg_num;
atomic_t refcnt;
u8 rom[9];
struct w1_master *master;
struct w1_family *family;
struct device dev;
struct completion dev_released;
};
struct w1_bus_master
{
unsigned long data;
u8 (*read_bit)(unsigned long);
void (*write_bit)(unsigned long, u8);
};
struct w1_master
{
struct list_head w1_master_entry;
struct module *owner;
unsigned char name[W1_MAXNAMELEN];
struct list_head slist;
int max_slave_count, slave_count;
unsigned long attempts;
int initialized;
u32 id;
atomic_t refcnt;
void *priv;
int priv_size;
int need_exit;
pid_t kpid;
wait_queue_head_t kwait;
struct semaphore mutex;
struct device_driver *driver;
struct device dev;
struct completion dev_released;
struct completion dev_exited;
struct w1_bus_master *bus_master;
u32 seq, groups;
struct sock *nls;
};
#endif /* __KERNEL__ */
#endif /* __W1_H */
/*
* w1_family.c
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/spinlock.h>
#include <linux/list.h>
#include "w1_family.h"
spinlock_t w1_flock = SPIN_LOCK_UNLOCKED;
static LIST_HEAD(w1_families);
int w1_register_family(struct w1_family *newf)
{
struct list_head *ent, *n;
struct w1_family *f;
int ret = 0;
spin_lock(&w1_flock);
list_for_each_safe(ent, n, &w1_families) {
f = list_entry(ent, struct w1_family, family_entry);
if (f->fid == newf->fid) {
ret = -EEXIST;
break;
}
}
if (!ret) {
atomic_set(&newf->refcnt, 0);
newf->need_exit = 0;
list_add_tail(&newf->family_entry, &w1_families);
}
spin_unlock(&w1_flock);
return ret;
}
void w1_unregister_family(struct w1_family *fent)
{
struct list_head *ent, *n;
struct w1_family *f;
spin_lock(&w1_flock);
list_for_each_safe(ent, n, &w1_families) {
f = list_entry(ent, struct w1_family, family_entry);
if (f->fid == fent->fid) {
list_del(&fent->family_entry);
break;
}
}
fent->need_exit = 1;
spin_unlock(&w1_flock);
while (atomic_read(&fent->refcnt))
schedule_timeout(10);
}
/*
* Should be called under w1_flock held.
*/
struct w1_family * w1_family_registered(u8 fid)
{
struct list_head *ent, *n;
struct w1_family *f = NULL;
int ret = 0;
list_for_each_safe(ent, n, &w1_families) {
f = list_entry(ent, struct w1_family, family_entry);
if (f->fid == fid) {
ret = 1;
break;
}
}
return (ret) ? f : NULL;
}
void w1_family_put(struct w1_family *f)
{
spin_lock(&w1_flock);
__w1_family_put(f);
spin_unlock(&w1_flock);
}
void __w1_family_put(struct w1_family *f)
{
if (atomic_dec_and_test(&f->refcnt))
f->need_exit = 1;
}
void w1_family_get(struct w1_family *f)
{
spin_lock(&w1_flock);
__w1_family_get(f);
spin_unlock(&w1_flock);
}
void __w1_family_get(struct w1_family *f)
{
atomic_inc(&f->refcnt);
}
EXPORT_SYMBOL(w1_family_get);
EXPORT_SYMBOL(w1_family_put);
EXPORT_SYMBOL(__w1_family_get);
EXPORT_SYMBOL(__w1_family_put);
EXPORT_SYMBOL(w1_family_registered);
EXPORT_SYMBOL(w1_unregister_family);
EXPORT_SYMBOL(w1_register_family);
/*
* w1_family.h
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __W1_FAMILY_H
#define __W1_FAMILY_H
#include <linux/types.h>
#include <linux/device.h>
#include <asm/atomic.h>
#define W1_FAMILY_DEFAULT 0
#define W1_FAMILY_THERM 0x10
#define W1_FAMILY_IBUT 0xff /* ? */
#define MAXNAMELEN 32
struct w1_family_ops
{
ssize_t (* rname)(struct device *, char *);
ssize_t (* rbin)(struct kobject *, char *, loff_t, size_t);
ssize_t (* rval)(struct device *, char *);
unsigned char rvalname[MAXNAMELEN];
};
struct w1_family
{
struct list_head family_entry;
u8 fid;
struct w1_family_ops *fops;
atomic_t refcnt;
u8 need_exit;
};
extern spinlock_t w1_flock;
void w1_family_get(struct w1_family *);
void w1_family_put(struct w1_family *);
void __w1_family_get(struct w1_family *);
void __w1_family_put(struct w1_family *);
struct w1_family * w1_family_registered(u8);
void w1_unregister_family(struct w1_family *);
int w1_register_family(struct w1_family *);
#endif /* __W1_FAMILY_H */
/*
* w1_int.c
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/list.h>
#include "w1.h"
#include "w1_log.h"
static u32 w1_ids = 1;
extern struct device_driver w1_driver;
extern struct bus_type w1_bus_type;
extern struct device w1_device;
extern struct device_attribute w1_master_attribute;
extern int w1_max_slave_count;
extern struct list_head w1_masters;
extern spinlock_t w1_mlock;
extern int w1_process(void *);
struct w1_master * w1_alloc_dev(u32 id, int slave_count,
struct device_driver *driver, struct device *device)
{
struct w1_master *dev;
int err;
/*
* We are in process context(kernel thread), so can sleep.
*/
dev = kmalloc(sizeof(struct w1_master) + sizeof(struct w1_bus_master), GFP_KERNEL);
if (!dev) {
printk(KERN_ERR
"Failed to allocate %d bytes for new w1 device.\n",
sizeof(struct w1_master));
return NULL;
}
memset(dev, 0, sizeof(struct w1_master) + sizeof(struct w1_bus_master));
dev->bus_master = (struct w1_bus_master *)(dev + 1);
dev->owner = THIS_MODULE;
dev->max_slave_count = slave_count;
dev->slave_count = 0;
dev->attempts = 0;
dev->kpid = -1;
dev->initialized = 0;
dev->id = id;
atomic_set(&dev->refcnt, 2);
INIT_LIST_HEAD(&dev->slist);
init_MUTEX(&dev->mutex);
init_waitqueue_head(&dev->kwait);
init_completion(&dev->dev_released);
init_completion(&dev->dev_exited);
memcpy(&dev->dev, device, sizeof(struct device));
snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
"w1_bus_master%u", dev->id);
snprintf(dev->name, sizeof(dev->name), "w1_bus_master%u", dev->id);
dev->driver = driver;
dev->groups = 23;
dev->seq = 1;
dev->nls = netlink_kernel_create(NETLINK_NFLOG, NULL);
if (!dev->nls) {
printk(KERN_ERR "Failed to create new netlink socket(%u).\n",
NETLINK_NFLOG);
memset(dev, 0, sizeof(struct w1_master));
kfree(dev);
dev = NULL;
}
err = device_register(&dev->dev);
if (err) {
printk(KERN_ERR "Failed to register master device. err=%d\n", err);
if (dev->nls->sk_socket)
sock_release(dev->nls->sk_socket);
memset(dev, 0, sizeof(struct w1_master));
kfree(dev);
dev = NULL;
}
return dev;
}
void w1_free_dev(struct w1_master *dev)
{
device_unregister(&dev->dev);
if (dev->nls->sk_socket)
sock_release(dev->nls->sk_socket);
memset(dev, 0, sizeof(struct w1_master) + sizeof(struct w1_bus_master));
kfree(dev);
}
int w1_add_master_device(struct w1_bus_master *master)
{
struct w1_master *dev;
int retval = 0;
dev = w1_alloc_dev(w1_ids++, w1_max_slave_count, &w1_driver, &w1_device);
if (!dev)
return -ENOMEM;
dev->kpid = kernel_thread(&w1_process, dev, 0);
if (dev->kpid < 0) {
dev_err(&dev->dev,
"Failed to create new kernel thread. err=%d\n",
dev->kpid);
retval = dev->kpid;
goto err_out_free_dev;
}
retval = device_create_file(&dev->dev, &w1_master_attribute);
if (retval)
goto err_out_kill_thread;
memcpy(dev->bus_master, master, sizeof(struct w1_bus_master));
dev->initialized = 1;
spin_lock(&w1_mlock);
list_add(&dev->w1_master_entry, &w1_masters);
spin_unlock(&w1_mlock);
return 0;
err_out_kill_thread:
dev->need_exit = 1;
if (kill_proc(dev->kpid, SIGTERM, 1))
dev_err(&dev->dev,
"Failed to send signal to w1 kernel thread %d.\n",
dev->kpid);
wait_for_completion(&dev->dev_exited);
err_out_free_dev:
w1_free_dev(dev);
return retval;
}
void __w1_remove_master_device(struct w1_master *dev)
{
int err;
dev->need_exit = 1;
err = kill_proc(dev->kpid, SIGTERM, 1);
if (err)
dev_err(&dev->dev,
"%s: Failed to send signal to w1 kernel thread %d.\n",
__func__, dev->kpid);
while (atomic_read(&dev->refcnt))
schedule_timeout(10);
w1_free_dev(dev);
}
void w1_remove_master_device(struct w1_bus_master *bm)
{
struct w1_master *dev = NULL;
struct list_head *ent, *n;
list_for_each_safe(ent, n, &w1_masters) {
dev = list_entry(ent, struct w1_master, w1_master_entry);
if (!dev->initialized)
continue;
if (dev->bus_master->data == bm->data)
break;
}
if (!dev) {
printk(KERN_ERR "Device doesn't exist.\n");
return;
}
__w1_remove_master_device(dev);
}
EXPORT_SYMBOL(w1_alloc_dev);
EXPORT_SYMBOL(w1_free_dev);
EXPORT_SYMBOL(w1_add_master_device);
EXPORT_SYMBOL(w1_remove_master_device);
EXPORT_SYMBOL(__w1_remove_master_device);
/*
* w1_int.h
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __W1_INT_H
#define __W1_INT_H
#include <linux/kernel.h>
#include <linux/device.h>
#include "w1.h"
struct w1_master * w1_alloc_dev(int, struct device_driver *, struct device *);
void w1_free_dev(struct w1_master *dev);
int w1_add_master_device(struct w1_bus_master *);
void w1_remove_master_device(struct w1_bus_master *);
void __w1_remove_master_device(struct w1_master *);
#endif /* __W1_INT_H */
/*
* w1_io.c
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <asm/io.h>
#include <asm/delay.h>
#include <linux/moduleparam.h>
#include "w1.h"
#include "w1_log.h"
#include "w1_io.h"
int w1_delay_parm = 1;
module_param_named(delay_coef, w1_delay_parm, int, 0);
static u8 w1_crc8_table[] = {
0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,
17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,
175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238,
50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};
void w1_delay(unsigned long tm)
{
udelay(tm * w1_delay_parm);
}
void w1_write_bit(struct w1_master *dev, int bit)
{
if (bit) {
dev->bus_master->write_bit(dev->bus_master->data, 0);
w1_delay(6);
dev->bus_master->write_bit(dev->bus_master->data, 1);
w1_delay(64);
} else {
dev->bus_master->write_bit(dev->bus_master->data, 0);
w1_delay(60);
dev->bus_master->write_bit(dev->bus_master->data, 1);
w1_delay(10);
}
}
void w1_write_8(struct w1_master *dev, u8 byte)
{
int i;
for (i = 0; i < 8; ++i)
w1_write_bit(dev, (byte >> i) & 0x1);
}
u8 w1_read_bit(struct w1_master *dev)
{
int result;
dev->bus_master->write_bit(dev->bus_master->data, 0);
w1_delay(6);
dev->bus_master->write_bit(dev->bus_master->data, 1);
w1_delay(9);
result = dev->bus_master->read_bit(dev->bus_master->data);
w1_delay(55);
return result & 0x1;
}
u8 w1_read_8(struct w1_master * dev)
{
int i;
u8 res = 0;
for (i = 0; i < 8; ++i)
res |= (w1_read_bit(dev) << i);
return res;
}
int w1_reset_bus(struct w1_master *dev)
{
int result;
dev->bus_master->write_bit(dev->bus_master->data, 0);
w1_delay(480);
dev->bus_master->write_bit(dev->bus_master->data, 1);
w1_delay(70);
result = dev->bus_master->read_bit(dev->bus_master->data) & 0x1;
w1_delay(410);
return result;
}
u8 w1_calc_crc8(u8 * data, int len)
{
u8 crc = 0;
while (len--)
crc = w1_crc8_table[crc ^ *data++];
return crc;
}
EXPORT_SYMBOL(w1_write_bit);
EXPORT_SYMBOL(w1_write_8);
EXPORT_SYMBOL(w1_read_bit);
EXPORT_SYMBOL(w1_read_8);
EXPORT_SYMBOL(w1_reset_bus);
EXPORT_SYMBOL(w1_calc_crc8);
EXPORT_SYMBOL(w1_delay);
/*
* w1_io.h
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __W1_IO_H
#define __W1_IO_H
#include "w1.h"
void w1_delay(unsigned long);
void w1_write_bit(struct w1_master *, int);
void w1_write_8(struct w1_master *, u8);
u8 w1_read_bit(struct w1_master *);
u8 w1_read_8(struct w1_master *);
int w1_reset_bus(struct w1_master *);
u8 w1_calc_crc8(u8 *, int);
#endif /* __W1_IO_H */
/*
* w1_log.h
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __W1_LOG_H
#define __W1_LOG_H
#define DEBUG
#ifdef W1_DEBUG
# define assert(expr) do {} while (0)
#else
# define assert(expr) \
if(unlikely(!(expr))) { \
printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
#expr,__FILE__,__FUNCTION__,__LINE__); \
}
#endif
#endif /* __W1_LOG_H */
/*
* w1_netlink.c
*
* Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include "w1.h"
#include "w1_log.h"
#include "w1_netlink.h"
void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
{
unsigned int size;
struct sk_buff *skb;
struct w1_netlink_msg *data;
struct nlmsghdr *nlh;
size = NLMSG_SPACE(sizeof(struct w1_netlink_msg));
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) {
dev_err(&dev->dev, "skb_alloc() failed.\n");
return;
}
nlh = NLMSG_PUT(skb, 0, dev->seq++, NLMSG_DONE, size - sizeof(*nlh));
data = (struct w1_netlink_msg *)NLMSG_DATA(nlh);
memcpy(data, msg, sizeof(struct w1_netlink_msg));
NETLINK_CB(skb).dst_groups = dev->groups;
netlink_broadcast(dev->nls, skb, 0, dev->groups, GFP_ATOMIC);
nlmsg_failure:
return;
}
/*
* w1_netlink.h
*
* Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __W1_NETLINK_H
#define __W1_NETLINK_H
#include <asm/types.h>
#include "w1.h"
struct w1_netlink_msg
{
union
{
struct w1_reg_num id;
__u64 w1_id;
} id;
__u64 val;
};
#ifdef __KERNEL__
void w1_netlink_send(struct w1_master *, struct w1_netlink_msg *);
#endif /* __KERNEL__ */
#endif /* __W1_NETLINK_H */
/*
* w1_therm.c
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the therms 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <asm/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/types.h>
#include "w1.h"
#include "w1_io.h"
#include "w1_int.h"
#include "w1_family.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family.");
static ssize_t w1_therm_read_name(struct device *, char *);
static ssize_t w1_therm_read_temp(struct device *, char *);
static ssize_t w1_therm_read_bin(struct kobject *, char *, loff_t, size_t);
static struct w1_family_ops w1_therm_fops = {
.rname = &w1_therm_read_name,
.rbin = &w1_therm_read_bin,
.rval = &w1_therm_read_temp,
.rvalname = "temp1_input",
};
static ssize_t w1_therm_read_name(struct device *dev, char *buf)
{
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
return sprintf(buf, "%s\n", sl->name);
}
static ssize_t w1_therm_read_temp(struct device *dev, char *buf)
{
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
s16 temp;
/*
* Must be more precise.
*/
temp = 0;
temp <<= sl->rom[1] / 2;
temp |= sl->rom[0] / 2;
return sprintf(buf, "%d\n", temp * 1000);
}
static ssize_t w1_therm_read_bin(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
struct w1_slave *sl = container_of(container_of(kobj, struct device, kobj),
struct w1_slave, dev);
struct w1_master *dev = sl->master;
u8 rom[9], crc, verdict;
size_t icount;
int i;
u16 temp;
atomic_inc(&sl->refcnt);
if (down_interruptible(&sl->master->mutex)) {
count = 0;
goto out_dec;
}
if (off > W1_SLAVE_DATA_SIZE) {
count = 0;
goto out;
}
if (off + count > W1_SLAVE_DATA_SIZE)
count = W1_SLAVE_DATA_SIZE - off;
icount = count;
memset(buf, 0, count);
memset(rom, 0, sizeof(rom));
count = 0;
verdict = 0;
crc = 0;
if (!w1_reset_bus(dev)) {
u64 id = *(u64 *) & sl->reg_num;
int count = 0;
w1_write_8(dev, W1_MATCH_ROM);
for (i = 0; i < 8; ++i)
w1_write_8(dev, (id >> i * 8) & 0xff);
w1_write_8(dev, W1_CONVERT_TEMP);
while (dev->bus_master->read_bit(dev->bus_master->data) == 0
&& count < 10) {
w1_delay(1);
count++;
}
if (count < 10) {
if (!w1_reset_bus(dev)) {
w1_write_8(dev, W1_MATCH_ROM);
for (i = 0; i < 8; ++i)
w1_write_8(dev,
(id >> i * 8) & 0xff);
w1_write_8(dev, W1_READ_SCRATCHPAD);
for (i = 0; i < 9; ++i)
rom[i] = w1_read_8(dev);
crc = w1_calc_crc8(rom, 8);
if (rom[8] == crc && rom[0])
verdict = 1;
}
}
else
dev_warn(&dev->dev,
"18S20 doesn't respond to CONVERT_TEMP.\n");
}
for (i = 0; i < 9; ++i)
count += snprintf(buf + count, icount - count, "%02x ", rom[i]);
count += snprintf(buf + count, icount - count, ": crc=%02x %s\n",
crc, (verdict) ? "YES" : "NO");
if (verdict)
memcpy(sl->rom, rom, sizeof(sl->rom));
for (i = 0; i < 9; ++i)
count += snprintf(buf + count, icount - count, "%02x ", sl->rom[i]);
temp = 0;
temp <<= sl->rom[1] / 2;
temp |= sl->rom[0] / 2;
count += snprintf(buf + count, icount - count, "t=%u\n", temp);
out:
up(&dev->mutex);
out_dec:
atomic_dec(&sl->refcnt);
return count;
}
static struct w1_family w1_therm_family = {
.fid = W1_FAMILY_THERM,
.fops = &w1_therm_fops,
};
static int __init w1_therm_init(void)
{
return w1_register_family(&w1_therm_family);
}
static void __exit w1_therm_fini(void)
{
w1_unregister_family(&w1_therm_family);
}
module_init(w1_therm_init);
module_exit(w1_therm_fini);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment