From c6da2cfeb05178a11c6d062a06f8078150ee492f Mon Sep 17 00:00:00 2001 From: codeworkx Date: Sat, 2 Jun 2012 13:09:29 +0200 Subject: samsung update 1 --- drivers/misc/stmpe811-adc.c | 508 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 508 insertions(+) create mode 100644 drivers/misc/stmpe811-adc.c (limited to 'drivers/misc/stmpe811-adc.c') diff --git a/drivers/misc/stmpe811-adc.c b/drivers/misc/stmpe811-adc.c new file mode 100644 index 0000000..9a02186 --- /dev/null +++ b/drivers/misc/stmpe811-adc.c @@ -0,0 +1,508 @@ +/* + * stmpe811-adc.c + * + * Copyright (C) 2011 Samsung Electronics + * SangYoung Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STMPE811_CHIP_ID 0x00 +#define STMPE811_ID_VER 0x02 +#define STMPE811_SYS_CTRL1 0x03 +#define STMPE811_SYS_CTRL2 0x04 +#define STMPE811_INT_CTRL 0x09 +#define STMPE811_INT_EN 0x0A +#define STMPE811_INT_STA 0x0B +#define STMPE811_ADC_INT_EN 0x0E +#define STMPE811_ADC_INT_STA 0x0F +#define STMPE811_ADC_CTRL1 0x20 +#define STMPE811_ADC_CTRL2 0x21 +#define STMPE811_ADC_CAPT 0x22 +#define STMPE811_ADC_DATA_CH0 0x30 +#define STMPE811_ADC_DATA_CH1 0x32 +#define STMPE811_ADC_DATA_CH2 0x34 +#define STMPE811_ADC_DATA_CH3 0x36 +#define STMPE811_ADC_DATA_CH4 0x38 +#define STMPE811_ADC_DATA_CH5 0x3A +#define STMPE811_ADC_DATA_CH6 0x3C +#define STMPE811_ADC_DATA_CH7 0x3E +#define STMPE811_GPIO_AF 0x17 +#define STMPE811_TSC_CTRL 0x40 + +static struct device *adc_dev; +static struct i2c_client *stmpe811_adc_i2c_client; + +struct stmpe811_adc_data { + struct i2c_client *client; + struct stmpe811_platform_data *pdata; + + struct mutex adc_lock; +}; + +static int stmpe811_i2c_read(struct i2c_client *client, + u8 reg, + u8 *data, + u8 length) +{ + int ret; + + ret = i2c_smbus_read_i2c_block_data(client, (u8)reg, length, data); + if (ret < 0) { + pr_err("%s: err %d, reg: 0x%02x\n", __func__, ret, reg); + return ret; + } + + return 0; +} + +int stmpe811_write_register(struct i2c_client *client, + u8 reg, + u16 w_data) +{ + int ret; + + ret = i2c_smbus_write_word_data(client, (u8)reg, w_data); + if (ret < 0) { + pr_err("%s: err %d, reg: 0x%02x\n", __func__, ret, reg); + return ret; + } + + return 0; +} + +u16 stmpe811_get_adc_data(u8 ch) +{ + struct i2c_client *client = stmpe811_adc_i2c_client; + u8 data[2]; + u16 w_data; + int prog, retry_cnt; + int data_channel_addr; + + stmpe811_write_register(client, STMPE811_ADC_CAPT, (1 << ch)); + + prog = 0; + retry_cnt = 10; + do { + stmpe811_i2c_read(client, STMPE811_ADC_CAPT, data, (u8)1); + pr_debug("%s: ADC_CAPT(0x%x)\n", __func__, data[0]); + + prog = (data[0] & (1 << ch)); + + if (prog) { + pr_debug("%s: ch%d conversion completed\n", + __func__, ch); + break; + } else { + pr_info("%s: ch%d conversion progressing\n", + __func__, ch); + msleep(20); + } + } while ((!prog) && (--retry_cnt > 0)); + + if (retry_cnt == 0) { + pr_err("%s: ch%d conversion fail\n", __func__, ch); + return -EBUSY; + } + + data_channel_addr = STMPE811_ADC_DATA_CH0 + (ch * 2); + stmpe811_i2c_read(client, data_channel_addr, data, (u8)2); + w_data = ((data[0] << 8) | data[1]) & 0x0FFF; + pr_debug("%s: ADC_DATA_CH%d(0x%x, %d)\n", __func__, ch, w_data, w_data); + + return w_data; +} +EXPORT_SYMBOL(stmpe811_get_adc_data); + +int stmpe811_get_adc_value(u8 channel) +{ + struct stmpe811_platform_data *pdata = + stmpe811_adc_i2c_client->dev.platform_data; + int adc_data; + int adc_value; + int low, mid, high; + struct adc_table_data *temper_table = NULL; + pr_debug("%s\n", __func__); + + adc_data = stmpe811_get_adc_data(channel); + + low = mid = high = 0; + switch (channel) { + case 0: + if ((!pdata->adc_table_ch0) || (!pdata->table_size_ch0)) + goto table_err; + temper_table = pdata->adc_table_ch0; + high = pdata->table_size_ch0 - 1; + break; + case 1: + if ((!pdata->adc_table_ch1) || (!pdata->table_size_ch1)) + goto table_err; + temper_table = pdata->adc_table_ch1; + high = pdata->table_size_ch1 - 1; + break; + case 2: + if ((!pdata->adc_table_ch2) || (!pdata->table_size_ch2)) + goto table_err; + temper_table = pdata->adc_table_ch2; + high = pdata->table_size_ch2 - 1; + break; + case 3: + if ((!pdata->adc_table_ch3) || (!pdata->table_size_ch3)) + goto table_err; + temper_table = pdata->adc_table_ch3; + high = pdata->table_size_ch3 - 1; + break; + case 4: + if ((!pdata->adc_table_ch4) || (!pdata->table_size_ch4)) + goto table_err; + temper_table = pdata->adc_table_ch4; + high = pdata->table_size_ch4 - 1; + break; + case 5: + if ((!pdata->adc_table_ch5) || (!pdata->table_size_ch5)) + goto table_err; + temper_table = pdata->adc_table_ch5; + high = pdata->table_size_ch5 - 1; + break; + case 6: + if ((!pdata->adc_table_ch6) || (!pdata->table_size_ch6)) + goto table_err; + temper_table = pdata->adc_table_ch6; + high = pdata->table_size_ch6 - 1; + break; + case 7: + if ((!pdata->adc_table_ch7) || (!pdata->table_size_ch7)) + goto table_err; + temper_table = pdata->adc_table_ch7; + high = pdata->table_size_ch7 - 1; + break; + default: + pr_info("%s: not exist temper table for ch(%d)\n", __func__, + channel); + return -EINVAL; + break; + } + + /* Out of table range */ + if (adc_data >= temper_table[low].adc) { + adc_value = temper_table[low].value * 10; + return adc_value; + } else if (adc_data <= temper_table[high].adc) { + adc_value = temper_table[high].value * 10; + return adc_value; + } + + while (low <= high) { + mid = (low + high) / 2; + if (temper_table[mid].adc > adc_data) + low = mid + 1; + else if (temper_table[mid].adc < adc_data) + high = mid - 1; + else + break; + } + adc_value = temper_table[mid].value * 10; + + pr_debug("%s: adc data(%d), adc value(%d)\n", __func__, + adc_data, adc_value); + return adc_value; + +table_err: + pr_err("%s: table for ch%d does not exist\n", __func__, channel); + return -EINVAL; +} +EXPORT_SYMBOL(stmpe811_get_adc_value); + +static ssize_t adc_data_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", "adc_test_show"); +} + +static ssize_t adc_data_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int mode; + s16 val; + sscanf(buf, "%d", &mode); + + if (mode < 0 || mode > 7) { + pr_err("invalid channel: %d", mode); + return -EINVAL; + } + + val = stmpe811_get_adc_data((u8)mode); + pr_info("adc data from ch%d: %d\n", mode, val); + + return count; +} +static DEVICE_ATTR(adc_data, S_IRUGO | S_IWUSR | S_IWGRP, + adc_data_show, adc_data_store); + +static ssize_t adc_value_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", "adc_test_show"); +} + +static ssize_t adc_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int mode; + s16 val; + sscanf(buf, "%d", &mode); + + if (mode < 0 || mode > 7) { + pr_err("invalid channel: %d", mode); + return -EINVAL; + } + + val = stmpe811_get_adc_value((u8)mode); + pr_info("adc value from ch%d: %d\n", mode, val); + + return count; +} +static DEVICE_ATTR(adc_value, S_IRUGO | S_IWUSR | S_IWGRP, + adc_value_show, adc_value_store); + +static int stmpe811_reg_init(struct stmpe811_adc_data *adc_data) +{ + struct i2c_client *client = adc_data->client; + int ret; + u8 data[2]; + u16 w_data; + pr_debug("%s\n", __func__); + + /* read device identification */ + ret = stmpe811_i2c_read(client, STMPE811_CHIP_ID, data, (u8)2); + pr_info("%s: ret: %d\n", __func__, ret); + if (ret < 0) { + pr_err("%s: reg init error: %d\n", __func__, ret); + goto reg_init_error; + } + + w_data = ((data[0]<<8) | data[1]) & 0x0FFF; + pr_info("%s: STMPE811_CHIP_ID(0x%x)\n", __func__, w_data); + + /* read revision number, 0x01 for es, 0x03 for final silicon */ + stmpe811_i2c_read(client, STMPE811_ID_VER, data, (u8)1); + pr_info("%s: STMPE811_ID_VER(0x%x)\n", __func__, data[0]); + + /* clock control: only adc on */ + w_data = 0x0E; + stmpe811_write_register(client, STMPE811_SYS_CTRL2, w_data); + stmpe811_i2c_read(client, STMPE811_SYS_CTRL2, data, (u8)1); + pr_info("%s: STMPE811_SYS_CTRL2(0x%x)\n", __func__, data[0]); + + /* interrupt enable: disable interrupt */ + w_data = 0x00; + stmpe811_write_register(client, STMPE811_INT_EN, w_data); + stmpe811_i2c_read(client, STMPE811_INT_EN, data, (u8)1); + pr_info("%s: STMPE811_INT_EN(0x%x)\n", __func__, data[0]); + + /* adc control: 64 sample time, 12bit adc, internel referance*/ + w_data = 0x38; + stmpe811_write_register(client, STMPE811_ADC_CTRL1, w_data); + stmpe811_i2c_read(client, STMPE811_ADC_CTRL1, data, (u8)1); + pr_info("%s: STMPE811_ADC_CTRL1(0x%x)\n", __func__, data[0]); + + /* adc control: 1.625MHz typ */ + w_data = 0x03; + stmpe811_write_register(client, STMPE811_ADC_CTRL2, w_data); + stmpe811_i2c_read(client, STMPE811_ADC_CTRL2, data, (u8)1); + pr_info("%s: STMPE811_ADC_CTRL2(0x%x)\n", __func__, data[0]); + + /* alt func: use for adc */ + w_data = 0x00; + stmpe811_write_register(client, STMPE811_GPIO_AF, w_data); + stmpe811_i2c_read(client, STMPE811_GPIO_AF, data, (u8)1); + pr_info("%s: STMPE811_GPIO_AF(0x%x)\n", __func__, data[0]); + + /* ts control: tsc disable */ + w_data = 0x00; + stmpe811_write_register(client, STMPE811_TSC_CTRL, w_data); + stmpe811_i2c_read(client, STMPE811_TSC_CTRL, data, (u8)1); + pr_info("%s: STMPE811_TSC_CTRL(0x%x)\n", __func__, data[0]); + +reg_init_error: + return ret; +} + +static const struct file_operations stmpe811_fops = { + .owner = THIS_MODULE, +}; + +static struct miscdevice stmpe811_adc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sec_adc", + .fops = &stmpe811_fops, +}; + +static int stmpe811_adc_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct stmpe811_adc_data *adc_data; + int ret; + u8 i2c_data[2]; + pr_info("%s: stmpe811 adc driver loading\n", __func__); + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + return -EIO; + + adc_data = kzalloc(sizeof(struct stmpe811_adc_data), GFP_KERNEL); + if (!adc_data) + return -ENOMEM; + + adc_data->client = client; + adc_data->pdata = client->dev.platform_data; + + i2c_set_clientdata(client, adc_data); + + stmpe811_adc_i2c_client = client; + + ret = misc_register(&stmpe811_adc_device); + if (ret) + goto misc_register_fail; + + mutex_init(&adc_data->adc_lock); + + /* initialize adc registers */ + ret = stmpe811_reg_init(adc_data); + if (ret < 0) + goto reg_init_error; + + /* TODO: ADC_INT setting */ + + /* create sysfs for debugging and factory mode*/ + adc_dev = device_create(sec_class, NULL, 0, NULL, "adc"); + if (IS_ERR(adc_dev)) { + ret = -ENOMEM; + goto deregister_misc; + } + + ret = device_create_file(adc_dev, &dev_attr_adc_data); + if (ret < 0) + goto destroy_device; + + ret = device_create_file(adc_dev, &dev_attr_adc_value); + if (ret < 0) + goto dev_attr_adc_value_err; + + stmpe811_i2c_read(client, STMPE811_CHIP_ID, i2c_data, (u8)2); + pr_info("stmpe811 adc(id 0x%x) initialized\n", + ((i2c_data[0]<<8) | i2c_data[1])); + + return 0; + +dev_attr_adc_value_err: + device_remove_file(adc_dev, &dev_attr_adc_data); +destroy_device: + device_destroy(sec_class, 0); +deregister_misc: +reg_init_error: + misc_deregister(&stmpe811_adc_device); +misc_register_fail: + pr_info("stmpe811 probe fail: %d\n", ret); + kfree(adc_data); + return ret; +} + +static int stmpe811_adc_i2c_remove(struct i2c_client *client) +{ + struct stmpe811_adc_data *adc = i2c_get_clientdata(client); + pr_info("%s\n", __func__); + + misc_deregister(&stmpe811_adc_device); + + device_remove_file(adc_dev, &dev_attr_adc_value); + device_remove_file(adc_dev, &dev_attr_adc_data); + device_destroy(sec_class, 0); + + mutex_destroy(&adc->adc_lock); + kfree(adc); + + return 0; +} + +static int stmpe811_adc_suspend(struct device *dev) +{ + + return 0; +} + +static int stmpe811_adc_resume(struct device *dev) +{ + return 0; +} + +static const struct i2c_device_id stmpe811_adc_device_id[] = { + {"stmpe811-adc", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, stmpe811_adc_device_id); + +static const struct dev_pm_ops stmpe811_adc_pm_ops = { + .suspend = stmpe811_adc_suspend, + .resume = stmpe811_adc_resume, +}; + +static struct i2c_driver stmpe811_adc_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "stmpe811-adc", + .pm = &stmpe811_adc_pm_ops, + }, + .probe = stmpe811_adc_i2c_probe, + .remove = stmpe811_adc_i2c_remove, + .id_table = stmpe811_adc_device_id, +}; + +static int __init stmpe811_adc_init(void) +{ + return i2c_add_driver(&stmpe811_adc_i2c_driver); +} + +static void __exit stmpe811_adc_exit(void) +{ + i2c_del_driver(&stmpe811_adc_i2c_driver); +} + +module_init(stmpe811_adc_init); +module_exit(stmpe811_adc_exit); + +MODULE_AUTHOR("SangYoung Son "); +MODULE_DESCRIPTION("stmpe811 adc driver"); +MODULE_LICENSE("GPL"); -- cgit v1.1