diff -drupN a/drivers/i2c/busses/i2c-sunxi-test.c b/drivers/i2c/busses/i2c-sunxi-test.c --- a/drivers/i2c/busses/i2c-sunxi-test.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/i2c/busses/i2c-sunxi-test.c 2022-06-12 05:28:14.000000000 +0300 @@ -0,0 +1,375 @@ +/* + * at24.c - handle most I2C EEPROMs + * + * Copyright (C) 2005-2007 David Brownell + * Copyright (C) 2008 Wolfram Sang, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "i2c-sunxi.h" + +static u32 debug_mask = 1; +#define dprintk(level_mask, fmt, arg...) \ +do { \ + if (unlikely(debug_mask & level_mask)) \ + pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg); \ +} while (0) + +#define I2C_ERR(fmt, arg...) pr_warn("%s()%d - "fmt, \ + __func__, __LINE__, ##arg) +#define EEPROM_NAME_SIZE 6 +#define I2C_TEST _IOR('i', 1, struct at24_data) + +struct at24_data { + unsigned short flags, num, len; + unsigned char command, value, *buf; +}; + +struct at24_dev { + struct device *dev; + struct cdev cdev; + dev_t chrdev; +}; + +static struct class *at24_class; +static struct at24_dev *at24_dev; +struct i2c_client *this_client; +static __u32 twi_id; +static const unsigned short i2c_address[] = {0x50, I2C_CLIENT_END}; +static const struct i2c_device_id at24_ids[] = { + { "24c16", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, at24_ids); + +/* + * at24_detect - Device detection callback for automatic device creation + * return value: + * = 0; success; + * < 0; err + */ +static int at24_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + struct device_node *np = NULL; + + np = of_find_node_by_name(NULL, "at24c"); + if (!np) { + I2C_ERR("get at24c para failed\n"); + return -EINVAL; + } + + if (!of_device_is_available(np)) { + I2C_ERR("at24c is not used\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "twi_id", &twi_id)) { + I2C_ERR("get at24c twi_id is failed\n"); + return -EINVAL; + } + + dprintk(DEBUG_INIT, "detecting\n"); + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + I2C_ERR("function err\n"); + return -ENODEV; + } + if (twi_id == adapter->nr) { + dprintk(DEBUG_INIT, "twi_id = %d\n", twi_id); + strlcpy(info->type, "24c16", EEPROM_NAME_SIZE); + } + + return 0; +} + +/* + * at24_ioctl - + * return value: + * = 0; success; + * < 0; err + */ +static long at24_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = file->private_data; + struct i2c_adapter *adapter = client->adapter; + struct at24_data *at24; + struct i2c_msg *msgs = NULL; + unsigned int size, i = 0; + unsigned char *buf = NULL; + int ret = 0; + + at24 = kzalloc(sizeof(at24), GFP_KERNEL); + if (IS_ERR_OR_NULL(at24)) { + I2C_ERR("kzalloc failed\n"); + return -ENOMEM; + } + + switch (cmd) { + case I2C_TEST: + size = _IOC_SIZE(cmd); + if (copy_from_user(at24, (void __user *)arg, size)) { + I2C_ERR("copy buffer err\n"); + return -ENOMEM; + } + + dprintk(DEBUG_INFO, "at24: flg = 0x%x, num = %d, len = %d\n", + at24->flags, at24->num, at24->len); + dprintk(DEBUG_INFO, " cmm = 0x%x, val = 0x%x\n", + at24->command, at24->value); + + if (at24->num == 1) { + if (at24->flags == 1) { + /* 1 msgs read */ + buf = kzalloc(sizeof(buf)*(at24->len), + GFP_KERNEL); + if (IS_ERR_OR_NULL(buf)) { + I2C_ERR("kzalloc failed\n"); + return -ENOMEM; + } + + ret = i2c_master_recv(client, buf, at24->len); + if (ret > 0) + ret = copy_to_user(at24->buf, buf, + at24->len) ? -EFAULT : 0; + } else if (at24->flags == 0) { + /* 1 msgs write */ + buf = kzalloc(at24->len + 1, GFP_KERNEL); + if (IS_ERR_OR_NULL(buf)) { + I2C_ERR("kzalloc failed\n"); + return -ENOMEM; + } + + buf[0] = at24->command; + if (at24->len) { + for (i = 0; i < at24->len; i++) + buf[i + 1] = at24->value; + } + at24->len += 1; + i2c_master_send(client, buf, at24->len); + ret = 0; + } else { + I2C_ERR("err flags : 0x%x\n", at24->flags); + return -EINVAL; + } + kfree(buf); + } else if (at24->num == 2) { + /* 2 msgs read */ + msgs = kzalloc(sizeof(msgs)*2, GFP_KERNEL); + buf = kzalloc(at24->len, GFP_KERNEL); + if (IS_ERR_OR_NULL(buf) || IS_ERR_OR_NULL(msgs)) { + I2C_ERR("kzalloc failed\n"); + return -ENOMEM; + } + + msgs[0].addr = client->addr; + msgs[0].len = 1; + msgs[0].buf = &(at24->command); + msgs[1].addr = client->addr; + msgs[1].flags |= I2C_M_RD, + msgs[1].len = at24->len; + msgs[1].buf = buf; + + ret = i2c_transfer(adapter, msgs, at24->num); + if (ret < 0) { + kfree(buf); + kfree(msgs); + buf = NULL; + msgs = NULL; + return ret; + } + + if (ret > 0) + ret = copy_to_user(at24->buf, buf, at24->len) + ? -EFAULT : 0; + kfree(buf); + kfree(msgs); + buf = NULL; + msgs = NULL; + } else { + /* n msgs write */ + struct i2c_msg msgsa[at24->num]; + + for (i = 0; i < at24->num; i++) { + msgsa[i].addr = client->addr; + msgsa[i].len = 2; + msgsa[i].buf = kzalloc(msgsa[i].len, GFP_KERNEL); + if (IS_ERR_OR_NULL(msgsa[i].buf)) { + I2C_ERR("kzalloc failed\n"); + return -ENOMEM; + } + msgsa[i].buf[0] = at24->command + i*2; + msgsa[i].buf[1] = at24->value; + msgsa[i].flags = 0; + } + + ret = i2c_transfer(adapter, msgsa, at24->num); + for (i = 0; i < at24->num; i++) { + kfree(msgsa[i].buf); + msgsa[i].buf = NULL; + } + if (ret == at24->num) + ret = 0; + else + return ret; + } + break; + default: + I2C_ERR("ERR FORMAT\n"); + return -ENOTTY; + } + kfree(at24); + return ret; +} + +static int at24_open(struct inode *inode, struct file *file) +{ + dprintk(DEBUG_INFO, "open\n"); + + if (this_client) + file->private_data = this_client; + else { + I2C_ERR("no such dev\n"); + return -ENODEV; + } + + return 0; +} + +static int at24_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations at24_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .unlocked_ioctl = at24_ioctl, + .open = at24_open, + .release = at24_release, +}; + +static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int err = 0; + struct device *dev; + + this_client = client; + + dprintk(DEBUG_INIT, "begin probe\n"); + + at24_dev = kzalloc(sizeof(struct at24_dev), GFP_KERNEL); + if (IS_ERR_OR_NULL(at24_dev)) { + I2C_ERR("kzalloc failed\n"); + return -ENOMEM; + } + + err = alloc_chrdev_region(&at24_dev->chrdev, 0, 1, "at24-dev"); + + if (err) { + I2C_ERR("alloc_chrdev_region failed\n"); + goto alloc_chrdev_err; + } + + cdev_init(&(at24_dev->cdev), &at24_fops); + at24_dev->cdev.owner = THIS_MODULE; + err = cdev_add(&(at24_dev->cdev), at24_dev->chrdev, 1); + if (err) { + I2C_ERR("cdev_add failed\n"); + goto cdev_add_err; + } + + at24_class = class_create(THIS_MODULE, "at24_char_class"); + if (IS_ERR(at24_class)) { + err = PTR_ERR(at24_class); + I2C_ERR("class_create failed\n"); + goto class_err; + } + + dev = device_create(at24_class, NULL, at24_dev->chrdev, NULL, + "at24"); + if (IS_ERR(dev)) { + err = PTR_ERR(dev); + I2C_ERR("device_create failed\n"); + goto device_err; + } + + dprintk(DEBUG_INIT, "ok\n"); + return 0; + +device_err: + device_destroy(at24_class, at24_dev->chrdev); +class_err: + cdev_del(&(at24_dev->cdev)); +cdev_add_err: + unregister_chrdev_region(at24_dev->chrdev, 1); +alloc_chrdev_err: + kfree(at24_dev); + + return err; +} + +static int at24_remove(struct i2c_client *client) +{ + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver at24_driver = { + .class = I2C_CLASS_SPD, + .driver = { + .name = "24c16", + .owner = THIS_MODULE, + }, + .probe = at24_probe, + .remove = at24_remove, + .id_table = at24_ids, + .detect = at24_detect, + .address_list = i2c_address, +}; + +static int __init at24_init(void) +{ + dprintk(DEBUG_INIT, "begin init\n"); + return i2c_add_driver(&at24_driver); + +} + +static void __exit at24_exit(void) +{ + cdev_del(&(at24_dev->cdev)); + unregister_chrdev_region(at24_dev->chrdev, 1); + device_destroy(at24_class, at24_dev->chrdev); + class_destroy(at24_class); + kfree(at24_dev); + i2c_del_driver(&at24_driver); +} + +module_init(at24_init); +module_exit(at24_exit); +module_param_named(debug, debug_mask, int, 0664); +MODULE_DESCRIPTION("24C16 simple test"); +MODULE_AUTHOR("pannan"); +MODULE_LICENSE("GPL");