kernel-brax3-ubuntu-touch/drivers/misc/mediatek/nfc_tms/guidev/guide_driver.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

687 lines
18 KiB
C
Executable file

/*****************************************************************************************
* Copyright (c) 2021- 2022 TsingTeng MicroSystem Corp.Ltd.
* TMS_EDIT
* File : guide_driver.c
* Description: Source file for tms devices guide
* Version : 1.0
* Date : 2022/4/11
* Team : NFC Middleware
* Author : Guoliang Wu
* --------------------------------- Revision History: ---------------------------------
* <version> <date> < author > <desc>
*******************************************************************************************/
#include "guide_driver.h"
/*********** PART0: Global Variables Area ***********/
struct guide_dev *guidev = NULL;
static chip_t chip_type = TMS_THN31;
/*********** PART1: Function declarations Area ***********/
#if defined(CONFIG_TMS_ESE_DEVICE) || defined(CONFIG_TMS_ESE_DEVICE_MODULE)
extern int ese_driver_init(void);
extern void ese_driver_exit(void);
#endif
//extern int xxx_probe(struct i2c_client *client, const struct i2c_device_id *id);
//extern int xxx_remove(struct i2c_client *client);
//extern int xxx_suspend(struct device *device);
//extern int xxx_resume(struct device *device);
//extern int xxx_ese_dev_init(void);
//extern void xxx_ese_dev_exit(void);
/*********** PART2: Guidev Driver Start Area ***********/
static void guidev_resource_release(struct guide_dev *gdata)
{
gpio_free(gdata->hw_res.irq_gpio);
gpio_free(gdata->hw_res.ven_gpio);
gpio_free(gdata->hw_res.download_gpio);
TMS_DEBUG("Finished");
}
static void guidev_hw_reset(uint32_t mdelay)
{
if (!guidev->tms->set_ven) {
TMS_ERR("guidev->tms->set_ven is NULL");
return;
}
guidev->tms->set_ven(guidev->hw_res, OFF);
usleep_range(GPIO_VEN_SET_WAIT_TIME_US, (GPIO_VEN_SET_WAIT_TIME_US) + 100);
guidev->tms->set_ven(guidev->hw_res, ON);
if (mdelay) {
usleep_range(mdelay, (mdelay) + 100);
}
TMS_DEBUG("Finished");
}
static int guidev_probe_driver(struct i2c_client *client,
const struct i2c_device_id *id, chip_t chip_type)
{
int ret = SUCCESS;
guidev_resource_release(guidev);
switch (chip_type) {
case TMS_THN31:
TMS_INFO("TMS THN31 driver start probe\n");
ret = nfc_device_probe(client, id);
#if defined(CONFIG_TMS_ESE_DEVICE) || defined(CONFIG_TMS_ESE_DEVICE_MODULE)
ese_driver_init();
#endif
break;
case SAMPLE_DEV_1:
//TMS_INFO("XXX XXX driver start probe\n");
//ret = sample_dev_1_probe(client, id);
//sample_dev_1_ese_driver_init();
break;
case SAMPLE_DEV_2:
//TMS_INFO("XXX XXX driver start probe\n");
//ret = sample_dev_2_probe(client, id);
//sample_dev_2_ese_driver_init();
break;
default:
TMS_ERR("Unknow chip %s[%d]\n", guidev->tms->nfc_name, chip_type);
break;
}
return ret;
}
static int guidev_parse_dts(struct guide_dev *gdata)
{
int rc;
int ret = 0;
struct device_node *np;
TMS_INFO("Start+\n");
np = gdata->dev->of_node;
gdata->hw_res.irq_gpio = of_get_named_gpio(np, "tms,irq-gpio", 0);
if (gpio_is_valid(gdata->hw_res.irq_gpio)) {
rc = gpio_request(gdata->hw_res.irq_gpio, "guidev_int");
if (rc) {
TMS_WARN("unable to request gpio[%d] as irq\n", gdata->hw_res.irq_gpio);
}
} else {
TMS_ERR("irq gpio not specified in dts\n");
return -EINVAL;
}
gdata->hw_res.ven_gpio = of_get_named_gpio_flags(np, "tms,ven-gpio", 0,
&gdata->hw_res.ven_flag);
if (gpio_is_valid(gdata->hw_res.ven_gpio)) {
rc = gpio_request(gdata->hw_res.ven_gpio, "guidev_ven");
if (rc) {
TMS_WARN("unable to request gpio[%d] as ven\n", gdata->hw_res.ven_gpio);
}
} else {
TMS_ERR("ven gpio not specified in dts\n");
ret = -EINVAL;
goto err_ven;
}
gdata->hw_res.download_gpio = of_get_named_gpio_flags(np, "tms,download-gpio",
0,
&gdata->hw_res.download_flag);
if (gpio_is_valid(gdata->hw_res.download_gpio)) {
rc = gpio_request(gdata->hw_res.download_gpio, "guidev_fw_download");
if (rc) {
TMS_WARN("unable to request gpio[%d] as download\n",
gdata->hw_res.download_gpio);
}
} else {
TMS_ERR("fw-download gpio not specified in dts\n");
ret = -EINVAL;
goto err_fw_download;
}
TMS_DEBUG("irq_gpio = %d, ven_gpio = %d, dwnld_gpio = %d, error:%d\n",
gdata->hw_res.irq_gpio, gdata->hw_res.ven_gpio, gdata->hw_res.download_gpio,
ret);
TMS_INFO("Normal end-\n");
return SUCCESS;
err_fw_download:
gpio_free(gdata->hw_res.ven_gpio);
err_ven:
gpio_free(gdata->hw_res.irq_gpio);
TMS_ERR("Error end, ret = %d\n", ret);
return ret;
}
static int guidev_gpio_configure(struct hw_resource hw_res)
{
int ret;
TMS_INFO("Start+\n");
if (gpio_is_valid(hw_res.irq_gpio)) {
ret = gpio_direction_input(hw_res.irq_gpio);
if (ret < 0) {
TMS_ERR("not able to set irq gpio as input\n");
return -ERROR;
}
TMS_INFO("set irq gpio as input\n");
}
if (gpio_is_valid(hw_res.ven_gpio)) {
ret = gpio_direction_output(hw_res.ven_gpio, hw_res.ven_flag);
if (ret < 0) {
TMS_ERR("not able to set ven gpio as output\n");
return -ERROR;
}
TMS_INFO("set ven gpio as output\n");
}
if (gpio_is_valid(hw_res.download_gpio)) {
ret = gpio_direction_output(hw_res.download_gpio, hw_res.download_flag);
if (ret < 0) {
TMS_ERR("not able to set dwnld gpio as output\n");
return -ERROR;
}
TMS_INFO("set dwnld gpio as output\n");
}
TMS_INFO("Normal end-\n");
return SUCCESS;
}
static int tms_chip_identification(struct match_info info, uint8_t *buf)
{
int ret, i;
int cmp_byte = 0;
TMS_INFO("Start+\n");
if (info.pattern == TMS_FW) {
cmp_byte = info.sum - TMS_FW_CMP_BYTE - info.check_sum;
} else if (info.pattern == TMS_BL) {
cmp_byte = info.sum - TMS_BL_CMP_BYTE - info.check_sum;
} else {
return -ERROR;
TMS_ERR("Chip identified fail\n");
}
TMS_INFO("Compare major version is %02x\n", buf[cmp_byte]);
for (i = 0; i < MAX_MAJOR_VERSION_NUM; i++) {
if ((buf[cmp_byte] & TMS_VERSION_MASK) == info.major_ver[i]) {
ret = SUCCESS;
TMS_DEBUG("Version matched\n");
break;
}
ret = -ERROR;
}
TMS_INFO("Normal end-\n");
return ret;
}
static int tms_receive_process(struct guide_dev *gdata, struct match_info *info,
uint8_t *buf)
{
int ret;
int retry;
int read_len = 0;
TMS_INFO("Start+\n");
retry = info->read_retry;
do {
msleep(5);
if (gpio_get_value(gdata->hw_res.irq_gpio)) {
read_len = TMS_CMD_HEAD_LEN;
ret = i2c_master_recv(gdata->client, buf, read_len);
if (ret == read_len) {
info->sum = ret;
} else {
TMS_ERR("Receive head error[%d]\n", ret);
return -ERROR;
}
read_len = buf[HEAD_PAYLOAD_BYTE];
if (read_len > (MAX_CMD_LEN - info->check_sum)) {
read_len = MAX_CMD_LEN - info->check_sum;
TMS_WARN("Receive payload is more than buf max\n");
}
ret = i2c_master_recv(gdata->client, buf + info->sum,
read_len + info->check_sum);
if (ret == (read_len + info->check_sum)) {
info->sum = info->sum + ret;
} else {
TMS_ERR("Receive payload error[%d]\n", ret);
return -ERROR;
}
ret = SUCCESS;
break;
}
TMS_ERR("detected interrupt again\n");
ret = -ERROR;
retry--;
} while (retry > 0);
if (ret != SUCCESS) {
TMS_ERR("No response is received\n");
return -ERROR;
}
tms_buffer_dump("Rx <-", buf, info->sum);
TMS_INFO("Normal end-\n");
return ret;
}
static int tms_check_chip_info(struct guide_dev *gdata, struct match_info info)
{
int ret;
int retry;
uint8_t read_buf[MAX_CMD_LEN];
TMS_INFO("Start+\n");
retry = info.write_retry;
if (info.pattern == TMS_FW) {
guidev_hw_reset(GPIO_VEN_SET_WAIT_TIME_US);
TMS_DEBUG("TMS chip FW match check...\n");
} else if (info.pattern == TMS_BL) {
if (!gdata->tms->set_download) {
TMS_ERR("gdata->tms->set_download is NULL");
return -ERROR;
}
gdata->tms->set_download(gdata->hw_res, ON);
guidev_hw_reset(GPIO_VEN_SET_WAIT_TIME_US);
TMS_DEBUG("TMS chip BL match check...\n");
} else {
TMS_ERR("TMS chip info error\n");
return -ERROR;
}
if (info.write_len > 0 && info.write_len < MAX_CMD_LEN) {
tms_buffer_dump("Tx ->", info.cmd, info.write_len);
} else {
TMS_WARN("Send Command too loog, set length is %d\n", MAX_CMD_LEN);
info.write_len = MAX_CMD_LEN;
}
while (retry > 0) {
ret = i2c_master_send(gdata->client, info.cmd, info.write_len);
if (ret == info.write_len) {
ret = tms_receive_process(gdata, &info, read_buf);
if (info.pattern == TMS_FW) {
/*if nci payload > 1, do not read + check_sum*/
info.check_sum = 0;
ret = tms_receive_process(gdata, &info, read_buf);
} else if (info.pattern == TMS_BL) {
gdata->tms->set_download(gdata->hw_res, OFF);
guidev_hw_reset(GPIO_VEN_SET_WAIT_TIME_US);
}
break;
}
TMS_DEBUG("Send Command error[%d]\n", ret);
retry--;
msleep(10);
ret = -ERROR;
};
if (ret != SUCCESS) {
TMS_ERR("Command transceive failed\n");
return -ERROR;
}
ret = tms_chip_identification(info, read_buf);
TMS_INFO("Normal end-\n");
return ret;
}
static int guidev_chip_match(struct guide_dev *gdata)
{
int ret = -ERROR;
int count = 0;
struct match_info info_list[] = {
{
.type = TMS_THN31,
.pattern = TMS_FW,
.name = "thn31",
.write_len = 4,
.write_retry = 3,
.read_retry = 10,
.check_sum = 1,
.major_ver = {0xC0, 0xD0},
.cmd = {0x20, 0x00, 0x01, 0x00},
},
{
.type = TMS_THN31,
.pattern = TMS_BL,
.name = "thn31",
.write_len = 9,
.write_retry = 3,
.read_retry = 10,
.check_sum = 1,
.major_ver = {0xB0},
.cmd = {0x5A, 0x00, 0x05, 0x00, 0x4a, 0x02, 0x00, 0x00, 0x17},
},
{
.type = SAMPLE_DEV_1,
.pattern = SAMPLE_MATCH_1,
.name = "sample_match_1",
.write_len = 9,
.write_retry = 3,
.read_retry = 10,
.check_sum = 1,
.major_ver = {0xB0},
.cmd = {0x5A, 0x00, 0x05, 0x00, 0x4a, 0x02, 0x00, 0x00, 0x17},
},
{
.type = SAMPLE_DEV_2,
.pattern = SAMPLE_MATCH_2,
.name = "sample_match_2",
.write_len = 9,
.write_retry = 3,
.read_retry = 10,
.check_sum = 1,
.major_ver = {0xB0},
.cmd = {0x5A, 0x00, 0x05, 0x00, 0x4a, 0x02, 0x00, 0x00, 0x17},
},
};
TMS_INFO("Start+\n");
for (count = 0; count < ARRAY_SIZE(info_list); count++) {
TMS_DEBUG("Check chip info %d times\n", count + 1);
if (info_list[count].type == TMS_THN31) {
ret = tms_check_chip_info(gdata, info_list[count]);
} else if (info_list[count].type == SAMPLE_DEV_1) {
//ret = sample_dev_1_check_chip_info(gdata, info_list);
ret = -ERROR;
} else if (info_list[count].type == SAMPLE_DEV_2) {
//ret = sample_dev_2_check_chip_info(gdata, info_list);
ret = -ERROR;
}
if (ret == SUCCESS) {
chip_type = info_list[count].type;
gdata->tms->nfc_name = info_list[count].name;
TMS_INFO("Matched chip is %s\n", gdata->tms->nfc_name);
break;
}
}
TMS_INFO("end-\n");
return ret;
}
static int guidev_dev_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
TMS_INFO("Start+\n");
/* step1 : alloc data */
guidev = kzalloc(sizeof(struct guide_dev), GFP_KERNEL);
if (guidev == NULL) {
TMS_ERR("Guide info alloc memory error\n");
return -ENOMEM;
}
/* step2 : binding tms common data */
guidev->tms = tms_common_data_binding();
if (guidev->tms == NULL) {
TMS_ERR("Get tms common info error\n");
ret = -ENOMEM;
goto err_free_guidev;
}
/* step3 : init and binding parameters for easy operate */
guidev->client = client;
guidev->dev = &client->dev;
/* step4 : dts parse */
ret = guidev_parse_dts(guidev);
if (ret) {
TMS_ERR("failed to parse dts\n");
goto err_free_guidev;
}
/* step5 : I2C function check */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
TMS_ERR("need I2C_FUNC_I2C\n");
guidev_resource_release(guidev);
ret = -ENODEV;
goto err_free_guidev;
}
/* step6 : Set gpio */
ret = guidev_gpio_configure(guidev->hw_res);
if (ret) {
TMS_ERR("failed to configure\n");
guidev_resource_release(guidev);
goto err_free_guidev;
}
/* step7 : match nfc chip info */
ret = guidev_chip_match(guidev);
if (ret) {
TMS_ERR("Fail to match chip\n");
}
/* step8 : Probe driver */
ret = guidev_probe_driver(client, id, chip_type);
if (ret) {
TMS_ERR("failed to configure\n");
goto err_free_guidev;
}
/* step9 : Create system property node */
ret = nfc_create_sysfs_interfaces(&client->dev);
if (ret < 0) {
TMS_ERR("Create sysfs interface failed\n");
goto err_free_guidev;
}
ret = sysfs_create_link(NULL, &client->dev.kobj, "nfc");
if (ret < 0) {
TMS_ERR("create sysfs link failed\n");
goto err_remove_sysfs_interface;
}
TMS_INFO("probe finished\n");
return SUCCESS;
err_remove_sysfs_interface:
nfc_remove_sysfs_interfaces(&client->dev);
err_free_guidev:
kfree(guidev);
guidev = NULL;
TMS_ERR("probe failed, check hardware\n");
return ret;
}
static int guidev_dev_remove(struct i2c_client *client)
{
switch (chip_type) {
case TMS_THN31:
nfc_device_remove(client);
TMS_INFO("TMS THN31 nfc start removed\n");
break;
case SAMPLE_DEV_1:
//TMS_INFO("XXX XXX nfc start removed\n");
//sample_dev_1_remove(client);
break;
case SAMPLE_DEV_2:
//TMS_INFO("XXX XXX nfc start removed\n");
//sample_dev_2_remove(client);
break;
default:
TMS_ERR("No chip to remove %d\n", chip_type);
break;
}
nfc_remove_sysfs_interfaces(&client->dev);
kfree(guidev);
guidev = NULL;
return SUCCESS;
}
void guidev_dev_shutdown(struct i2c_client *client)
{
if (!guidev || !guidev->tms || !guidev->tms->set_ven) {
TMS_ERR("set_ven is NULL");
return;
}
guidev->tms->set_ven(guidev->hw_res, OFF);
msleep(20); // hard reset guard time
}
static int guide_suspend(struct device *device)
{
switch (chip_type) {
case TMS_THN31:
TMS_INFO("TMS THN31 nfc start suspend\n");
nfc_device_suspend(device);
break;
case SAMPLE_DEV_1:
//TMS_INFO("XXX XXX nfc start suspend\n");
//sample_dev_1_suspend(device);
break;
case SAMPLE_DEV_2:
//TMS_INFO("XXX XXX nfc start suspend\n");
//sample_dev_2_suspend(device);
break;
default:
TMS_ERR("No chip to suspend %d\n", chip_type);
break;
}
return SUCCESS;
}
static int guide_resume(struct device *device)
{
switch (chip_type) {
case TMS_THN31:
TMS_INFO("TMS THN31 nfc start resume\n");
nfc_device_resume(device);
break;
case SAMPLE_DEV_1:
//TMS_INFO("XXX XXX nfc start resume\n");
//sample_dev_1_resume(device);
break;
case SAMPLE_DEV_2:
//TMS_INFO("XXX XXX nfc start resume\n");
//sample_dev_2_suspend(device);
break;
default:
TMS_ERR("No chip to resume %d\n", chip_type);
break;
}
return SUCCESS;
}
static const struct i2c_device_id guide_dev_id[] = {
{GUIDEDEV_NAME, 0 },
{ }
};
static struct of_device_id guide_match_table[] = {
{ .compatible = GUIDEDEV_NAME, },
{ }
};
static const struct dev_pm_ops guide_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(guide_suspend, guide_resume)
};
static struct i2c_driver tms_guide_driver = {
.id_table = guide_dev_id,
.probe = guidev_dev_probe,
.remove = guidev_dev_remove,
.shutdown = guidev_dev_shutdown,
.driver = {
.name = GUIDEDEV_NAME,
.of_match_table = guide_match_table,
.pm = &guide_pm_ops,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
MODULE_DEVICE_TABLE(of, guide_match_table);
static int __init tms_guide_init(void)
{
int ret = 0;
TMS_INFO("Loading guide driver\n");
ret = i2c_add_driver(&tms_guide_driver);
if (ret != 0) {
TMS_ERR("Add driver error ret = %d\n", ret);
}
return ret;
}
static void __exit tms_guide_exit(void)
{
switch (chip_type) {
case TMS_THN31:
#if defined(CONFIG_TMS_ESE_DEVICE) || defined(CONFIG_TMS_ESE_DEVICE_MODULE)
ese_driver_exit();
#endif
break;
case SAMPLE_DEV_1:
//sample_dev_1_driver_exit(device);
break;
case SAMPLE_DEV_2:
//sample_dev_2_driver_exit(device);
break;
default:
TMS_ERR("No chip to resume %d\n", chip_type);
break;
}
TMS_INFO("Unloading guide driver\n");
i2c_del_driver(&tms_guide_driver);
}
module_init(tms_guide_init);
module_exit(tms_guide_exit);
MODULE_DESCRIPTION("TMS Guide Driver");
MODULE_LICENSE("GPL v2");