#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IRQ_SEN INT_EI4
#define X 7
#define Y 6
#define Z 5
#define TIMES 4
/*
* The ddd_mma7455 device is one of the misc char devices.
* This is its minor number.
*/
#define ddd_MMA7455_DEV_MINOR 229
static unsigned short probe_i2c[] = {0, 0x1D, I2C_CLIENT_END};
static unsigned short dummy[] = {I2C_CLIENT_END};
static struct i2c_client_address_data addr_data = {
.normal_i2c = dummy,
.probe = probe_i2c,
.ignore = dummy,
};
/* Each client has this additional data */
struct ddd_mma7455_data {
struct i2c_client client;
int write; /* Remember last written value */
};
static struct ddd_mma7455_data *p_ddd_mma7455_data;
static int ddd_mma7455_attach_adapter(struct i2c_adapter *adapter);
static int ddd_mma7455_detect(struct i2c_adapter *adapter, int address, int kind);
static int ddd_mma7455_detach_client(struct i2c_client *client);
static void ddd_mma7455_init_client(struct i2c_client *client);
/* This is the driver that will be inserted */
static struct i2c_driver ddd_mma7455_driver = {
.driver = {
.name = "ddd_mma7455",
.owner = THIS_MODULE,
},
.id = I2C_DRIVERID_MMA7455,
.attach_adapter = ddd_mma7455_attach_adapter,
.detach_client = ddd_mma7455_detach_client,
};
static struct delayed_work sen_work;
static int ddd_mma7455_open(struct inode * inode, struct file * filp)
{
return 0;
}
//#define AXP182_READ_DATA 0
//#define AXP182_READ_BATTERY 1
static int ddd_mma7455_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg)
{
int ret = 0;
switch (cmd) {
#if 0 /* comment by csduan */
case AXP182_READ_DATA:
data = mma7455_read(0xa7);
copy_to_user((int *)arg, (const int *)data, sizeof(int));
break;
case AXP182_READ_BATTERY:
data = mma7455_read(0xa8);
copy_to_user((int *)arg, (const int *)data, sizeof(int));
break;
#endif /* comment by csduan */
default:
break;
}
return ret;
}
static struct file_operations ddd_mma7455_fops = {
.owner = THIS_MODULE,
.ioctl = ddd_mma7455_ioctl,
.open = ddd_mma7455_open,
};
static struct miscdevice ddd_mma7455_device = {
.minor = ddd_MMA7455_DEV_MINOR,
.name = "ddd_mma7455",
.fops = &ddd_mma7455_fops
};
extern int i2c_xfer(unsigned char slave_addr,unsigned char out_len, unsigned char* out_buf,unsigned char in_len, unsigned char* in_buf,int ch);
u8 mma7455_read(unsigned int reg)
{
u8 DestAddress;
u8 i2cData_data[2] = {0,0};
DestAddress = 0x3a;
i2cData_data[0] = reg;
i2c_xfer(DestAddress, 1, i2cData_data, 0, 0, 1);
i2cData_data[1] = 0;
i2c_xfer(DestAddress, 0, 0, 1, &i2cData_data[1], 1);
return i2cData_data[1];
}
int mma7455_write(unsigned int reg, u8 value)
{
u8 DestAddress;
u8 i2cData_data[2] = {0,0};
DestAddress = 0x3a;
i2cData_data[0] = reg;
i2cData_data[1] = value;
return i2c_xfer(DestAddress, 2, i2cData_data, 0, 0, 1);
}
static irqreturn_t sen_irq(int irq, void *dev_id)
{
// printk(KERN_EMERG"%s\n",__func__);
disable_irq(IRQ_SEN);
schedule_delayed_work(&sen_work, HZ / 5);
return IRQ_HANDLED;
}
static int ddd_mma7455_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, ddd_mma7455_detect);
}
/* This function is called by i2c_probe */
static struct i2c_client *new_client;
static int ddd_mma7455_detect(struct i2c_adapter *adapter, int address, int kind)
{
int err = 0;
const char *client_name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) {
printk("%s: i2c_check_functionality failed\n", __func__);
goto exit;
}
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet. */
if (!(p_ddd_mma7455_data = kzalloc(sizeof(struct ddd_mma7455_data), GFP_KERNEL))) {
err = -ENOMEM;
printk("%s: kzalloc failed\n", __func__);
goto exit;
}
new_client = &p_ddd_mma7455_data->client;
i2c_set_clientdata(new_client, p_ddd_mma7455_data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &ddd_mma7455_driver;
new_client->flags = 0;
client_name = "ddd_mma7455";
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
/* Tell the I2C layer a new client has arrived */
if (i2c_attach_client(new_client) < 0) {
printk("%s: i2c_attach_client failed\n", __func__);
goto exit_free;
}
/* Initialize the ddd_mma7455 chip */
ddd_mma7455_init_client(new_client);
return 0;
//exit_detach:
i2c_detach_client(new_client);
exit_free:
kfree(p_ddd_mma7455_data);
exit:
return err;
}
static int ddd_mma7455_detach_client(struct i2c_client *client)
{
int err;
if ((err = i2c_detach_client(client)))
return err;
kfree(p_ddd_mma7455_data);
kfree(i2c_get_clientdata(client));
return 0;
}
static int direction, cur_axe, last_axe, cur_valid, cont_time = 0;
static void sen_work_handler(struct work_struct *work)
{
u8 temp,output,output_x,output_y;
int now_axe,now_valid,temp_direction;
temp = mma7455_read(0xa);
mma7455_write(0x17,0x3);
mma7455_write(0x17,0x0);
now_axe = 0;
temp &= 0xe0;
// printk(KERN_EMERG"temp = %x\n",temp);
if(!temp){
enable_irq(IRQ_SEN);
return;
}
#if 0 /* comment by csduan */
if(temp & (1 << Z)) {
now_axe = Z;
direction = !!(mma7455_read(0x8) & (1 << 7));
}
#endif /* comment by csduan */
if(temp & (1 << Y)) {
now_axe = Y;
output = mma7455_read(0x7);
direction = !!(output & (1 << 7));
if(!direction)
output_y = output & 0x7f;
else
output_y = ~(output - 1);
}
if(temp & (1 << X)) {
output = mma7455_read(0x6);
temp_direction = !!(output & (1 << 7));
if(!temp_direction)
output_x = output & 0x7f;
else
output_x = ~(output - 1);
if((now_axe != Y)||(output_x > output_y))
{
now_axe = X;
direction = temp_direction;
}
}
// printk(KERN_EMERG"now cur %d,%d\n",now_axe,cur_axe);
if(now_axe == last_axe) {
cont_time++;
if(TIMES <= cont_time) {
last_axe = now_axe;
cont_time = 0;
now_valid = direction ? 0 - now_axe:now_axe;
if(now_valid == -7) //-X
mma7455_write(0x18,0x28);
else if(now_valid == -6) //-Y
mma7455_write(0x18,0x30);
else{
cont_time = 0;
mma7455_write(0x18,0x20);
mma7455_write(0x17,0x3);
mma7455_write(0x17,0x0);
enable_irq(IRQ_SEN);
return;
}
mma7455_write(0x17,0x3);
mma7455_write(0x17,0x0);
if(now_valid != cur_axe) {
cur_axe = now_valid;
if((now_valid == -6)||(now_valid == -7)){
if(now_valid != cur_valid) {
cur_valid = now_valid;
kobject_uevent(&ddd_mma7455_device.this_device->kobj, KOBJ_CHANGE);
}
}
}
} else {
cont_time++;
}
} else {
last_axe = now_axe;
cont_time = 0;
mma7455_write(0x18,0x20);
mma7455_write(0x17,0x3);
mma7455_write(0x17,0x0);
}
enable_irq(IRQ_SEN);
}
static ssize_t ddd_mma7455_show_direction(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return snprintf(buf, sizeof(cur_valid), "%d\n", cur_valid);
}
static DEVICE_ATTR(direction, 0666, ddd_mma7455_show_direction, NULL);
static struct attribute *attrs[] = {
&dev_attr_direction.attr,
NULL,
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
/* Called when we have found a new ddd_mma7455. */
static void ddd_mma7455_init_client(struct i2c_client *client)
{
int ret;
volatile PGPIO pGPIO = (volatile PGPIO)ccc_p2v(HwGPIO_BASE);
volatile PPIC pPIC = (volatile PPIC)ccc_p2v(HwPIC_BASE);
mma7455_write(0x18,0x20);//thopt = 0 x,y enable
msleep(1);
mma7455_write(0x16,0x44);//2g and stand by mode
msleep(1);
mma7455_write(0x19,0x0);//ldpl = 0
msleep(1);
mma7455_write(0x1a,0xa);//set threshold
msleep(1);
mma7455_write(0x17,0x3);//clear the INT
msleep(1);
mma7455_write(0x17,0x0);
//set GPIOA2 to INT_EI4
BITCSET(pGPIO->EINTSEL1, Hw6-Hw0, 2);
BITCLR(pPIC->POL0, 1 << IRQ_SEN); //active high
BITSET(pPIC->INTMSK0, 1 << IRQ_SEN);
BITSET(pPIC->MODE0, 1 << IRQ_SEN); //set interrupt as level mode
ccc_gpio_direction_input2(CCC_GPA2);
gpio_direction_input(CCC_GPA3);
ret = request_irq(IRQ_SEN,sen_irq,IRQF_SAMPLE_RANDOM,"sen_irq",NULL);
if(ret)
printk(KERN_ERR "Request SEN IRQ fail\n");
}
#define CONFIG_MMA7455_PROCFS
#ifdef CONFIG_MMA7455_PROCFS
#define MMA7455_PROC_ENTRY "driver/mma7455"
static int mma7455_proc_read(char* page, char** start, off_t off, int count,
int* eof, void* data)
{
int i;
if (off) return 0;
data = (void*)page;
page += sprintf(page, "the reg reading from mma7455 is:\n");
for (i = 0;i < 31;i++)
page += sprintf(page, "%x:%02x \n",i,mma7455_read(i));
return((page += sprintf(page, "\n")) - (char*)data);
}
static int mma7455_proc_write(struct file* file, const char* buffer,
unsigned long count, void* data)
{
#define MAX_BUFLEN 16
u8 reg;
u16 val = MAX_BUFLEN - 1;
char *ptr, tmp_buf[MAX_BUFLEN];
if (count < MAX_BUFLEN) val = count - 1;tmp_buf[val] = 0;
if (copy_from_user(tmp_buf, buffer, val)) return -EFAULT;
for (ptr = tmp_buf; isspace(*ptr); ++ptr) ;
reg = simple_strtoul(ptr, &ptr, 16);
while (isspace(*ptr)) ++ptr;
val = simple_strtoul(ptr, &ptr, 16);
mma7455_write(reg, val);
return count;
}
static int __init mma7455_proc_init(void)
{
struct proc_dir_entry* mma7455_entry;
if (!(mma7455_entry = create_proc_entry(MMA7455_PROC_ENTRY, S_IRUGO | S_IWUSR, NULL)))
return -ENOMEM;
printk(KERN_INFO "Proc-FS interface for mma7455\n");
mma7455_entry->owner = THIS_MODULE;
mma7455_entry->write_proc = mma7455_proc_write;
mma7455_entry->read_proc = mma7455_proc_read;
mma7455_entry->data = NULL;
return 0;
}
static void __exit mma7455_proc_exit(void)
{
remove_proc_entry(MMA7455_PROC_ENTRY, NULL);
}
#endif//CONFIG_MMA7455_PROCFS
static int __init ddd_mma7455_init(void)
{
INIT_DELAYED_WORK(&sen_work, sen_work_handler);
misc_register(&ddd_mma7455_device);
if (sysfs_create_group(&ddd_mma7455_device.this_device->kobj, &attr_group)) {
/* Error happens, nothing to do */
}
#ifdef CONFIG_MMA7455_PROCFS
mma7455_proc_init();
#endif//CONFIG_MMA7455_PROCFS
return i2c_add_driver(&ddd_mma7455_driver);
}
static void __exit ddd_mma7455_exit(void)
{
misc_deregister(&ddd_mma7455_device);
mma7455_proc_exit();
i2c_del_driver(&ddd_mma7455_driver);
}
late_initcall(ddd_mma7455_init);
module_exit(ddd_mma7455_exit);
MODULE_AUTHOR("csduan");
MODULE_DESCRIPTION("PMU for ddd");
MODULE_LICENSE("GPL");