// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #define pr_fmt(fmt) "[AKM09918] " fmt #include "cust_mag.h" #include "akm09918.h" #include "mag.h" #define DEBUG 0 #define AKM09918_DEV_NAME "akm09918" #define DRIVER_VERSION "1.0.1" #define AKM09918_RETRY_COUNT 10 #define AKM09918_DEFAULT_DELAY 100 static DECLARE_WAIT_QUEUE_HEAD(open_wq); #define AKM_CONTINUOUS 1 #if AKM_CONTINUOUS #define AKM_CONTINUOUS_MODE #endif #define DRIVER_ATTR(_name, _mode, _show, _store) \ struct driver_attribute driver_attr_##_name = \ __ATTR(_name, _mode, _show, _store) static short akmd_delay = AKM09918_DEFAULT_DELAY; static int factory_mode; static int akm09918_init_flag; static struct i2c_client *this_client; static int8_t akm_device; static uint8_t akm_fuse[3] = {0}; /*----------------------------------------------------------------------------*/ static const struct i2c_device_id akm09918_i2c_id[] = { {AKM09918_DEV_NAME, 0}, {} }; /* Maintain cust info here */ struct mag_hw mag_cust; static struct mag_hw *hw = &mag_cust; /* For driver get cust info */ struct mag_hw *get_cust_mag(void) { return &mag_cust; } /*----------------------------------------------------------------------------*/ static int akm09918_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id); static int akm09918_i2c_remove(struct i2c_client *client); static int akm09918_i2c_detect(struct i2c_client *client, struct i2c_board_info *info); static int akm09918_suspend(struct device *dev); static int akm09918_resume(struct device *dev); static int akm09918_local_init(void); static int akm09918_remove(void); static int akm09918_flush(void); static struct mag_init_info akm09918_init_info = { .name = "akm09918", .init = akm09918_local_init, .uninit = akm09918_remove, }; /*----------------------------------------------------------------------------*/ enum { AMK_FUN_DEBUG = 0x01, AMK_DATA_DEBUG = 0X02, AMK_HWM_DEBUG = 0X04, AMK_CTR_DEBUG = 0X08, AMK_I2C_DEBUG = 0x10, } AMK_TRC; /*----------------------------------------------------------------------------*/ struct akm09918_i2c_data { struct i2c_client *client; struct mag_hw *hw; atomic_t layout; atomic_t trace; struct hwmsen_convert cvt; bool flush; bool enable; }; /*----------------------------------------------------------------------------*/ #ifdef CONFIG_OF static const struct of_device_id mag_of_match[] = { {.compatible = "mediatek,msensor"}, {}, }; #endif #ifdef CONFIG_PM_SLEEP static const struct dev_pm_ops akm09918_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(akm09918_suspend, akm09918_resume) }; #endif static struct i2c_driver akm09918_i2c_driver = { .driver = { .name = AKM09918_DEV_NAME, #ifdef CONFIG_PM_SLEEP .pm = &akm09918_pm_ops, #endif #ifdef CONFIG_OF .of_match_table = mag_of_match, #endif }, .probe = akm09918_i2c_probe, .remove = akm09918_i2c_remove, .detect = akm09918_i2c_detect, .id_table = akm09918_i2c_id, }; /* akm_map value rang is 1-8, so akm_map[0] is invalid*/ static struct hwmsen_convert akm_map[] = { { { 0, 0, 0}, {0, 0, 0} }, { { 1, 1, 1}, {0, 1, 2} }, { { 1, -1, 1}, {1, 0, 2} }, { {-1, -1, 1}, {0, 1, 2} }, { {-1, 1, 1}, {1, 0, 2} }, { {-1, 1, -1}, {0, 1, 2} }, { { 1, 1, -1}, {1, 0, 2} }, { { 1, -1, -1}, {0, 1, 2} }, { {-1, -1, -1}, {1, 0, 2} }, }; /*----------------------------------------------------------------------------*/ static atomic_t dev_open_count; /*----------------------------------------------------------------------------*/ static DEFINE_MUTEX(akm09918_i2c_mutex); #ifndef CONFIG_MTK_I2C_EXTENSION static int mag_i2c_read_block(struct i2c_client *client, u8 addr, u8 *data, u8 len) { int err = 0; u8 beg = addr; struct i2c_msg msgs[2] = { {0}, {0} }; if (!client) { return -EINVAL; } else if (len > C_I2C_FIFO_SIZE) { pr_err(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE); return -EINVAL; } mutex_lock(&akm09918_i2c_mutex); msgs[0].addr = client->addr; msgs[0].flags = 0; msgs[0].len = 1; msgs[0].buf = &beg; msgs[1].addr = client->addr; msgs[1].flags = I2C_M_RD; msgs[1].len = len; msgs[1].buf = data; err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (err != 2) { pr_err_ratelimited("i2c_transfer error: (%d %p %d) %d\n", addr, data, len, err); err = -EIO; } else { err = 0; } mutex_unlock(&akm09918_i2c_mutex); return err; } static int mag_i2c_write_block(struct i2c_client *client, u8 addr, u8 *data, u8 len) { /*address also occupies one byte, the maximum length for write is 7 bytes */ int err = 0, num = 0; char buf[C_I2C_FIFO_SIZE]; unsigned int idx = 0; err = 0; mutex_lock(&akm09918_i2c_mutex); if (!client) { mutex_unlock(&akm09918_i2c_mutex); return -EINVAL; } else if (len >= C_I2C_FIFO_SIZE) { mutex_unlock(&akm09918_i2c_mutex); pr_err(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE); return -EINVAL; } num = 0; buf[num++] = addr; for (idx = 0; idx < len; idx++) buf[num++] = data[idx]; err = i2c_master_send(client, buf, num); if (err < 0) { mutex_unlock(&akm09918_i2c_mutex); pr_err_ratelimited("send command error!!\n"); return -EFAULT; } mutex_unlock(&akm09918_i2c_mutex); return err; } #endif static void akm09918_power(struct mag_hw *hw, unsigned int on) { } static long AKI2C_RxData(char *rxData, int length) { #ifndef CONFIG_MTK_I2C_EXTENSION struct i2c_client *client = this_client; int res = 0; char addr; if ((rxData == NULL) || (length < 1)) return -EINVAL; addr = rxData[0]; res = mag_i2c_read_block(client, addr, rxData, length); if (res < 0) return -1; return 0; #else uint8_t loop_i = 0; #if DEBUG int i = 0; struct i2c_client *client = this_client; struct akm09918_i2c_data *data = i2c_get_clientdata(client); char addr = rxData[0]; #endif /* Caller should check parameter validity. */ if ((rxData == NULL) || (length < 1)) return -EINVAL; mutex_lock(&akm09918_i2c_mutex); for (loop_i = 0; loop_i < AKM09918_RETRY_COUNT; loop_i++) { this_client->addr = this_client->addr & I2C_MASK_FLAG; this_client->addr = this_client->addr | I2C_WR_FLAG; if (i2c_master_send(this_client, (const char *)rxData, ((length << 0X08) | 0X01))) break; mdelay(10); } if (loop_i >= AKM09918_RETRY_COUNT) { mutex_unlock(&akm09918_i2c_mutex); pr_err("%s retry over %d\n", __func__, AKM09918_RETRY_COUNT); return -EIO; } mutex_unlock(&akm09918_i2c_mutex); #if DEBUG if (atomic_read(&data->trace) & AMK_I2C_DEBUG) { pr_debug("RxData: len=%02x, addr=%02x\n data=", length, addr); for (i = 0; i < length; i++) pr_debug(" %02x", rxData[i]); pr_debug("\n"); } #endif return 0; #endif } static long AKI2C_TxData(char *txData, int length) { #ifndef CONFIG_MTK_I2C_EXTENSION struct i2c_client *client = this_client; int res = 0; char addr; u8 *buff; if ((txData == NULL) || (length < 2)) return -EINVAL; addr = txData[0]; buff = &txData[1]; res = mag_i2c_write_block(client, addr, buff, (length - 1)); if (res < 0) return -1; return 0; #else uint8_t loop_i = 0; #if DEBUG int i = 0; struct i2c_client *client = this_client; struct akm09918_i2c_data *data = i2c_get_clientdata(client); #endif /* Caller should check parameter validity. */ if ((txData == NULL) || (length < 2)) return -EINVAL; mutex_lock(&akm09918_i2c_mutex); this_client->addr = this_client->addr & I2C_MASK_FLAG; for (loop_i = 0; loop_i < AKM09918_RETRY_COUNT; loop_i++) { if (i2c_master_send(this_client, (const char *)txData, length) > 0) break; mdelay(10); } if (loop_i >= AKM09918_RETRY_COUNT) { mutex_unlock(&akm09918_i2c_mutex); pr_err("%s retry over %d\n", __func__, AKM09918_RETRY_COUNT); return -EIO; } mutex_unlock(&akm09918_i2c_mutex); #if DEBUG if (atomic_read(&data->trace) & AMK_I2C_DEBUG) { pr_debug("TxData: len=%02x, addr=%02x\n data=", length, txData[0]); for (i = 0; i < (length - 1); i++) pr_debug(" %02x", txData[i + 1]); pr_debug("\n"); } #endif return 0; #endif } static long AKECS_SetMode_SngMeasure(void) { char buffer[2]; /* Set measure mode */ buffer[0] = AKM_REG_MODE; buffer[1] = AKM_MODE_SNG_MEASURE; /* Set data */ return AKI2C_TxData(buffer, 2); } #ifdef AKM_CONTINUOUS_MODE static long AKECS_SetMode_CntMeasure(char mode) { char buffer[2]; /* Set measure mode */ buffer[0] = AKM_REG_MODE; buffer[1] = mode; /* Set data */ return AKI2C_TxData(buffer, 2); } #endif static long AKECS_SetMode_SelfTest(void) { char buffer[2]; /* Set selftest mode */ buffer[0] = AKM_REG_MODE; buffer[1] = AKM_MODE_SELF_TEST; /* Set data */ return AKI2C_TxData(buffer, 2); } static long AKECS_SetMode_FUSEAccess(void) { char buffer[2]; /* Set FuseROM Access mode */ buffer[0] = AKM_REG_MODE; buffer[1] = AKM_MODE_FUSE_ACCESS; /* Set data */ return AKI2C_TxData(buffer, 2); } static int AKECS_SetMode_PowerDown(void) { char buffer[2]; /* Set powerdown mode */ buffer[0] = AKM_REG_MODE; buffer[1] = AKM_MODE_POWERDOWN; /* Set data */ return AKI2C_TxData(buffer, 2); } static long AKECS_Reset(int hard) { unsigned char buffer[2]; long err = 0; if (hard != 0) { /*TODO change to board setting */ /* gpio_set_value(akm->rstn, 0); */ udelay(5); /* gpio_set_value(akm->rstn, 1); */ } else { /* Set reset mode */ buffer[0] = AKM_REG_RESET; buffer[1] = 0x01; err = AKI2C_TxData(buffer, 2); if (err < 0) pr_debug("%s: Can not set SRST bit.\n", __func__); else pr_debug("Soft reset is done.\n"); } /* Device will be accessible 300 us after */ udelay(300); /* 100 */ return err; } static long AKECS_SetMode(char mode) { long ret; switch (mode & 0x1F) { case AKM_MODE_SNG_MEASURE: ret = AKECS_SetMode_SngMeasure(); break; case AKM_MODE_SELF_TEST: ret = AKECS_SetMode_SelfTest(); break; case AKM_MODE_FUSE_ACCESS: ret = AKECS_SetMode_FUSEAccess(); break; case AKM_MODE_POWERDOWN: ret = AKECS_SetMode_PowerDown(); break; default: pr_debug("%s: Unknown mode(%d)\n", __func__, mode); return -EINVAL; } /* wait at least 100us after changing mode */ udelay(100); return ret; } static int AKECS_ReadFuse(void) { int ret = 0; ret = AKECS_SetMode_FUSEAccess(); if (ret < 0) { pr_debug("AKM set read fuse mode fail ret:%d\n", ret); return ret; } akm_fuse[0] = AK0991X_FUSE_ASAX; ret = AKI2C_RxData(akm_fuse, 3); if (ret < 0) { pr_debug("AKM read fuse fail ret:%d\n", ret); return ret; } ret = AKECS_SetMode_PowerDown(); return ret; } static int AKECS_CheckDevice(void) { char buffer[2]; int ret; /* Read WIA1 */ buffer[0] = AK0991X_REG_WIA1; /* Read data */ ret = AKI2C_RxData(buffer, 2); if (ret < 0) return ret; /* Check read data */ if (buffer[0] != 0x48) return -ENXIO; akm_device = buffer[1]; if ((akm_device == 0x05) || (akm_device == 0x04)) { ret = AKECS_ReadFuse(); if (ret < 0) { pr_err("%s AKM09918: read fuse fail\n", __func__); return -ENXIO; } } else if ((akm_device == 0x10) || (akm_device == 0x09) || (akm_device == 0x0b) || (akm_device == 0x0c)) { akm_fuse[0] = 0x80; akm_fuse[1] = 0x80; akm_fuse[2] = 0x80; } return 0; } static int AKECS_AxisInfoToPat( const uint8_t axis_order[3], const uint8_t axis_sign[3], int16_t *pat) { /* check invalid input */ if ((axis_order[0] < 0) || (axis_order[0] > 2) || (axis_order[1] < 0) || (axis_order[1] > 2) || (axis_order[2] < 0) || (axis_order[2] > 2) || (axis_sign[0] < 0) || (axis_sign[0] > 1) || (axis_sign[1] < 0) || (axis_sign[1] > 1) || (axis_sign[2] < 0) || (axis_sign[2] > 1) || ((axis_order[0] * axis_order[1] * axis_order[2]) != 0) || ((axis_order[0] + axis_order[1] + axis_order[2]) != 3)) { *pat = 0; return -1; } /* calculate pat * BIT MAP * [8] = sign_x * [7] = sign_y * [6] = sign_z * [5:4] = order_x * [3:2] = order_y * [1:0] = order_z */ *pat = ((int16_t)axis_sign[0] << 8); *pat += ((int16_t)axis_sign[1] << 7); *pat += ((int16_t)axis_sign[2] << 6); *pat += ((int16_t)axis_order[0] << 4); *pat += ((int16_t)axis_order[1] << 2); *pat += ((int16_t)axis_order[2] << 0); return 0; } static int16_t AKECS_SetCert(void) { struct i2c_client *client = this_client; struct akm09918_i2c_data *data = i2c_get_clientdata(client); uint8_t axis_sign[3] = {0}; uint8_t axis_order[3] = {0}; int16_t ret = 0; int i = 0; int16_t cert = 0x06; for (i = 0; i < 3; i++) axis_order[i] = (uint8_t)data->cvt.map[i]; for (i = 0; i < 3; i++) { if (data->cvt.sign[i] > 0) axis_sign[i] = 0; else if (data->cvt.sign[i] < 0) axis_sign[i] = 1; } #if 0 axis_order[0] = 0; axis_order[1] = 1; axis_order[2] = 2; axis_sign[0] = 0; axis_sign[1] = 0; axis_sign[2] = 0; #endif ret = AKECS_AxisInfoToPat(axis_order, axis_sign, &cert); if (ret != 0) return 0; return cert; } /* M-sensor daemon application have set the sng mode */ static long AKECS_GetData(char *rbuf, int size) { char temp; int loop_i, ret; #if DEBUG struct i2c_client *client = this_client; struct akm09918_i2c_data *data = i2c_get_clientdata(client); #endif if (size < SENSOR_DATA_SIZE) { pr_err("buff size is too small %d!\n", size); return -1; } memset(rbuf, 0, SENSOR_DATA_SIZE); rbuf[0] = AK0991X_REG_ST1; for (loop_i = 0; loop_i < AKM09918_RETRY_COUNT; loop_i++) { ret = AKI2C_RxData(rbuf, 1); if (ret) { pr_err_ratelimited("read ST1 resigster failed!\n"); return -1; } if ((rbuf[0] & 0x01) == 0x01) break; mdelay(2); rbuf[0] = AK0991X_REG_ST1; } if (loop_i >= AKM09918_RETRY_COUNT) { pr_err("Data read retry larger the max count!\n"); if (factory_mode == 0) /* if return we can not get data at factory mode */ return -1; } temp = rbuf[0]; rbuf[1] = AK0991X_REG_HXL; ret = AKI2C_RxData(&rbuf[1], SENSOR_DATA_SIZE - 1); if (ret < 0) { pr_err_ratelimited("AKM8975 akm8975_work_func: I2C failed\n"); return -1; } rbuf[0] = temp; return 0; } static int AKECS_GetConvert(int direction, struct hwmsen_convert *cvt) { if (!cvt) return -EINVAL; else if ((direction > 8) || (direction <= 0)) return -EINVAL; *cvt = akm_map[direction]; return 0; } /*----------------------------------------------------------------------------*/ static int akm09918_ReadChipInfo(char *buf, int bufsize) { if ((!buf) || (bufsize <= AKM09918_BUFSIZE - 1)) return -1; if (!this_client) { *buf = 0; return -2; } sprintf(buf, "akm09918 Chip"); return 0; } /*----------------------------shipment test----------------------*/ /*! *@return If @a testdata is in the range of between @a lolimit and @a hilimit, *the return value is 1, otherwise -1. *@param[in] testno A pointer to a text string. *@param[in] testname A pointer to a text string. *@param[in] testdata A data to be tested. *@param[in] lolimit The maximum allowable value of @a testdata. *@param[in] hilimit The minimum allowable value of @a testdata. *@param[in,out] pf_total */ int TEST_DATA(const char testno[], const char testname[], const int testdata, const int lolimit, const int hilimit, int *pf_total) { int pf; /* Pass;1, Fail;-1 */ if ((testno == NULL) && (strncmp(testname, "START", 5) == 0)) { pr_debug(" Test Name Fail Test Data [Low High]\n"); pf = 1; } else if ((testno == NULL) && (strncmp(testname, "END", 3) == 0)) { if (*pf_total == 1) pr_debug("Factory shipment test was passed.\n\n"); else pr_debug("Factory shipment test was failed.\n\n"); pf = 1; } else { if ((lolimit <= testdata) && (testdata <= hilimit)) pf = 1; else pf = -1; /* display result */ pr_debug(" %7s %-10s %c %9d [%9d %9d]\n", testno, testname, ((pf == 1) ? ('.') : ('F')), testdata, lolimit, hilimit); } /* Pass/Fail check */ if (*pf_total != 0) { if ((*pf_total == 1) && (pf == 1)) *pf_total = 1; /* Pass */ else *pf_total = -1; /* Fail */ } return pf; } /*! *Execute "Onboard Function Test" (NOT includes "START" and "END" command). *@retval 1 The test is passed successfully. *@retval -1 The test is failed. *@retval 0 The test is aborted by kind of system error. */ int FST_AK09911(void) { int pf_total; /* p/f flag for this subtest */ char i2cData[16]; int hdata[3]; int asax; int asay; int asaz; /* *********************************************** */ /* Reset Test Result */ /* *********************************************** */ pf_total = 1; /* *********************************************** */ /* Step1 */ /* *********************************************** */ /* Reset device. */ if (AKECS_Reset(0) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* Read values from WIA. */ i2cData[0] = AK0991X_REG_WIA1; if (AKI2C_RxData(i2cData, 2) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* TEST */ TEST_DATA(TLIMIT_NO_RST_WIA1_09911, TLIMIT_TN_RST_WIA1_09911, (int)i2cData[0], TLIMIT_LO_RST_WIA1_09911, TLIMIT_HI_RST_WIA1_09911, &pf_total); TEST_DATA(TLIMIT_NO_RST_WIA2_09911, TLIMIT_TN_RST_WIA2_09911, (int)i2cData[1], TLIMIT_LO_RST_WIA2_09911, TLIMIT_HI_RST_WIA2_09911, &pf_total); /* Set to FUSE ROM access mode */ if (AKECS_SetMode(AKM_MODE_FUSE_ACCESS) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* Read values from ASAX to ASAZ */ i2cData[0] = AK0991X_FUSE_ASAX; if (AKI2C_RxData(i2cData, 3) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } asax = (int)i2cData[0]; asay = (int)i2cData[1]; asaz = (int)i2cData[2]; /* TEST */ TEST_DATA(TLIMIT_NO_ASAX_09911, TLIMIT_TN_ASAX_09911, asax, TLIMIT_LO_ASAX_09911, TLIMIT_HI_ASAX_09911, &pf_total); TEST_DATA(TLIMIT_NO_ASAY_09911, TLIMIT_TN_ASAY_09911, asay, TLIMIT_LO_ASAY_09911, TLIMIT_HI_ASAY_09911, &pf_total); TEST_DATA(TLIMIT_NO_ASAZ_09911, TLIMIT_TN_ASAZ_09911, asaz, TLIMIT_LO_ASAZ_09911, TLIMIT_HI_ASAZ_09911, &pf_total); /* Set to PowerDown mode */ if (AKECS_SetMode(AKM_MODE_POWERDOWN) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* *********************************************** */ /* Step2 */ /* *********************************************** */ /* Set to SNG measurement pattern (Set CNTL register) */ if (AKECS_SetMode(AKM_MODE_SNG_MEASURE) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* Wait for DRDY pin changes to HIGH. */ /* usleep(AKM_MEASURE_TIME_US); */ /* Get measurement data from AK09911 */ /* ST1 + (HXL + HXH) + (HYL + HYH) + (HZL + HZH) + TEMP + ST2 */ /* = 1 + (1 + 1) + (1 + 1) + (1 + 1) + 1 + 1 = 9yte */ /* if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) { */ if (AKECS_GetData(i2cData, SENSOR_DATA_SIZE) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* hdata[0] = (int)((((uint)(i2cData[2]))<<8)+(uint)(i2cData[1])); */ /* hdata[1] = (int)((((uint)(i2cData[4]))<<8)+(uint)(i2cData[3])); */ /* hdata[2] = (int)((((uint)(i2cData[6]))<<8)+(uint)(i2cData[5])); */ hdata[0] = (int16_t) (i2cData[1] | (i2cData[2] << 8)); hdata[1] = (int16_t) (i2cData[3] | (i2cData[4] << 8)); hdata[2] = (int16_t) (i2cData[5] | (i2cData[6] << 8)); /* TEST */ i2cData[0] &= 0x7F; TEST_DATA(TLIMIT_NO_SNG_ST1_09911, TLIMIT_TN_SNG_ST1_09911, (int)i2cData[0], TLIMIT_LO_SNG_ST1_09911, TLIMIT_HI_SNG_ST1_09911, &pf_total); /* TEST */ TEST_DATA(TLIMIT_NO_SNG_HX_09911, TLIMIT_TN_SNG_HX_09911, hdata[0], TLIMIT_LO_SNG_HX_09911, TLIMIT_HI_SNG_HX_09911, &pf_total); TEST_DATA(TLIMIT_NO_SNG_HY_09911, TLIMIT_TN_SNG_HY_09911, hdata[1], TLIMIT_LO_SNG_HY_09911, TLIMIT_HI_SNG_HY_09911, &pf_total); TEST_DATA(TLIMIT_NO_SNG_HZ_09911, TLIMIT_TN_SNG_HZ_09911, hdata[2], TLIMIT_LO_SNG_HZ_09911, TLIMIT_HI_SNG_HZ_09911, &pf_total); TEST_DATA(TLIMIT_NO_SNG_ST2_09911, TLIMIT_TN_SNG_ST2_09911, (int)i2cData[8], TLIMIT_LO_SNG_ST2_09911, TLIMIT_HI_SNG_ST2_09911, &pf_total); /* Set to Self-test mode (Set CNTL register) */ if (AKECS_SetMode(AKM_MODE_SELF_TEST) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* Wait for DRDY pin changes to HIGH. */ /* usleep(AKM_MEASURE_TIME_US); */ /* Get measurement data from AK09911 */ /* ST1 + (HXL + HXH) + (HYL + HYH) + (HZL + HZH) + TEMP + ST2 */ /* = 1 + (1 + 1) + (1 + 1) + (1 + 1) + 1 + 1 = 9byte */ /* if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) { */ if (AKECS_GetData(i2cData, SENSOR_DATA_SIZE) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* TEST */ i2cData[0] &= 0x7F; TEST_DATA(TLIMIT_NO_SLF_ST1_09911, TLIMIT_TN_SLF_ST1_09911, (int)i2cData[0], TLIMIT_LO_SLF_ST1_09911, TLIMIT_HI_SLF_ST1_09911, &pf_total); /* hdata[0] = (int)((((uint)(i2cData[2]))<<8)+(uint)(i2cData[1])); */ /* hdata[1] = (int)((((uint)(i2cData[4]))<<8)+(uint)(i2cData[3])); */ /* hdata[2] = (int)((((uint)(i2cData[6]))<<8)+(uint)(i2cData[5])); */ hdata[0] = (int16_t) (i2cData[1] | (i2cData[2] << 8)); hdata[1] = (int16_t) (i2cData[3] | (i2cData[4] << 8)); hdata[2] = (int16_t) (i2cData[5] | (i2cData[6] << 8)); /* TEST */ TEST_DATA(TLIMIT_NO_SLF_RVHX_09911, TLIMIT_TN_SLF_RVHX_09911, (hdata[0]) * (asax / 128 + 1), TLIMIT_LO_SLF_RVHX_09911, TLIMIT_HI_SLF_RVHX_09911, &pf_total); TEST_DATA(TLIMIT_NO_SLF_RVHY_09911, TLIMIT_TN_SLF_RVHY_09911, (hdata[1]) * (asay / 128 + 1), TLIMIT_LO_SLF_RVHY_09911, TLIMIT_HI_SLF_RVHY_09911, &pf_total); TEST_DATA(TLIMIT_NO_SLF_RVHZ_09911, TLIMIT_TN_SLF_RVHZ_09911, (hdata[2]) * (asaz / 128 + 1), TLIMIT_LO_SLF_RVHZ_09911, TLIMIT_HI_SLF_RVHZ_09911, &pf_total); TEST_DATA(TLIMIT_NO_SLF_ST2_09911, TLIMIT_TN_SLF_ST2_09911, (int)i2cData[8], TLIMIT_LO_SLF_ST2_09911, TLIMIT_HI_SLF_ST2_09911, &pf_total); return pf_total; } int FST_AK09916(void) { int pf_total; //p/f flag for this subtest char i2cData[16]; int hdata[3]; // Reset Test Result pf_total = 1; // Step1 // Reset device. if (AKECS_Reset(0) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } // Read values from WIA. i2cData[0] = AK0991X_REG_WIA1; if (AKI2C_RxData(i2cData, 2) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* TEST */ TEST_DATA(TLIMIT_NO_RST_WIA1_09916, TLIMIT_TN_RST_WIA1_09916, (int)i2cData[0], TLIMIT_LO_RST_WIA1_09916, TLIMIT_HI_RST_WIA1_09916, &pf_total); TEST_DATA(TLIMIT_NO_RST_WIA2_09916, TLIMIT_TN_RST_WIA2_09916, (int)i2cData[1], TLIMIT_LO_RST_WIA2_09916, TLIMIT_HI_RST_WIA2_09916, &pf_total); /* Set to PowerDown mode */ if (AKECS_SetMode(AKM_MODE_POWERDOWN) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* *********************************************** */ /* Step2 */ /* *********************************************** */ /* Set to SNG measurement pattern (Set CNTL register) */ if (AKECS_SetMode(AKM_MODE_SNG_MEASURE) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* Wait for DRDY pin changes to HIGH. */ /* usleep(AKM_MEASURE_TIME_US); */ /* Get measurement data from AK09916 */ /* ST1 + (HXL + HXH) + (HYL + HYH) + (HZL + HZH) + TEMP + ST2 */ /* = 1 + (1 + 1) + (1 + 1) + (1 + 1) + 1 + 1 = 9yte */ /* if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) { */ if (AKECS_GetData(i2cData, SENSOR_DATA_SIZE) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } hdata[0] = (int16_t) (i2cData[1] | (i2cData[2] << 8)); hdata[1] = (int16_t) (i2cData[3] | (i2cData[4] << 8)); hdata[2] = (int16_t) (i2cData[5] | (i2cData[6] << 8)); /* TEST */ TEST_DATA(TLIMIT_NO_SNG_ST1_09916, TLIMIT_TN_SNG_ST1_09916, (int)i2cData[0], TLIMIT_LO_SNG_ST1_09916, TLIMIT_HI_SNG_ST1_09916, &pf_total); /* TEST */ TEST_DATA(TLIMIT_NO_SNG_HX_09916, TLIMIT_TN_SNG_HX_09916, hdata[0], TLIMIT_LO_SNG_HX_09916, TLIMIT_HI_SNG_HX_09916, &pf_total); TEST_DATA(TLIMIT_NO_SNG_HY_09916, TLIMIT_TN_SNG_HY_09916, hdata[1], TLIMIT_LO_SNG_HY_09916, TLIMIT_HI_SNG_HY_09916, &pf_total); TEST_DATA(TLIMIT_NO_SNG_HZ_09916, TLIMIT_TN_SNG_HZ_09916, hdata[2], TLIMIT_LO_SNG_HZ_09916, TLIMIT_HI_SNG_HZ_09916, &pf_total); TEST_DATA(TLIMIT_NO_SNG_ST2_09916, TLIMIT_TN_SNG_ST2_09916, (int)i2cData[8] & TLIMIT_ST2_MASK_09916, TLIMIT_LO_SNG_ST2_09916, TLIMIT_HI_SNG_ST2_09916, &pf_total); /* Set to Self-test mode (Set CNTL register) */ if (AKECS_SetMode(AKM_MODE_SELF_TEST) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* Wait for DRDY pin changes to HIGH. */ /* usleep(AKM_MEASURE_TIME_US); */ /* Get measurement data from AK09916 */ /* ST1 + (HXL + HXH) + (HYL + HYH) + (HZL + HZH) + TEMP + ST2 */ /* = 1 + (1 + 1) + (1 + 1) + (1 + 1) + 1 + 1 = 9byte */ /* if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) { */ if (AKECS_GetData(i2cData, SENSOR_DATA_SIZE) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* TEST */ TEST_DATA(TLIMIT_NO_SLF_ST1_09916, TLIMIT_TN_SLF_ST1_09916, (int)i2cData[0], TLIMIT_LO_SLF_ST1_09916, TLIMIT_HI_SLF_ST1_09916, &pf_total); hdata[0] = (int16_t) (i2cData[1] | (i2cData[2] << 8)); hdata[1] = (int16_t) (i2cData[3] | (i2cData[4] << 8)); hdata[2] = (int16_t) (i2cData[5] | (i2cData[6] << 8)); /* TEST */ TEST_DATA(TLIMIT_NO_SLF_RVHX_09916, TLIMIT_TN_SLF_RVHX_09916, hdata[0], TLIMIT_LO_SLF_RVHX_09916, TLIMIT_HI_SLF_RVHX_09916, &pf_total); TEST_DATA(TLIMIT_NO_SLF_RVHY_09916, TLIMIT_TN_SLF_RVHY_09916, hdata[1], TLIMIT_LO_SLF_RVHY_09916, TLIMIT_HI_SLF_RVHY_09916, &pf_total); TEST_DATA(TLIMIT_NO_SLF_RVHZ_09916, TLIMIT_TN_SLF_RVHZ_09916, hdata[2], TLIMIT_LO_SLF_RVHZ_09916, TLIMIT_HI_SLF_RVHZ_09916, &pf_total); TEST_DATA(TLIMIT_NO_SLF_ST2_09916, TLIMIT_TN_SLF_ST2_09916, (int)i2cData[8] & TLIMIT_ST2_MASK_09916, TLIMIT_LO_SLF_ST2_09916, TLIMIT_HI_SLF_ST2_09916, &pf_total); return pf_total; } int FST_AK09918(void) { int pf_total; //p/f flag for this subtest char i2cData[16]; int hdata[3]; //Reset Test Result pf_total = 1; //Step1 // Reset device. if (AKECS_Reset(0) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } // Read values from WIA. i2cData[0] = AK0991X_REG_WIA1; if (AKI2C_RxData(i2cData, 2) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* TEST */ TEST_DATA(TLIMIT_NO_RST_WIA1_09918, TLIMIT_TN_RST_WIA1_09918, (int)i2cData[0], TLIMIT_LO_RST_WIA1_09918, TLIMIT_HI_RST_WIA1_09918, &pf_total); TEST_DATA(TLIMIT_NO_RST_WIA2_09918, TLIMIT_TN_RST_WIA2_09918, (int)i2cData[1], TLIMIT_LO_RST_WIA2_09918, TLIMIT_HI_RST_WIA2_09918, &pf_total); /* Set to PowerDown mode */ if (AKECS_SetMode(AKM_MODE_POWERDOWN) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* *********************************************** */ /* Step2 */ /* *********************************************** */ /* Set to SNG measurement pattern (Set CNTL register) */ if (AKECS_SetMode(AKM_MODE_SNG_MEASURE) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* Wait for DRDY pin changes to HIGH. */ /* usleep(AKM_MEASURE_TIME_US); */ /* Get measurement data from AK09918 */ /* ST1 + (HXL + HXH) + (HYL + HYH) + (HZL + HZH) + TEMP + ST2 */ /* = 1 + (1 + 1) + (1 + 1) + (1 + 1) + 1 + 1 = 9yte */ /* if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) { */ if (AKECS_GetData(i2cData, SENSOR_DATA_SIZE) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } hdata[0] = (int16_t) (i2cData[1] | (i2cData[2] << 8)); hdata[1] = (int16_t) (i2cData[3] | (i2cData[4] << 8)); hdata[2] = (int16_t) (i2cData[5] | (i2cData[6] << 8)); /* TEST */ TEST_DATA(TLIMIT_NO_SNG_ST1_09918, TLIMIT_TN_SNG_ST1_09918, (int)i2cData[0], TLIMIT_LO_SNG_ST1_09918, TLIMIT_HI_SNG_ST1_09918, &pf_total); /* TEST */ TEST_DATA(TLIMIT_NO_SNG_HX_09918, TLIMIT_TN_SNG_HX_09918, hdata[0], TLIMIT_LO_SNG_HX_09918, TLIMIT_HI_SNG_HX_09918, &pf_total); TEST_DATA(TLIMIT_NO_SNG_HY_09918, TLIMIT_TN_SNG_HY_09918, hdata[1], TLIMIT_LO_SNG_HY_09918, TLIMIT_HI_SNG_HY_09918, &pf_total); TEST_DATA(TLIMIT_NO_SNG_HZ_09918, TLIMIT_TN_SNG_HZ_09918, hdata[2], TLIMIT_LO_SNG_HZ_09918, TLIMIT_HI_SNG_HZ_09918, &pf_total); TEST_DATA(TLIMIT_NO_SNG_ST2_09918, TLIMIT_TN_SNG_ST2_09918, (int)i2cData[8] & TLIMIT_ST2_MASK_09918, TLIMIT_LO_SNG_ST2_09918, TLIMIT_HI_SNG_ST2_09918, &pf_total); /* Set to Self-test mode (Set CNTL register) */ if (AKECS_SetMode(AKM_MODE_SELF_TEST) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* Wait for DRDY pin changes to HIGH. */ /* usleep(AKM_MEASURE_TIME_US); */ /* Get measurement data from AK09918 */ /* ST1 + (HXL + HXH) + (HYL + HYH) + (HZL + HZH) + TEMP + ST2 */ /* = 1 + (1 + 1) + (1 + 1) + (1 + 1) + 1 + 1 = 9byte */ /* if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) { */ if (AKECS_GetData(i2cData, SENSOR_DATA_SIZE) < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); return 0; } /* TEST */ TEST_DATA(TLIMIT_NO_SLF_ST1_09918, TLIMIT_TN_SLF_ST1_09918, (int)i2cData[0], TLIMIT_LO_SLF_ST1_09918, TLIMIT_HI_SLF_ST1_09918, &pf_total); hdata[0] = (int16_t) (i2cData[1] | (i2cData[2] << 8)); hdata[1] = (int16_t) (i2cData[3] | (i2cData[4] << 8)); hdata[2] = (int16_t) (i2cData[5] | (i2cData[6] << 8)); /* TEST */ TEST_DATA(TLIMIT_NO_SLF_RVHX_09918, TLIMIT_TN_SLF_RVHX_09918, hdata[0], TLIMIT_LO_SLF_RVHX_09918, TLIMIT_HI_SLF_RVHX_09918, &pf_total); TEST_DATA(TLIMIT_NO_SLF_RVHY_09918, TLIMIT_TN_SLF_RVHY_09918, hdata[1], TLIMIT_LO_SLF_RVHY_09918, TLIMIT_HI_SLF_RVHY_09918, &pf_total); TEST_DATA(TLIMIT_NO_SLF_RVHZ_09918, TLIMIT_TN_SLF_RVHZ_09918, hdata[2], TLIMIT_LO_SLF_RVHZ_09918, TLIMIT_HI_SLF_RVHZ_09918, &pf_total); TEST_DATA(TLIMIT_NO_SLF_ST2_09918, TLIMIT_TN_SLF_ST2_09918, (int)i2cData[8] & TLIMIT_ST2_MASK_09918, TLIMIT_LO_SLF_ST2_09918, TLIMIT_HI_SLF_ST2_09918, &pf_total); return pf_total; } /*! *Execute "Onboard Function Test" (includes "START" and "END" command). *@retval 1 The test is passed successfully. *@retval -1 The test is failed. *@retval 0 The test is aborted by kind of system error. */ int FctShipmntTestProcess_Body(void) { int pf_total = 1; /* *********************************************** */ /* Reset Test Result */ /* *********************************************** */ TEST_DATA(NULL, "START", 0, 0, 0, &pf_total); /* *********************************************** */ /* Step 1 to 2 */ /* *********************************************** */ #if defined(AKM_Device_AK09911) pf_total = FST_AK09911(); #elif defined(AKM_Device_AK09916) pf_total = FST_AK09916(); #else pf_total = FST_AK09918(); #endif /* *********************************************** */ /* Judge Test Result */ /* *********************************************** */ TEST_DATA(NULL, "END", 0, 0, 0, &pf_total); return pf_total; } static ssize_t store_shipment_test(struct device_driver *ddri, const char *buf, size_t count) { /* struct i2c_client *client = this_client; */ /* struct akm09918_i2c_data *data = i2c_get_clientdata(client); */ /* int layout = 0; */ return count; } static ssize_t show_shipment_test(struct device_driver *ddri, char *buf) { char result[10]; int res = 0; res = FctShipmntTestProcess_Body(); if (res == 1) { pr_debug("shipment_test pass\n"); strlcpy(result, "y", sizeof(result)); } else if (res == -1) { pr_debug("shipment_test fail\n"); strlcpy(result, "n", sizeof(result)); } else { pr_debug("shipment_test NaN\n"); strlcpy(result, "NaN", sizeof(result)); } return sprintf(buf, "%s\n", result); } static ssize_t show_daemon_name(struct device_driver *ddri, char *buf) { char strbuf[AKM09918_BUFSIZE]; int ret; ret = sprintf(strbuf, "akmd09918"); if (ret < 0) pr_debug("%s:strbuf sprintf Error:%d\n", __func__, ret); ret = sprintf(buf, "%s", strbuf); if (ret < 0) pr_debug("%s:strbuf to buf sprintf Error:%d\n", __func__, ret); return ret; } static ssize_t show_chipinfo_value(struct device_driver *ddri, char *buf) { char strbuf[AKM09918_BUFSIZE]; akm09918_ReadChipInfo(strbuf, AKM09918_BUFSIZE); return sprintf(buf, "%s\n", strbuf); } /*----------------------------------------------------------------------------*/ static ssize_t show_sensordata_value(struct device_driver *ddri, char *buf) { char sensordata[SENSOR_DATA_SIZE]; char strbuf[AKM09918_BUFSIZE]; char buffer[1]; int ret; buffer[0] = AK0991X_REG_CNTL2; ret = AKI2C_RxData(buffer, 1); /* Check if e-compass is measuring by checking the CNTL2 register. * If (buffer[0] & 0x0F) is 0, e-compass is not measuring. * Set it to single measurement mode */ if (ret < 0) { pr_debug("%s:%d Error.\n", __func__, __LINE__); } else { if (!(buffer[0] & 0x0F)) { AKECS_SetMode_SngMeasure(); mdelay(10); } } AKECS_GetData(sensordata, SENSOR_DATA_SIZE); ret = sprintf(strbuf, "%d %d %d %d %d %d %d %d %d\n", sensordata[0], sensordata[1], sensordata[2], sensordata[3], sensordata[4], sensordata[5], sensordata[6], sensordata[7], sensordata[8]); if (ret < 0) pr_debug("%s:sensor_data sprintf Error:%d\n", __func__, ret); ret = sprintf(buf, "%s\n", strbuf); if (ret < 0) pr_debug("%s:sensor_data to buf sprintf Error:%d\n", __func__, ret); return ret; } /*----------------------------------------------------------------------------*/ static ssize_t show_layout_value(struct device_driver *ddri, char *buf) { struct i2c_client *client = this_client; struct akm09918_i2c_data *data = i2c_get_clientdata(client); return sprintf(buf, "(%d, %d)\n[%+2d %+2d %+2d]\n[%+2d %+2d %+2d]\n", data->hw->direction, atomic_read(&data->layout), data->cvt.sign[0], data->cvt.sign[1], data->cvt.sign[2], data->cvt.map[0], data->cvt.map[1], data->cvt.map[2]); } /*----------------------------------------------------------------------------*/ static ssize_t store_layout_value(struct device_driver *ddri, const char *buf, size_t count) { struct i2c_client *client = this_client; struct akm09918_i2c_data *data = i2c_get_clientdata(client); int layout = 0; int ret = 0; ret = kstrtoint(buf, 10, &layout); if (ret == 0) { atomic_set(&data->layout, layout); if (AKECS_GetConvert(layout, &data->cvt)) pr_err("HWMSEN_GET_CONVERT function error!\r\n"); else if (AKECS_GetConvert(data->hw->direction, &data->cvt)) pr_err("invalid layout: %d, restore to %d\n", layout, data->hw->direction); else { pr_err("invalid layout: (%d, %d)\n", layout, data->hw->direction); ret = AKECS_GetConvert(1, &data->cvt); if (ret) pr_err("HWMSEN_GET_CONVERT function error!\r\n"); } } else pr_err("invalid format = '%s'\n", buf); return count; } /*----------------------------------------------------------------------------*/ static ssize_t show_status_value(struct device_driver *ddri, char *buf) { struct i2c_client *client = this_client; struct akm09918_i2c_data *data = i2c_get_clientdata(client); ssize_t len = 0; if (data->hw) len += snprintf(buf + len, PAGE_SIZE - len, "CUST: %d %d (%d %d)\n", data->hw->i2c_num, data->hw->direction, data->hw->power_id, data->hw->power_vol); else len += snprintf(buf + len, PAGE_SIZE - len, "CUST: NULL\n"); len += snprintf(buf + len, PAGE_SIZE - len, "OPEN: %d\n", atomic_read(&dev_open_count)); return len; } /*----------------------------------------------------------------------------*/ static ssize_t show_trace_value(struct device_driver *ddri, char *buf) { ssize_t res; struct akm09918_i2c_data *obj = i2c_get_clientdata(this_client); if (obj == NULL) { pr_err("akm09918_i2c_data is null!!\n"); return 0; } res = snprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&obj->trace)); return res; } /*----------------------------------------------------------------------------*/ static ssize_t store_trace_value(struct device_driver *ddri, const char *buf, size_t count) { struct akm09918_i2c_data *obj = i2c_get_clientdata(this_client); int trace; if (obj == NULL) { pr_err("akm09918_i2c_data is null!!\n"); return 0; } if (sscanf(buf, "0x%x", &trace) == 1) atomic_set(&obj->trace, trace); else pr_err("invalid content: '%s', length = %zu\n", buf, count); return count; } static ssize_t show_chip_orientation(struct device_driver *ddri, char *buf) { ssize_t _tLength = 0; struct mag_hw *_ptAccelHw = hw; pr_debug("[%s] default direction: %d\n", __func__, _ptAccelHw->direction); _tLength = snprintf(buf, PAGE_SIZE, "default direction = %d\n", _ptAccelHw->direction); return _tLength; } static ssize_t store_chip_orientation(struct device_driver *ddri, const char *buf, size_t tCount) { int _nDirection = 0; int ret = 0; struct akm09918_i2c_data *_pt_i2c_obj = i2c_get_clientdata(this_client); if (_pt_i2c_obj == NULL) return 0; ret = kstrtoint(buf, 10, &_nDirection); if (ret == 0) { if (AKECS_GetConvert(_nDirection, &_pt_i2c_obj->cvt)) pr_err("ERR: fail to set direction\n"); } pr_debug("[%s] set direction: %d\n", __func__, _nDirection); return tCount; } static ssize_t show_power_status(struct device_driver *ddri, char *buf) { int ret = 0; ssize_t res = 0; u8 uData = AK0991X_REG_CNTL2; struct akm09918_i2c_data *obj = i2c_get_clientdata(this_client); if (obj == NULL) { pr_err("i2c_data obj is null!!\n"); return 0; } ret = AKI2C_RxData(&uData, 1); if (ret < 0) pr_debug("%s:%d Error.\n", __func__, __LINE__); res = snprintf(buf, PAGE_SIZE, "0x%04X\n", uData); if (res < 0) pr_debug("%s:PAGE_SIZE snprintf Error:%d\n", __func__, res); return res; } static ssize_t show_regiter_map(struct device_driver *ddri, char *buf) { u8 _bIndex = 0; u8 _baRegMap[] = { 0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x30, 0x31, 0x32, 0x33, 0x60, 0x61, 0x62 }; /* u8 _baRegValue[20]; */ ssize_t _tLength = 0; char tmp[2] = { 0 }; int ret = 0; for (_bIndex = 0; _bIndex < 20; _bIndex++) { tmp[0] = _baRegMap[_bIndex]; ret = AKI2C_RxData(tmp, 1); if (ret < 0) pr_debug("%s:%d Error.\n", __func__, __LINE__); _tLength += snprintf((buf + _tLength), (PAGE_SIZE - _tLength), "Reg[0x%02X]: 0x%02X\n", _baRegMap[_bIndex], tmp[0]); } return _tLength; } /*----------------------------------------------------------------------------*/ static DRIVER_ATTR(daemon, 0444, show_daemon_name, NULL); static DRIVER_ATTR(shipmenttest, 0644, show_shipment_test, store_shipment_test); static DRIVER_ATTR(chipinfo, 0444, show_chipinfo_value, NULL); static DRIVER_ATTR(sensordata, 0444, show_sensordata_value, NULL); static DRIVER_ATTR(layout, 0644, show_layout_value, store_layout_value); static DRIVER_ATTR(status, 0444, show_status_value, NULL); static DRIVER_ATTR(trace, 0644, show_trace_value, store_trace_value); static DRIVER_ATTR(orientation, 0644, show_chip_orientation, store_chip_orientation); static DRIVER_ATTR(power, 0444, show_power_status, NULL); static DRIVER_ATTR(regmap, 0444, show_regiter_map, NULL); /*----------------------------------------------------------------------------*/ static struct driver_attribute *akm09918_attr_list[] = { &driver_attr_daemon, &driver_attr_shipmenttest, &driver_attr_chipinfo, &driver_attr_sensordata, &driver_attr_layout, &driver_attr_status, &driver_attr_trace, &driver_attr_orientation, &driver_attr_power, &driver_attr_regmap, }; /*----------------------------------------------------------------------------*/ static int akm09918_create_attr(struct device_driver *driver) { int idx, err = 0; int num = (int)(ARRAY_SIZE(akm09918_attr_list)); if (driver == NULL) return -EINVAL; for (idx = 0; idx < num; idx++) { err = driver_create_file(driver, akm09918_attr_list[idx]); if (err) { pr_err("driver_create_file (%s) = %d\n", akm09918_attr_list[idx]->attr.name, err); break; } } return err; } /*----------------------------------------------------------------------------*/ static int akm09918_delete_attr(struct device_driver *driver) { int idx, err = 0; int num = (int)(ARRAY_SIZE(akm09918_attr_list)); if (driver == NULL) return -EINVAL; for (idx = 0; idx < num; idx++) driver_remove_file(driver, akm09918_attr_list[idx]); return err; } /*----------------------------------------------------------------------------*/ static int akm09918_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct akm09918_i2c_data *obj = i2c_get_clientdata(client); akm09918_power(obj->hw, 0); return 0; } /*----------------------------------------------------------------------------*/ static int akm09918_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct akm09918_i2c_data *obj = i2c_get_clientdata(client); akm09918_power(obj->hw, 1); return 0; } /*----------------------------------------------------------------------------*/ static int akm09918_i2c_detect(struct i2c_client *client, struct i2c_board_info *info) { strlcpy(info->type, AKM09918_DEV_NAME, sizeof(info->type)); return 0; } static int akm09918_enable(int en) { int value = 0; int err = 0; struct akm09918_i2c_data *f_obj = i2c_get_clientdata(this_client); if (f_obj == NULL) return -1; value = en; factory_mode = 1; if (value == 1) { f_obj->enable = true; #ifdef AKM_CONTINUOUS_MODE err = AKECS_SetMode_CntMeasure(AKM_MODE_CNT_MEASURE_4); #else err = AKECS_SetMode(AKM_MODE_SNG_MEASURE); #endif if (err < 0) { pr_err("%s:AKECS_SetMode Error.\n", __func__); return err; } } else { f_obj->enable = false; err = AKECS_SetMode(AKM_MODE_POWERDOWN); if (err < 0) { pr_err("%s:AKECS_SetMode Error.\n", __func__); return err; } } if (f_obj->flush) { if (value == 1) { pr_debug("%s will call akm09918_flush\n", __func__); akm09918_flush(); } else f_obj->flush = false; } wake_up(&open_wq); return err; } static int akm09918_set_delay(u64 ns) { int value = 0; value = (int)ns / 1000 / 1000; if (value <= 10) akmd_delay = 10; else akmd_delay = value; return 0; } static int akm09918_open_report_data(int open) { return 0; } static int akm09918_coordinate_convert(int16_t *mag_data) { struct i2c_client *client = this_client; struct akm09918_i2c_data *data = i2c_get_clientdata(client); int16_t temp_data[3]; int i = 0; for (i = 0; i < 3; i++) temp_data[i] = mag_data[i]; /* remap coordinate */ mag_data[0] = data->cvt.sign[0] * temp_data[data->cvt.map[0]]; mag_data[1] = data->cvt.sign[1] * temp_data[data->cvt.map[1]]; mag_data[2] = data->cvt.sign[2] * temp_data[data->cvt.map[2]]; return 0; } static int akm09918_get_data(int *x, int *y, int *z, int *status) { char strbuf[SENSOR_DATA_SIZE]; int16_t data[3]; #ifdef AKM_CONTINUOUS_MODE //AKECS_SetMode_CntMeasure(AKM_MODE_CNT_MEASURE_4); #else AKECS_SetMode_SngMeasure(); mdelay(10); #endif AKECS_GetData(strbuf, SENSOR_DATA_SIZE); data[0] = (int16_t)(strbuf[1] | (strbuf[2] << 8)); data[1] = (int16_t)(strbuf[3] | (strbuf[4] << 8)); data[2] = (int16_t)(strbuf[5] | (strbuf[6] << 8)); akm09918_coordinate_convert(data); if (akm_device == 0x04) {/* ak09912 */ *x = data[0] * CONVERT_M_DIV * AKECS_ASA_CACULATE_AK09912(akm_fuse[0]); *y = data[1] * CONVERT_M_DIV * AKECS_ASA_CACULATE_AK09912(akm_fuse[1]); *z = data[2] * CONVERT_M_DIV * AKECS_ASA_CACULATE_AK09912(akm_fuse[2]); } else if (akm_device == 0x05) { *x = data[0] * CONVERT_M_DIV * AKECS_ASA_CACULATE_AK09911(akm_fuse[0]); *y = data[1] * CONVERT_M_DIV * AKECS_ASA_CACULATE_AK09911(akm_fuse[1]); *z = data[2] * CONVERT_M_DIV * AKECS_ASA_CACULATE_AK09911(akm_fuse[2]); } else if ((akm_device == 0x10) || (akm_device == 0x09) || (akm_device == 0x0b) || (akm_device == 0x0c)) { *x = data[0] * CONVERT_M_DIV; *y = data[1] * CONVERT_M_DIV; *z = data[2] * CONVERT_M_DIV; } *status = strbuf[8]; return 0; } static int akm09918_batch(int flag, int64_t samplingPeriodNs, int64_t maxBatchReportLatencyNs) { int value = 0; value = (int)samplingPeriodNs / 1000 / 1000; if (value <= 10) akmd_delay = 10; else akmd_delay = value; pr_debug("akm09918 mag set delay = (%d) ok.\n", value); return 0; } static int akm09918_flush(void) { /*Only flush after sensor was enabled*/ int err = 0; struct akm09918_i2c_data *f_obj = i2c_get_clientdata(this_client); if (f_obj == NULL) return -1; if (!f_obj->enable) { f_obj->flush = true; return 0; } err = mag_flush_report(); if (err >= 0) f_obj->flush = false; return err; } static int akm09918_factory_enable_sensor(bool enabledisable, int64_t sample_periods_ms) { int err; err = akm09918_enable(enabledisable == true ? 1 : 0); if (err) { pr_err("%s enable sensor failed!\n", __func__); return -1; } err = akm09918_batch(0, sample_periods_ms * 1000000, 0); if (err) { pr_err("%s enable set batch failed!\n", __func__); return -1; } return 0; } static int akm09918_factory_get_data(int32_t data[3], int *status) { int ret = 0; /* get raw data */ ret = akm09918_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 0; } static int akm09918_factory_get_raw_data(int32_t data[3]) { pr_debug("%s do not support!\n", __func__); return 0; } static int akm09918_factory_enable_calibration(void) { return 0; } static int akm09918_factory_clear_cali(void) { return 0; } static int akm09918_factory_set_cali(int32_t data[3]) { return 0; } static int akm09918_factory_get_cali(int32_t data[3]) { return 0; } static int akm09918_factory_do_self_test(void) { return 0; } static struct mag_factory_fops akm09918_factory_fops = { .enable_sensor = akm09918_factory_enable_sensor, .get_data = akm09918_factory_get_data, .get_raw_data = akm09918_factory_get_raw_data, .enable_calibration = akm09918_factory_enable_calibration, .clear_cali = akm09918_factory_clear_cali, .set_cali = akm09918_factory_set_cali, .get_cali = akm09918_factory_get_cali, .do_self_test = akm09918_factory_do_self_test, }; static struct mag_factory_public akm09918_factory_device = { .gain = 1, .sensitivity = 1, .fops = &akm09918_factory_fops, }; /*----------------------------------------------------------------------------*/ static int akm09918_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int err = 0; struct i2c_client *new_client; struct akm09918_i2c_data *data; struct mag_control_path ctl = { 0 }; struct mag_data_path mag_data = { 0 }; struct platform_driver *paddr = akm09918_init_info.platform_diver_addr; pr_debug("%s\n", __func__); err = get_mag_dts_func(client->dev.of_node, hw); if (err) { pr_err("get dts info fail\n"); err = -EFAULT; goto exit; } data = kzalloc(sizeof(struct akm09918_i2c_data), GFP_KERNEL); if (!data) { err = -ENOMEM; goto exit; } data->hw = hw; /*akm_map is different from MTK, so do not use hwmsen_get_convert*/ //err = hwmsen_get_convert(data->hw->direction, &data->cvt); err = AKECS_GetConvert(data->hw->direction, &data->cvt); if (err) { /*direction is 1 - 8*/ pr_err("invalid direction: %d\n", data->hw->direction); goto exit_kfree; } atomic_set(&data->layout, data->hw->direction); atomic_set(&data->trace, 0); /* init_waitqueue_head(&data_ready_wq); */ init_waitqueue_head(&open_wq); data->client = client; new_client = data->client; i2c_set_clientdata(new_client, data); this_client = new_client; /* Check connection */ err = AKECS_CheckDevice(); if (err < 0) { pr_err("AKM09918 akm09918_probe: check device connect error\n"); goto exit_init_failed; } err = mag_factory_device_register(&akm09918_factory_device); if (err) { pr_err("misc device register failed, err = %d\n", err); goto exit_misc_device_register_failed; } /* Register sysfs attribute */ err = akm09918_create_attr(&(paddr->driver)); if (err) { pr_err("create attribute err = %d\n", err); goto exit_sysfs_create_group_failed; } ctl.is_use_common_factory = false; ctl.enable = akm09918_enable; ctl.set_delay = akm09918_set_delay; ctl.open_report_data = akm09918_open_report_data; ctl.batch = akm09918_batch; ctl.flush = akm09918_flush; ctl.is_report_input_direct = false; ctl.is_support_batch = data->hw->is_batch_supported; strlcpy(ctl.libinfo.libname, "akm", sizeof(ctl.libinfo.libname)); ctl.libinfo.layout = AKECS_SetCert(); ctl.libinfo.deviceid = akm_device; err = mag_register_control_path(&ctl); if (err) { pr_err("register mag control path err\n"); goto exit_kfree; } mag_data.div = CONVERT_M_DIV; mag_data.get_data = akm09918_get_data; err = mag_register_data_path(&mag_data); if (err) { pr_err("register data control path err\n"); goto exit_kfree; } pr_debug("%s: OK\n", __func__); akm09918_init_flag = 1; return 0; exit_sysfs_create_group_failed: exit_init_failed: exit_misc_device_register_failed: exit_kfree: kfree(data); data = NULL; exit: pr_err("%s: err = %d\n", __func__, err); akm09918_init_flag = -1; return err; } /*----------------------------------------------------------------------------*/ static int akm09918_i2c_remove(struct i2c_client *client) { int err; struct platform_driver *paddr = akm09918_init_info.platform_diver_addr; err = akm09918_delete_attr(&(paddr->driver)); if (err) pr_err("akm09918_delete_attr fail: %d\n", err); this_client = NULL; i2c_unregister_device(client); mag_factory_device_deregister(&akm09918_factory_device); kfree(i2c_get_clientdata(client)); return 0; } /*----------------------------------------------------------------------------*/ static int akm09918_remove(void) { akm09918_power(hw, 0); atomic_set(&dev_open_count, 0); i2c_del_driver(&akm09918_i2c_driver); return 0; } static int akm09918_local_init(void) { akm09918_power(hw, 1); if (i2c_add_driver(&akm09918_i2c_driver)) { pr_err("i2c_add_driver error\n"); return -1; } if (-1 == akm09918_init_flag) return -1; return 0; } /*----------------------------------------------------------------------------*/ static int __init akm09918_init(void) { mag_driver_add(&akm09918_init_info); return 0; } /*----------------------------------------------------------------------------*/ static void __exit akm09918_exit(void) { #ifdef CONFIG_CUSTOM_KERNEL_MAGNETOMETER_MODULE mag_success_Flag = false; #endif } /*----------------------------------------------------------------------------*/ module_init(akm09918_init); module_exit(akm09918_exit); MODULE_AUTHOR("MTK"); MODULE_DESCRIPTION("AKM09918 compass driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRIVER_VERSION);