0162818a8a
- 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");
|