Rtwo/kernel/motorola/sm8550/drivers/i2c/busses/i2c-slave-test.c
2025-09-30 19:22:48 -05:00

393 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#define I2C_DRIVER_NAME "slave_test_i2c"
#define MAX_BUF_SIZE 128
/**
* i2c_slave_write_test: To write data to master.
* @client: Pointer to I2C client.
*
* This function will write word data to master.
*
* Return: 0 on success, negative number for error condition.
*/
int i2c_slave_write_test(struct i2c_client *client)
{
u16 data = 0x1234;
u8 command = 0;
int ret;
dev_err(&client->dev, "I2C-SLAVE: Running Writing Test...\n");
ret = i2c_smbus_write_word_data(client, command, data);
if (ret >= 0)
dev_err(&client->dev, "I2C-SLAVE: I2c Slave Write success ret: %d\n", ret);
else
dev_err(&client->dev, "I2C-SLAVE: I2c Slave Write Failed ret: %d\n", ret);
return ret;
}
/**
* read_byte_show: To read byte data from master.
* @dev: Pointer to i2c device.
* @attr: device attribute.
* @buf: data buffer.
*
* This function will read byte data from master.
*
* Return: Number of characters or values that have been
* written to buffer.
*/
static ssize_t read_byte_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
int data;
data = i2c_smbus_read_byte(client);
if (data < 0)
return scnprintf(buf, MAX_BUF_SIZE,
"I2C-SLAVE-TEST: Read byte Failed ret:%d\n", data);
return scnprintf(buf, MAX_BUF_SIZE, "0x%x\n", data);
}
/**
* read_word_show: To read word data from master.
* @dev: Pointer to i2c device.
* @attr: device attribute.
* @buf: data buffer.
*
* This function will read word data from master.
*
* Return: Number of characters or values that have been
* written to buffer.
*/
static ssize_t read_word_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
int data;
u8 command = 0;
data = i2c_smbus_read_word_data(client, command);
if (data < 0)
return scnprintf(buf, MAX_BUF_SIZE,
"I2C-SLAVE-TEST: Read word Failed ret:%d\n", data);
return scnprintf(buf, MAX_BUF_SIZE,
"0x%x 0x%x\n", ((data & 0xFF00) >> 8), (data & 0xFF));
}
/**
* read_block_show: To read block data from master.
* @dev: Pointer to i2c device.
* @attr: device attribute.
* @buf: data buffer.
*
* This function will read block data from master.
*
* Return: Number of characters or values that have been
* written to buffer.
*/
static ssize_t read_block_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
u8 command = 0, data[I2C_SMBUS_BLOCK_MAX];
int i, j, count = 0;
count = i2c_smbus_read_block_data(client, command, data);
if (count < 0)
return scnprintf(buf, MAX_BUF_SIZE,
"I2C-SLAVE-TEST: Read block Failed ret:%d\n", count);
if (count == 0)
return scnprintf(buf, MAX_BUF_SIZE,
"I2C-SLAVE-TEST: Data not available\n");
if (count > I2C_SMBUS_BLOCK_MAX)
count = I2C_SMBUS_BLOCK_MAX;
j = scnprintf(buf, MAX_BUF_SIZE, "Block Data: ");
for (i = 0; i < count; i++)
j += scnprintf(buf + j, MAX_BUF_SIZE, "0x%x ", data[i]);
return scnprintf(buf, MAX_BUF_SIZE, "%s\n", buf);
}
/**
* write_byte_store: To write byte data to master.
* @dev: Pointer to i2c device.
* @attr: device attribute.
* @buf: data buffer.
* @count: data count.
*
* This function will write byte data to master.
*
* Return: data count on success, negative number for error condition.
*/
static ssize_t write_byte_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val;
int ret, command = 0;
ret = kstrtoul(buf, 10, &val);
if (ret < 0) {
dev_err(dev, "couldn't parse string %d\n", ret);
return ret;
}
ret = i2c_smbus_write_byte_data(client, command, val);
if (ret < 0)
dev_err(&client->dev, "i2c_smbus_write_byte_data failed ret:%d\n", ret);
return count;
}
/**
* write_word_store: To write word data to master.
* @dev: Pointer to i2c device.
* @attr: device attribute.
* @buf: data buffer.
* @count: data count.
*
* This function will write word data to master.
*
* Return: data count on success, negative number for error condition.
*/
static ssize_t write_word_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int ret = 0, command = 0;
unsigned long val;
ret = kstrtoul(buf, 10, &val);
if (ret < 0) {
dev_err(dev, "couldn't parse string %d\n", ret);
return ret;
}
ret = i2c_smbus_write_word_data(client, command, val);
if (ret < 0)
dev_err(&client->dev, "i2c_smbus_write_word_data failed ret:%d\n", ret);
return count;
}
/**
* write_block_store: To write block data to master.
* @dev: Pointer to i2c device.
* @attr: device attribute.
* @buf: data buffer.
* @count: data count.
*
* This function will write block data to master.
*
* Return: 0 for success, negative number for error condition.
*/
static ssize_t write_block_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
u8 data[I2C_SMBUS_BLOCK_MAX];
int ret = 0, command = 0;
ret = kstrtoul(buf, 10, &count);
if (ret < 0) {
dev_err(dev, "couldn't parse string %d\n", ret);
return ret;
}
if (count > I2C_SMBUS_BLOCK_MAX)
count = I2C_SMBUS_BLOCK_MAX;
get_random_bytes(data, count);
ret = i2c_smbus_write_block_data(client, command, count, data);
if (ret < 0)
dev_err(&client->dev, "i2c_smbus_write_block_data failed ret:%d\n", ret);
return count;
}
static DEVICE_ATTR_RO(read_byte);
static DEVICE_ATTR_RO(read_word);
static DEVICE_ATTR_RO(read_block);
static DEVICE_ATTR_WO(write_byte);
static DEVICE_ATTR_WO(write_word);
static DEVICE_ATTR_WO(write_block);
/**
* i2c_slave_create_sysfs: To create sysfe attribute.
* @client: Pointer to I2C client.
*
* This function will create sysfs file to pass
* data from user space .
*
* Return: 0 on success, Negative on error.
*/
int i2c_slave_create_sysfs(struct i2c_client *client)
{
int ret = 0;
ret = device_create_file(&client->dev, &dev_attr_read_byte);
if (ret) {
dev_err(&client->dev, "I2C-SLAVE-TEST: failed to create sysfs file ret: %d\n", ret);
goto err;
}
ret = device_create_file(&client->dev, &dev_attr_write_byte);
if (ret) {
dev_err(&client->dev, "I2C-SLAVE-TEST: failed to create sysfs file ret: %d\n", ret);
goto err_write_byte;
}
ret = device_create_file(&client->dev, &dev_attr_read_word);
if (ret) {
dev_err(&client->dev, "I2C-SLAVE-TEST: failed to create sysfs file ret: %d\n", ret);
goto err_read_word;
}
ret = device_create_file(&client->dev, &dev_attr_write_word);
if (ret) {
dev_err(&client->dev, "I2C-SLAVE-TEST: failed to create sysfs file ret: %d\n", ret);
goto err_write_word;
}
ret = device_create_file(&client->dev, &dev_attr_read_block);
if (ret) {
dev_err(&client->dev, "I2C-SLAVE-TEST: failed to create sysfs file ret: %d\n", ret);
goto err_read_block;
}
ret = device_create_file(&client->dev, &dev_attr_write_block);
if (ret) {
dev_err(&client->dev, "I2C-SLAVE-TEST: failed to create sysfs file ret: %d\n", ret);
goto err_write_block;
}
return 0;
err_write_block:
device_remove_file(&client->dev, &dev_attr_read_block);
err_read_block:
device_remove_file(&client->dev, &dev_attr_write_word);
err_write_word:
device_remove_file(&client->dev, &dev_attr_read_word);
err_read_word:
device_remove_file(&client->dev, &dev_attr_write_byte);
err_write_byte:
device_remove_file(&client->dev, &dev_attr_read_byte);
err:
return ret;
}
/**
* i2c_slave_test_probe: Driver Probe function.
* @client: Pointer to I2C client.
* @dev_id: I2C device id.
*
* This function will performs pre-initialization tasks such as verify,
* I2C functionality, create sysfs attributes and perform read/write operation.
*
* Return: 0 for success, negative number for error condition.
*/
static int i2c_slave_test_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
int ret = 0;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
dev_err(&client->dev, "%s: Need I2C_FUNC_SMBUS_BYTE\n", __func__);
return -EINVAL;
}
ret = i2c_slave_write_test(client);
if (ret < 0) {
dev_err(&client->dev, "I2C slave read test failed ret: $d\n", ret);
goto err;
}
ret = i2c_slave_create_sysfs(client);
if (ret) {
dev_err(&client->dev, "I2C-SLAVE-TEST: failed to create sysfs file ret: %d\n", ret);
goto err;
}
dev_err(&client->dev, "I2C-SLAVE-TEST: Probe done\n");
return 0;
err:
dev_err(&client->dev, "I2C-SLAVE-TEST: Probe failed ret: %d\n", ret);
return ret;
}
/**
* i2c_slave_test_remove: Driver remove function.
* @client: Pointer to I2C client.
*
* This function will remove sysfs attributes.
*
* Return: 0 for success.
*/
static int i2c_slave_test_remove(struct i2c_client *client)
{
device_remove_file(&client->dev, &dev_attr_write_block);
device_remove_file(&client->dev, &dev_attr_read_block);
device_remove_file(&client->dev, &dev_attr_write_word);
device_remove_file(&client->dev, &dev_attr_read_word);
device_remove_file(&client->dev, &dev_attr_write_byte);
device_remove_file(&client->dev, &dev_attr_read_byte);
return 0;
}
static const struct i2c_device_id slave_test_id_table[] = {
{I2C_DRIVER_NAME, 0},
{},
};
MODULE_DEVICE_TABLE(i2c, slave_test_id_table);
static const struct of_device_id slave_test_of_match_table[] = {
{
.compatible = "qcom,i2c-slave-test",
},
{},
};
MODULE_DEVICE_TABLE(of, slave_test_of_match_table);
static struct i2c_driver i2c_slave_test_driver = {
.driver = {
.name = I2C_DRIVER_NAME,
.of_match_table = slave_test_of_match_table,
},
.probe = i2c_slave_test_probe,
.remove = i2c_slave_test_remove,
.id_table = slave_test_id_table,
};
static int __init i2c_slave_test_init(void)
{
return i2c_add_driver(&i2c_slave_test_driver);
}
static void __exit i2c_slave_test_exit(void)
{
i2c_del_driver(&i2c_slave_test_driver);
}
module_init(i2c_slave_test_init);
module_exit(i2c_slave_test_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("I2C Slave Test driver");