/* * att7022 power test spi serial driver * * Copyright (C) 2013 BLX IC Design Corp.,Ltd. * Author: Davied, apple_guet@126.com * */ #include #include #include #include #include #include #include #include #ifdef CONFIG_SPI_POWER_ATT7022_DEBUG #define DBG(msg...) do { \ printk(KERN_INFO msg); \ } while (0) #else #define DBG(msg...) do { } while(0) #endif #define ATT7022_BIT_LOCK_OPEN 0x00 /* 16 spi flash should be enough for everyone */ #define ATT7022_MAX_MINOR 16 #define ATT7022_POWER_NAME "att7022-power-test" struct cmd_info_s { u8 addr; u8 data[3]; }; /* ioctl command */ #define ATT7022_IOC_MAGIC 'a' #define ATT7022_IOC_MAXNR 2 #define ATT7022_READ_DATA _IOWR(ATT7022_IOC_MAGIC, 0, struct cmd_info_s) #define ATT7022_CHECK_DATA _IOWR(ATT7022_IOC_MAGIC, 1, struct cmd_info_s) struct att7022_dev { char name[20]; unsigned long bit_lock; dev_t devt; struct cdev cdev; struct device dev; struct class *class; struct mutex mlock; struct spi_device *spi; struct cmd_info_s *cmd_info; struct list_head device_entry; }; static int att7022_open(struct inode *inode, struct file *file) { struct att7022_dev *att7022 = container_of(inode->i_cdev, struct att7022_dev, cdev); if (test_and_set_bit(ATT7022_BIT_LOCK_OPEN, &att7022->bit_lock)) { DBG("!!!!att7022 open err, busy!\n"); return -EBUSY; } file->private_data = att7022; return 0; } static int at7022_read_data(struct att7022_dev *att7022) { u8 i = 0, rx = 0, dummy_value = 0xff; struct spi_transfer x[8]; struct spi_message message; memset(x, 0, sizeof x); spi_message_init(&message); for (i = 0; i < 8; i++) { x[i].len = 1; spi_message_add_tail(&x[i], &message); } att7022->cmd_info->addr &= 0x7F; x[0].tx_buf = &att7022->cmd_info->addr; //read cmd x[1].rx_buf = ℞ x[2].tx_buf = &dummy_value; x[3].rx_buf = &att7022->cmd_info->data[2]; x[4].tx_buf = &dummy_value; x[5].rx_buf = &att7022->cmd_info->data[1]; x[6].tx_buf = &dummy_value; x[7].rx_buf = &att7022->cmd_info->data[0]; /* do the i/o */ return spi_sync(att7022->spi, &message); } static int at7022_check_data(struct att7022_dev *att7022) { u8 i = 0, rx = 0; struct spi_transfer x[8]; struct spi_message message; memset(x, 0, sizeof x); spi_message_init(&message); for (i = 0; i < 8; i++) { x[i].len = 1; spi_message_add_tail(&x[i], &message); } switch (att7022->cmd_info->addr) { case 0x80: case 0xC3: case 0xD3: att7022->cmd_info->addr |= 0xC0; x[0].tx_buf = &att7022->cmd_info->addr; //special cmd break; default: att7022->cmd_info->addr &= 0xBF; att7022->cmd_info->addr |= 0x80; x[0].tx_buf = &att7022->cmd_info->addr; //check cmd break; } x[1].rx_buf = ℞ x[2].tx_buf = &att7022->cmd_info->data[2]; x[3].rx_buf = ℞ x[4].tx_buf = &att7022->cmd_info->data[1]; x[5].rx_buf = ℞ x[6].tx_buf = &att7022->cmd_info->data[0]; x[7].rx_buf = ℞ /* do the i/o */ return spi_sync(att7022->spi, &message); } static long att7022_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; void __user *argp = (void __user *)arg; struct att7022_dev *att7022= file->private_data; DBG("@@@@att7022 ioctl start.\n"); ret = mutex_lock_interruptible(&att7022->mlock); if (ret) { DBG("!!!!mutex lock error!\n"); return ret; } att7022->cmd_info = kzalloc(sizeof(struct cmd_info_s), GFP_KERNEL); if (!att7022->cmd_info) { DBG("!!!!kzalloc error!\n"); ret = -ENOMEM; goto exit; } if (copy_from_user(att7022->cmd_info, argp, sizeof(struct cmd_info_s))) { DBG("!!!!copy_from_user() error!\n"); ret = -EFAULT; goto exit; } if ((_IOC_TYPE(cmd) != ATT7022_IOC_MAGIC) || (_IOC_NR(cmd) > ATT7022_IOC_MAXNR)) { DBG("!!!!ioc type or ioc nr error!\n"); ret = -ENOTTY; goto exit; } switch(cmd) { case ATT7022_READ_DATA: DBG("att7022 read data cmd\n"); ret = at7022_read_data(att7022); break; case ATT7022_CHECK_DATA: DBG("att7022 check data cmd\n"); ret = at7022_check_data(att7022); break; default: DBG("!!!!cmd error!\n"); ret = -ENOTTY; break; } if (ret == 0) { if (copy_to_user(argp, att7022->cmd_info, sizeof(struct cmd_info_s))) ret = -EFAULT; } exit: mutex_unlock(&att7022->mlock); kfree(att7022->cmd_info); if (ret != 0) DBG("!!!!att7022 ioctl error!\n"); else { DBG("att7022 ioctl success.\n"); } return ret; } static int att7022_release(struct inode *inode, struct file *file) { int ret = 0; struct att7022_dev *att7022= file->private_data; clear_bit_unlock(ATT7022_BIT_LOCK_OPEN, &att7022->bit_lock); return ret; } static const struct file_operations att7022_fops = { .owner = THIS_MODULE, .open = att7022_open, .unlocked_ioctl = att7022_ioctl, .release = att7022_release, }; static int __devinit att7022_probe(struct spi_device *spi) { int ret = 0; struct att7022_dev *att7022; DBG("############\n"); DBG("att7022 power test probe start.\n"); att7022 = kzalloc(sizeof *att7022, GFP_KERNEL); if (!att7022) { DBG("!!!!kzalloc error!\n"); return -ENOMEM; } att7022->spi = spi; mutex_init(&att7022->mlock); strlcpy(att7022->name, ATT7022_POWER_NAME, sizeof(att7022->name)); ret = alloc_chrdev_region(&att7022->devt, 0, ATT7022_MAX_MINOR, "att7022"); if (ret < 0) { DBG("!!!!%s: failed to allocate char dev region!\n", __FILE__); goto err_kzall; } att7022->dev.devt = MKDEV(MAJOR(att7022->devt), 1); cdev_init(&att7022->cdev, &att7022_fops); att7022->cdev.owner = THIS_MODULE; ret = cdev_add(&att7022->cdev, att7022->devt, 1); if (ret) { DBG("!!!!cdev add error!\n"); goto err_alloc; } att7022->class = class_create(THIS_MODULE, "att7022-spi"); if (IS_ERR(att7022->class)) { DBG("!!!!failed in create att7022 power test class!\n"); goto err_alloc; } device_create(att7022->class, NULL, att7022->devt, NULL, "att7022"); dev_set_drvdata(&spi->dev, att7022); DBG("att7022 power test probe success.\n"); DBG("############\n"); return 0; err_alloc: unregister_chrdev_region(att7022->devt, ATT7022_MAX_MINOR); err_kzall: kfree(att7022); printk(KERN_ERR "!!!!!!att7022 power test probe error!\n"); return ret; } static int __devexit att7022_remove(struct spi_device *spi) { struct att7022_dev *att7022 = dev_get_drvdata(&spi->dev); cdev_del(&att7022->cdev); unregister_chrdev_region(att7022->devt, ATT7022_MAX_MINOR); device_destroy(att7022->class, att7022->devt); class_destroy(att7022->class); kfree(att7022); return 0; } static struct spi_driver att7022_driver = { .driver = { .name = "spi-att7022", .owner = THIS_MODULE, }, //.id_table = att7022_ids, .probe = att7022_probe, .remove = __devexit_p(att7022_remove), }; static int __init att7022_init(void) { return spi_register_driver(&att7022_driver); } static void __exit att7022_exit(void) { spi_unregister_driver(&att7022_driver); } module_init(att7022_init); module_exit(att7022_exit); MODULE_DESCRIPTION("att7022 spi power test"); MODULE_AUTHOR("Davied"); MODULE_LICENSE("GPL"); MODULE_ALIAS("spi driver:att7022");