/*
* rtc-fm31256.c - RTC driver for Ramtron FM31256 I2C chip.
*
* Copyright (C) 2008 Sergey Lapin
* Based on fm31256 driver by James Chapman and David Brownell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/semaphore.h>
#define DEVICE_NAME "fm31256fram"
#define CP_CONTROL 0x0b
#define FM31256_CP_CONTROL_BIT_WP (3<<3)
struct fm31256 {
u8 reg_addr_time;
u8 regs[1];
u8 flag;
struct i2c_msg msg[2];
struct i2c_client *client;
struct semaphore sem; /*并发控制用的信号量*/
};
static const struct i2c_device_id fm31256_id[] = {
{ "fm31256fram", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, fm31256_id);
struct fm31256 *fm31256;
static struct i2c_driver fm31256_driver;
/*----------------------------------------------------------------------*/
#define FRAM_SIZE 256
/*ijsung: arch-indep function*/
static int FM31xx_open(struct inode *inode, struct file *file)
{
printk("Open file success!\n");
return 0;
}
static int FM31xx_release(struct inode *inode, struct file *file)
{
return 0;
}
ssize_t FM31xx_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
char* i2c_buf;
char addr_buf[2];
struct i2c_client *client;
int result ;
loff_t off;
off=*ppos;
printk("%d\n",(unsigned int) off);
client = fm31256->client;
if (unlikely(off >= FRAM_SIZE))
return 0;
if ((off + count) > FRAM_SIZE)
count = FRAM_SIZE - off;
if (unlikely(!count))
return count;
i2c_buf=kmalloc(count,GFP_KERNEL);
if(NULL==i2c_buf){
printk("Error:while kmalloc for the MEM!\n");
return -1;
}
if (down_interruptible(&fm31256->sem)){
goto err;
}
fm31256->regs[0]=i2c_smbus_read_byte_data(client, CP_CONTROL);
/* Disabling Write Protect.mode */
if (fm31256->regs[0] & FM31256_CP_CONTROL_BIT_WP) {
i2c_smbus_write_byte_data(client,CP_CONTROL,
fm31256->regs[0] &~(FM31256_CP_CONTROL_BIT_WP));
//dev_warn(&client->dev, "Disabling Write Protectmode!\n");
}
memset(i2c_buf,0,sizeof(u8)*count);
addr_buf[0]=(off>>8)&0xff;
addr_buf[1]=off&0xff;
/* Messages to read time */
fm31256->msg[0].addr = client->addr;
fm31256->msg[0].flags =0;
fm31256->msg[0].len = 2;
fm31256->msg[0].buf= addr_buf;
fm31256->msg[1].addr = client->addr;
fm31256->msg[1].flags = I2C_M_RD;
fm31256->msg[1].len =count;
fm31256->msg[1].buf = i2c_buf;
result = i2c_transfer(to_i2c_adapter(fm31256->client->dev.parent),fm31256->msg, 2);
if (result != 2) {
dev_err(&client->dev, "%s error %d\n", "read", result);
goto err;
}
if(copy_to_user ((void *)buf, i2c_buf, count)){
printk("Error:read count is not you want!\n");
goto err;
}
*ppos=off+count;
if(NULL!=i2c_buf)
kfree(i2c_buf);
fm31256->regs[0]=FM31256_CP_CONTROL_BIT_WP;
/* enabling Protect.mode */
i2c_smbus_write_byte_data(client,CP_CONTROL,fm31256->regs[0]);
up(&fm31256->sem); //释放信号量
return count;
err:
up(&fm31256->sem); //释放信号量
if(NULL!=i2c_buf)
kfree(i2c_buf);
fm31256->regs[0]=FM31256_CP_CONTROL_BIT_WP;
/* enabling Protect.mode */
i2c_smbus_write_byte_data(client,CP_CONTROL,fm31256->regs[0]);
return -EIO;
}
ssize_t FM31xx_write(struct file *file, char *buf, size_t count, loff_t *ppos)
{
u8 *i2c_buf;
struct i2c_client *client;
int result ;
loff_t off;
off=*ppos;
printk("%d\n",(unsigned int) off);
client = fm31256->client;
if (unlikely(off >= FRAM_SIZE))
return 0;
if ((off + count) > FRAM_SIZE)
count = FRAM_SIZE - off;
if (unlikely(!count))
return count;
i2c_buf=kmalloc((count+2),GFP_KERNEL);
if(NULL==i2c_buf){
printk("Error:while kmalloc for the MEM!\n");
return -1;
}
if (down_interruptible(&fm31256->sem)){
goto err;
}
memset(i2c_buf,0,(count+2));
if( copy_from_user(&i2c_buf[2],buf,count) ){
printk("Error:write count is not you want!\n");
goto err;
}
fm31256->regs[0]=i2c_smbus_read_byte_data(client, CP_CONTROL);
/* Disabling Write Protect.mode */
if (fm31256->regs[0] & FM31256_CP_CONTROL_BIT_WP) {
i2c_smbus_write_byte_data(client,CP_CONTROL,
fm31256->regs[0] &
~(FM31256_CP_CONTROL_BIT_WP));
//dev_warn(&client->dev, "Disabling Write Protectmode!\n");
}
i2c_buf[0]=(off>>8)&0xff;
i2c_buf[1]=off&0xff;
/* Messages to read time */
fm31256->msg[0].addr = client->addr;
fm31256->msg[0].flags =0;
fm31256->msg[0].len =count+2;
fm31256->msg[0].buf=i2c_buf;
result = i2c_transfer(to_i2c_adapter(fm31256->client->dev.parent),
fm31256->msg, 1);
if (result != 1) {
dev_err(&client->dev, "%s error %d\n", "write", result);
goto err;
}
*ppos=off+count;
if(NULL!=i2c_buf)
kfree(i2c_buf);
fm31256->regs[0]=FM31256_CP_CONTROL_BIT_WP;
/* enabling Protect.mode */
i2c_smbus_write_byte_data(client,CP_CONTROL,fm31256->regs[0]);
up(&fm31256->sem); //释放信号量
return count;
err:
up(&fm31256->sem); //释放信号量
if(NULL!=i2c_buf)
kfree(i2c_buf);
fm31256->regs[0]=FM31256_CP_CONTROL_BIT_WP;
/* enabling Protect.mode */
i2c_smbus_write_byte_data(client,CP_CONTROL,fm31256->regs[0]);
return -EIO;
}
static loff_t FM31xx_llseek (struct file *filp, loff_t off,int whence)
{
loff_t newpos=0;
switch(whence){
case SEEK_SET:
newpos = off;
break;
case SEEK_CUR:
newpos = filp->f_pos + off;
break;
case 2:
newpos = FRAM_SIZE + off;
break;
default:
return -EINVAL;
}
if (newpos < 0)
return -EINVAL;
filp->f_pos= newpos;
return newpos;
}
/*文件操作结构体*/
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.read = FM31xx_read,
.write = FM31xx_write,
.open = FM31xx_open,
.llseek = FM31xx_llseek,
.release = FM31xx_release,
};
static struct miscdevice fm31xfram_miscdev ={
.minor =MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,//关联文件操作
};
static int __devinit fm31256_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
printk("fm31256_probe.....!\n");
int err = -ENODEV;
int tmp;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
if (!i2c_check_functionality(adapter,I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
return -EIO;
fm31256 = kzalloc(sizeof(struct fm31256), GFP_KERNEL);
if (!fm31256)
return -ENOMEM;
fm31256->flag=0;
fm31256->client = client;
i2c_set_clientdata(client, fm31256);
sema_init(&fm31256->sem,1); /*初始化信号量*/
if (misc_register(&fm31xfram_miscdev)==0){
fm31256->flag=1;
};//返回0,成功,负数,不成功
return 0;
exit_free:
kfree(fm31256);
return err;
}
static int __devexit fm31256_remove(struct i2c_client *client)
{
kfree(fm31256);
return 0;
}
static struct i2c_driver fm31256_driver = {
.driver = {
.name = "fm31256fram",
.owner = THIS_MODULE,
},
.probe = fm31256_probe,
.remove = __devexit_p(fm31256_remove),
.id_table = fm31256_id,
};
static int __init fm31256_init(void)
{
return i2c_add_driver(&fm31256_driver);
}
module_init(fm31256_init);
static void __exit fm31256_exit(void)
{
if(fm31256->flag){
if (misc_deregister(&fm31xfram_miscdev)==0){
printk (DEVICE_NAME"\texit\n");
};//返回0,成功,负数,不成功
}
i2c_del_driver(&fm31256_driver);
}
module_exit(fm31256_exit);
MODULE_DESCRIPTION("FRAM driver for FM31256");
MODULE_AUTHOR("AUTHOR by hust zkc");
MODULE_LICENSE("GPL");