ad525x_dpot: add support for SPI parts
Split the bus logic out into separate files so that we can handle I2C and SPI busses independently. The new SPI bus logic brings in support for a lot more parts: AD5160, AD5161, AD5162, AD5165, AD5200, AD5201, AD5203, AD5204, AD5206, AD5207, AD5231, AD5232, AD5233, AD5235, AD5260, AD5262, AD5263, AD5290, AD5291, AD5292, AD5293, AD7376, AD8400, AD8402, AD8403, ADN2850 [randy.dunlap@oracle.com: fix ad525X_dpot build] Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Signed-off-by: Mike Frysinger <vapier@gentoo.org> Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
0c53b9fbcc
commit
6c536e4ce8
|
@ -14,11 +14,15 @@ menuconfig MISC_DEVICES
|
|||
if MISC_DEVICES
|
||||
|
||||
config AD525X_DPOT
|
||||
tristate "Analog Devices AD525x Digital Potentiometers"
|
||||
depends on I2C && SYSFS
|
||||
tristate "Analog Devices Digital Potentiometers"
|
||||
depends on (I2C || SPI) && SYSFS
|
||||
help
|
||||
If you say yes here, you get support for the Analog Devices
|
||||
AD5258, AD5259, AD5251, AD5252, AD5253, AD5254 and AD5255
|
||||
AD5258, AD5259, AD5251, AD5252, AD5253, AD5254, AD5255
|
||||
AD5160, AD5161, AD5162, AD5165, AD5200, AD5201, AD5203,
|
||||
AD5204, AD5206, AD5207, AD5231, AD5232, AD5233, AD5235,
|
||||
AD5260, AD5262, AD5263, AD5290, AD5291, AD5292, AD5293,
|
||||
AD7376, AD8400, AD8402, AD8403, ADN2850
|
||||
digital potentiometer chips.
|
||||
|
||||
See Documentation/misc-devices/ad525x_dpot.txt for the
|
||||
|
@ -27,6 +31,26 @@ config AD525X_DPOT
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called ad525x_dpot.
|
||||
|
||||
config AD525X_DPOT_I2C
|
||||
tristate "support I2C bus connection"
|
||||
depends on AD525X_DPOT && I2C
|
||||
help
|
||||
Say Y here if you have a digital potentiometers hooked to an I2C bus.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad525x_dpot-i2c.
|
||||
|
||||
config AD525X_DPOT_SPI
|
||||
tristate "support SPI bus connection"
|
||||
depends on AD525X_DPOT && SPI_MASTER
|
||||
help
|
||||
Say Y here if you have a digital potentiometers hooked to an SPI bus.
|
||||
|
||||
If unsure, say N (but it's safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad525x_dpot-spi.
|
||||
|
||||
config ATMEL_PWM
|
||||
tristate "Atmel AT32/AT91 PWM support"
|
||||
depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
obj-$(CONFIG_IBM_ASM) += ibmasm/
|
||||
obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
|
||||
obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
|
||||
obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
|
||||
obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
|
||||
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
|
||||
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
|
||||
|
|
119
drivers/misc/ad525x_dpot-i2c.c
Normal file
119
drivers/misc/ad525x_dpot-i2c.c
Normal file
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Driver for the Analog Devices digital potentiometers (I2C bus)
|
||||
*
|
||||
* Copyright (C) 2010 Michael Hennerich, Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "ad525x_dpot.h"
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* I2C bus functions */
|
||||
static int write_d8(void *client, u8 val)
|
||||
{
|
||||
return i2c_smbus_write_byte(client, val);
|
||||
}
|
||||
|
||||
static int write_r8d8(void *client, u8 reg, u8 val)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(client, reg, val);
|
||||
}
|
||||
|
||||
static int write_r8d16(void *client, u8 reg, u16 val)
|
||||
{
|
||||
return i2c_smbus_write_word_data(client, reg, val);
|
||||
}
|
||||
|
||||
static int read_d8(void *client)
|
||||
{
|
||||
return i2c_smbus_read_byte(client);
|
||||
}
|
||||
|
||||
static int read_r8d8(void *client, u8 reg)
|
||||
{
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
}
|
||||
|
||||
static int read_r8d16(void *client, u8 reg)
|
||||
{
|
||||
return i2c_smbus_read_word_data(client, reg);
|
||||
}
|
||||
|
||||
static const struct ad_dpot_bus_ops bops = {
|
||||
.read_d8 = read_d8,
|
||||
.read_r8d8 = read_r8d8,
|
||||
.read_r8d16 = read_r8d16,
|
||||
.write_d8 = write_d8,
|
||||
.write_r8d8 = write_r8d8,
|
||||
.write_r8d16 = write_r8d16,
|
||||
};
|
||||
|
||||
static int __devinit ad_dpot_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ad_dpot_bus_data bdata = {
|
||||
.client = client,
|
||||
.bops = &bops,
|
||||
};
|
||||
|
||||
struct ad_dpot_id dpot_id = {
|
||||
.name = (char *) &id->name,
|
||||
.devid = id->driver_data,
|
||||
};
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA)) {
|
||||
dev_err(&client->dev, "SMBUS Word Data not Supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return ad_dpot_probe(&client->dev, &bdata, &dpot_id);
|
||||
}
|
||||
|
||||
static int __devexit ad_dpot_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
return ad_dpot_remove(&client->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad_dpot_id[] = {
|
||||
{"ad5258", AD5258_ID},
|
||||
{"ad5259", AD5259_ID},
|
||||
{"ad5251", AD5251_ID},
|
||||
{"ad5252", AD5252_ID},
|
||||
{"ad5253", AD5253_ID},
|
||||
{"ad5254", AD5254_ID},
|
||||
{"ad5255", AD5255_ID},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad_dpot_id);
|
||||
|
||||
static struct i2c_driver ad_dpot_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ad_dpot",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad_dpot_i2c_probe,
|
||||
.remove = __devexit_p(ad_dpot_i2c_remove),
|
||||
.id_table = ad_dpot_id,
|
||||
};
|
||||
|
||||
static int __init ad_dpot_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&ad_dpot_i2c_driver);
|
||||
}
|
||||
module_init(ad_dpot_i2c_init);
|
||||
|
||||
static void __exit ad_dpot_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&ad_dpot_i2c_driver);
|
||||
}
|
||||
module_exit(ad_dpot_i2c_exit);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("digital potentiometer I2C bus driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("i2c:ad_dpot");
|
172
drivers/misc/ad525x_dpot-spi.c
Normal file
172
drivers/misc/ad525x_dpot-spi.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Driver for the Analog Devices digital potentiometers (SPI bus)
|
||||
*
|
||||
* Copyright (C) 2010 Michael Hennerich, Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "ad525x_dpot.h"
|
||||
|
||||
static const struct ad_dpot_id ad_dpot_spi_devlist[] = {
|
||||
{.name = "ad5160", .devid = AD5160_ID},
|
||||
{.name = "ad5161", .devid = AD5161_ID},
|
||||
{.name = "ad5162", .devid = AD5162_ID},
|
||||
{.name = "ad5165", .devid = AD5165_ID},
|
||||
{.name = "ad5200", .devid = AD5200_ID},
|
||||
{.name = "ad5201", .devid = AD5201_ID},
|
||||
{.name = "ad5203", .devid = AD5203_ID},
|
||||
{.name = "ad5204", .devid = AD5204_ID},
|
||||
{.name = "ad5206", .devid = AD5206_ID},
|
||||
{.name = "ad5207", .devid = AD5207_ID},
|
||||
{.name = "ad5231", .devid = AD5231_ID},
|
||||
{.name = "ad5232", .devid = AD5232_ID},
|
||||
{.name = "ad5233", .devid = AD5233_ID},
|
||||
{.name = "ad5235", .devid = AD5235_ID},
|
||||
{.name = "ad5260", .devid = AD5260_ID},
|
||||
{.name = "ad5262", .devid = AD5262_ID},
|
||||
{.name = "ad5263", .devid = AD5263_ID},
|
||||
{.name = "ad5290", .devid = AD5290_ID},
|
||||
{.name = "ad5291", .devid = AD5291_ID},
|
||||
{.name = "ad5292", .devid = AD5292_ID},
|
||||
{.name = "ad5293", .devid = AD5293_ID},
|
||||
{.name = "ad7376", .devid = AD7376_ID},
|
||||
{.name = "ad8400", .devid = AD8400_ID},
|
||||
{.name = "ad8402", .devid = AD8402_ID},
|
||||
{.name = "ad8403", .devid = AD8403_ID},
|
||||
{.name = "adn2850", .devid = ADN2850_ID},
|
||||
{}
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* SPI bus functions */
|
||||
static int write8(void *client, u8 val)
|
||||
{
|
||||
u8 data = val;
|
||||
return spi_write(client, &data, 1);
|
||||
}
|
||||
|
||||
static int write16(void *client, u8 reg, u8 val)
|
||||
{
|
||||
u8 data[2] = {reg, val};
|
||||
return spi_write(client, data, 1);
|
||||
}
|
||||
|
||||
static int write24(void *client, u8 reg, u16 val)
|
||||
{
|
||||
u8 data[3] = {reg, val >> 8, val};
|
||||
return spi_write(client, data, 1);
|
||||
}
|
||||
|
||||
static int read8(void *client)
|
||||
{
|
||||
int ret;
|
||||
u8 data;
|
||||
ret = spi_read(client, &data, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int read16(void *client, u8 reg)
|
||||
{
|
||||
int ret;
|
||||
u8 buf_rx[2];
|
||||
|
||||
write16(client, reg, 0);
|
||||
ret = spi_read(client, buf_rx, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (buf_rx[0] << 8) | buf_rx[1];
|
||||
}
|
||||
|
||||
static int read24(void *client, u8 reg)
|
||||
{
|
||||
int ret;
|
||||
u8 buf_rx[3];
|
||||
|
||||
write24(client, reg, 0);
|
||||
ret = spi_read(client, buf_rx, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (buf_rx[1] << 8) | buf_rx[2];
|
||||
}
|
||||
|
||||
static const struct ad_dpot_bus_ops bops = {
|
||||
.read_d8 = read8,
|
||||
.read_r8d8 = read16,
|
||||
.read_r8d16 = read24,
|
||||
.write_d8 = write8,
|
||||
.write_r8d8 = write16,
|
||||
.write_r8d16 = write24,
|
||||
};
|
||||
|
||||
static const struct ad_dpot_id *dpot_match_id(const struct ad_dpot_id *id,
|
||||
char *name)
|
||||
{
|
||||
while (id->name && id->name[0]) {
|
||||
if (strcmp(name, id->name) == 0)
|
||||
return id;
|
||||
id++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __devinit ad_dpot_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
char *name = spi->dev.platform_data;
|
||||
const struct ad_dpot_id *dpot_id;
|
||||
|
||||
struct ad_dpot_bus_data bdata = {
|
||||
.client = spi,
|
||||
.bops = &bops,
|
||||
};
|
||||
|
||||
dpot_id = dpot_match_id(ad_dpot_spi_devlist, name);
|
||||
|
||||
if (dpot_id == NULL) {
|
||||
dev_err(&spi->dev, "%s not in supported device list", name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return ad_dpot_probe(&spi->dev, &bdata, dpot_id);
|
||||
}
|
||||
|
||||
static int __devexit ad_dpot_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return ad_dpot_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static struct spi_driver ad_dpot_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad_dpot",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad_dpot_spi_probe,
|
||||
.remove = __devexit_p(ad_dpot_spi_remove),
|
||||
};
|
||||
|
||||
static int __init ad_dpot_spi_init(void)
|
||||
{
|
||||
return spi_register_driver(&ad_dpot_spi_driver);
|
||||
}
|
||||
module_init(ad_dpot_spi_init);
|
||||
|
||||
static void __exit ad_dpot_spi_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&ad_dpot_spi_driver);
|
||||
}
|
||||
module_exit(ad_dpot_spi_exit);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("digital potentiometer SPI bus driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("spi:ad_dpot");
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* ad525x_dpot: Driver for the Analog Devices AD525x digital potentiometers
|
||||
* Copyright (c) 2009 Analog Devices, Inc.
|
||||
* ad525x_dpot: Driver for the Analog Devices digital potentiometers
|
||||
* Copyright (c) 2009-2010 Analog Devices, Inc.
|
||||
* Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
|
||||
*
|
||||
* DEVID #Wipers #Positions Resistor Options (kOhm)
|
||||
|
@ -11,6 +11,32 @@
|
|||
* AD5255 3 512 25, 250
|
||||
* AD5253 4 64 1, 10, 50, 100
|
||||
* AD5254 4 256 1, 10, 50, 100
|
||||
* AD5160 1 256 5, 10, 50, 100
|
||||
* AD5161 1 256 5, 10, 50, 100
|
||||
* AD5162 2 256 2.5, 10, 50, 100
|
||||
* AD5165 1 256 100
|
||||
* AD5200 1 256 10, 50
|
||||
* AD5201 1 33 10, 50
|
||||
* AD5203 4 64 10, 100
|
||||
* AD5204 4 256 10, 50, 100
|
||||
* AD5206 6 256 10, 50, 100
|
||||
* AD5207 2 256 10, 50, 100
|
||||
* AD5231 1 1024 10, 50, 100
|
||||
* AD5232 2 256 10, 50, 100
|
||||
* AD5233 4 64 10, 50, 100
|
||||
* AD5235 2 1024 25, 250
|
||||
* AD5260 1 256 20, 50, 200
|
||||
* AD5262 2 256 20, 50, 200
|
||||
* AD5263 4 256 20, 50, 200
|
||||
* AD5290 1 256 10, 50, 100
|
||||
* AD5291 1 256 20
|
||||
* AD5292 1 1024 20
|
||||
* AD5293 1 1024 20
|
||||
* AD7376 1 128 10, 50, 100, 1M
|
||||
* AD8400 1 256 1, 10, 50, 100
|
||||
* AD8402 2 256 1, 10, 50, 100
|
||||
* AD8403 4 256 1, 10, 50, 100
|
||||
* ADN2850 3 512 25, 250
|
||||
*
|
||||
* See Documentation/misc-devices/ad525x_dpot.txt for more info.
|
||||
*
|
||||
|
@ -28,77 +54,182 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define DRIVER_NAME "ad525x_dpot"
|
||||
#define DRIVER_VERSION "0.1"
|
||||
#define DRIVER_VERSION "0.2"
|
||||
|
||||
enum dpot_devid {
|
||||
AD5258_ID,
|
||||
AD5259_ID,
|
||||
AD5251_ID,
|
||||
AD5252_ID,
|
||||
AD5253_ID,
|
||||
AD5254_ID,
|
||||
AD5255_ID,
|
||||
};
|
||||
|
||||
#define AD5258_MAX_POSITION 64
|
||||
#define AD5259_MAX_POSITION 256
|
||||
#define AD5251_MAX_POSITION 64
|
||||
#define AD5252_MAX_POSITION 256
|
||||
#define AD5253_MAX_POSITION 64
|
||||
#define AD5254_MAX_POSITION 256
|
||||
#define AD5255_MAX_POSITION 512
|
||||
|
||||
#define AD525X_RDAC0 0
|
||||
#define AD525X_RDAC1 1
|
||||
#define AD525X_RDAC2 2
|
||||
#define AD525X_RDAC3 3
|
||||
|
||||
#define AD525X_REG_TOL 0x18
|
||||
#define AD525X_TOL_RDAC0 (AD525X_REG_TOL | AD525X_RDAC0)
|
||||
#define AD525X_TOL_RDAC1 (AD525X_REG_TOL | AD525X_RDAC1)
|
||||
#define AD525X_TOL_RDAC2 (AD525X_REG_TOL | AD525X_RDAC2)
|
||||
#define AD525X_TOL_RDAC3 (AD525X_REG_TOL | AD525X_RDAC3)
|
||||
|
||||
/* RDAC-to-EEPROM Interface Commands */
|
||||
#define AD525X_I2C_RDAC (0x00 << 5)
|
||||
#define AD525X_I2C_EEPROM (0x01 << 5)
|
||||
#define AD525X_I2C_CMD (0x80)
|
||||
|
||||
#define AD525X_DEC_ALL_6DB (AD525X_I2C_CMD | (0x4 << 3))
|
||||
#define AD525X_INC_ALL_6DB (AD525X_I2C_CMD | (0x9 << 3))
|
||||
#define AD525X_DEC_ALL (AD525X_I2C_CMD | (0x6 << 3))
|
||||
#define AD525X_INC_ALL (AD525X_I2C_CMD | (0xB << 3))
|
||||
|
||||
static s32 ad525x_read(struct i2c_client *client, u8 reg);
|
||||
static s32 ad525x_write(struct i2c_client *client, u8 reg, u16 value);
|
||||
#include "ad525x_dpot.h"
|
||||
|
||||
/*
|
||||
* Client data (each client gets its own)
|
||||
*/
|
||||
|
||||
struct dpot_data {
|
||||
struct ad_dpot_bus_data bdata;
|
||||
struct mutex update_lock;
|
||||
unsigned rdac_mask;
|
||||
unsigned max_pos;
|
||||
unsigned devid;
|
||||
unsigned long devid;
|
||||
unsigned uid;
|
||||
unsigned feat;
|
||||
unsigned wipers;
|
||||
u16 rdac_cache[8];
|
||||
};
|
||||
|
||||
static inline int dpot_read_d8(struct dpot_data *dpot)
|
||||
{
|
||||
return dpot->bdata.bops->read_d8(dpot->bdata.client);
|
||||
}
|
||||
|
||||
static inline int dpot_read_r8d8(struct dpot_data *dpot, u8 reg)
|
||||
{
|
||||
return dpot->bdata.bops->read_r8d8(dpot->bdata.client, reg);
|
||||
}
|
||||
|
||||
static inline int dpot_read_r8d16(struct dpot_data *dpot, u8 reg)
|
||||
{
|
||||
return dpot->bdata.bops->read_r8d16(dpot->bdata.client, reg);
|
||||
}
|
||||
|
||||
static inline int dpot_write_d8(struct dpot_data *dpot, u8 val)
|
||||
{
|
||||
return dpot->bdata.bops->write_d8(dpot->bdata.client, val);
|
||||
}
|
||||
|
||||
static inline int dpot_write_r8d8(struct dpot_data *dpot, u8 reg, u16 val)
|
||||
{
|
||||
return dpot->bdata.bops->write_r8d8(dpot->bdata.client, reg, val);
|
||||
}
|
||||
|
||||
static inline int dpot_write_r8d16(struct dpot_data *dpot, u8 reg, u16 val)
|
||||
{
|
||||
return dpot->bdata.bops->write_r8d16(dpot->bdata.client, reg, val);
|
||||
}
|
||||
|
||||
static s32 dpot_read(struct dpot_data *dpot, u8 reg)
|
||||
{
|
||||
unsigned val = 0;
|
||||
|
||||
if (dpot->feat & F_SPI) {
|
||||
if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD))) {
|
||||
|
||||
if (dpot->feat & F_RDACS_WONLY)
|
||||
return dpot->rdac_cache[reg & DPOT_RDAC_MASK];
|
||||
|
||||
if (dpot->uid == DPOT_UID(AD5291_ID) ||
|
||||
dpot->uid == DPOT_UID(AD5292_ID) ||
|
||||
dpot->uid == DPOT_UID(AD5293_ID))
|
||||
return dpot_read_r8d8(dpot,
|
||||
DPOT_AD5291_READ_RDAC << 2);
|
||||
|
||||
val = DPOT_SPI_READ_RDAC;
|
||||
} else if (reg & DPOT_ADDR_EEPROM) {
|
||||
val = DPOT_SPI_READ_EEPROM;
|
||||
}
|
||||
|
||||
if (dpot->feat & F_SPI_16BIT)
|
||||
return dpot_read_r8d8(dpot, val);
|
||||
else if (dpot->feat & F_SPI_24BIT)
|
||||
return dpot_read_r8d16(dpot, val);
|
||||
|
||||
} else { /* I2C */
|
||||
|
||||
if ((reg & DPOT_REG_TOL) || (dpot->max_pos > 256))
|
||||
return dpot_read_r8d16(dpot, (reg & 0xF8) |
|
||||
((reg & 0x7) << 1));
|
||||
else
|
||||
return dpot_read_r8d8(dpot, reg);
|
||||
|
||||
}
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
static s32 dpot_write(struct dpot_data *dpot, u8 reg, u16 value)
|
||||
{
|
||||
unsigned val = 0;
|
||||
|
||||
if (dpot->feat & F_SPI) {
|
||||
if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD))) {
|
||||
if (dpot->feat & F_RDACS_WONLY)
|
||||
dpot->rdac_cache[reg & DPOT_RDAC_MASK] = value;
|
||||
|
||||
if (dpot->feat & F_AD_APPDATA) {
|
||||
if (dpot->feat & F_SPI_8BIT) {
|
||||
val = ((reg & DPOT_RDAC_MASK) <<
|
||||
DPOT_MAX_POS(dpot->devid)) |
|
||||
value;
|
||||
return dpot_write_d8(dpot, val);
|
||||
} else if (dpot->feat & F_SPI_16BIT) {
|
||||
val = ((reg & DPOT_RDAC_MASK) <<
|
||||
DPOT_MAX_POS(dpot->devid)) |
|
||||
value;
|
||||
return dpot_write_r8d8(dpot, val >> 8,
|
||||
val & 0xFF);
|
||||
} else
|
||||
BUG();
|
||||
} else {
|
||||
if (dpot->uid == DPOT_UID(AD5291_ID) ||
|
||||
dpot->uid == DPOT_UID(AD5292_ID) ||
|
||||
dpot->uid == DPOT_UID(AD5293_ID))
|
||||
return dpot_write_r8d8(dpot,
|
||||
(DPOT_AD5291_RDAC << 2) |
|
||||
(value >> 8), value & 0xFF);
|
||||
|
||||
val = DPOT_SPI_RDAC | (reg & DPOT_RDAC_MASK);
|
||||
}
|
||||
} else if (reg & DPOT_ADDR_EEPROM) {
|
||||
val = DPOT_SPI_EEPROM | (reg & DPOT_RDAC_MASK);
|
||||
} else if (reg & DPOT_ADDR_CMD) {
|
||||
switch (reg) {
|
||||
case DPOT_DEC_ALL_6DB:
|
||||
val = DPOT_SPI_DEC_ALL_6DB;
|
||||
break;
|
||||
case DPOT_INC_ALL_6DB:
|
||||
val = DPOT_SPI_INC_ALL_6DB;
|
||||
break;
|
||||
case DPOT_DEC_ALL:
|
||||
val = DPOT_SPI_DEC_ALL;
|
||||
break;
|
||||
case DPOT_INC_ALL:
|
||||
val = DPOT_SPI_INC_ALL;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
BUG();
|
||||
|
||||
if (dpot->feat & F_SPI_16BIT)
|
||||
return dpot_write_r8d8(dpot, val, value);
|
||||
else if (dpot->feat & F_SPI_24BIT)
|
||||
return dpot_write_r8d16(dpot, val, value);
|
||||
} else {
|
||||
/* Only write the instruction byte for certain commands */
|
||||
if (reg & DPOT_ADDR_CMD)
|
||||
return dpot_write_d8(dpot, reg);
|
||||
|
||||
if (dpot->max_pos > 256)
|
||||
return dpot_write_r8d16(dpot, (reg & 0xF8) |
|
||||
((reg & 0x7) << 1), value);
|
||||
else
|
||||
/* All other registers require instruction + data bytes */
|
||||
return dpot_write_r8d8(dpot, reg, value);
|
||||
|
||||
}
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* sysfs functions */
|
||||
|
||||
static ssize_t sysfs_show_reg(struct device *dev,
|
||||
struct device_attribute *attr, char *buf, u32 reg)
|
||||
struct device_attribute *attr,
|
||||
char *buf, u32 reg)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct dpot_data *data = i2c_get_clientdata(client);
|
||||
struct dpot_data *data = dev_get_drvdata(dev);
|
||||
s32 value;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
value = ad525x_read(client, reg);
|
||||
value = dpot_read(data, reg);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
if (value < 0)
|
||||
|
@ -111,7 +242,7 @@ static ssize_t sysfs_show_reg(struct device *dev,
|
|||
* datasheet (Rev. A) for more details.
|
||||
*/
|
||||
|
||||
if (reg & AD525X_REG_TOL)
|
||||
if (reg & DPOT_REG_TOL)
|
||||
return sprintf(buf, "0x%04x\n", value & 0xFFFF);
|
||||
else
|
||||
return sprintf(buf, "%u\n", value & data->rdac_mask);
|
||||
|
@ -121,8 +252,7 @@ static ssize_t sysfs_set_reg(struct device *dev,
|
|||
struct device_attribute *attr,
|
||||
const char *buf, size_t count, u32 reg)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct dpot_data *data = i2c_get_clientdata(client);
|
||||
struct dpot_data *data = dev_get_drvdata(dev);
|
||||
unsigned long value;
|
||||
int err;
|
||||
|
||||
|
@ -134,8 +264,8 @@ static ssize_t sysfs_set_reg(struct device *dev,
|
|||
value = data->rdac_mask;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
ad525x_write(client, reg, value);
|
||||
if (reg & AD525X_I2C_EEPROM)
|
||||
dpot_write(data, reg, value);
|
||||
if (reg & DPOT_ADDR_EEPROM)
|
||||
msleep(26); /* Sleep while the EEPROM updates */
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
|
@ -146,11 +276,10 @@ static ssize_t sysfs_do_cmd(struct device *dev,
|
|||
struct device_attribute *attr,
|
||||
const char *buf, size_t count, u32 reg)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct dpot_data *data = i2c_get_clientdata(client);
|
||||
struct dpot_data *data = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
ad525x_write(client, reg, 0);
|
||||
dpot_write(data, reg, 0);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
|
@ -182,51 +311,58 @@ static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, show_##name, set_##name);
|
|||
DPOT_DEVICE_SHOW(name, reg) \
|
||||
static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, show_##name, NULL);
|
||||
|
||||
DPOT_DEVICE_SHOW_SET(rdac0, AD525X_I2C_RDAC | AD525X_RDAC0);
|
||||
DPOT_DEVICE_SHOW_SET(eeprom0, AD525X_I2C_EEPROM | AD525X_RDAC0);
|
||||
DPOT_DEVICE_SHOW_ONLY(tolerance0, AD525X_I2C_EEPROM | AD525X_TOL_RDAC0);
|
||||
DPOT_DEVICE_SHOW_SET(rdac0, DPOT_ADDR_RDAC | DPOT_RDAC0);
|
||||
DPOT_DEVICE_SHOW_SET(eeprom0, DPOT_ADDR_EEPROM | DPOT_RDAC0);
|
||||
DPOT_DEVICE_SHOW_ONLY(tolerance0, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC0);
|
||||
|
||||
DPOT_DEVICE_SHOW_SET(rdac1, AD525X_I2C_RDAC | AD525X_RDAC1);
|
||||
DPOT_DEVICE_SHOW_SET(eeprom1, AD525X_I2C_EEPROM | AD525X_RDAC1);
|
||||
DPOT_DEVICE_SHOW_ONLY(tolerance1, AD525X_I2C_EEPROM | AD525X_TOL_RDAC1);
|
||||
DPOT_DEVICE_SHOW_SET(rdac1, DPOT_ADDR_RDAC | DPOT_RDAC1);
|
||||
DPOT_DEVICE_SHOW_SET(eeprom1, DPOT_ADDR_EEPROM | DPOT_RDAC1);
|
||||
DPOT_DEVICE_SHOW_ONLY(tolerance1, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC1);
|
||||
|
||||
DPOT_DEVICE_SHOW_SET(rdac2, AD525X_I2C_RDAC | AD525X_RDAC2);
|
||||
DPOT_DEVICE_SHOW_SET(eeprom2, AD525X_I2C_EEPROM | AD525X_RDAC2);
|
||||
DPOT_DEVICE_SHOW_ONLY(tolerance2, AD525X_I2C_EEPROM | AD525X_TOL_RDAC2);
|
||||
DPOT_DEVICE_SHOW_SET(rdac2, DPOT_ADDR_RDAC | DPOT_RDAC2);
|
||||
DPOT_DEVICE_SHOW_SET(eeprom2, DPOT_ADDR_EEPROM | DPOT_RDAC2);
|
||||
DPOT_DEVICE_SHOW_ONLY(tolerance2, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC2);
|
||||
|
||||
DPOT_DEVICE_SHOW_SET(rdac3, AD525X_I2C_RDAC | AD525X_RDAC3);
|
||||
DPOT_DEVICE_SHOW_SET(eeprom3, AD525X_I2C_EEPROM | AD525X_RDAC3);
|
||||
DPOT_DEVICE_SHOW_ONLY(tolerance3, AD525X_I2C_EEPROM | AD525X_TOL_RDAC3);
|
||||
DPOT_DEVICE_SHOW_SET(rdac3, DPOT_ADDR_RDAC | DPOT_RDAC3);
|
||||
DPOT_DEVICE_SHOW_SET(eeprom3, DPOT_ADDR_EEPROM | DPOT_RDAC3);
|
||||
DPOT_DEVICE_SHOW_ONLY(tolerance3, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC3);
|
||||
|
||||
static struct attribute *ad525x_attributes_wipers[4][4] = {
|
||||
{
|
||||
DPOT_DEVICE_SHOW_SET(rdac4, DPOT_ADDR_RDAC | DPOT_RDAC4);
|
||||
DPOT_DEVICE_SHOW_SET(eeprom4, DPOT_ADDR_EEPROM | DPOT_RDAC4);
|
||||
DPOT_DEVICE_SHOW_ONLY(tolerance4, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC4);
|
||||
|
||||
DPOT_DEVICE_SHOW_SET(rdac5, DPOT_ADDR_RDAC | DPOT_RDAC5);
|
||||
DPOT_DEVICE_SHOW_SET(eeprom5, DPOT_ADDR_EEPROM | DPOT_RDAC5);
|
||||
DPOT_DEVICE_SHOW_ONLY(tolerance5, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC5);
|
||||
|
||||
static const struct attribute *dpot_attrib_wipers[] = {
|
||||
&dev_attr_rdac0.attr,
|
||||
&dev_attr_eeprom0.attr,
|
||||
&dev_attr_tolerance0.attr,
|
||||
NULL
|
||||
}, {
|
||||
&dev_attr_rdac1.attr,
|
||||
&dev_attr_eeprom1.attr,
|
||||
&dev_attr_tolerance1.attr,
|
||||
NULL
|
||||
}, {
|
||||
&dev_attr_rdac2.attr,
|
||||
&dev_attr_eeprom2.attr,
|
||||
&dev_attr_tolerance2.attr,
|
||||
NULL
|
||||
}, {
|
||||
&dev_attr_rdac3.attr,
|
||||
&dev_attr_eeprom3.attr,
|
||||
&dev_attr_tolerance3.attr,
|
||||
&dev_attr_rdac4.attr,
|
||||
&dev_attr_rdac5.attr,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
static const struct attribute_group ad525x_group_wipers[] = {
|
||||
{.attrs = ad525x_attributes_wipers[AD525X_RDAC0]},
|
||||
{.attrs = ad525x_attributes_wipers[AD525X_RDAC1]},
|
||||
{.attrs = ad525x_attributes_wipers[AD525X_RDAC2]},
|
||||
{.attrs = ad525x_attributes_wipers[AD525X_RDAC3]},
|
||||
static const struct attribute *dpot_attrib_eeprom[] = {
|
||||
&dev_attr_eeprom0.attr,
|
||||
&dev_attr_eeprom1.attr,
|
||||
&dev_attr_eeprom2.attr,
|
||||
&dev_attr_eeprom3.attr,
|
||||
&dev_attr_eeprom4.attr,
|
||||
&dev_attr_eeprom5.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute *dpot_attrib_tolerance[] = {
|
||||
&dev_attr_tolerance0.attr,
|
||||
&dev_attr_tolerance1.attr,
|
||||
&dev_attr_tolerance2.attr,
|
||||
&dev_attr_tolerance3.attr,
|
||||
&dev_attr_tolerance4.attr,
|
||||
&dev_attr_tolerance5.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
@ -240,10 +376,10 @@ set_##_name(struct device *dev, \
|
|||
} \
|
||||
static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, NULL, set_##_name);
|
||||
|
||||
DPOT_DEVICE_DO_CMD(inc_all, AD525X_INC_ALL);
|
||||
DPOT_DEVICE_DO_CMD(dec_all, AD525X_DEC_ALL);
|
||||
DPOT_DEVICE_DO_CMD(inc_all_6db, AD525X_INC_ALL_6DB);
|
||||
DPOT_DEVICE_DO_CMD(dec_all_6db, AD525X_DEC_ALL_6DB);
|
||||
DPOT_DEVICE_DO_CMD(inc_all, DPOT_INC_ALL);
|
||||
DPOT_DEVICE_DO_CMD(dec_all, DPOT_DEC_ALL);
|
||||
DPOT_DEVICE_DO_CMD(inc_all_6db, DPOT_INC_ALL_6DB);
|
||||
DPOT_DEVICE_DO_CMD(dec_all_6db, DPOT_DEC_ALL_6DB);
|
||||
|
||||
static struct attribute *ad525x_attributes_commands[] = {
|
||||
&dev_attr_inc_all.attr,
|
||||
|
@ -257,74 +393,44 @@ static const struct attribute_group ad525x_group_commands = {
|
|||
.attrs = ad525x_attributes_commands,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* i2c device functions */
|
||||
|
||||
/**
|
||||
* ad525x_read - return the value contained in the specified register
|
||||
* on the AD5258 device.
|
||||
* @client: value returned from i2c_new_device()
|
||||
* @reg: the register to read
|
||||
*
|
||||
* If the tolerance register is specified, 2 bytes are returned.
|
||||
* Otherwise, 1 byte is returned. A negative value indicates an error
|
||||
* occurred while reading the register.
|
||||
*/
|
||||
static s32 ad525x_read(struct i2c_client *client, u8 reg)
|
||||
__devinit int ad_dpot_add_files(struct device *dev,
|
||||
unsigned features, unsigned rdac)
|
||||
{
|
||||
struct dpot_data *data = i2c_get_clientdata(client);
|
||||
int err = sysfs_create_file(&dev->kobj,
|
||||
dpot_attrib_wipers[rdac]);
|
||||
if (features & F_CMD_EEP)
|
||||
err |= sysfs_create_file(&dev->kobj,
|
||||
dpot_attrib_eeprom[rdac]);
|
||||
if (features & F_CMD_TOL)
|
||||
err |= sysfs_create_file(&dev->kobj,
|
||||
dpot_attrib_tolerance[rdac]);
|
||||
|
||||
if ((reg & AD525X_REG_TOL) || (data->max_pos > 256))
|
||||
return i2c_smbus_read_word_data(client, (reg & 0xF8) |
|
||||
((reg & 0x7) << 1));
|
||||
else
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
if (err)
|
||||
dev_err(dev, "failed to register sysfs hooks for RDAC%d\n",
|
||||
rdac);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad525x_write - store the given value in the specified register on
|
||||
* the AD5258 device.
|
||||
* @client: value returned from i2c_new_device()
|
||||
* @reg: the register to write
|
||||
* @value: the byte to store in the register
|
||||
*
|
||||
* For certain instructions that do not require a data byte, "NULL"
|
||||
* should be specified for the "value" parameter. These instructions
|
||||
* include NOP, RESTORE_FROM_EEPROM, and STORE_TO_EEPROM.
|
||||
*
|
||||
* A negative return value indicates an error occurred while reading
|
||||
* the register.
|
||||
*/
|
||||
static s32 ad525x_write(struct i2c_client *client, u8 reg, u16 value)
|
||||
inline void ad_dpot_remove_files(struct device *dev,
|
||||
unsigned features, unsigned rdac)
|
||||
{
|
||||
struct dpot_data *data = i2c_get_clientdata(client);
|
||||
|
||||
/* Only write the instruction byte for certain commands */
|
||||
if (reg & AD525X_I2C_CMD)
|
||||
return i2c_smbus_write_byte(client, reg);
|
||||
|
||||
if (data->max_pos > 256)
|
||||
return i2c_smbus_write_word_data(client, (reg & 0xF8) |
|
||||
((reg & 0x7) << 1), value);
|
||||
else
|
||||
/* All other registers require instruction + data bytes */
|
||||
return i2c_smbus_write_byte_data(client, reg, value);
|
||||
sysfs_remove_file(&dev->kobj,
|
||||
dpot_attrib_wipers[rdac]);
|
||||
if (features & F_CMD_EEP)
|
||||
sysfs_remove_file(&dev->kobj,
|
||||
dpot_attrib_eeprom[rdac]);
|
||||
if (features & F_CMD_TOL)
|
||||
sysfs_remove_file(&dev->kobj,
|
||||
dpot_attrib_tolerance[rdac]);
|
||||
}
|
||||
|
||||
static int ad525x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
__devinit int ad_dpot_probe(struct device *dev,
|
||||
struct ad_dpot_bus_data *bdata, const struct ad_dpot_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
|
||||
struct dpot_data *data;
|
||||
int err = 0;
|
||||
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
|
||||
dev_err(dev, "missing I2C functionality for this driver\n");
|
||||
goto exit;
|
||||
}
|
||||
int i, err = 0;
|
||||
|
||||
data = kzalloc(sizeof(struct dpot_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
|
@ -332,183 +438,74 @@ static int ad525x_probe(struct i2c_client *client,
|
|||
goto exit;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
dev_set_drvdata(dev, data);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
switch (id->driver_data) {
|
||||
case AD5258_ID:
|
||||
data->max_pos = AD5258_MAX_POSITION;
|
||||
err = sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC0]);
|
||||
break;
|
||||
case AD5259_ID:
|
||||
data->max_pos = AD5259_MAX_POSITION;
|
||||
err = sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC0]);
|
||||
break;
|
||||
case AD5251_ID:
|
||||
data->max_pos = AD5251_MAX_POSITION;
|
||||
err = sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC1]);
|
||||
err |= sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC3]);
|
||||
err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
|
||||
break;
|
||||
case AD5252_ID:
|
||||
data->max_pos = AD5252_MAX_POSITION;
|
||||
err = sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC1]);
|
||||
err |= sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC3]);
|
||||
err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
|
||||
break;
|
||||
case AD5253_ID:
|
||||
data->max_pos = AD5253_MAX_POSITION;
|
||||
err = sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC0]);
|
||||
err |= sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC1]);
|
||||
err |= sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC2]);
|
||||
err |= sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC3]);
|
||||
err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
|
||||
break;
|
||||
case AD5254_ID:
|
||||
data->max_pos = AD5254_MAX_POSITION;
|
||||
err = sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC0]);
|
||||
err |= sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC1]);
|
||||
err |= sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC2]);
|
||||
err |= sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC3]);
|
||||
err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
|
||||
break;
|
||||
case AD5255_ID:
|
||||
data->max_pos = AD5255_MAX_POSITION;
|
||||
err = sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC0]);
|
||||
err |= sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC1]);
|
||||
err |= sysfs_create_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC2]);
|
||||
err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
|
||||
break;
|
||||
default:
|
||||
err = -ENODEV;
|
||||
goto exit_free;
|
||||
data->bdata = *bdata;
|
||||
data->devid = id->devid;
|
||||
|
||||
data->max_pos = 1 << DPOT_MAX_POS(data->devid);
|
||||
data->rdac_mask = data->max_pos - 1;
|
||||
data->feat = DPOT_FEAT(data->devid);
|
||||
data->uid = DPOT_UID(data->devid);
|
||||
data->wipers = DPOT_WIPERS(data->devid);
|
||||
|
||||
for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++)
|
||||
if (data->wipers & (1 << i)) {
|
||||
err = ad_dpot_add_files(dev, data->feat, i);
|
||||
if (err)
|
||||
goto exit_remove_files;
|
||||
/* power-up midscale */
|
||||
if (data->feat & F_RDACS_WONLY)
|
||||
data->rdac_cache[i] = data->max_pos / 2;
|
||||
}
|
||||
|
||||
if (data->feat & F_CMD_INC)
|
||||
err = sysfs_create_group(&dev->kobj, &ad525x_group_commands);
|
||||
|
||||
if (err) {
|
||||
dev_err(dev, "failed to register sysfs hooks\n");
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
data->devid = id->driver_data;
|
||||
data->rdac_mask = data->max_pos - 1;
|
||||
|
||||
dev_info(dev, "%s %d-Position Digital Potentiometer registered\n",
|
||||
id->name, data->max_pos);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove_files:
|
||||
for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++)
|
||||
if (data->wipers & (1 << i))
|
||||
ad_dpot_remove_files(dev, data->feat, i);
|
||||
|
||||
exit_free:
|
||||
kfree(data);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
dev_set_drvdata(dev, NULL);
|
||||
exit:
|
||||
dev_err(dev, "failed to create client\n");
|
||||
dev_err(dev, "failed to create client for %s ID 0x%lX\n",
|
||||
id->name, id->devid);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(ad_dpot_probe);
|
||||
|
||||
static int __devexit ad525x_remove(struct i2c_client *client)
|
||||
__devexit int ad_dpot_remove(struct device *dev)
|
||||
{
|
||||
struct dpot_data *data = i2c_get_clientdata(client);
|
||||
struct device *dev = &client->dev;
|
||||
struct dpot_data *data = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
switch (data->devid) {
|
||||
case AD5258_ID:
|
||||
case AD5259_ID:
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC0]);
|
||||
break;
|
||||
case AD5251_ID:
|
||||
case AD5252_ID:
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC1]);
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC3]);
|
||||
sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
|
||||
break;
|
||||
case AD5253_ID:
|
||||
case AD5254_ID:
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC0]);
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC1]);
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC2]);
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC3]);
|
||||
sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
|
||||
break;
|
||||
case AD5255_ID:
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC0]);
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC1]);
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&ad525x_group_wipers[AD525X_RDAC2]);
|
||||
sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
|
||||
break;
|
||||
}
|
||||
for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++)
|
||||
if (data->wipers & (1 << i))
|
||||
ad_dpot_remove_files(dev, data->feat, i);
|
||||
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ad_dpot_remove);
|
||||
|
||||
static const struct i2c_device_id ad525x_idtable[] = {
|
||||
{"ad5258", AD5258_ID},
|
||||
{"ad5259", AD5259_ID},
|
||||
{"ad5251", AD5251_ID},
|
||||
{"ad5252", AD5252_ID},
|
||||
{"ad5253", AD5253_ID},
|
||||
{"ad5254", AD5254_ID},
|
||||
{"ad5255", AD5255_ID},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, ad525x_idtable);
|
||||
|
||||
static struct i2c_driver ad525x_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
.id_table = ad525x_idtable,
|
||||
.probe = ad525x_probe,
|
||||
.remove = __devexit_p(ad525x_remove),
|
||||
};
|
||||
|
||||
static int __init ad525x_init(void)
|
||||
{
|
||||
return i2c_add_driver(&ad525x_driver);
|
||||
}
|
||||
|
||||
module_init(ad525x_init);
|
||||
|
||||
static void __exit ad525x_exit(void)
|
||||
{
|
||||
i2c_del_driver(&ad525x_driver);
|
||||
}
|
||||
|
||||
module_exit(ad525x_exit);
|
||||
|
||||
MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>, "
|
||||
"Michael Hennerich <hennerich@blackfin.uclinux.org>, ");
|
||||
MODULE_DESCRIPTION("AD5258/9 digital potentiometer driver");
|
||||
"Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Digital potentiometer driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
|
|
173
drivers/misc/ad525x_dpot.h
Normal file
173
drivers/misc/ad525x_dpot.h
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Driver for the Analog Devices digital potentiometers
|
||||
*
|
||||
* Copyright (C) 2010 Michael Hennerich, Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#ifndef _AD_DPOT_H_
|
||||
#define _AD_DPOT_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define DPOT_CONF(features, wipers, max_pos, uid) \
|
||||
(((features) << 18) | (((wipers) & 0xFF) << 10) | \
|
||||
((max_pos & 0xF) << 6) | (uid & 0x3F))
|
||||
|
||||
#define DPOT_UID(conf) (conf & 0x3F)
|
||||
#define DPOT_MAX_POS(conf) ((conf >> 6) & 0xF)
|
||||
#define DPOT_WIPERS(conf) ((conf >> 10) & 0xFF)
|
||||
#define DPOT_FEAT(conf) (conf >> 18)
|
||||
|
||||
#define BRDAC0 (1 << 0)
|
||||
#define BRDAC1 (1 << 1)
|
||||
#define BRDAC2 (1 << 2)
|
||||
#define BRDAC3 (1 << 3)
|
||||
#define BRDAC4 (1 << 4)
|
||||
#define BRDAC5 (1 << 5)
|
||||
|
||||
#define F_CMD_INC (1 << 0) /* Features INC/DEC ALL, 6dB */
|
||||
#define F_CMD_EEP (1 << 1) /* Features EEPROM */
|
||||
#define F_CMD_TOL (1 << 2) /* RDACS are Read/Write + Tolerance REG */
|
||||
#define F_RDACS_RW (1 << 3) /* RDACS are Read/Write + Tolerance REG */
|
||||
#define F_RDACS_WONLY (1 << 4) /* RDACS are Write only */
|
||||
#define F_AD_APPDATA (1 << 5) /* RDAC Address append to data */
|
||||
#define F_SPI_8BIT (1 << 6) /* All SPI XFERS are 8-bit */
|
||||
#define F_SPI_16BIT (1 << 7) /* All SPI XFERS are 16-bit */
|
||||
#define F_SPI_24BIT (1 << 8) /* All SPI XFERS are 24-bit */
|
||||
|
||||
#define F_RDACS_RW_TOL (F_RDACS_RW | F_CMD_EEP | F_CMD_TOL)
|
||||
#define F_RDACS_RW_EEP (F_RDACS_RW | F_CMD_EEP)
|
||||
#define F_SPI (F_SPI_8BIT | F_SPI_16BIT | F_SPI_24BIT)
|
||||
|
||||
enum dpot_devid {
|
||||
AD5258_ID = DPOT_CONF(F_RDACS_RW_TOL, BRDAC0, 6, 0), /* I2C */
|
||||
AD5259_ID = DPOT_CONF(F_RDACS_RW_TOL, BRDAC0, 8, 1),
|
||||
AD5251_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
|
||||
BRDAC0 | BRDAC3, 6, 2),
|
||||
AD5252_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
|
||||
BRDAC0 | BRDAC3, 8, 3),
|
||||
AD5253_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
|
||||
BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 6, 4),
|
||||
AD5254_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
|
||||
BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 8, 5),
|
||||
AD5255_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
|
||||
BRDAC0 | BRDAC1 | BRDAC2, 9, 6),
|
||||
AD5160_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
|
||||
BRDAC0, 8, 7), /* SPI */
|
||||
AD5161_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
|
||||
BRDAC0, 8, 8),
|
||||
AD5162_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
|
||||
BRDAC0 | BRDAC1, 8, 9),
|
||||
AD5165_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
|
||||
BRDAC0, 8, 10),
|
||||
AD5200_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
|
||||
BRDAC0, 8, 11),
|
||||
AD5201_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
|
||||
BRDAC0, 5, 12),
|
||||
AD5203_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
|
||||
BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 6, 13),
|
||||
AD5204_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
|
||||
BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 8, 14),
|
||||
AD5206_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
|
||||
BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3 | BRDAC4 | BRDAC5,
|
||||
8, 15),
|
||||
AD5207_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
|
||||
BRDAC0 | BRDAC1, 8, 16),
|
||||
AD5231_ID = DPOT_CONF(F_RDACS_RW_EEP | F_CMD_INC | F_SPI_24BIT,
|
||||
BRDAC0, 10, 17),
|
||||
AD5232_ID = DPOT_CONF(F_RDACS_RW_EEP | F_CMD_INC | F_SPI_16BIT,
|
||||
BRDAC0 | BRDAC1, 8, 18),
|
||||
AD5233_ID = DPOT_CONF(F_RDACS_RW_EEP | F_CMD_INC | F_SPI_16BIT,
|
||||
BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 6, 19),
|
||||
AD5235_ID = DPOT_CONF(F_RDACS_RW_EEP | F_CMD_INC | F_SPI_24BIT,
|
||||
BRDAC0 | BRDAC1, 10, 20),
|
||||
AD5260_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
|
||||
BRDAC0, 8, 21),
|
||||
AD5262_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
|
||||
BRDAC0 | BRDAC1, 8, 22),
|
||||
AD5263_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
|
||||
BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 8, 23),
|
||||
AD5290_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
|
||||
BRDAC0, 8, 24),
|
||||
AD5291_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 8, 25),
|
||||
AD5292_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 10, 26),
|
||||
AD5293_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 10, 27),
|
||||
AD7376_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
|
||||
BRDAC0, 7, 28),
|
||||
AD8400_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
|
||||
BRDAC0, 8, 29),
|
||||
AD8402_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
|
||||
BRDAC0 | BRDAC1, 8, 30),
|
||||
AD8403_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
|
||||
BRDAC0 | BRDAC1 | BRDAC2, 8, 31),
|
||||
ADN2850_ID = DPOT_CONF(F_RDACS_RW_EEP | F_CMD_INC | F_SPI_24BIT,
|
||||
BRDAC0 | BRDAC1, 10, 32),
|
||||
};
|
||||
|
||||
#define DPOT_RDAC0 0
|
||||
#define DPOT_RDAC1 1
|
||||
#define DPOT_RDAC2 2
|
||||
#define DPOT_RDAC3 3
|
||||
#define DPOT_RDAC4 4
|
||||
#define DPOT_RDAC5 5
|
||||
|
||||
#define DPOT_RDAC_MASK 0x1F
|
||||
|
||||
#define DPOT_REG_TOL 0x18
|
||||
#define DPOT_TOL_RDAC0 (DPOT_REG_TOL | DPOT_RDAC0)
|
||||
#define DPOT_TOL_RDAC1 (DPOT_REG_TOL | DPOT_RDAC1)
|
||||
#define DPOT_TOL_RDAC2 (DPOT_REG_TOL | DPOT_RDAC2)
|
||||
#define DPOT_TOL_RDAC3 (DPOT_REG_TOL | DPOT_RDAC3)
|
||||
#define DPOT_TOL_RDAC4 (DPOT_REG_TOL | DPOT_RDAC4)
|
||||
#define DPOT_TOL_RDAC5 (DPOT_REG_TOL | DPOT_RDAC5)
|
||||
|
||||
/* RDAC-to-EEPROM Interface Commands */
|
||||
#define DPOT_ADDR_RDAC (0x00 << 5)
|
||||
#define DPOT_ADDR_EEPROM (0x01 << 5)
|
||||
#define DPOT_ADDR_CMD (0x80)
|
||||
|
||||
#define DPOT_DEC_ALL_6DB (DPOT_ADDR_CMD | (0x4 << 3))
|
||||
#define DPOT_INC_ALL_6DB (DPOT_ADDR_CMD | (0x9 << 3))
|
||||
#define DPOT_DEC_ALL (DPOT_ADDR_CMD | (0x6 << 3))
|
||||
#define DPOT_INC_ALL (DPOT_ADDR_CMD | (0xB << 3))
|
||||
|
||||
#define DPOT_SPI_RDAC 0xB0
|
||||
#define DPOT_SPI_EEPROM 0x30
|
||||
#define DPOT_SPI_READ_RDAC 0xA0
|
||||
#define DPOT_SPI_READ_EEPROM 0x90
|
||||
#define DPOT_SPI_DEC_ALL_6DB 0x50
|
||||
#define DPOT_SPI_INC_ALL_6DB 0xD0
|
||||
#define DPOT_SPI_DEC_ALL 0x70
|
||||
#define DPOT_SPI_INC_ALL 0xF0
|
||||
|
||||
/* AD5291/2/3 use special commands */
|
||||
#define DPOT_AD5291_RDAC 0x01
|
||||
#define DPOT_AD5291_READ_RDAC 0x02
|
||||
|
||||
struct dpot_data;
|
||||
|
||||
struct ad_dpot_bus_ops {
|
||||
int (*read_d8) (void *client);
|
||||
int (*read_r8d8) (void *client, u8 reg);
|
||||
int (*read_r8d16) (void *client, u8 reg);
|
||||
int (*write_d8) (void *client, u8 val);
|
||||
int (*write_r8d8) (void *client, u8 reg, u8 val);
|
||||
int (*write_r8d16) (void *client, u8 reg, u16 val);
|
||||
};
|
||||
|
||||
struct ad_dpot_bus_data {
|
||||
void *client;
|
||||
const struct ad_dpot_bus_ops *bops;
|
||||
};
|
||||
|
||||
struct ad_dpot_id {
|
||||
char *name;
|
||||
unsigned long devid;
|
||||
};
|
||||
|
||||
int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata, const struct ad_dpot_id *id);
|
||||
int ad_dpot_remove(struct device *dev);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue