488 lines
11 KiB
C
488 lines
11 KiB
C
/* drivers/misc/bma150_spi.c - bma150 G-sensor driver
|
|
*
|
|
* 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/i2c.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/input.h>
|
|
#include <linux/bma150.h>
|
|
#include <asm/gpio.h>
|
|
#include <linux/earlysuspend.h>
|
|
#include <linux/platform_device.h>
|
|
#include <mach/atmega_microp.h>
|
|
|
|
struct early_suspend bma_early_suspend;
|
|
|
|
static struct bma150_platform_data *this_pdata;
|
|
|
|
static struct mutex gsensor_RW_mutex;
|
|
static struct mutex gsensor_set_mode_mutex;
|
|
|
|
static int spi_microp_enable(uint8_t on)
|
|
{
|
|
int ret;
|
|
ret = microp_spi_vote_enable(SPI_GSENSOR, on);
|
|
if (ret < 0)
|
|
printk(KERN_ERR "%s: i2c_write_block fail\n", __func__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int spi_gsensor_read(uint8_t *data)
|
|
{
|
|
int ret;
|
|
|
|
mutex_lock(&gsensor_RW_mutex);
|
|
|
|
ret = microp_i2c_write(MICROP_I2C_WCMD_GSENSOR_REG_DATA_REQ, data, 1);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: i2c_write_block fail\n", __func__);
|
|
mutex_unlock(&gsensor_RW_mutex);
|
|
return ret;
|
|
}
|
|
|
|
ret = microp_i2c_read(MICROP_I2C_RCMD_GSENSOR_REG_DATA, data, 2);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: i2c_read_block fail\n", __func__);
|
|
mutex_unlock(&gsensor_RW_mutex);
|
|
return ret;
|
|
}
|
|
|
|
mutex_unlock(&gsensor_RW_mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int spi_gsensor_write(uint8_t *data)
|
|
{
|
|
int ret;
|
|
|
|
mutex_lock(&gsensor_RW_mutex);
|
|
|
|
ret = microp_i2c_write(MICROP_I2C_WCMD_GSENSOR_REG, data, 2);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: i2c_write_block fail\n", __func__);
|
|
mutex_unlock(&gsensor_RW_mutex);
|
|
return ret;
|
|
}
|
|
|
|
mutex_unlock(&gsensor_RW_mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int spi_gsensor_init_hw(void)
|
|
{
|
|
char buffer[2];
|
|
|
|
memset(buffer, 0x0, sizeof(buffer));
|
|
buffer[0] = RANGE_BWIDTH_REG;
|
|
if (spi_gsensor_read(buffer) < 0)
|
|
return -EIO;
|
|
|
|
/*printk("spi_gsensor_init_hw,read RANGE_BWIDTH_REG = %x "
|
|
, buffer[1]);*/
|
|
|
|
buffer[1] = (buffer[1]&0xe0);
|
|
buffer[0] = RANGE_BWIDTH_REG;
|
|
if (spi_gsensor_write(buffer) < 0)
|
|
return -EIO;
|
|
|
|
buffer[0] = SMB150_CONF2_REG;
|
|
if (spi_gsensor_read(buffer) < 0)
|
|
return -EIO;
|
|
|
|
buffer[1] = buffer[1]|1<<3;
|
|
buffer[0] = SMB150_CONF2_REG;
|
|
if (spi_gsensor_write(buffer) < 0)
|
|
return -EIO;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
static int spi_gsensor_read_version(void)
|
|
{
|
|
uint8_t buffer[2];
|
|
int ret = -EIO;
|
|
|
|
buffer[0] = VERSION_REG;
|
|
buffer[1] = 1;
|
|
ret = spi_gsensor_read(buffer);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: get al_version fail(%d)\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
printk(KERN_INFO "%s: al_version: 0x%2.2X\n", __func__, buffer[0]);
|
|
|
|
buffer[0] = CHIP_ID_REG;
|
|
buffer[1] = 1;
|
|
ret = spi_gsensor_read(buffer);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: get chip_id fail(%d)\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
printk(KERN_INFO "%s: chip_id: 0x%2.2X\n", __func__, buffer[0]);
|
|
return 0;
|
|
}
|
|
*/
|
|
static int spi_bma150_TransRBuff(short *rbuf)
|
|
{
|
|
int ret;
|
|
unsigned char buffer[6];
|
|
memset(buffer, 0, 6);
|
|
|
|
mutex_lock(&gsensor_RW_mutex);
|
|
|
|
buffer[0] = 1;
|
|
ret = microp_i2c_write(MICROP_I2C_WCMD_GSENSOR_DATA_REQ, buffer, 1);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: i2c_write_block fail\n", __func__);
|
|
mutex_unlock(&gsensor_RW_mutex);
|
|
return ret;
|
|
}
|
|
|
|
if (this_pdata && this_pdata->microp_new_cmd &&
|
|
this_pdata->microp_new_cmd == 1) {
|
|
/*printk(KERN_DEBUG "%s: New MicroP command\n", __func__);*/
|
|
ret = microp_i2c_read(MICROP_I2C_RCMD_GSENSOR_DATA, buffer, 6);
|
|
rbuf[0] = buffer[0]<<2|buffer[1]>>6;
|
|
if (rbuf[0]&0x200)
|
|
rbuf[0] -= 1<<10;
|
|
rbuf[1] = buffer[2]<<2|buffer[3]>>6;
|
|
if (rbuf[1]&0x200)
|
|
rbuf[1] -= 1<<10;
|
|
rbuf[2] = buffer[4]<<2|buffer[5]>>6;
|
|
if (rbuf[2]&0x200)
|
|
rbuf[2] -= 1<<10;
|
|
} else {
|
|
/* For Passion with V01 ~ V05 Microp */
|
|
/*printk(KERN_DEBUG "%s: Old MicroP command\n", __func__);*/
|
|
ret = microp_i2c_read(MICROP_I2C_RCMD_GSENSOR_X_DATA,
|
|
buffer, 2);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: i2c_read_block fail\n", __func__);
|
|
mutex_unlock(&gsensor_RW_mutex);
|
|
return ret;
|
|
}
|
|
rbuf[0] = buffer[0]<<2|buffer[1]>>6;
|
|
if (rbuf[0]&0x200)
|
|
rbuf[0] -= 1<<10;
|
|
|
|
ret = microp_i2c_read(MICROP_I2C_RCMD_GSENSOR_Y_DATA,
|
|
buffer, 2);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: i2c_read_block fail\n", __func__);
|
|
mutex_unlock(&gsensor_RW_mutex);
|
|
return ret;
|
|
}
|
|
rbuf[1] = buffer[0]<<2|buffer[1]>>6;
|
|
if (rbuf[1]&0x200)
|
|
rbuf[1] -= 1<<10;
|
|
|
|
ret = microp_i2c_read(MICROP_I2C_RCMD_GSENSOR_Z_DATA,
|
|
buffer, 2);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: i2c_read_block fail\n", __func__);
|
|
mutex_unlock(&gsensor_RW_mutex);
|
|
return ret;
|
|
}
|
|
rbuf[2] = buffer[0]<<2|buffer[1]>>6;
|
|
if (rbuf[2]&0x200)
|
|
rbuf[2] -= 1<<10;
|
|
}
|
|
/* printk("X=%d, Y=%d, Z=%d\n",rbuf[0],rbuf[1],rbuf[2]);*/
|
|
|
|
/* printk(KERN_DEBUG "%s: 0x%2.2X 0x%2.2X 0x%2.2X \
|
|
0x%2.2X 0x%2.2X 0x%2.2X\n",
|
|
__func__, buffer[0], buffer[1], buffer[2], \
|
|
buffer[3], buffer[4], buffer[5]);*/
|
|
|
|
mutex_unlock(&gsensor_RW_mutex);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int __spi_bma150_set_mode(char mode)
|
|
{
|
|
char buffer[2];
|
|
int ret;
|
|
mutex_lock(&gsensor_set_mode_mutex);
|
|
if (mode == BMA_MODE_NORMAL) {
|
|
spi_microp_enable(1);
|
|
printk(KERN_INFO "%s: BMA get into NORMAL mode!\n",
|
|
__func__);
|
|
}
|
|
|
|
buffer[0] = SMB150_CTRL_REG;
|
|
ret = spi_gsensor_read(buffer);
|
|
if (ret < 0) {
|
|
mutex_unlock(&gsensor_set_mode_mutex);
|
|
return -1;
|
|
}
|
|
|
|
buffer[1] = (buffer[1]&0xfe)|mode;
|
|
buffer[0] = SMB150_CTRL_REG;
|
|
ret = spi_gsensor_write(buffer);
|
|
|
|
if (mode == BMA_MODE_SLEEP) {
|
|
spi_microp_enable(0);
|
|
printk(KERN_INFO "%s: BMA get into SLEEP mode!\n",
|
|
__func__);
|
|
}
|
|
mutex_unlock(&gsensor_set_mode_mutex);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int spi_bma150_open(struct inode *inode, struct file *file)
|
|
{
|
|
return nonseekable_open(inode, file);
|
|
}
|
|
|
|
static int spi_bma150_release(struct inode *inode, struct file *file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int spi_bma150_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
void __user *argp = (void __user *)arg;
|
|
char rwbuf[8];
|
|
char *toRbuf;
|
|
int ret = -1;
|
|
short buf[8], temp;
|
|
|
|
switch (cmd) {
|
|
case BMA_IOCTL_READ:
|
|
case BMA_IOCTL_WRITE:
|
|
case BMA_IOCTL_SET_MODE:
|
|
if (copy_from_user(&rwbuf, argp, sizeof(rwbuf)))
|
|
return -EFAULT;
|
|
break;
|
|
case BMA_IOCTL_READ_ACCELERATION:
|
|
if (copy_from_user(&buf, argp, sizeof(buf)))
|
|
return -EFAULT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case BMA_IOCTL_INIT:
|
|
ret = spi_gsensor_init_hw();
|
|
if (ret < 0)
|
|
return ret;
|
|
break;
|
|
|
|
case BMA_IOCTL_READ:
|
|
if (rwbuf[0] < 1)
|
|
return -EINVAL;
|
|
ret = spi_gsensor_read(&rwbuf[1]);
|
|
if (ret < 0)
|
|
return ret;
|
|
break;
|
|
case BMA_IOCTL_WRITE:
|
|
if (rwbuf[0] < 2)
|
|
return -EINVAL;
|
|
ret = spi_gsensor_write(&rwbuf[1]);
|
|
if (ret < 0)
|
|
return ret;
|
|
break;
|
|
case BMA_IOCTL_READ_ACCELERATION:
|
|
ret = spi_bma150_TransRBuff(&buf[0]);
|
|
if (ret < 0)
|
|
return ret;
|
|
break;
|
|
case BMA_IOCTL_SET_MODE:
|
|
/*printk(KERN_DEBUG
|
|
"%s: BMA_IOCTL_SET_MODE by ioctl = %d\n",
|
|
__func__,rwbuf[0]);*/
|
|
ret = __spi_bma150_set_mode(rwbuf[0]);
|
|
if (ret < 0)
|
|
return ret;
|
|
break;
|
|
case BMA_IOCTL_GET_INT:
|
|
temp = 0;
|
|
break;
|
|
case BMA_IOCTL_GET_CHIP_LAYOUT:
|
|
if (this_pdata)
|
|
temp = this_pdata->chip_layout;
|
|
break;
|
|
default:
|
|
return -ENOTTY;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case BMA_IOCTL_READ:
|
|
toRbuf = &rwbuf[1];
|
|
if (copy_to_user(argp, toRbuf, sizeof(rwbuf)-1))
|
|
return -EFAULT;
|
|
break;
|
|
case BMA_IOCTL_READ_ACCELERATION:
|
|
if (copy_to_user(argp, &buf, sizeof(buf)))
|
|
return -EFAULT;
|
|
break;
|
|
case BMA_IOCTL_GET_INT:
|
|
if (copy_to_user(argp, &temp, sizeof(temp)))
|
|
return -EFAULT;
|
|
break;
|
|
case BMA_IOCTL_GET_CHIP_LAYOUT:
|
|
if (copy_to_user(argp, &temp, sizeof(temp)))
|
|
return -EFAULT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct file_operations spi_bma_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = spi_bma150_open,
|
|
.release = spi_bma150_release,
|
|
.ioctl = spi_bma150_ioctl,
|
|
};
|
|
|
|
static struct miscdevice spi_bma_device = {
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = "bma150",
|
|
.fops = &spi_bma_fops,
|
|
};
|
|
|
|
static void bma150_early_suspend(struct early_suspend *handler)
|
|
{
|
|
int ret = 0;
|
|
ret = __spi_bma150_set_mode(BMA_MODE_SLEEP);
|
|
|
|
/*printk(KERN_DEBUG
|
|
"%s: spi_bma150_set_mode returned = %d!\n",
|
|
__func__, ret);*/
|
|
}
|
|
|
|
static void bma150_early_resume(struct early_suspend *handler)
|
|
{
|
|
/*printk(KERN_DEBUG
|
|
"%s: spi_bma150_set_mode returned = %d!\n",
|
|
__func__, ret);*/
|
|
}
|
|
|
|
static int spi_gsensor_initial(void)
|
|
{
|
|
int ret;
|
|
/* ret = spi_microp_enable(1);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: spi_microp_enable fail\n", __func__);
|
|
return ret;
|
|
}*/
|
|
/* ret = spi_gsensor_read_version();
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: get version fail\n", __func__);
|
|
return ret;
|
|
}*/
|
|
|
|
/* ret = microp_gsensor_init_hw(client);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: init g-sensor fail\n", __func__);
|
|
return ret;
|
|
}
|
|
*/
|
|
ret = misc_register(&spi_bma_device);
|
|
if (ret < 0) {
|
|
printk(KERN_ERR "%s: init misc_register fail\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
mutex_init(&gsensor_RW_mutex);
|
|
mutex_init(&gsensor_set_mode_mutex);
|
|
|
|
|
|
ret = spi_microp_enable(1);
|
|
if (ret) {
|
|
printk(KERN_ERR "%s: spi_microp_enable(1) fail!\n", __func__);
|
|
goto err_spi_enable;
|
|
}
|
|
|
|
ret = __spi_bma150_set_mode(BMA_MODE_SLEEP);
|
|
if (ret) {
|
|
printk(KERN_ERR "%s: set BMA_MODE_SLEEP fail!\n", __func__);
|
|
goto err_set_mode;
|
|
}
|
|
|
|
bma_early_suspend.suspend = bma150_early_suspend;
|
|
bma_early_suspend.resume = bma150_early_resume;
|
|
register_early_suspend(&bma_early_suspend);
|
|
|
|
return 0;
|
|
|
|
err_set_mode:
|
|
spi_microp_enable(0);
|
|
err_spi_enable:
|
|
misc_deregister(&spi_bma_device);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int spi_bma150_probe(struct platform_device *pdev)
|
|
{
|
|
printk(KERN_INFO "%s: G-sensor connect with microP: "
|
|
"start initial\n", __func__);
|
|
|
|
this_pdata = pdev->dev.platform_data;
|
|
/*
|
|
printk(KERN_DEBUG "%s: this_pdata->microp_new_cmd = %d\n",
|
|
__func__, this_pdata->microp_new_cmd);
|
|
*/
|
|
spi_gsensor_initial();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int spi_bma150_remove(struct platform_device *pdev)
|
|
{
|
|
mutex_destroy(&gsensor_set_mode_mutex);
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver spi_bma150_driver = {
|
|
.probe = spi_bma150_probe,
|
|
.remove = spi_bma150_remove,
|
|
.driver = {
|
|
.name = BMA150_G_SENSOR_NAME,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
static int __init spi_bma150_init(void)
|
|
{
|
|
return platform_driver_register(&spi_bma150_driver);
|
|
|
|
}
|
|
|
|
static void __exit spi_bma150_exit(void)
|
|
{
|
|
platform_driver_unregister(&spi_bma150_driver);
|
|
}
|
|
|
|
module_init(spi_bma150_init);
|
|
module_exit(spi_bma150_exit);
|
|
|
|
MODULE_DESCRIPTION("BMA150 G-sensor driver");
|
|
MODULE_LICENSE("GPL");
|