From 93aa4fc5d32534422e80bbb7d1929f533c4ce65f Mon Sep 17 00:00:00 2001 From: Matthieu Crapet Date: Sat, 19 Jun 2010 15:49:34 +0200 Subject: [PATCH 13/18] ts72xx_max197 --- arch/arm/mach-ep93xx/ts72xx.c | 30 +++++ drivers/misc/Kconfig | 21 ++++ drivers/misc/Makefile | 1 + drivers/misc/ts72xx_max197.c | 235 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 287 insertions(+), 0 deletions(-) create mode 100644 drivers/misc/ts72xx_max197.c diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c index a46b370..ba27d9d 100644 --- a/arch/arm/mach-ep93xx/ts72xx.c +++ b/arch/arm/mach-ep93xx/ts72xx.c @@ -227,6 +227,32 @@ static struct platform_device ts72xx_wdt_device = { }; /************************************************************************* + * MAX197 (8 * 12-bit A/D converter) option + *************************************************************************/ +static struct resource ts72xx_max197_resources[] = { + [0] = { /* sample/control register */ + .start = TS72XX_MAX197_SAMPLE_PHYS_BASE, + .end = TS72XX_MAX197_SAMPLE_PHYS_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { /* busy bit */ + .start = TS72XX_JUMPERS_MAX197_PHYS_BASE, + .end = TS72XX_JUMPERS_MAX197_PHYS_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device ts72xx_max197_device = { + .name = "ts72xx-max197", + .id = -1, + .dev = { + .platform_data = NULL, + }, + .num_resources = ARRAY_SIZE(ts72xx_max197_resources), + .resource = ts72xx_max197_resources, +}; + +/************************************************************************* * Ethernet *************************************************************************/ static struct ep93xx_eth_data ts72xx_eth_data = { @@ -260,6 +286,10 @@ static void __init ts72xx_init_machine(void) ts72xx_i2c_board_info, ARRAY_SIZE(ts72xx_i2c_board_info)); + if (is_max197_installed()) { + platform_device_register(&ts72xx_max197_device); + } + /* PWM1 is DIO_6 on TS-72xx header */ ep93xx_register_pwm(0, 1); } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0d0d625..45c4be8 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -332,4 +332,25 @@ source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" source "drivers/misc/iwmc3200top/Kconfig" +config TS72XX_MAX197 + tristate "TS-72xx MAX197 support" + depends on ARCH_EP93XX && MACH_TS72XX && SYSFS + help + Say Y here if to include support for the MAX197 A/D converter + optionally included on Technologic Systems SBCs. + Default acquisition range is [0..5V]. + + To compile this driver as a module, choose M here: the + module will be called ts72xx_max197. + +if TS72XX_MAX197 + +config TS72XX_MAX197_AVERAGE + bool "Average measurement" + help + Say Y here to enable making average measurement. Default is 1. + See /sys/module/ts72xx_max197/parameters/average file. + +endif # TS72XX_MAX197 + endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 7b6f7ee..388f636 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_HP_ILO) += hpilo.o obj-$(CONFIG_ISL29003) += isl29003.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o +obj-$(CONFIG_TS72XX_MAX197) += ts72xx_max197.o obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o obj-$(CONFIG_C2PORT) += c2port/ diff --git a/drivers/misc/ts72xx_max197.c b/drivers/misc/ts72xx_max197.c new file mode 100644 index 0000000..4121ae5 --- /dev/null +++ b/drivers/misc/ts72xx_max197.c @@ -0,0 +1,235 @@ +/* + * TS-72XX max197 driver for Technologic Systems boards. + * + * Voltage conversion is taken from adc_logger from Jim Jackson. + * (c) Copyright 2008 Matthieu Crapet + * + * 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. + */ + +#include +#include +#include +#include + +#define DRV_VERSION "0.2" +#define PFX "ts72xx_max197: " + +#define MAX197_RANGE_5_5 1 // [- 5V + 5V] +#define MAX197_RANGE_10_10 3 // [-10V +10V] +#define MAX197_RANGE_0_5 0 // [ 0V + 5V] +#define MAX197_RANGE_0_10 2 // [ 0V +10V] + +#define MAX197_RESET_CHANNEL_CONF(x) (~(3 << (2*(x)))) +#define MAX197_SET_CHANNEL_CONF(x, range) ((range) << (2*(x))) +#define MAX197_GET_CHANNEL_CONF(x, conf) (((conf) >> (2*(x))) & 3) + +struct max197_config +{ + void __iomem *control_and_data_register; + void __iomem *busy_bit_register; + unsigned int channels; // two bits per channels +}; + +static struct max197_config conf; +#ifdef CONFIG_TS72XX_MAX197_AVERAGE +static ushort average = 1; +#endif + +static ssize_t max197_acquire(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int range, n; + signed short val; +#ifdef CONFIG_TS72XX_MAX197_AVERAGE + int i, total; +#endif + + n = attr->attr.name[2] - 0x31; + range = MAX197_GET_CHANNEL_CONF(n, conf.channels); + +#ifdef CONFIG_TS72XX_MAX197_AVERAGE + val = 0; total = 0; + for (i = 0; i < average; i++) { +#endif + + __raw_writeb(((range << 3) | n | 0x40) & 0xFF, + conf.control_and_data_register); + while (__raw_readb(conf.busy_bit_register) & 0x80); + val = __raw_readw(conf.control_and_data_register); + + //printk(PFX "%hd/%hd: 0x%04X\n", i+1, average, val); + +#ifdef CONFIG_TS72XX_MAX197_AVERAGE + total += val; + } + total /= average; + val = (signed short)total; +#endif + + /* We want three digit precision */ + switch (range) { + case MAX197_RANGE_0_5: + val = ((val * 50000/4096)+5)/10; + break; + case MAX197_RANGE_5_5: + case MAX197_RANGE_0_10: + val = ((val * 100000/4096)+5)/10; + break; + case MAX197_RANGE_10_10: + val = ((val * 200000/4096)+5)/10; + break; + } + + return sprintf(buf, "%d.%03ld\n", val/1000, abs(val%1000)); +} + +static ssize_t max197_configure(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int n = attr->attr.name[2] - 0x31; + + long val = simple_strtol(buf, NULL, 10); + switch (val) { + case 10: + conf.channels &= MAX197_RESET_CHANNEL_CONF(n); + conf.channels |= MAX197_SET_CHANNEL_CONF(n, MAX197_RANGE_0_10); + break; + case 5: + conf.channels &= MAX197_RESET_CHANNEL_CONF(n); + conf.channels |= MAX197_SET_CHANNEL_CONF(n, MAX197_RANGE_0_5); + break; + case -10: + conf.channels &= MAX197_RESET_CHANNEL_CONF(n); + conf.channels |= MAX197_SET_CHANNEL_CONF(n, MAX197_RANGE_10_10); + break; + case -5: + conf.channels &= MAX197_RESET_CHANNEL_CONF(n); + conf.channels |= MAX197_SET_CHANNEL_CONF(n, MAX197_RANGE_5_5); + break; + + default: + return -EINVAL; + } + + return len; +} + +static DEVICE_ATTR(ch1, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); +static DEVICE_ATTR(ch2, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); +static DEVICE_ATTR(ch3, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); +static DEVICE_ATTR(ch4, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); +static DEVICE_ATTR(ch5, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); +static DEVICE_ATTR(ch6, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); +static DEVICE_ATTR(ch7, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); +static DEVICE_ATTR(ch8, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); + +static struct attribute *max197_attributes[] = { + &dev_attr_ch1.attr, + &dev_attr_ch2.attr, + &dev_attr_ch3.attr, + &dev_attr_ch4.attr, + &dev_attr_ch5.attr, + &dev_attr_ch6.attr, + &dev_attr_ch7.attr, + &dev_attr_ch8.attr, + NULL +}; + +static struct attribute_group max197_group = { + .attrs = max197_attributes, + //.name = "channels", +}; + +static __devinit int ts72xx_max197_probe(struct platform_device *pdev) +{ + int err = 0; + struct resource *r_data, *r_busy; + + r_data = platform_get_resource(pdev, IORESOURCE_MEM, 0); + r_busy = platform_get_resource(pdev, IORESOURCE_MEM, 1); + + if (!r_data || !r_busy) { + dev_err(&pdev->dev, "missing resource(s)\n"); + return -EINVAL; + } + + conf.control_and_data_register = ioremap(r_data->start, r_data->end - r_data->start + 1); + if (!conf.control_and_data_register) { + err = -ENODEV; + goto exit; + } + + conf.busy_bit_register = ioremap(r_busy->start, r_busy->end - r_busy->start + 1); + if (!conf.busy_bit_register) { + err = -ENODEV; + goto exit_unmap1; + } + + conf.channels = + MAX197_SET_CHANNEL_CONF(0, MAX197_RANGE_0_5) | + MAX197_SET_CHANNEL_CONF(1, MAX197_RANGE_0_5) | + MAX197_SET_CHANNEL_CONF(2, MAX197_RANGE_0_5) | + MAX197_SET_CHANNEL_CONF(3, MAX197_RANGE_0_5) | + MAX197_SET_CHANNEL_CONF(4, MAX197_RANGE_0_5) | + MAX197_SET_CHANNEL_CONF(5, MAX197_RANGE_0_5) | + MAX197_SET_CHANNEL_CONF(6, MAX197_RANGE_0_5) | + MAX197_SET_CHANNEL_CONF(7, MAX197_RANGE_0_5); + + /* Register sysfs hooks */ + if ((err = sysfs_create_group(&pdev->dev.kobj, &max197_group))) + goto exit_unmap2; + + printk(PFX "TS-72xx max197 driver, v%s\n", DRV_VERSION); + return 0; + +exit_unmap2: + iounmap(conf.busy_bit_register); +exit_unmap1: + iounmap(conf.control_and_data_register); +exit: + return err; +} + +static int __devexit ts72xx_max197_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &max197_group); + iounmap(conf.busy_bit_register); + iounmap(conf.control_and_data_register); + return 0; +} + +static struct platform_driver ts72xx_max197_platform_driver = { + .probe = ts72xx_max197_probe, + .remove = __devexit_p(ts72xx_max197_remove), + .driver = { + .name = "ts72xx-max197", + .owner = THIS_MODULE, + }, +}; + +static int __init ts72xx_max197_init(void) +{ + return platform_driver_register(&ts72xx_max197_platform_driver); +} + +static void __exit ts72xx_max197_exit(void) +{ + platform_driver_unregister(&ts72xx_max197_platform_driver); +} + +#ifdef CONFIG_TS72XX_MAX197_AVERAGE +module_param(average, ushort, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(average, "Allow average measurement (default=1)"); +#endif + +MODULE_AUTHOR("Matthieu Crapet "); +MODULE_DESCRIPTION("TS-72xx max197 driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(ts72xx_max197_init); +module_exit(ts72xx_max197_exit); -- 1.7.1