// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2019 MediaTek Inc. */ #define pr_fmt(fmt) "[Msensor] " fmt #include #include "maghub.h" #include "mag.h" #include #include "SCP_power_monitor.h" #define MAGHUB_DEV_NAME "mag_hub" #define DRIVER_VERSION "1.0.1" struct maghub_ipi_data *mag_ipi_data; static struct mag_init_info maghub_init_info; static int maghub_init_flag = -1; static DEFINE_SPINLOCK(calibration_lock); enum { MAG_FUN_DEBUG = 0x01, MAG_MDATA_DEBUG = 0X02, MAG_ODATA_DEBUG = 0X04, MAG_CTR_DEBUG = 0X08, MAG_IPI_DEBUG = 0x10, }; struct maghub_ipi_data { int direction; int32_t dynamic_cali[MAGHUB_AXES_NUM]; int32_t parameter_cali[6]; atomic_t trace; atomic_t suspend; atomic_t scp_init_done; atomic_t first_ready_after_boot; atomic_t selftest_status; struct work_struct init_done_work; struct data_unit_t m_data_t; bool factory_enable; bool android_enable; struct sensorInfo_t mag_info; struct completion selftest_done; }; static int maghub_m_setPowerMode(bool enable) { int res = 0; pr_debug("magnetic enable value = %d\n", enable); res = sensor_enable_to_hub(ID_MAGNETIC, enable); if (res < 0) pr_err("%s is failed!!\n", __func__); return res; } static int maghub_GetMData(char *buf, int size) { struct maghub_ipi_data *obj = mag_ipi_data; struct data_unit_t data; uint64_t time_stamp = 0; int mag_m[MAGHUB_AXES_NUM]; int err = 0; unsigned int status = 0; if (atomic_read(&obj->suspend)) return -3; if (buf == NULL) return -1; err = sensor_get_data_from_hub(ID_MAGNETIC, &data); if (err < 0) { pr_err("sensor_get_data_from_hub fail!\n"); return err; } time_stamp = data.time_stamp; mag_m[MAGHUB_AXIS_X] = data.magnetic_t.x; mag_m[MAGHUB_AXIS_Y] = data.magnetic_t.y; mag_m[MAGHUB_AXIS_Z] = data.magnetic_t.z; status = data.magnetic_t.status; sprintf(buf, "%04x %04x %04x %04x", mag_m[MAGHUB_AXIS_X], mag_m[MAGHUB_AXIS_Y], mag_m[MAGHUB_AXIS_Z], status); if (atomic_read(&obj->trace) & MAG_MDATA_DEBUG) pr_debug("RAW DATA: %s!\n", buf); return 0; } static int maghub_ReadChipInfo(char *buf, int bufsize) { if ((!buf) || (bufsize <= MAGHUB_BUFSIZE - 1)) return -1; sprintf(buf, "maghub Chip"); return 0; } static ssize_t chipinfo_show(struct device_driver *ddri, char *buf) { char strbuf[MAGHUB_BUFSIZE] = {0}; maghub_ReadChipInfo(strbuf, MAGHUB_BUFSIZE); return snprintf(buf, PAGE_SIZE, "%s\n", strbuf); } static ssize_t sensordata_show(struct device_driver *ddri, char *buf) { char strbuf[MAGHUB_BUFSIZE] = {0}; maghub_m_setPowerMode(true); msleep(20); maghub_GetMData(strbuf, MAGHUB_BUFSIZE); return snprintf(buf, PAGE_SIZE, "%s\n", strbuf); } static ssize_t trace_show(struct device_driver *ddri, char *buf) { ssize_t res = 0; struct maghub_ipi_data *obj = mag_ipi_data; if (obj == NULL) { pr_err("maghub_ipi_data is null!!\n"); return 0; } res = snprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&obj->trace)); return res; } static ssize_t trace_store(struct device_driver *ddri, const char *buf, size_t count) { struct maghub_ipi_data *obj = mag_ipi_data; int trace = 0; int res = 0; if (obj == NULL) { pr_err("maghub_ipi_data is null!!\n"); return 0; } if (sscanf(buf, "0x%x", &trace) != 1) { pr_err("invalid content: '%s',length = %zu\n", buf, count); return count; } atomic_set(&obj->trace, trace); res = sensor_set_cmd_to_hub(ID_MAGNETIC, CUST_ACTION_SET_TRACE, &trace); if (res < 0) { pr_err("sensor_set_cmd_to_hub fail,(ID:%d),(action:%d)\n", ID_MAGNETIC, CUST_ACTION_SET_TRACE); return 0; } return count; } static ssize_t orientation_show(struct device_driver *ddri, char *buf) { ssize_t _tLength = 0; struct maghub_ipi_data *obj = mag_ipi_data; pr_debug("[%s] default direction: %d\n", __func__, obj->direction); _tLength = snprintf(buf, PAGE_SIZE, "default direction = %d\n", obj->direction); return _tLength; } static ssize_t orientation_store(struct device_driver *ddri, const char *buf, size_t tCount) { int _nDirection = 0, err = 0; struct maghub_ipi_data *obj = mag_ipi_data; int res = 0; if (obj == NULL) return 0; err = kstrtoint(buf, 10, &_nDirection); if (err != 0) { pr_debug("[%s] set direction: %d\n", __func__, _nDirection); return tCount; } obj->direction = _nDirection; res = sensor_set_cmd_to_hub(ID_MAGNETIC, CUST_ACTION_SET_DIRECTION, &_nDirection); if (res < 0) { pr_err("sensor_set_cmd_to_hub fail,(ID:%d),(action:%d)\n", ID_MAGNETIC, CUST_ACTION_SET_DIRECTION); return 0; } pr_debug("[%s] set direction: %d\n", __func__, _nDirection); return tCount; } static ssize_t regmap_show(struct device_driver *ddri, char *buf) { ssize_t _tLength = 0; return _tLength; } static DRIVER_ATTR_RO(chipinfo); static DRIVER_ATTR_RO(sensordata); static DRIVER_ATTR_RW(trace); static DRIVER_ATTR_RW(orientation); static DRIVER_ATTR_RO(regmap); static struct driver_attribute *maghub_attr_list[] = { &driver_attr_chipinfo, &driver_attr_sensordata, &driver_attr_trace, &driver_attr_orientation, &driver_attr_regmap, }; static int maghub_create_attr(struct device_driver *driver) { int idx = 0, err = 0; int num = (int)(ARRAY_SIZE(maghub_attr_list)); if (driver == NULL) return -EINVAL; for (idx = 0; idx < num; idx++) { err = driver_create_file(driver, maghub_attr_list[idx]); if (err) { pr_err("driver_create_file (%s) = %d\n", maghub_attr_list[idx]->attr.name, err); break; } } return err; } static int maghub_delete_attr(struct device_driver *driver) { int idx = 0, err = 0; int num = (int)(ARRAY_SIZE(maghub_attr_list)); if (driver == NULL) return -EINVAL; for (idx = 0; idx < num; idx++) driver_remove_file(driver, maghub_attr_list[idx]); return err; } static void scp_init_work_done(struct work_struct *work) { int32_t cfg_data[9] = {0}; struct maghub_ipi_data *obj = mag_ipi_data; int err = 0; struct mag_libinfo_t mag_libinfo; if (atomic_read(&obj->scp_init_done) == 0) { pr_err("scp is not ready to send cmd\n"); return; } if (atomic_xchg(&obj->first_ready_after_boot, 1) == 0) { err = sensor_set_cmd_to_hub(ID_MAGNETIC, CUST_ACTION_GET_SENSOR_INFO, &obj->mag_info); if (err < 0) { pr_err("set_cmd_to_hub fail, (ID: %d),(action: %d)\n", ID_MAGNETIC, CUST_ACTION_GET_SENSOR_INFO); return; } strlcpy(mag_libinfo.libname, obj->mag_info.mag_dev_info.libname, sizeof(mag_libinfo.libname)); mag_libinfo.layout = obj->mag_info.mag_dev_info.layout; mag_libinfo.deviceid = obj->mag_info.mag_dev_info.deviceid; err = mag_info_record(&mag_libinfo); return; } spin_lock(&calibration_lock); cfg_data[0] = obj->dynamic_cali[0]; cfg_data[1] = obj->dynamic_cali[1]; cfg_data[2] = obj->dynamic_cali[2]; cfg_data[3] = obj->parameter_cali[0]; cfg_data[4] = obj->parameter_cali[1]; cfg_data[5] = obj->parameter_cali[2]; cfg_data[6] = obj->parameter_cali[3]; cfg_data[7] = obj->parameter_cali[4]; cfg_data[8] = obj->parameter_cali[5]; spin_unlock(&calibration_lock); err = sensor_cfg_to_hub(ID_MAGNETIC, (uint8_t *)cfg_data, sizeof(cfg_data)); if (err < 0) pr_err("sensor_cfg_to_hub fail\n"); } static int mag_recv_data(struct data_unit_t *event, void *reserved) { int err = 0; struct mag_data data; struct maghub_ipi_data *obj = mag_ipi_data; data.x = event->magnetic_t.x; data.y = event->magnetic_t.y; data.z = event->magnetic_t.z; data.status = event->magnetic_t.status; data.timestamp = (int64_t)event->time_stamp; data.reserved[0] = event->reserve[0]; if (event->flush_action == DATA_ACTION && READ_ONCE(obj->android_enable) == true) err = mag_data_report(&data); else if (event->flush_action == FLUSH_ACTION) err = mag_flush_report(); else if (event->flush_action == BIAS_ACTION) { data.x = event->magnetic_t.x_bias; data.y = event->magnetic_t.y_bias; data.z = event->magnetic_t.z_bias; err = mag_bias_report(&data); spin_lock(&calibration_lock); obj->dynamic_cali[MAGHUB_AXIS_X] = event->magnetic_t.x_bias; obj->dynamic_cali[MAGHUB_AXIS_Y] = event->magnetic_t.y_bias; obj->dynamic_cali[MAGHUB_AXIS_Z] = event->magnetic_t.z_bias; spin_unlock(&calibration_lock); } else if (event->flush_action == TEST_ACTION) { atomic_set(&obj->selftest_status, event->magnetic_t.status); complete(&obj->selftest_done); } else if (event->flush_action == CALI_ACTION) { err = mag_cali_report(event->data); spin_lock(&calibration_lock); obj->parameter_cali[0] = event->data[0]; obj->parameter_cali[1] = event->data[1]; obj->parameter_cali[2] = event->data[2]; obj->parameter_cali[3] = event->data[3]; obj->parameter_cali[4] = event->data[4]; obj->parameter_cali[5] = event->data[5]; spin_unlock(&calibration_lock); } return err; } static int maghub_enable(int en) { int res = 0; struct maghub_ipi_data *obj = mag_ipi_data; if (en == true) WRITE_ONCE(obj->android_enable, true); else WRITE_ONCE(obj->android_enable, false); res = maghub_m_setPowerMode(en); if (res) pr_err("maghub_m_setPowerMode is failed!!\n"); return res; } static int maghub_set_delay(u64 ns) { #if defined CONFIG_MTK_SCP_SENSORHUB_V1 int delayms = 0, err = 0; struct maghub_ipi_data *obj = mag_ipi_data; delayms = (int)ns / 1000 / 1000; err = sensor_set_delay_to_hub(ID_MAGNETIC, delayms); if (err < 0) { pr_err("maghub_m_set_delay fail!\n"); return err; } pr_debug("maghub_m_set_delay (%d)\n", delayms); return err; #elif defined CONFIG_NANOHUB return 0; #else return 0; #endif } static int maghub_batch(int flag, int64_t samplingPeriodNs, int64_t maxBatchReportLatencyNs) { #if defined CONFIG_MTK_SCP_SENSORHUB_V1 maghub_set_delay(samplingPeriodNs); #endif return sensor_batch_to_hub(ID_MAGNETIC, flag, samplingPeriodNs, maxBatchReportLatencyNs); } static int maghub_flush(void) { return sensor_flush_to_hub(ID_MAGNETIC); } static int maghub_set_cali(uint8_t *data, uint8_t count) { int32_t *buf = (int32_t *)data; struct maghub_ipi_data *obj = mag_ipi_data; spin_lock(&calibration_lock); obj->dynamic_cali[0] = buf[0]; obj->dynamic_cali[1] = buf[1]; obj->dynamic_cali[2] = buf[2]; obj->parameter_cali[0] = buf[3]; obj->parameter_cali[1] = buf[4]; obj->parameter_cali[2] = buf[5]; obj->parameter_cali[3] = buf[6]; obj->parameter_cali[4] = buf[7]; obj->parameter_cali[5] = buf[8]; spin_unlock(&calibration_lock); return sensor_cfg_to_hub(ID_MAGNETIC, data, count); } static int maghub_open_report_data(int open) { return 0; } static int maghub_get_data(int *x, int *y, int *z, int *status) { char buff[MAGHUB_BUFSIZE] = { 0 }; int ret; ret = maghub_GetMData(buff, MAGHUB_BUFSIZE); if (ret < 0) { pr_err("maghub_GetMData fail, ret:%d\n", ret); return ret; } if (sscanf(buff, "%x %x %x %x", x, y, z, status) != 4) pr_err("maghub_m_get_data sscanf fail!!\n"); return 0; } static int scp_ready_event(uint8_t event, void *ptr) { struct maghub_ipi_data *obj = mag_ipi_data; switch (event) { case SENSOR_POWER_UP: atomic_set(&obj->scp_init_done, 1); schedule_work(&obj->init_done_work); break; case SENSOR_POWER_DOWN: atomic_set(&obj->scp_init_done, 0); break; } return 0; } static struct scp_power_monitor scp_ready_notifier = { .name = "mag", .notifier_call = scp_ready_event, }; static int maghub_factory_enable_sensor(bool enabledisable, int64_t sample_periods_ms) { int err = 0; struct maghub_ipi_data *obj = mag_ipi_data; if (enabledisable == true) WRITE_ONCE(obj->factory_enable, true); else WRITE_ONCE(obj->factory_enable, false); if (enabledisable == 1) { err = sensor_set_delay_to_hub(ID_MAGNETIC, sample_periods_ms); if (err < 0) { pr_err("sensor_set_delay_to_hub fail!\r\n"); return -1; } } err = sensor_enable_to_hub(ID_MAGNETIC, enabledisable == true ? 1 : 0); if (err < 0) { pr_err("sensor_enable_to_hub fail!\r\n"); return -1; } return 0; } static int maghub_factory_get_data(int32_t data[3], int *status) { int err = 0; /* get raw data */ err = maghub_get_data(&data[0], &data[1], &data[2], status); data[0] = data[0] / CONVERT_M_DIV; data[1] = data[1] / CONVERT_M_DIV; data[2] = data[2] / CONVERT_M_DIV; return err; } static int maghub_factory_get_raw_data(int32_t data[3]) { pr_debug("%s do not support!\n", __func__); return 0; } static int maghub_factory_enable_calibration(void) { return 0; } static int maghub_factory_clear_cali(void) { return 0; } static int maghub_factory_set_cali(int32_t data[3]) { return 0; } static int maghub_factory_get_cali(int32_t data[3]) { return 0; } static int maghub_factory_do_self_test(void) { int ret = 0; struct maghub_ipi_data *obj = mag_ipi_data; ret = sensor_selftest_to_hub(ID_MAGNETIC); if (ret < 0) return -1; ret = wait_for_completion_timeout(&obj->selftest_done, msecs_to_jiffies(3000)); if (!ret) return -1; return atomic_read(&obj->selftest_status); } static struct mag_factory_fops maghub_factory_fops = { .enable_sensor = maghub_factory_enable_sensor, .get_data = maghub_factory_get_data, .get_raw_data = maghub_factory_get_raw_data, .enable_calibration = maghub_factory_enable_calibration, .clear_cali = maghub_factory_clear_cali, .set_cali = maghub_factory_set_cali, .get_cali = maghub_factory_get_cali, .do_self_test = maghub_factory_do_self_test, }; static struct mag_factory_public maghub_factory_device = { .gain = 1, .sensitivity = 1, .fops = &maghub_factory_fops, }; static int maghub_probe(struct platform_device *pdev) { int err = 0; struct maghub_ipi_data *data; struct mag_control_path ctl = { 0 }; struct mag_data_path mag_data = { 0 }; struct platform_driver *paddr = maghub_init_info.platform_diver_addr; pr_debug("%s\n", __func__); data = kzalloc(sizeof(struct maghub_ipi_data), GFP_KERNEL); if (!data) { err = -ENOMEM; goto exit; } mag_ipi_data = data; atomic_set(&data->trace, 0); atomic_set(&data->selftest_status, 0); WRITE_ONCE(data->factory_enable, false); WRITE_ONCE(data->android_enable, false); init_completion(&data->selftest_done); platform_set_drvdata(pdev, data); err = scp_sensorHub_data_registration(ID_MAGNETIC, mag_recv_data); if (err < 0) { pr_err("scp_sensorHub_data_registration failed\n"); goto exit_kfree; } err = mag_factory_device_register(&maghub_factory_device); if (err) { pr_err("mag_factory_device_register register failed\n"); goto exit_kfree; } /* Register sysfs attribute */ err = maghub_create_attr(&paddr->driver); if (err) { pr_err("create attribute err = %d\n", err); goto exit_misc_device_register_failed; } ctl.is_use_common_factory = false; ctl.enable = maghub_enable; ctl.set_delay = maghub_set_delay; ctl.batch = maghub_batch; ctl.flush = maghub_flush; ctl.set_cali = maghub_set_cali; ctl.open_report_data = maghub_open_report_data; #if defined CONFIG_MTK_SCP_SENSORHUB_V1 ctl.is_report_input_direct = true; ctl.is_support_batch = false; #elif defined CONFIG_NANOHUB ctl.is_report_input_direct = true; ctl.is_support_batch = true; #else #endif err = mag_register_control_path(&ctl); if (err) { pr_err("register mag control path err\n"); goto create_attr_failed; } mag_data.div = CONVERT_M_DIV; mag_data.get_data = maghub_get_data; err = mag_register_data_path(&mag_data); if (err) { pr_err("register data control path err\n"); goto create_attr_failed; } pr_debug("%s: OK\n", __func__); maghub_init_flag = 1; /*Mointor scp ready notify, *need monitor at the end of probe for two function: * 1.read mag_info from sensorhub, * write to mag context * 2.set cali to sensorhub */ INIT_WORK(&data->init_done_work, scp_init_work_done); scp_power_monitor_register(&scp_ready_notifier); return 0; create_attr_failed: maghub_delete_attr(&(maghub_init_info.platform_diver_addr->driver)); exit_misc_device_register_failed: mag_factory_device_deregister(&maghub_factory_device); exit_kfree: kfree(data); exit: pr_err("%s: err = %d\n", __func__, err); maghub_init_flag = -1; return err; } /*----------------------------------------------------------------------------*/ static int maghub_remove(struct platform_device *pdev) { int err = 0; struct platform_driver *paddr = maghub_init_info.platform_diver_addr; err = maghub_delete_attr(&paddr->driver); if (err) pr_err("maghub_delete_attr fail: %d\n", err); kfree(platform_get_drvdata(pdev)); mag_factory_device_deregister(&maghub_factory_device); return 0; } static int maghub_suspend(struct platform_device *pdev, pm_message_t msg) { return 0; } static int maghub_resume(struct platform_device *pdev) { return 0; } static struct platform_device maghub_device = { .name = MAGHUB_DEV_NAME, .id = -1, }; static struct platform_driver maghub_driver = { .driver = { .name = MAGHUB_DEV_NAME, }, .probe = maghub_probe, .remove = maghub_remove, .suspend = maghub_suspend, .resume = maghub_resume, }; static int maghub_local_remove(void) { platform_driver_unregister(&maghub_driver); return 0; } static int maghub_local_init(void) { if (platform_driver_register(&maghub_driver)) { pr_err("add_driver error\n"); return -1; } if (-1 == maghub_init_flag) return -1; return 0; } static struct mag_init_info maghub_init_info = { .name = "maghub", .init = maghub_local_init, .uninit = maghub_local_remove, }; static int __init maghub_init(void) { if (platform_device_register(&maghub_device)) { pr_err("platform device error\n"); return -1; } mag_driver_add(&maghub_init_info); return 0; } static void __exit maghub_exit(void) { pr_debug("%s\n", __func__); } module_init(maghub_init); module_exit(maghub_exit); MODULE_AUTHOR("hongxu.zhao@mediatek.com"); MODULE_DESCRIPTION("MAGHUB compass driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRIVER_VERSION);