- remove all lights.*.so files from /system/lib/hw (backup if u want) and replace with lights.qsd8k.so from Bravo ROM or download from http://www.multiupload.com/2M5T02M3TU
 - if button backlight don’t work: turn screen on, press any key and put phone under bright light ( this should activate light sensor and backlight)
Conflicts:
	arch/arm/configs/htcleo_defconfig
	arch/arm/mach-msm/board-htcleo.h
Add commit from 654ce990ab
		
	
		
			
				
	
	
		
			880 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			880 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* arch/arm/mach-msm/atmega_microp_common.c
 | |
|  * Copyright (C) 2009 HTC Corporation.
 | |
|  *
 | |
|  * This software is licensed under the terms of the GNU General Public
 | |
|  * License version 2, as published by the Free Software Foundation, and
 | |
|  * may be copied, distributed, and modified under those terms.
 | |
|  *
 | |
|  * 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 <linux/kernel.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/workqueue.h>
 | |
| #include <linux/i2c.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/gpio.h>
 | |
| #include <linux/syscalls.h>
 | |
| #include <mach/atmega_microp.h>
 | |
| #include <asm/mach-types.h>
 | |
| #include <linux/earlysuspend.h>
 | |
| #include <mach/drv_callback.h>
 | |
| #include <linux/wakelock.h>
 | |
| #include <linux/miscdevice.h>
 | |
| #include <linux/lightsensor.h>
 | |
| #include <linux/irq.h>
 | |
| #include <asm/uaccess.h>
 | |
| #include "proc_comm.h"
 | |
| 
 | |
| 
 | |
| #define I2C_READ_RETRY_TIMES			10
 | |
| #define I2C_WRITE_RETRY_TIMES			10
 | |
| #define MICROP_I2C_WRITE_BLOCK_SIZE		80
 | |
| 
 | |
| static struct i2c_client *private_microp_client;
 | |
| static struct microp_ops *board_ops;
 | |
| 
 | |
| static int microp_rw_delay;
 | |
| 
 | |
| static char *hex2string(uint8_t *data, int len)
 | |
| {
 | |
| 	static char buf[MICROP_I2C_WRITE_BLOCK_SIZE*4];
 | |
| 	int i;
 | |
| 
 | |
| 	i = (sizeof(buf) - 1) / 4;
 | |
| 	if (len > i)
 | |
| 		len = i;
 | |
| 
 | |
| 	for (i = 0; i < len; i++)
 | |
| 		sprintf(buf + i * 4, "[%02X]", data[i]);
 | |
| 
 | |
| 	return buf;
 | |
| }
 | |
| 
 | |
| static int i2c_read_block(struct i2c_client *client, uint8_t addr,
 | |
| 	uint8_t *data, int length)
 | |
| {
 | |
| 	int retry;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 	struct i2c_msg msgs[] = {
 | |
| 		{
 | |
| 			.addr = client->addr,
 | |
| 			.flags = 0,
 | |
| 			.len = 1,
 | |
| 			.buf = &addr,
 | |
| 		},
 | |
| 		{
 | |
| 			.addr = client->addr,
 | |
| 			.flags = I2C_M_RD,
 | |
| 			.len = length,
 | |
| 			.buf = data,
 | |
| 		}
 | |
| 	};
 | |
| 
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 	mutex_lock(&cdata->microp_i2c_rw_mutex);
 | |
| 	hr_msleep(1);
 | |
| 	for (retry = 0; retry <= I2C_READ_RETRY_TIMES; retry++) {
 | |
| 		if (i2c_transfer(client->adapter, msgs, 2) == 2)
 | |
| 			break;
 | |
| 		msleep(microp_rw_delay);
 | |
| 	}
 | |
| 	mutex_unlock(&cdata->microp_i2c_rw_mutex);
 | |
| 	dev_dbg(&client->dev, "R [%02X] = %s\n",
 | |
| 			addr, hex2string(data, length));
 | |
| 
 | |
| 	if (retry > I2C_READ_RETRY_TIMES) {
 | |
| 		dev_err(&client->dev, "i2c_read_block retry over %d\n",
 | |
| 			I2C_READ_RETRY_TIMES);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int i2c_write_block(struct i2c_client *client, uint8_t addr,
 | |
| 	uint8_t *data, int length)
 | |
| {
 | |
| 	int retry;
 | |
| 	uint8_t buf[MICROP_I2C_WRITE_BLOCK_SIZE];
 | |
| 	int i;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 	struct i2c_msg msg[] = {
 | |
| 		{
 | |
| 			.addr = client->addr,
 | |
| 			.flags = 0,
 | |
| 			.len = length + 1,
 | |
| 			.buf = buf,
 | |
| 		}
 | |
| 	};
 | |
| 
 | |
| 	dev_dbg(&client->dev, "W [%02X] = %s\n",
 | |
| 			addr, hex2string(data, length));
 | |
| 
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 	if (length + 1 > MICROP_I2C_WRITE_BLOCK_SIZE) {
 | |
| 		dev_err(&client->dev, "i2c_write_block length too long\n");
 | |
| 		return -E2BIG;
 | |
| 	}
 | |
| 
 | |
| 	buf[0] = addr;
 | |
| 	for (i = 0; i < length; i++)
 | |
| 		buf[i+1] = data[i];
 | |
| 
 | |
| 	mutex_lock(&cdata->microp_i2c_rw_mutex);
 | |
| 	hr_msleep(1);
 | |
| 	for (retry = 0; retry <= I2C_WRITE_RETRY_TIMES; retry++) {
 | |
| 		if (i2c_transfer(client->adapter, msg, 1) == 1)
 | |
| 			break;
 | |
| 		msleep(microp_rw_delay);
 | |
| 	}
 | |
| 	if (retry > I2C_WRITE_RETRY_TIMES) {
 | |
| 		dev_err(&client->dev, "i2c_write_block retry over %d\n",
 | |
| 			I2C_WRITE_RETRY_TIMES);
 | |
| 		mutex_unlock(&cdata->microp_i2c_rw_mutex);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 	if (addr == MICROP_I2C_WCMD_LCM_BURST_EN)
 | |
| 		udelay(500);/*1.5ms for microp SPI write */
 | |
| 	mutex_unlock(&cdata->microp_i2c_rw_mutex);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #ifndef CONFIG_MACH_HTCLEO
 | |
| int microp_i2c_read(uint8_t addr, uint8_t *data, int length)
 | |
| {
 | |
| 	struct i2c_client *client = private_microp_client;
 | |
| 
 | |
| 	if (!client)	{
 | |
| 		printk(KERN_ERR "%s: dataset: client is empty\n", __func__);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| 	if (i2c_read_block(client, addr, data, length) < 0)	{
 | |
| 		dev_err(&client->dev, "%s: write microp i2c fail\n", __func__);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| EXPORT_SYMBOL(microp_i2c_read);
 | |
| 
 | |
| int microp_i2c_write(uint8_t addr, uint8_t *data, int length)
 | |
| {
 | |
| 	struct i2c_client *client = private_microp_client;
 | |
| 
 | |
| 	if (!client)	{
 | |
| 		printk(KERN_ERR "%s: dataset: client is empty\n", __func__);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| 	if (i2c_write_block(client, addr, data, length) < 0)	{
 | |
| 		dev_err(&client->dev, "%s: write microp i2c fail\n", __func__);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| EXPORT_SYMBOL(microp_i2c_write);
 | |
| #endif
 | |
| 
 | |
| void microp_mobeam_enable(int enable)
 | |
| {
 | |
| 	if (enable)
 | |
| 		microp_rw_delay = 500;
 | |
| 	else
 | |
| 		microp_rw_delay = 5;
 | |
| }
 | |
| EXPORT_SYMBOL(microp_mobeam_enable);
 | |
| 
 | |
| void microp_register_ops(struct microp_ops *ops)
 | |
| {
 | |
| 	board_ops = ops;
 | |
| }
 | |
| 
 | |
| int microp_function_check(struct i2c_client *client, uint8_t category)
 | |
| {
 | |
| 	struct microp_i2c_platform_data *pdata;
 | |
| 	int i, ret = -1;
 | |
| 
 | |
| 	pdata = client->dev.platform_data;
 | |
| 
 | |
| 	for (i = 0; i < pdata->num_functions; i++) {
 | |
| 		if (pdata->microp_function[i].category == category) {
 | |
| 			ret = i;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	if (ret < 0)
 | |
| 		pr_err("%s: No function %d !!\n", __func__, category);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int microp_write_interrupt(struct i2c_client *client,
 | |
| 		uint16_t interrupt, uint8_t enable)
 | |
| {
 | |
| 	uint8_t data[2], addr;
 | |
| 	int ret = -1;
 | |
| 
 | |
| 	if (enable)
 | |
| 		addr = MICROP_I2C_WCMD_GPI_INT_CTL_EN;
 | |
| 	else
 | |
| 		addr = MICROP_I2C_WCMD_GPI_INT_CTL_DIS;
 | |
| 
 | |
| 	data[0] = interrupt >> 8;
 | |
| 	data[1] = interrupt & 0xFF;
 | |
| 	ret = i2c_write_block(client, addr, data, 2);
 | |
| 
 | |
| 	if (ret < 0)
 | |
| 		dev_err(&client->dev, "%s: %s 0x%x interrupt failed\n",
 | |
| 			__func__, (enable ? "enable" : "disable"), interrupt);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| #ifndef CONFIG_MACH_HTCLEO
 | |
| int microp_set_adc_req(uint8_t value)
 | |
| {
 | |
| 	struct i2c_client *client;
 | |
| 	int ret;
 | |
| 	uint8_t cmd[1];
 | |
| 
 | |
| 	client = private_microp_client;	
 | |
| 	cmd[0] = value;
 | |
| 	ret = i2c_write_block(client, MICROP_I2C_WCMD_ADC_REQ, cmd, 1);
 | |
| 	if (ret < 0) 
 | |
| 	{
 | |
| 		dev_err(&client->dev, "%s: request adc fail\n", __func__);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int microp_get_remote_adc(uint32_t *val)
 | |
| {
 | |
| 	struct i2c_client *client;
 | |
| 	int ret;
 | |
| 	uint8_t data[4];
 | |
| 
 | |
| 	if (!val)
 | |
| 		return -EIO; 
 | |
| 
 | |
| 	client = private_microp_client;	
 | |
| 	ret = i2c_read_block(client, MICROP_I2C_RCMD_ADC_VALUE, data, 2);
 | |
| 	if (ret < 0) 
 | |
| 	{
 | |
| 		dev_err(&client->dev, "%s: request adc fail\n", __func__);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| //	printk("%x %x\n", data[0], data[1]);
 | |
| 	*val = data[1] | (data[0] << 8);
 | |
| 	printk("remote adc %d\n", *val);
 | |
| 	return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int microp_read_adc(uint8_t *data)
 | |
| {
 | |
| 	struct i2c_client *client;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	client = private_microp_client;
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 
 | |
| 	mutex_lock(&cdata->microp_adc_mutex);
 | |
| 	if (i2c_write_block(client, MICROP_I2C_WCMD_READ_ADC_VALUE_REQ,
 | |
| 			data, 2) < 0) {
 | |
| 		dev_err(&client->dev, "%s: request adc fail\n", __func__);
 | |
| 		ret = -EIO;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 	memset(data, 0x00, sizeof(data));
 | |
| 	if (i2c_read_block(client, MICROP_I2C_RCMD_ADC_VALUE, data, 2) < 0) {
 | |
| 		dev_err(&client->dev, "%s: read adc fail\n", __func__);
 | |
| 		ret = -EIO;
 | |
| 		goto exit;
 | |
| 	}
 | |
| exit:
 | |
| 	mutex_unlock(&cdata->microp_adc_mutex);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| EXPORT_SYMBOL(microp_read_adc);
 | |
| 
 | |
| int microp_read_gpio_status(uint8_t *data)
 | |
| {
 | |
| 	struct i2c_client *client;
 | |
| 	struct microp_i2c_platform_data *pdata;
 | |
| 	int length;
 | |
| 
 | |
| 	client = private_microp_client;
 | |
| 	pdata = client->dev.platform_data;
 | |
| 
 | |
| 	if (pdata->cmd_diff & CMD_83_DIFF)
 | |
| 		length = 2;
 | |
| 	else
 | |
| 		length = 3;
 | |
| 	memset(data, 0x00, sizeof(data));
 | |
| 	if (i2c_read_block(client, MICROP_I2C_RCMD_GPIO_STATUS,
 | |
| 			data, length) < 0) {
 | |
| 		dev_err(&client->dev, "%s: read gpio status fail\n", __func__);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void microp_pm_power_off(struct i2c_client *client)
 | |
| {
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| static void microp_reset_system(void)
 | |
| {
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| static int microp_oj_intr_enable(struct i2c_client *client, uint8_t enable)
 | |
| {
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 	enable = enable ? 1 : 0;
 | |
| 	return microp_write_interrupt(client,
 | |
| 			cdata->int_pin.int_oj, enable);
 | |
| }
 | |
| 
 | |
| static int microp_spi_enable(struct i2c_client *client, uint8_t enable)
 | |
| {
 | |
| 	uint8_t data;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	data = enable ? 1 : 0;
 | |
| 	ret = i2c_write_block(client, MICROP_I2C_WCMD_SPI_EN, &data, 1);
 | |
| 	if (ret != 0)
 | |
| 		printk(KERN_ERR "%s: set SPI %s fail\n", __func__,
 | |
| 			(enable ? "enable" : "disable"));
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| #ifndef CONFIG_MACH_HTCLEO
 | |
| int microp_spi_vote_enable(int spi_device, uint8_t enable)
 | |
| {
 | |
| 	struct i2c_client *client = private_microp_client;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 	struct microp_i2c_platform_data *pdata;
 | |
| 	uint8_t data[2] = {0, 0};
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (!client)	{
 | |
| 		printk(KERN_ERR "%s: dataset: client is empty\n", __func__);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 	pdata = client->dev.platform_data;
 | |
| 
 | |
| 	if (spi_device == SPI_OJ)
 | |
| 		microp_oj_intr_enable(client, enable);
 | |
| 
 | |
| 	mutex_lock(&cdata->microp_adc_mutex);
 | |
| 	if (enable)
 | |
| 		cdata->spi_devices_vote |= spi_device;
 | |
| 	else
 | |
| 		cdata->spi_devices_vote &= ~spi_device;
 | |
| 
 | |
| 	ret = i2c_read_block(client, MICROP_I2C_RCMD_SPI_BL_STATUS, data, 2);
 | |
| 	if (ret != 0) {
 | |
| 		printk(KERN_ERR "%s: read SPI/BL status fail\n", __func__);
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	if ((data[1] & 0x01) ==
 | |
| 		((pdata->spi_devices & cdata->spi_devices_vote) ? 1 : 0))
 | |
| 		goto exit;
 | |
| 
 | |
| 	if (pdata->spi_devices & cdata->spi_devices_vote)
 | |
| 		enable = 1;
 | |
| 	else
 | |
| 		enable = 0;
 | |
| 	mutex_unlock(&cdata->microp_adc_mutex);
 | |
| 
 | |
| 	ret = microp_spi_enable(client, enable);
 | |
| 	return ret;
 | |
| 
 | |
| exit:
 | |
| 	mutex_unlock(&cdata->microp_adc_mutex);
 | |
| 	return ret;
 | |
| 
 | |
| }
 | |
| 
 | |
| EXPORT_SYMBOL(microp_spi_vote_enable);
 | |
| #endif
 | |
| 
 | |
| static void microp_reset_microp(struct i2c_client *client)
 | |
| {
 | |
| 	struct microp_i2c_platform_data *pdata;
 | |
| 
 | |
| 	pdata = client->dev.platform_data;
 | |
| 
 | |
| 	gpio_set_value(pdata->gpio_reset, 0);
 | |
| 	udelay(120);
 | |
| 	gpio_set_value(pdata->gpio_reset, 1);
 | |
| 	mdelay(5);
 | |
| }
 | |
| 
 | |
| static ssize_t microp_version_show(struct device *dev,
 | |
| 				  struct device_attribute *attr, char *buf)
 | |
| {
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 
 | |
| 	cdata = i2c_get_clientdata(to_i2c_client(dev));
 | |
| 
 | |
| 	return sprintf(buf, "%04X\n", cdata->version);
 | |
| }
 | |
| 
 | |
| static DEVICE_ATTR(version, 0644, microp_version_show, NULL);
 | |
| 
 | |
| static ssize_t microp_reset_store(struct device *dev,
 | |
| 				   struct device_attribute *attr,
 | |
| 				   const char *buf, size_t count)
 | |
| {
 | |
| 	struct i2c_client *client;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 	int val;
 | |
| 
 | |
| 	val = -1;
 | |
| 	sscanf(buf, "%u", &val);
 | |
| 	if (val != 1)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	client = to_i2c_client(dev);
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 
 | |
| 	microp_reset_microp(client);
 | |
| 	if (board_ops->init_microp_func)
 | |
| 		board_ops->init_microp_func(client);
 | |
| 
 | |
| 	return count;
 | |
| }
 | |
| 
 | |
| static DEVICE_ATTR(reset, 0644, NULL, microp_reset_store);
 | |
| 
 | |
| static ssize_t microp_gpio_show(struct device *dev,
 | |
| 				  struct device_attribute *attr, char *buf)
 | |
| {
 | |
| 	uint8_t data[3] = {0, 0, 0};
 | |
| 	int ret;
 | |
| 
 | |
| 	microp_read_gpio_status(data);
 | |
| 	ret = sprintf(buf, "PB = 0x%x, PC = 0x%x, PD = 0x%x\n",
 | |
| 				data[0], data[1], data[2]);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static ssize_t microp_gpio_store(struct device *dev,
 | |
| 				   struct device_attribute *attr,
 | |
| 				   const char *buf, size_t count)
 | |
| {
 | |
| 	struct i2c_client *client;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 	int enable = 0, tmp[3] = {0, 0, 0};
 | |
| 	uint8_t addr, data[3] = {0, 0, 0};
 | |
| 
 | |
| 	sscanf(buf, "%d %d %d %d", &enable, &tmp[0], &tmp[1], &tmp[2]);
 | |
| 
 | |
| 	if (enable != 0 && enable != 1)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	client = to_i2c_client(dev);
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 
 | |
| 	if (enable)
 | |
| 		addr = MICROP_I2C_WCMD_GPO_LED_STATUS_EN;
 | |
| 	else
 | |
| 		addr = MICROP_I2C_WCMD_GPO_LED_STATUS_DIS;
 | |
| 	data[0] = (uint8_t)tmp[0];
 | |
| 	data[1] = (uint8_t)tmp[1];
 | |
| 	data[2] = (uint8_t)tmp[2];
 | |
| 	i2c_write_block(client, addr, data, 3);
 | |
| 
 | |
| 	return count;
 | |
| }
 | |
| 
 | |
| static DEVICE_ATTR(gpio, 0644,  microp_gpio_show,
 | |
| 			microp_gpio_store);
 | |
| 
 | |
| static irqreturn_t microp_intr_irq_handler(int irq, void *dev_id)
 | |
| {
 | |
| 	struct i2c_client *client;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 
 | |
| 	client = to_i2c_client(dev_id);
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 
 | |
| 	disable_irq_nosync(client->irq);
 | |
| 	queue_work(cdata->microp_queue, &cdata->microp_intr_work);
 | |
| 	return IRQ_HANDLED;
 | |
| }
 | |
| 
 | |
| static void microp_int_dispatch(u32 status)
 | |
| {
 | |
| 	unsigned int mask;
 | |
| 	int irq;
 | |
| 
 | |
| 	while (status) {
 | |
| 		mask = status & -status;
 | |
| 		irq = fls(mask) - 1;
 | |
| 		status &= ~mask;
 | |
| 		generic_handle_irq(FIRST_MICROP_IRQ + irq);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static enum hrtimer_restart hr_dispath_irq_func(struct hrtimer *data)
 | |
| {
 | |
| 	struct i2c_client *client = private_microp_client;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 	microp_int_dispatch(cdata->intr_status);
 | |
| 	cdata->intr_status = 0;
 | |
| 	return HRTIMER_NORESTART;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void microp_intr_work_func(struct work_struct *work)
 | |
| {
 | |
| 	struct i2c_client *client = private_microp_client;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 	struct microp_i2c_platform_data *pdata;
 | |
| 	uint8_t data[3];
 | |
| 	uint16_t intr_status = 0;
 | |
| 	int sd_insert = 0;
 | |
| 	ktime_t zero_debounce;
 | |
| 
 | |
| 	zero_debounce = ktime_set(0, 0);  /* No debounce time */
 | |
| 
 | |
| 	if (!client) {
 | |
| 		printk(KERN_ERR "%s: dataset: client is empty\n", __func__);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 	pdata = client->dev.platform_data;
 | |
| 
 | |
| 	memset(data, 0x00, sizeof(data));
 | |
| 	if (i2c_read_block(client, MICROP_I2C_RCMD_GPI_INT_STATUS,
 | |
| 			data, 2) < 0)
 | |
| 		dev_err(&client->dev, "%s: read interrupt status fail\n",
 | |
| 				__func__);
 | |
| 	intr_status = data[0]<<8 | data[1];
 | |
| 	if (i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_STATUS_CLR,
 | |
| 			data, 2) < 0)
 | |
| 		dev_err(&client->dev, "%s: clear interrupt status fail\n",
 | |
| 				__func__);
 | |
| 
 | |
| 	if (intr_status & cdata->int_pin.int_reset) {
 | |
| 		dev_info(&client->dev, "Reset button is pressed\n");
 | |
| 		microp_reset_system();
 | |
| 	}
 | |
| 	if (intr_status & cdata->int_pin.int_simcard) {
 | |
| 		dev_info(&client->dev, "SIM Card is plugged/unplugged\n");
 | |
| 		microp_pm_power_off(client);
 | |
| 	}
 | |
| 
 | |
| 	if (intr_status & cdata->int_pin.int_sdcard) {
 | |
| 		dev_info(&client->dev, "SD Card is plugged/unplugged\n");
 | |
| 		msleep(300);
 | |
| 		microp_read_gpio_status(data);
 | |
| 		sd_insert = ((data[0] << 16 | data[1] << 8 | data[2])
 | |
| 				& cdata->gpio.sdcard) ? 1 : 0;
 | |
| 		if (sd_insert != cdata->sdcard_is_in) {
 | |
| 			cdata->sdcard_is_in = sd_insert;
 | |
| 			cnf_driver_event("sdcard_detect", &cdata->sdcard_is_in);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	cdata->intr_status = intr_status;
 | |
| 	hrtimer_start(&cdata->gen_irq_timer, zero_debounce, HRTIMER_MODE_REL);
 | |
| 	enable_irq(client->irq);
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_HAS_EARLYSUSPEND
 | |
| static void microp_early_suspend(struct early_suspend *h)
 | |
| {
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 	struct i2c_client *client = private_microp_client;
 | |
| 	struct microp_i2c_platform_data *pdata;
 | |
| 
 | |
| 	if (!client) {
 | |
| 		printk(KERN_ERR "%s: dataset: client is empty\n", __func__);
 | |
| 		return;
 | |
| 	}
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 	pdata = client->dev.platform_data;
 | |
| 
 | |
| 	atomic_set(&cdata->microp_is_suspend, 1);
 | |
| }
 | |
| 
 | |
| static void microp_late_resume(struct early_suspend *h)
 | |
| {
 | |
| 	struct i2c_client *client = private_microp_client;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 	struct microp_i2c_platform_data *pdata;
 | |
| 
 | |
| 	if (!client) {
 | |
| 		printk(KERN_ERR "%s: dataset: client is empty\n", __func__);
 | |
| 		return;
 | |
| 	}
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 	pdata = client->dev.platform_data;
 | |
| 
 | |
| 	atomic_set(&cdata->microp_is_suspend, 0);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int __devexit microp_i2c_remove(struct i2c_client *client)
 | |
| {
 | |
| 	struct microp_i2c_platform_data *pdata;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 
 | |
| 	pdata = client->dev.platform_data;
 | |
| 	cdata = i2c_get_clientdata(client);
 | |
| 
 | |
| #ifdef CONFIG_HAS_EARLYSUSPEND
 | |
| 	unregister_early_suspend(&cdata->early_suspend);
 | |
| #endif
 | |
| 
 | |
| 	if (client->irq)
 | |
| 		free_irq(client->irq, &client->dev);
 | |
| 
 | |
| 	gpio_free(pdata->gpio_reset);
 | |
| 
 | |
| 	device_remove_file(&client->dev, &dev_attr_reset);
 | |
| 	device_remove_file(&client->dev, &dev_attr_version);
 | |
| 	device_remove_file(&client->dev, &dev_attr_gpio);
 | |
| 	destroy_workqueue(cdata->microp_queue);
 | |
| 	kfree(cdata);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int microp_i2c_suspend(struct i2c_client *client,
 | |
| 	pm_message_t mesg)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int microp_i2c_resume(struct i2c_client *client)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void register_microp_devices(struct platform_device *devices, int num)
 | |
| {
 | |
| 	int i;
 | |
| 	for (i = 0; i < num; i++) {
 | |
| 		platform_device_register(devices + i);
 | |
| 		dev_set_drvdata(&(devices + i)->dev, private_microp_client);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int microp_i2c_probe(struct i2c_client *client
 | |
| 	, const struct i2c_device_id *id)
 | |
| {
 | |
| 	struct microp_i2c_platform_data *pdata;
 | |
| 	struct microp_i2c_client_data *cdata;
 | |
| 	uint8_t data[6];
 | |
| 	int ret;
 | |
| 
 | |
| 	cdata = kzalloc(sizeof(struct microp_i2c_client_data), GFP_KERNEL);
 | |
| 	if (!cdata) {
 | |
| 		ret = -ENOMEM;
 | |
| 		dev_err(&client->dev, "failed on allocat cdata\n");
 | |
| 		goto err_cdata;
 | |
| 	}
 | |
| 
 | |
| 	i2c_set_clientdata(client, cdata);
 | |
| 
 | |
| 	mutex_init(&cdata->microp_adc_mutex);
 | |
| 	mutex_init(&cdata->microp_i2c_rw_mutex);
 | |
| 
 | |
| 	private_microp_client = client;
 | |
| 	pdata = client->dev.platform_data;
 | |
| 	if (!pdata) {
 | |
| 		ret = -EBUSY;
 | |
| 		dev_err(&client->dev, "failed on get pdata\n");
 | |
| 		goto err_exit;
 | |
| 	}
 | |
| 	pdata->dev_id = (void *)&client->dev;
 | |
| 	microp_rw_delay = 5;
 | |
| 
 | |
| 	ret = i2c_read_block(client, MICROP_I2C_RCMD_VERSION, data, 2);
 | |
| 	if (ret || !(data[0] && data[1])) {
 | |
| 		ret = -ENODEV;
 | |
| 		dev_err(&client->dev, "failed on get microp version\n");
 | |
| 		goto err_exit;
 | |
| 	}
 | |
| 	dev_info(&client->dev, "microp version [%02X][%02X]\n",
 | |
| 			data[0], data[1]);
 | |
| 
 | |
| 	ret = gpio_request(pdata->gpio_reset, "atmega_microp");
 | |
| 	if (ret < 0) {
 | |
| 		dev_err(&client->dev, "failed on request gpio reset\n");
 | |
| 		goto err_exit;
 | |
| 	}
 | |
| 	ret = gpio_direction_output(pdata->gpio_reset, 1);
 | |
| 	if (ret < 0) {
 | |
| 		dev_err(&client->dev,
 | |
| 				"failed on gpio_direction_output reset\n");
 | |
| 		goto err_gpio_reset;
 | |
| 	}
 | |
| 
 | |
| 	cdata->version = data[0] << 8 | data[1];
 | |
| 	atomic_set(&cdata->microp_is_suspend, 0);
 | |
| 
 | |
| 	cdata->spi_devices_vote = pdata->spi_devices_init;
 | |
| 
 | |
| 	cdata->intr_status = 0;
 | |
| 	hrtimer_init(&cdata->gen_irq_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
 | |
| 	cdata->gen_irq_timer.function = hr_dispath_irq_func;
 | |
| 
 | |
| 	cdata->microp_queue = create_singlethread_workqueue("microp_work_q");
 | |
| 	if (cdata->microp_queue == NULL) {
 | |
| 		ret = -ENOMEM;
 | |
| 		goto err_create_work_queue;
 | |
| 	}
 | |
| 
 | |
| 	if (client->irq) {
 | |
| 		INIT_WORK(&cdata->microp_intr_work, microp_intr_work_func);
 | |
| 
 | |
| 		ret = request_irq(client->irq, microp_intr_irq_handler,
 | |
| 			IRQF_TRIGGER_LOW, "microp_intrrupt",
 | |
| 			&client->dev);
 | |
| 		if (ret) {
 | |
| 			dev_err(&client->dev, "request_irq failed\n");
 | |
| 			goto err_intr;
 | |
| 		}
 | |
| 		ret = set_irq_wake(client->irq, 1);
 | |
| 		if (ret) {
 | |
| 			dev_err(&client->dev, "set_irq_wake failed\n");
 | |
| 			goto err_intr;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| #ifdef CONFIG_HAS_EARLYSUSPEND
 | |
| 	cdata->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
 | |
| 	cdata->early_suspend.suspend = microp_early_suspend;
 | |
| 	cdata->early_suspend.resume = microp_late_resume;
 | |
| 	register_early_suspend(&cdata->early_suspend);
 | |
| #endif
 | |
| 	ret = device_create_file(&client->dev, &dev_attr_reset);
 | |
| 	ret = device_create_file(&client->dev, &dev_attr_version);
 | |
| 	ret = device_create_file(&client->dev, &dev_attr_gpio);
 | |
| 
 | |
| 	register_microp_devices(pdata->microp_devices, pdata->num_devices);
 | |
| 	if (board_ops->init_microp_func) {
 | |
| 		ret = board_ops->init_microp_func(client);
 | |
| 		if (ret) {
 | |
| 			dev_err(&client->dev,
 | |
| 				"failed on microp function initialize\n");
 | |
| 			goto err_fun_init;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| err_fun_init:
 | |
| #ifdef CONFIG_HAS_EARLYSUSPEND
 | |
| 	unregister_early_suspend(&cdata->early_suspend);
 | |
| #endif
 | |
| 	device_remove_file(&client->dev, &dev_attr_reset);
 | |
| 	device_remove_file(&client->dev, &dev_attr_version);
 | |
| 	device_remove_file(&client->dev, &dev_attr_gpio);
 | |
| 	destroy_workqueue(cdata->microp_queue);
 | |
| err_intr:
 | |
| err_create_work_queue:
 | |
| 	kfree(cdata);
 | |
| err_gpio_reset:
 | |
| 	gpio_free(pdata->gpio_reset);
 | |
| err_exit:
 | |
| 	private_microp_client = NULL;
 | |
| err_cdata:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static const struct i2c_device_id microp_i2c_id[] = {
 | |
| 	{ MICROP_I2C_NAME, 0 },
 | |
| 	{ }
 | |
| };
 | |
| 
 | |
| static struct i2c_driver microp_i2c_driver = {
 | |
| 	.driver = {
 | |
| 		   .name = MICROP_I2C_NAME,
 | |
| 		   },
 | |
| 	.id_table = microp_i2c_id,
 | |
| 	.probe = microp_i2c_probe,
 | |
| 	.suspend = microp_i2c_suspend,
 | |
| 	.resume = microp_i2c_resume,
 | |
| 	.remove = __devexit_p(microp_i2c_remove),
 | |
| };
 | |
| 
 | |
| static void microp_irq_ack(unsigned int irq)
 | |
| {
 | |
| 	;
 | |
| }
 | |
| 
 | |
| static void microp_irq_mask(unsigned int irq)
 | |
| {
 | |
| 	;
 | |
| }
 | |
| 
 | |
| static void microp_irq_unmask(unsigned int irq)
 | |
| {
 | |
| 	;
 | |
| }
 | |
| 
 | |
| static struct irq_chip microp_irq_chip = {
 | |
| 	.name = "microp",
 | |
| 	.disable = microp_irq_mask,
 | |
| 	.ack = microp_irq_ack,
 | |
| 	.mask = microp_irq_mask,
 | |
| 	.unmask = microp_irq_unmask,
 | |
| };
 | |
| 
 | |
| static int __init microp_common_init(void)
 | |
| {
 | |
| 	int ret;
 | |
| 	int n, MICROP_IRQ_END = FIRST_MICROP_IRQ + NR_MICROP_IRQS;
 | |
| 
 | |
| 	for (n = FIRST_MICROP_IRQ; n < MICROP_IRQ_END; n++) {
 | |
| 		set_irq_chip(n, µp_irq_chip);
 | |
| 		set_irq_handler(n, handle_level_irq);
 | |
| 		set_irq_flags(n, IRQF_VALID);
 | |
| 	}
 | |
| 
 | |
| 	ret = i2c_add_driver(µp_i2c_driver);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void __exit microp_common_exit(void)
 | |
| {
 | |
| 	i2c_del_driver(µp_i2c_driver);
 | |
| }
 | |
| 
 | |
| module_init(microp_common_init);
 | |
| module_exit(microp_common_exit);
 | |
| 
 | |
| MODULE_AUTHOR("Eric Huang <Eric.SP_Huang@htc.com>");
 | |
| MODULE_DESCRIPTION("Atmega MicroP driver");
 | |
| MODULE_LICENSE("GPL");
 |