android-kernel-a71/drivers/adsp_factory/ssc_core.c
2024-11-30 08:51:05 +01:00

602 lines
16 KiB
C

/*
* Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/adsp/slpi_motor.h>
#include <linux/adsp/ssc_ssr_reason.h>
#ifdef CONFIG_SUPPORT_SSC_MODE
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#endif
#ifdef CONFIG_SUPPORT_DEVICE_MODE
#include <linux/hall.h>
#endif
#include "adsp.h"
#define SSR_REASON_LEN 128
#ifdef CONFIG_SEC_FACTORY
#define SLPI_STUCK "SLPI_STUCK"
#define SLPI_PASS "SLPI_PASS"
#endif
#define SENSOR_DUMP_CNT 3 /* Accel/Gyro, Magnetic, Light */
#define SENSOR_DUMP_DONE "SENSOR_DUMP_DONE"
#ifdef CONFIG_SUPPORT_SSC_MODE
#ifdef CONFIG_SUPPORT_SSC_MODE_FOR_MAG
#define ANT_NFC_MST 0
#define ANT_NFC_ONLY 1
#define ANT_DUMMY 2
#endif
#endif
struct sdump_data {
struct workqueue_struct *sdump_wq;
struct work_struct work_sdump;
struct adsp_data *dev_data;
};
struct sdump_data *pdata_sdump;
static int pid;
static char panic_msg[SSR_REASON_LEN];
/*************************************************************************/
/* factory Sysfs */
/*************************************************************************/
static char operation_mode_flag[11];
static ssize_t dumpstate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int32_t type[1];
if (pid != 0) {
pr_info("[FACTORY] to take the logs\n");
type[0] = 0;
} else {
type[0] = 2;
pr_info("[FACTORY] logging service was stopped %d\n", pid);
}
adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE);
return snprintf(buf, PAGE_SIZE, "SSC_CORE\n");
}
static ssize_t operation_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s", operation_mode_flag);
}
static ssize_t operation_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int i;
for (i = 0; i < 10 && buf[i] != '\0'; i++)
operation_mode_flag[i] = buf[i];
operation_mode_flag[i] = '\0';
pr_info("[FACTORY] %s: operation_mode_flag = %s\n", __func__,
operation_mode_flag);
return size;
}
static ssize_t mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long timeout;
unsigned long timeout_2;
int32_t type[1];
if (pid != 0) {
timeout = jiffies + (2 * HZ);
timeout_2 = jiffies + (10 * HZ);
type[0] = 1;
pr_info("[FACTORY] To stop logging %d\n", pid);
adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE);
while (pid != 0) {
msleep(25);
if (time_after(jiffies, timeout))
pr_info("[FACTORY] %s: Timeout!!!\n", __func__);
if (time_after(jiffies, timeout_2)) {
pr_info("[FACTORY] pid %d\n", pid);
return snprintf(buf, PAGE_SIZE, "1\n");
}
}
}
return snprintf(buf, PAGE_SIZE, "0\n");
}
static ssize_t mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int data = 0;
int32_t type[1];
if (kstrtoint(buf, 10, &data)) {
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
return -EINVAL;
}
if (data != 1) {
pr_err("[FACTORY] %s: data was wrong\n", __func__);
return -EINVAL;
}
if (pid != 0) {
type[0] = 1;
adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE);
}
return size;
}
static ssize_t pid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", pid);
}
static ssize_t pid_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int data = 0;
if (kstrtoint(buf, 10, &data)) {
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
return -EINVAL;
}
pid = data;
pr_info("[FACTORY] %s: pid %d\n", __func__, pid);
return size;
}
static ssize_t remove_sensor_sysfs_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
unsigned int type = MSG_SENSOR_MAX;
struct adsp_data *data = dev_get_drvdata(dev);
if (kstrtouint(buf, 10, &type)) {
pr_err("[FACTORY] %s: kstrtouint fail\n", __func__);
return -EINVAL;
}
if (type > PHYSICAL_SENSOR_SYSFS) {
pr_err("[FACTORY] %s: Invalid type %u\n", __func__, type);
return size;
}
pr_info("[FACTORY] %s: type = %u\n", __func__, type);
mutex_lock(&data->remove_sysfs_mutex);
adsp_factory_unregister(type);
mutex_unlock(&data->remove_sysfs_mutex);
return size;
}
static unsigned int system_rev __read_mostly;
static int __init sec_hw_rev_setup(char *p)
{
int ret;
ret = kstrtouint(p, 0, &system_rev);
if (unlikely(ret < 0)) {
pr_warn("androidboot.revision is malformed (%s)\n", p);
return -EINVAL;
}
pr_info("androidboot.revision %x\n", system_rev);
return 0;
}
early_param("androidboot.revision", sec_hw_rev_setup);
static unsigned int sec_hw_rev(void)
{
return system_rev;
}
int ssc_system_rev_test;
static ssize_t ssc_hw_rev_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
pr_info("[FACTORY] ssc_rev:%d\n", sec_hw_rev());
if (!ssc_system_rev_test)
return snprintf(buf, PAGE_SIZE, "%d\n", sec_hw_rev());
else
return snprintf(buf, PAGE_SIZE, "%d\n", ssc_system_rev_test);
}
static ssize_t ssc_hw_rev_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
if (kstrtoint(buf, 10, &ssc_system_rev_test)) {
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
return -EINVAL;
}
pr_info("[FACTORY] system_rev_ssc %d\n", ssc_system_rev_test);
return size;
}
#ifdef CONFIG_SUPPORT_SSC_MODE
static int ssc_mode;
static ssize_t ssc_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
pr_info("[FACTORY] ssc_mode:%d\n", ssc_mode);
return snprintf(buf, PAGE_SIZE, "%d\n", ssc_mode);
}
static ssize_t ssc_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
if (kstrtoint(buf, 10, &ssc_mode)) {
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
return -EINVAL;
}
pr_info("[FACTORY] ssc_mode %d\n", ssc_mode);
return size;
}
#endif
void ssr_reason_call_back(char reason[], int len)
{
if (len <= 0) {
pr_info("[FACTORY] ssr %d\n", len);
return;
}
memset(panic_msg, 0, SSR_REASON_LEN);
strlcpy(panic_msg, reason, min(len, (int)(SSR_REASON_LEN - 1)));
pr_info("[FACTORY] ssr %s\n", panic_msg);
}
static ssize_t ssr_msg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
#ifdef CONFIG_SEC_FACTORY
struct adsp_data *data = dev_get_drvdata(dev);
int32_t raw_data_acc[3] = {0, };
int32_t raw_data_mag[3] = {0, };
int ret_acc = 0;
int ret_mag = 0;
mutex_lock(&data->accel_factory_mutex);
ret_acc = get_accel_raw_data(raw_data_acc);
mutex_unlock(&data->accel_factory_mutex);
ret_mag = get_mag_raw_data(raw_data_mag);
pr_info("[FACTORY] %s: accel(%d, %d, %d), mag(%d, %d, %d)\n", __func__,
raw_data_acc[0], raw_data_acc[1], raw_data_acc[2],
raw_data_mag[0], raw_data_mag[1], raw_data_mag[2]);
if (ret_acc == -1 && ret_mag == -1) {
pr_err("[FACTORY] %s: SLPI stuck, check hal log\n", __func__);
return snprintf(buf, PAGE_SIZE, "%s\n", SLPI_STUCK);
} else {
return snprintf(buf, PAGE_SIZE, "%s\n", SLPI_PASS);
}
#else
return snprintf(buf, PAGE_SIZE, "%s\n", panic_msg);
#endif
}
static ssize_t ssr_reset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int32_t type[1];
type[0] = 0xff;
adsp_unicast(type, sizeof(type), MSG_SSC_CORE, 0, MSG_TYPE_DUMPSTATE);
pr_info("[FACTORY] %s\n", __func__);
return snprintf(buf, PAGE_SIZE, "%s\n", "Success");
}
#ifdef CONFIG_SUPPORT_DEVICE_MODE
int sns_device_mode_notify(struct notifier_block *nb,
unsigned long flip_state, void *v)
{
int state = (int)flip_state;
pr_info("[FACTORY] %s - device mode %d", __func__, state);
if(state == 0)
adsp_unicast(NULL, 0, MSG_SSC_CORE, 0, MSG_TYPE_FACTORY_ENABLE);
else
adsp_unicast(NULL, 0, MSG_SSC_CORE, 0, MSG_TYPE_FACTORY_DISABLE);
return 0;
}
static struct notifier_block sns_device_mode_notifier = {
.notifier_call = sns_device_mode_notify,
.priority = 1,
};
#endif
static void print_sensor_dump(struct adsp_data *data, int sensor)
{
int i = 0;
switch (sensor) {
case MSG_ACCEL:
for (i = 0; i < 8; i++) {
pr_info("[FACTORY] %s - %d: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n",
__func__, sensor,
data->msg_buf[sensor][i * 16 + 0],
data->msg_buf[sensor][i * 16 + 1],
data->msg_buf[sensor][i * 16 + 2],
data->msg_buf[sensor][i * 16 + 3],
data->msg_buf[sensor][i * 16 + 4],
data->msg_buf[sensor][i * 16 + 5],
data->msg_buf[sensor][i * 16 + 6],
data->msg_buf[sensor][i * 16 + 7],
data->msg_buf[sensor][i * 16 + 8],
data->msg_buf[sensor][i * 16 + 9],
data->msg_buf[sensor][i * 16 + 10],
data->msg_buf[sensor][i * 16 + 11],
data->msg_buf[sensor][i * 16 + 12],
data->msg_buf[sensor][i * 16 + 13],
data->msg_buf[sensor][i * 16 + 14],
data->msg_buf[sensor][i * 16 + 15]);
}
break;
case MSG_MAG:
pr_info("[FACTORY] %s - %d: WIA1/2: 0x%02x/0x%02x, ST1/2: 0x%02x/0x%02x, DATA: 0x%02x/0x%02x/0x%02x/0x%02x/0x%02x/0x%02x, CNTL1/2/3: 0x%02x/0x%02x/0x%02x\n",
__func__, sensor,
data->msg_buf[sensor][0], data->msg_buf[sensor][1],
data->msg_buf[sensor][2], data->msg_buf[sensor][9],
data->msg_buf[sensor][3], data->msg_buf[sensor][4],
data->msg_buf[sensor][5], data->msg_buf[sensor][6],
data->msg_buf[sensor][7], data->msg_buf[sensor][8],
data->msg_buf[sensor][10], data->msg_buf[sensor][11],
data->msg_buf[sensor][12]);
break;
default:
break;
}
}
void sensor_dump_work_func(struct work_struct *work)
{
struct sdump_data *sensor_dump_data = container_of((struct work_struct *)work,
struct sdump_data, work_sdump);
struct adsp_data *data = sensor_dump_data->dev_data;
int sensor_type[SENSOR_DUMP_CNT] = { MSG_ACCEL, MSG_MAG, MSG_LIGHT };
int i, cnt;
for (i = 0; i < SENSOR_DUMP_CNT; i++) {
if (!data->sysfs_created[sensor_type[i]]) {
pr_info("[FACTORY] %s: %d was not probed\n",
__func__, sensor_type[i]);
continue;
}
pr_info("[FACTORY] %s: %d\n", __func__, sensor_type[i]);
cnt = 0;
adsp_unicast(NULL, 0, sensor_type[i], 0, MSG_TYPE_GET_DHR_INFO);
while (!(data->ready_flag[MSG_TYPE_GET_DHR_INFO] & 1 << sensor_type[i]) &&
cnt++ < TIMEOUT_CNT)
msleep(10);
data->ready_flag[MSG_TYPE_GET_DHR_INFO] &= ~(1 << sensor_type[i]);
if (cnt >= TIMEOUT_CNT) {
pr_err("[FACTORY] %s: %d Timeout!!!\n",
__func__, sensor_type[i]);
} else {
print_sensor_dump(data, sensor_type[i]);
msleep(200);
}
}
}
static ssize_t sensor_dump_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct adsp_data *data = dev_get_drvdata(dev);
pdata_sdump->dev_data = data;
queue_work(pdata_sdump->sdump_wq, &pdata_sdump->work_sdump);
return snprintf(buf, PAGE_SIZE, "%s\n", SENSOR_DUMP_DONE);
}
static ssize_t ar_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int32_t msg_buf[2] = {OPTION_TYPE_SSC_AUTO_ROTATION_MODE, 0};
msg_buf[1] = buf[0] - 48;
pr_info("[FACTORY]%s: ar_mode:%d\n", __func__, msg_buf[1]);
adsp_unicast(msg_buf, sizeof(msg_buf),
MSG_SSC_CORE, 0, MSG_TYPE_OPTION_DEFINE);
return size;
}
static int sbm_init;
static ssize_t sbm_init_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
pr_info("[FACTORY] %s sbm_init_show:%d\n", __func__, sbm_init);
return snprintf(buf, PAGE_SIZE, "%d\n", sbm_init);
}
static ssize_t sbm_init_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int32_t msg_buf[2] = {OPTION_TYPE_SSC_SBM_INIT, 0};
if (kstrtoint(buf, 10, &sbm_init)) {
pr_err("[FACTORY] %s: kstrtoint fail\n", __func__);
return -EINVAL;
}
if (sbm_init) {
msg_buf[1] = sbm_init;
pr_info("[FACTORY] %s sbm_init_store %d\n", __func__, sbm_init);
adsp_unicast(msg_buf, sizeof(msg_buf),
MSG_SSC_CORE, 0, MSG_TYPE_OPTION_DEFINE);
}
return size;
}
static DEVICE_ATTR(dumpstate, 0440, dumpstate_show, NULL);
static DEVICE_ATTR(operation_mode, 0664,
operation_mode_show, operation_mode_store);
static DEVICE_ATTR(mode, 0660, mode_show, mode_store);
static DEVICE_ATTR(ssc_pid, 0660, pid_show, pid_store);
static DEVICE_ATTR(remove_sysfs, 0220, NULL, remove_sensor_sysfs_store);
static DEVICE_ATTR(ssc_hw_rev, 0664, ssc_hw_rev_show, ssc_hw_rev_store);
static DEVICE_ATTR(ssr_msg, 0440, ssr_msg_show, NULL);
static DEVICE_ATTR(ssr_reset, 0440, ssr_reset_show, NULL);
#ifdef CONFIG_SUPPORT_SSC_MODE
static DEVICE_ATTR(ssc_mode, 0664, ssc_mode_show, ssc_mode_store);
#endif
static DEVICE_ATTR(sensor_dump, 0444, sensor_dump_show, NULL);
static DEVICE_ATTR(sbm_init, 0660, sbm_init_show, sbm_init_store);
static DEVICE_ATTR(ar_mode, 0220, NULL, ar_mode_store);
static struct device_attribute *core_attrs[] = {
&dev_attr_dumpstate,
&dev_attr_operation_mode,
&dev_attr_mode,
&dev_attr_ssc_pid,
&dev_attr_remove_sysfs,
&dev_attr_ssc_hw_rev,
&dev_attr_ssr_msg,
&dev_attr_ssr_reset,
&dev_attr_sensor_dump,
#ifdef CONFIG_SUPPORT_SSC_MODE
&dev_attr_ssc_mode,
#endif
&dev_attr_sbm_init,
&dev_attr_ar_mode,
NULL,
};
#ifdef CONFIG_SUPPORT_SSC_MODE
static int ssc_core_probe(struct platform_device *pdev)
{
#ifdef CONFIG_SUPPORT_SSC_MODE_FOR_MAG
struct device *dev = &pdev->dev;
#ifdef CONFIG_OF
if (dev->of_node) {
struct device_node *np = dev->of_node;
int check_mst_gpio, check_nfc_gpio;
int value_mst = 0, value_nfc = 0;
check_mst_gpio = of_get_named_gpio_flags(np, "ssc_core,mst_gpio", 0, NULL);
if(check_mst_gpio >= 0)
value_mst = gpio_get_value(check_mst_gpio);
if (value_mst == 1) {
ssc_mode = ANT_NFC_MST;
return 0;
}
check_nfc_gpio = of_get_named_gpio_flags(np, "ssc_core,nfc_gpio", 0, NULL);
if(check_nfc_gpio >= 0)
value_nfc = gpio_get_value(check_nfc_gpio);
if (value_nfc == 1) {
ssc_mode = ANT_NFC_ONLY;
return 0;
}
}
#endif
ssc_mode = ANT_DUMMY;
#endif
return 0;
}
#if defined(CONFIG_OF)
static const struct of_device_id ssc_core_dt_ids[] = {
{ .compatible = "ssc_core" },
{ },
};
MODULE_DEVICE_TABLE(of, ssc_core_dt_ids);
#endif /* CONFIG_OF */
static struct platform_driver ssc_core_driver = {
.probe = ssc_core_probe,
.driver = {
.name = "ssc_core",
.owner = THIS_MODULE,
#if defined(CONFIG_OF)
.of_match_table = ssc_core_dt_ids,
#endif /* CONFIG_OF */
}
};
#endif
static int __init core_factory_init(void)
{
adsp_factory_register(MSG_SSC_CORE, core_attrs);
#ifdef CONFIG_SUPPORT_SSC_MODE
platform_driver_register(&ssc_core_driver);
#endif
pdata_sdump = kzalloc(sizeof(*pdata_sdump), GFP_KERNEL);
if (pdata_sdump == NULL)
return -ENOMEM;
pdata_sdump->sdump_wq = create_singlethread_workqueue("sdump_wq");
if (pdata_sdump->sdump_wq == NULL) {
pr_err("[FACTORY]: %s - could not create sdump wq\n", __func__);
kfree(pdata_sdump);
return -ENOMEM;
}
INIT_WORK(&pdata_sdump->work_sdump, sensor_dump_work_func);
pr_info("[FACTORY] %s\n", __func__);
return 0;
}
static void __exit core_factory_exit(void)
{
adsp_factory_unregister(MSG_SSC_CORE);
#ifdef CONFIG_SUPPORT_SSC_MODE
platform_driver_unregister(&ssc_core_driver);
#endif
if (pdata_sdump->sdump_wq)
destroy_workqueue(pdata_sdump->sdump_wq);
if (pdata_sdump)
kfree(pdata_sdump);
pr_info("[FACTORY] %s\n", __func__);
}
module_init(core_factory_init);
module_exit(core_factory_exit);