Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit baf12ae2 authored by Evgeniy Polyakov's avatar Evgeniy Polyakov Committed by Greg Kroah-Hartman
Browse files

[PATCH] W1: Add the DS2482 I2C-to-w1 bridge driver.

parent bd529cfb
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
Kernel driver ds2482
====================

Supported chips:
  * Maxim DS2482-100, Maxim DS2482-800
    Prefix: 'ds2482'
    Addresses scanned: None
    Datasheets:
        http://pdfserv.maxim-ic.com/en/ds/DS2482-100-DS2482S-100.pdf
        http://pdfserv.maxim-ic.com/en/ds/DS2482-800-DS2482S-800.pdf

Author: Ben Gardner <bgardner@wabtec.com>


Description
-----------

The Maixm/Dallas Semiconductor DS2482 is a I2C device that provides
one (DS2482-100) or eight (DS2482-800) 1-wire busses.


General Remarks
---------------

Valid addresses are 0x18, 0x19, 0x1a, and 0x1b.
However, the device cannot be detected without writing to the i2c bus, so no
detection is done.
You should force the device address.

$ modprobe ds2482 force=0,0x18
+10 −0
Original line number Diff line number Diff line
@@ -34,5 +34,15 @@ config W1_MASTER_DS9490_BRIDGE
	  This support is also available as a module.  If so, the module
	  will be called ds_w1_bridge.ko.

config W1_MASTER_DS2482
	tristate "Maxim DS2482 I2C to 1-Wire bridge"
	depends on I2C && W1 && EXPERIMENTAL
	help
	  If you say yes here you get support for the Maxim DS2482
	  I2C to 1-Wire bridge.

	  This driver can also be built as a module.  If so, the module
	  will be called ds2482.

endmenu
+2 −0
Original line number Diff line number Diff line
@@ -9,3 +9,5 @@ ds9490r-objs := dscore.o

obj-$(CONFIG_W1_MASTER_DS9490_BRIDGE)	+= ds_w1_bridge.o

obj-$(CONFIG_W1_MASTER_DS2482)		+= ds2482.o
+564 −0
Original line number Diff line number Diff line
/**
 * ds2482.c - provides i2c to w1-master bridge(s)
 * Copyright (C) 2005  Ben Gardner <bgardner@wabtec.com>
 *
 * The DS2482 is a sensor chip made by Dallas Semiconductor (Maxim).
 * It is a I2C to 1-wire bridge.
 * There are two variations: -100 and -800, which have 1 or 8 1-wire ports.
 * The complete datasheet can be obtained from MAXIM's website at:
 *   http://www.maxim-ic.com/quick_view2.cfm/qv_pk/4382
 *
 * 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; version 2 of the License.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <asm/delay.h>

#include "../w1.h"
#include "../w1_int.h"

/**
 * Address is selected using 2 pins, resulting in 4 possible addresses.
 *  0x18, 0x19, 0x1a, 0x1b
 * However, the chip cannot be detected without doing an i2c write,
 * so use the force module parameter.
 */
static unsigned short normal_i2c[] = {I2C_CLIENT_END};

/**
 * Insmod parameters
 */
I2C_CLIENT_INSMOD_1(ds2482);

/**
 * The DS2482 registers - there are 3 registers that are addressed by a read
 * pointer. The read pointer is set by the last command executed.
 *
 * To read the data, issue a register read for any address
 */
#define DS2482_CMD_RESET		0xF0	/* No param */
#define DS2482_CMD_SET_READ_PTR		0xE1	/* Param: DS2482_PTR_CODE_xxx */
#define DS2482_CMD_CHANNEL_SELECT	0xC3	/* Param: Channel byte - DS2482-800 only */
#define DS2482_CMD_WRITE_CONFIG		0xD2	/* Param: Config byte */
#define DS2482_CMD_1WIRE_RESET		0xB4	/* Param: None */
#define DS2482_CMD_1WIRE_SINGLE_BIT	0x87	/* Param: Bit byte (bit7) */
#define DS2482_CMD_1WIRE_WRITE_BYTE	0xA5	/* Param: Data byte */
#define DS2482_CMD_1WIRE_READ_BYTE	0x96	/* Param: None */
/* Note to read the byte, Set the ReadPtr to Data then read (any addr) */
#define DS2482_CMD_1WIRE_TRIPLET	0x78	/* Param: Dir byte (bit7) */

/* Values for DS2482_CMD_SET_READ_PTR */
#define DS2482_PTR_CODE_STATUS		0xF0
#define DS2482_PTR_CODE_DATA		0xE1
#define DS2482_PTR_CODE_CHANNEL		0xD2	/* DS2482-800 only */
#define DS2482_PTR_CODE_CONFIG		0xC3

/**
 * Configure Register bit definitions
 * The top 4 bits always read 0.
 * To write, the top nibble must be the 1's compl. of the low nibble.
 */
#define DS2482_REG_CFG_1WS		0x08
#define DS2482_REG_CFG_SPU		0x04
#define DS2482_REG_CFG_PPM		0x02
#define DS2482_REG_CFG_APU		0x01


/**
 * Write and verify codes for the CHANNEL_SELECT command (DS2482-800 only).
 * To set the channel, write the value at the index of the channel.
 * Read and compare against the corresponding value to verify the change.
 */
static const u8 ds2482_chan_wr[8] =
	{ 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87 };
static const u8 ds2482_chan_rd[8] =
	{ 0xB8, 0xB1, 0xAA, 0xA3, 0x9C, 0x95, 0x8E, 0x87 };


/**
 * Status Register bit definitions (read only)
 */
#define DS2482_REG_STS_DIR		0x80
#define DS2482_REG_STS_TSB		0x40
#define DS2482_REG_STS_SBR		0x20
#define DS2482_REG_STS_RST		0x10
#define DS2482_REG_STS_LL		0x08
#define DS2482_REG_STS_SD		0x04
#define DS2482_REG_STS_PPD		0x02
#define DS2482_REG_STS_1WB		0x01


static int ds2482_attach_adapter(struct i2c_adapter *adapter);
static int ds2482_detect(struct i2c_adapter *adapter, int address, int kind);
static int ds2482_detach_client(struct i2c_client *client);


/**
 * Driver data (common to all clients)
 */
static struct i2c_driver ds2482_driver = {
	.driver = {
		.owner	= THIS_MODULE,
		.name	= "ds2482",
	},
	.attach_adapter	= ds2482_attach_adapter,
	.detach_client	= ds2482_detach_client,
};

/*
 * Client data (each client gets its own)
 */

struct ds2482_data;

struct ds2482_w1_chan {
	struct ds2482_data	*pdev;
	u8			channel;
	struct w1_bus_master	w1_bm;
};

struct ds2482_data {
	struct i2c_client	client;
	struct semaphore	access_lock;

	/* 1-wire interface(s) */
	int			w1_count;	/* 1 or 8 */
	struct ds2482_w1_chan	w1_ch[8];

	/* per-device values */
	u8			channel;
	u8			read_prt;	/* see DS2482_PTR_CODE_xxx */
	u8			reg_config;
};


/**
 * Sets the read pointer.
 * @param pdev		The ds2482 client pointer
 * @param read_ptr	see DS2482_PTR_CODE_xxx above
 * @return -1 on failure, 0 on success
 */
static inline int ds2482_select_register(struct ds2482_data *pdev, u8 read_ptr)
{
	if (pdev->read_prt != read_ptr) {
		if (i2c_smbus_write_byte_data(&pdev->client,
					      DS2482_CMD_SET_READ_PTR,
					      read_ptr) < 0)
			return -1;

		pdev->read_prt = read_ptr;
	}
	return 0;
}

/**
 * Sends a command without a parameter
 * @param pdev	The ds2482 client pointer
 * @param cmd	DS2482_CMD_RESET,
 *		DS2482_CMD_1WIRE_RESET,
 *		DS2482_CMD_1WIRE_READ_BYTE
 * @return -1 on failure, 0 on success
 */
static inline int ds2482_send_cmd(struct ds2482_data *pdev, u8 cmd)
{
	if (i2c_smbus_write_byte(&pdev->client, cmd) < 0)
		return -1;

	pdev->read_prt = DS2482_PTR_CODE_STATUS;
	return 0;
}

/**
 * Sends a command with a parameter
 * @param pdev	The ds2482 client pointer
 * @param cmd	DS2482_CMD_WRITE_CONFIG,
 *		DS2482_CMD_1WIRE_SINGLE_BIT,
 *		DS2482_CMD_1WIRE_WRITE_BYTE,
 *		DS2482_CMD_1WIRE_TRIPLET
 * @param byte	The data to send
 * @return -1 on failure, 0 on success
 */
static inline int ds2482_send_cmd_data(struct ds2482_data *pdev,
				       u8 cmd, u8 byte)
{
	if (i2c_smbus_write_byte_data(&pdev->client, cmd, byte) < 0)
		return -1;

	/* all cmds leave in STATUS, except CONFIG */
	pdev->read_prt = (cmd != DS2482_CMD_WRITE_CONFIG) ?
			 DS2482_PTR_CODE_STATUS : DS2482_PTR_CODE_CONFIG;
	return 0;
}


/*
 * 1-Wire interface code
 */

#define DS2482_WAIT_IDLE_TIMEOUT	100

/**
 * Waits until the 1-wire interface is idle (not busy)
 *
 * @param pdev Pointer to the device structure
 * @return the last value read from status or -1 (failure)
 */
static int ds2482_wait_1wire_idle(struct ds2482_data *pdev)
{
	int temp = -1;
	int retries = 0;

	if (!ds2482_select_register(pdev, DS2482_PTR_CODE_STATUS)) {
		do {
			temp = i2c_smbus_read_byte(&pdev->client);
		} while ((temp >= 0) && (temp & DS2482_REG_STS_1WB) &&
			 (++retries > DS2482_WAIT_IDLE_TIMEOUT));
	}

	if (retries > DS2482_WAIT_IDLE_TIMEOUT)
		printk(KERN_ERR "%s: timeout on channel %d\n",
		       __func__, pdev->channel);

	return temp;
}

/**
 * Selects a w1 channel.
 * The 1-wire interface must be idle before calling this function.
 *
 * @param pdev		The ds2482 client pointer
 * @param channel	0-7
 * @return		-1 (failure) or 0 (success)
 */
static int ds2482_set_channel(struct ds2482_data *pdev, u8 channel)
{
	if (i2c_smbus_write_byte_data(&pdev->client, DS2482_CMD_CHANNEL_SELECT,
				      ds2482_chan_wr[channel]) < 0)
		return -1;

	pdev->read_prt = DS2482_PTR_CODE_CHANNEL;
	pdev->channel = -1;
	if (i2c_smbus_read_byte(&pdev->client) == ds2482_chan_rd[channel]) {
		pdev->channel = channel;
		return 0;
	}
	return -1;
}


/**
 * Performs the touch-bit function, which writes a 0 or 1 and reads the level.
 *
 * @param data	The ds2482 channel pointer
 * @param bit	The level to write: 0 or non-zero
 * @return	The level read: 0 or 1
 */
static u8 ds2482_w1_touch_bit(void *data, u8 bit)
{
	struct ds2482_w1_chan *pchan = data;
	struct ds2482_data    *pdev = pchan->pdev;
	int status = -1;

	down(&pdev->access_lock);

	/* Select the channel */
	ds2482_wait_1wire_idle(pdev);
	if (pdev->w1_count > 1)
		ds2482_set_channel(pdev, pchan->channel);

	/* Send the touch command, wait until 1WB == 0, return the status */
	if (!ds2482_send_cmd_data(pdev, DS2482_CMD_1WIRE_SINGLE_BIT,
				  bit ? 0xFF : 0))
		status = ds2482_wait_1wire_idle(pdev);

	up(&pdev->access_lock);

	return (status & DS2482_REG_STS_SBR) ? 1 : 0;
}

/**
 * Performs the triplet function, which reads two bits and writes a bit.
 * The bit written is determined by the two reads:
 *   00 => dbit, 01 => 0, 10 => 1
 *
 * @param data	The ds2482 channel pointer
 * @param dbit	The direction to choose if both branches are valid
 * @return	b0=read1 b1=read2 b3=bit written
 */
static u8 ds2482_w1_triplet(void *data, u8 dbit)
{
	struct ds2482_w1_chan *pchan = data;
	struct ds2482_data    *pdev = pchan->pdev;
	int status = (3 << 5);

	down(&pdev->access_lock);

	/* Select the channel */
	ds2482_wait_1wire_idle(pdev);
	if (pdev->w1_count > 1)
		ds2482_set_channel(pdev, pchan->channel);

	/* Send the triplet command, wait until 1WB == 0, return the status */
	if (!ds2482_send_cmd_data(pdev, DS2482_CMD_1WIRE_TRIPLET,
				  dbit ? 0xFF : 0))
		status = ds2482_wait_1wire_idle(pdev);

	up(&pdev->access_lock);

	/* Decode the status */
	return (status >> 5);
}

/**
 * Performs the write byte function.
 *
 * @param data	The ds2482 channel pointer
 * @param byte	The value to write
 */
static void ds2482_w1_write_byte(void *data, u8 byte)
{
	struct ds2482_w1_chan *pchan = data;
	struct ds2482_data    *pdev = pchan->pdev;

	down(&pdev->access_lock);

	/* Select the channel */
	ds2482_wait_1wire_idle(pdev);
	if (pdev->w1_count > 1)
		ds2482_set_channel(pdev, pchan->channel);

	/* Send the write byte command */
	ds2482_send_cmd_data(pdev, DS2482_CMD_1WIRE_WRITE_BYTE, byte);

	up(&pdev->access_lock);
}

/**
 * Performs the read byte function.
 *
 * @param data	The ds2482 channel pointer
 * @return	The value read
 */
static u8 ds2482_w1_read_byte(void *data)
{
	struct ds2482_w1_chan *pchan = data;
	struct ds2482_data    *pdev = pchan->pdev;
	int result;

	down(&pdev->access_lock);

	/* Select the channel */
	ds2482_wait_1wire_idle(pdev);
	if (pdev->w1_count > 1)
		ds2482_set_channel(pdev, pchan->channel);

	/* Send the read byte command */
	ds2482_send_cmd(pdev, DS2482_CMD_1WIRE_READ_BYTE);

	/* Wait until 1WB == 0 */
	ds2482_wait_1wire_idle(pdev);

	/* Select the data register */
	ds2482_select_register(pdev, DS2482_PTR_CODE_DATA);

	/* Read the data byte */
	result = i2c_smbus_read_byte(&pdev->client);

	up(&pdev->access_lock);

	return result;
}


/**
 * Sends a reset on the 1-wire interface
 *
 * @param data	The ds2482 channel pointer
 * @return	0=Device present, 1=No device present or error
 */
static u8 ds2482_w1_reset_bus(void *data)
{
	struct ds2482_w1_chan *pchan = data;
	struct ds2482_data    *pdev = pchan->pdev;
	int err;
	u8 retval = 1;

	down(&pdev->access_lock);

	/* Select the channel */
	ds2482_wait_1wire_idle(pdev);
	if (pdev->w1_count > 1)
		ds2482_set_channel(pdev, pchan->channel);

	/* Send the reset command */
	err = ds2482_send_cmd(pdev, DS2482_CMD_1WIRE_RESET);
	if (err >= 0) {
		/* Wait until the reset is complete */
		err = ds2482_wait_1wire_idle(pdev);
		retval = !(err & DS2482_REG_STS_PPD);

		/* If the chip did reset since detect, re-config it */
		if (err & DS2482_REG_STS_RST)
			ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG,
					     0xF0);
	}

	up(&pdev->access_lock);

	return retval;
}


/**
 * Called to see if the device exists on an i2c bus.
 */
static int ds2482_attach_adapter(struct i2c_adapter *adapter)
{
	return i2c_probe(adapter, &addr_data, ds2482_detect);
}


/*
 * The following function does more than just detection. If detection
 * succeeds, it also registers the new chip.
 */
static int ds2482_detect(struct i2c_adapter *adapter, int address, int kind)
{
	struct ds2482_data *data;
	struct i2c_client  *new_client;
	int err = 0;
	int temp1;
	int idx;

	if (!i2c_check_functionality(adapter,
				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
				     I2C_FUNC_SMBUS_BYTE))
		goto exit;

	if (!(data = kzalloc(sizeof(struct ds2482_data), GFP_KERNEL))) {
		err = -ENOMEM;
		goto exit;
	}

	new_client = &data->client;
	i2c_set_clientdata(new_client, data);
	new_client->addr = address;
	new_client->driver = &ds2482_driver;
	new_client->adapter = adapter;

	/* Reset the device (sets the read_ptr to status) */
	if (ds2482_send_cmd(data, DS2482_CMD_RESET) < 0) {
		dev_dbg(&adapter->dev, "DS2482 reset failed at 0x%02x.\n",
			address);
		goto exit_free;
	}

	/* Sleep at least 525ns to allow the reset to complete */
	ndelay(525);

	/* Read the status byte - only reset bit and line should be set */
	temp1 = i2c_smbus_read_byte(new_client);
	if (temp1 != (DS2482_REG_STS_LL | DS2482_REG_STS_RST)) {
		dev_dbg(&adapter->dev, "DS2482 (0x%02x) reset status "
			"0x%02X - not a DS2482\n", address, temp1);
		goto exit_free;
	}

	/* Detect the 8-port version */
	data->w1_count = 1;
	if (ds2482_set_channel(data, 7) == 0)
		data->w1_count = 8;

	/* Set all config items to 0 (off) */
	ds2482_send_cmd_data(data, DS2482_CMD_WRITE_CONFIG, 0xF0);

	/* We can fill in the remaining client fields */
	snprintf(new_client->name, sizeof(new_client->name), "ds2482-%d00",
		 data->w1_count);

	init_MUTEX(&data->access_lock);

	/* Tell the I2C layer a new client has arrived */
	if ((err = i2c_attach_client(new_client)))
		goto exit_free;

	/* Register 1-wire interface(s) */
	for (idx = 0; idx < data->w1_count; idx++) {
		data->w1_ch[idx].pdev = data;
		data->w1_ch[idx].channel = idx;

		/* Populate all the w1 bus master stuff */
		data->w1_ch[idx].w1_bm.data       = &data->w1_ch[idx];
		data->w1_ch[idx].w1_bm.read_byte  = ds2482_w1_read_byte;
		data->w1_ch[idx].w1_bm.write_byte = ds2482_w1_write_byte;
		data->w1_ch[idx].w1_bm.touch_bit  = ds2482_w1_touch_bit;
		data->w1_ch[idx].w1_bm.triplet    = ds2482_w1_triplet;
		data->w1_ch[idx].w1_bm.reset_bus  = ds2482_w1_reset_bus;

		err = w1_add_master_device(&data->w1_ch[idx].w1_bm);
		if (err) {
			data->w1_ch[idx].pdev = NULL;
			goto exit_w1_remove;
		}
	}

	return 0;

exit_w1_remove:
	i2c_detach_client(new_client);

	for (idx = 0; idx < data->w1_count; idx++) {
		if (data->w1_ch[idx].pdev != NULL)
			w1_remove_master_device(&data->w1_ch[idx].w1_bm);
	}
exit_free:
	kfree(data);
exit:
	return err;
}

static int ds2482_detach_client(struct i2c_client *client)
{
	struct ds2482_data   *data = i2c_get_clientdata(client);
	int err, idx;

	/* Unregister the 1-wire bridge(s) */
	for (idx = 0; idx < data->w1_count; idx++) {
		if (data->w1_ch[idx].pdev != NULL)
			w1_remove_master_device(&data->w1_ch[idx].w1_bm);
	}

	/* Detach the i2c device */
	if ((err = i2c_detach_client(client))) {
		dev_err(&client->dev,
			"Deregistration failed, client not detached.\n");
		return err;
	}

	/* Free the memory */
	kfree(data);
	return 0;
}

static int __init sensors_ds2482_init(void)
{
	return i2c_add_driver(&ds2482_driver);
}

static void __exit sensors_ds2482_exit(void)
{
	i2c_del_driver(&ds2482_driver);
}

MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
MODULE_DESCRIPTION("DS2482 driver");
MODULE_LICENSE("GPL");

module_init(sensors_ds2482_init);
module_exit(sensors_ds2482_exit);