/* drivers/input/misc/akm8963.c - akm8963 compass driver * * Copyright (c) 2015, The Linux Foundation. All rights reserved. * * Copyright (C) 2007-2008 HTC Corporation. * Author: Hou-Kun Chen * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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. * */ /*#define DEBUG*/ /*#define VERBOSE_DEBUG*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define AKM_DEBUG_IF 0 #define AKM_HAS_RESET 0 #define AKM_INPUT_DEVICE_NAME "compass" #define AKM_DRDY_TIMEOUT_MS 100 #define AKM_BASE_NUM 10 #define AKM_IS_MAG_DATA_ENABLED() (akm->enable_flag & (1 << MAG_DATA_FLAG)) /* POWER SUPPLY VOLTAGE RANGE */ #define AKM8963_VDD_MIN_UV 2000000 #define AKM8963_VDD_MAX_UV 3300000 #define AKM8963_VIO_MIN_UV 1750000 #define AKM8963_VIO_MAX_UV 1950000 #define STATUS_ERROR(st) (((st)&0x08) != 0x0) #define REG_CNTL1_MODE(reg_cntl1) (reg_cntl1 & 0x0F) /* Save last device state for power down */ struct akm_sensor_state { bool power_on; uint8_t mode; }; struct akm_compass_data { struct i2c_client *i2c; struct input_dev *input; struct device *class_dev; struct class *compass; struct pinctrl *pinctrl; struct pinctrl_state *pin_default; struct pinctrl_state *pin_sleep; struct sensors_classdev cdev; struct workqueue_struct *data_wq; struct delayed_work dwork; struct mutex op_mutex; wait_queue_head_t drdy_wq; wait_queue_head_t open_wq; /* These two buffers are initialized at start up. After that, the value is not changed */ uint8_t sense_info[AKM_SENSOR_INFO_SIZE]; uint8_t sense_conf[AKM_SENSOR_CONF_SIZE]; struct mutex sensor_mutex; uint8_t sense_data[AKM_SENSOR_DATA_SIZE]; struct mutex accel_mutex; int16_t accel_data[3]; /* Positive value means the device is working. 0 or negative value means the device is not woking, i.e. in power-down mode. */ int8_t is_busy; struct mutex val_mutex; uint32_t enable_flag; int32_t delay[AKM_NUM_SENSORS]; atomic_t active; atomic_t drdy; int gpio_rstn; bool power_enabled; bool use_poll; struct regulator *vdd; struct regulator *vio; struct akm_sensor_state state; struct akm8963_platform_data *pdata; }; static struct sensors_classdev sensors_cdev = { .name = "akm8963-mag", .vendor = "Asahi Kasei Microdevices Corporation", .version = 1, .handle = SENSORS_MAGNETIC_FIELD_HANDLE, .type = SENSOR_TYPE_MAGNETIC_FIELD, .max_range = "1228.8", .resolution = "0.15", .sensor_power = "0.35", .min_delay = 10000, .max_delay = 10000, .fifo_reserved_event_count = 0, .fifo_max_event_count = 0, .enabled = 0, .delay_msec = 10, .sensors_enable = NULL, .sensors_poll_delay = NULL, }; static struct akm_compass_data *s_akm; static int akm_compass_power_set(struct akm_compass_data *data, bool on); /***** I2C I/O function ***********************************************/ static int akm_i2c_rxdata( struct i2c_client *i2c, uint8_t *rxData, int length) { int ret; struct i2c_msg msgs[] = { { .addr = i2c->addr, .flags = 0, .len = 1, .buf = rxData, }, { .addr = i2c->addr, .flags = I2C_M_RD, .len = length, .buf = rxData, }, }; uint8_t addr = rxData[0]; ret = i2c_transfer(i2c->adapter, msgs, ARRAY_SIZE(msgs)); if (ret < 0) { dev_err(&i2c->dev, "%s: transfer failed.", __func__); return ret; } else if (ret != ARRAY_SIZE(msgs)) { dev_err(&i2c->dev, "%s: transfer failed(size error).\n", __func__); return -ENXIO; } dev_vdbg(&i2c->dev, "RxData: len=%02x, addr=%02x, data=%02x", length, addr, rxData[0]); return 0; } static int akm_i2c_txdata( struct i2c_client *i2c, uint8_t *txData, int length) { int ret; struct i2c_msg msg[] = { { .addr = i2c->addr, .flags = 0, .len = length, .buf = txData, }, }; ret = i2c_transfer(i2c->adapter, msg, ARRAY_SIZE(msg)); if (ret < 0) { dev_err(&i2c->dev, "%s: transfer failed.", __func__); return ret; } else if (ret != ARRAY_SIZE(msg)) { dev_err(&i2c->dev, "%s: transfer failed(size error).", __func__); return -ENXIO; } dev_vdbg(&i2c->dev, "TxData: len=%02x, addr=%02x data=%02x", length, txData[0], txData[1]); return 0; } /***** akm miscdevice functions *************************************/ static int AKECS_Set_CNTL( struct akm_compass_data *akm, uint8_t mode) { uint8_t buffer[2]; int err; /***** lock *****/ mutex_lock(&akm->sensor_mutex); /* Busy check */ if (akm->is_busy > 0) { dev_err(&akm->i2c->dev, "%s: device is busy.", __func__); err = -EBUSY; } else { /* Set measure mode */ buffer[0] = AKM_REG_MODE; buffer[1] = mode; err = akm_i2c_txdata(akm->i2c, buffer, 2); if (err < 0) { dev_err(&akm->i2c->dev, "%s: Can not set CNTL.", __func__); } else { dev_vdbg(&akm->i2c->dev, "Mode is set to (%d).", mode); /* Set flag */ akm->is_busy = 1; atomic_set(&akm->drdy, 0); /* wait at least 100us after changing mode */ udelay(100); } } mutex_unlock(&akm->sensor_mutex); /***** unlock *****/ return err; } static int AKECS_Set_PowerDown( struct akm_compass_data *akm) { uint8_t buffer[2]; int err; /***** lock *****/ mutex_lock(&akm->sensor_mutex); /* Set powerdown mode */ buffer[0] = AKM_REG_MODE; buffer[1] = AKM_MODE_POWERDOWN; err = akm_i2c_txdata(akm->i2c, buffer, 2); if (err < 0) { dev_err(&akm->i2c->dev, "%s: Can not set to powerdown mode.", __func__); } else { dev_dbg(&akm->i2c->dev, "Powerdown mode is set."); /* wait at least 100us after changing mode */ udelay(100); } /* Clear status */ akm->is_busy = 0; atomic_set(&akm->drdy, 0); mutex_unlock(&akm->sensor_mutex); /***** unlock *****/ return err; } static int AKECS_Reset( struct akm_compass_data *akm, int hard) { int err; #if AKM_HAS_RESET uint8_t buffer[2]; /***** lock *****/ mutex_lock(&akm->sensor_mutex); if (hard != 0) { gpio_set_value(akm->gpio_rstn, 0); udelay(5); gpio_set_value(akm->gpio_rstn, 1); /* No error is returned */ err = 0; } else { buffer[0] = AKM_REG_RESET; buffer[1] = AKM_RESET_DATA; err = akm_i2c_txdata(akm->i2c, buffer, 2); if (err < 0) { dev_err(&akm->i2c->dev, "%s: Can not set SRST bit.", __func__); } else { dev_dbg(&akm->i2c->dev, "Soft reset is done."); } } /* Device will be accessible 100 us after */ udelay(100); /* Clear status */ akm->is_busy = 0; atomic_set(&akm->drdy, 0); mutex_unlock(&akm->sensor_mutex); /***** unlock *****/ #else err = AKECS_Set_PowerDown(akm); #endif return err; } static int AKECS_SetMode( struct akm_compass_data *akm, uint8_t mode) { int err; switch (REG_CNTL1_MODE(mode)) { case AKM_MODE_SNG_MEASURE: case AKM_MODE_SELF_TEST: case AK8963_MODE_CONT1_MEASURE: case AK8963_MODE_CONT2_MEASURE: case AK8963_MODE_EXT_TRIG_MEASURE: case AKM_MODE_FUSE_ACCESS: err = AKECS_Set_CNTL(akm, mode); break; case AKM_MODE_POWERDOWN: err = AKECS_Set_PowerDown(akm); break; default: dev_err(&akm->i2c->dev, "%s: Unknown mode(%d).", __func__, mode); return -EINVAL; } akm->state.mode = mode; return err; } static void AKECS_SetYPR( struct akm_compass_data *akm, int *rbuf) { uint32_t ready; dev_vdbg(&akm->i2c->dev, "%s: flag =0x%X", __func__, rbuf[0]); dev_vdbg(&akm->input->dev, " Acc [LSB] : %6d,%6d,%6d stat=%d", rbuf[1], rbuf[2], rbuf[3], rbuf[4]); dev_vdbg(&akm->input->dev, " Geo [LSB] : %6d,%6d,%6d stat=%d", rbuf[5], rbuf[6], rbuf[7], rbuf[8]); dev_vdbg(&akm->input->dev, " Orientation : %6d,%6d,%6d", rbuf[9], rbuf[10], rbuf[11]); dev_vdbg(&akm->input->dev, " Rotation V : %6d,%6d,%6d,%6d", rbuf[12], rbuf[13], rbuf[14], rbuf[15]); /* No events are reported */ if (!rbuf[0]) { dev_dbg(&akm->i2c->dev, "Don't waste a time."); return; } mutex_lock(&akm->val_mutex); ready = (akm->enable_flag & (uint32_t)rbuf[0]); mutex_unlock(&akm->val_mutex); /* Report acceleration sensor information */ if (ready & ACC_DATA_READY) { input_report_abs(akm->input, ABS_X, rbuf[1]); input_report_abs(akm->input, ABS_Y, rbuf[2]); input_report_abs(akm->input, ABS_Z, rbuf[3]); input_report_abs(akm->input, ABS_RX, rbuf[4]); } /* Report magnetic vector information */ if (ready & MAG_DATA_READY) { input_report_abs(akm->input, ABS_X, rbuf[5]); input_report_abs(akm->input, ABS_Y, rbuf[6]); input_report_abs(akm->input, ABS_Z, rbuf[7]); } /* Report fusion sensor information */ if (ready & FUSION_DATA_READY) { /* Orientation */ input_report_abs(akm->input, ABS_HAT0Y, rbuf[9]); input_report_abs(akm->input, ABS_HAT1X, rbuf[10]); input_report_abs(akm->input, ABS_HAT1Y, rbuf[11]); /* Rotation Vector */ input_report_abs(akm->input, ABS_TILT_X, rbuf[12]); input_report_abs(akm->input, ABS_TILT_Y, rbuf[13]); input_report_abs(akm->input, ABS_TOOL_WIDTH, rbuf[14]); input_report_abs(akm->input, ABS_VOLUME, rbuf[15]); } input_sync(akm->input); } /* This function will block a process until the latest measurement * data is available. */ static int AKECS_GetData( struct akm_compass_data *akm, uint8_t *rbuf, int size) { int err; /* Block! */ err = wait_event_interruptible_timeout( akm->drdy_wq, atomic_read(&akm->drdy), msecs_to_jiffies(AKM_DRDY_TIMEOUT_MS)); if (err < 0) { dev_err(&akm->i2c->dev, "%s: wait_event failed (%d).", __func__, err); return err; } if (!atomic_read(&akm->drdy)) { dev_err(&akm->i2c->dev, "%s: DRDY is not set.", __func__); return -ENODATA; } /***** lock *****/ mutex_lock(&akm->sensor_mutex); memcpy(rbuf, akm->sense_data, size); atomic_set(&akm->drdy, 0); mutex_unlock(&akm->sensor_mutex); /***** unlock *****/ return 0; } static int AKECS_GetData_Poll( struct akm_compass_data *akm, uint8_t *rbuf, int size) { uint8_t buffer[AKM_SENSOR_DATA_SIZE]; int err; /* Read status */ buffer[0] = AKM_REG_STATUS; err = akm_i2c_rxdata(akm->i2c, buffer, 1); if (err < 0) { dev_err(&akm->i2c->dev, "%s failed.", __func__); return err; } /* Check ST bit */ if (!(AKM_DRDY_IS_HIGH(buffer[0]))) return -EAGAIN; /* Read rest data */ buffer[1] = AKM_REG_STATUS + 1; err = akm_i2c_rxdata(akm->i2c, &(buffer[1]), AKM_SENSOR_DATA_SIZE-1); if (err < 0) { dev_err(&akm->i2c->dev, "%s failed.", __func__); return err; } memcpy(rbuf, buffer, size); atomic_set(&akm->drdy, 0); /***** lock *****/ mutex_lock(&akm->sensor_mutex); akm->is_busy = 0; mutex_unlock(&akm->sensor_mutex); /***** unlock *****/ return 0; } static int AKECS_GetOpenStatus( struct akm_compass_data *akm) { return wait_event_interruptible( akm->open_wq, (atomic_read(&akm->active) > 0)); } static int AKECS_GetCloseStatus( struct akm_compass_data *akm) { return wait_event_interruptible( akm->open_wq, (atomic_read(&akm->active) <= 0)); } static int AKECS_Open(struct inode *inode, struct file *file) { file->private_data = s_akm; return nonseekable_open(inode, file); } static int AKECS_Release(struct inode *inode, struct file *file) { return 0; } static long AKECS_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; struct akm_compass_data *akm = file->private_data; /* NOTE: In this function the size of "char" should be 1-byte. */ uint8_t i2c_buf[AKM_RWBUF_SIZE]; /* for READ/WRITE */ uint8_t dat_buf[AKM_SENSOR_DATA_SIZE];/* for GET_DATA */ int32_t ypr_buf[AKM_YPR_DATA_SIZE]; /* for SET_YPR */ int32_t delay[AKM_NUM_SENSORS]; /* for GET_DELAY */ int16_t acc_buf[3]; /* for GET_ACCEL */ uint8_t mode; /* for SET_MODE*/ int status; /* for OPEN/CLOSE_STATUS */ int ret = 0; /* Return value. */ switch (cmd) { case ECS_IOCTL_READ: case ECS_IOCTL_WRITE: if (argp == NULL) { dev_err(&akm->i2c->dev, "invalid argument."); return -EINVAL; } if (copy_from_user(&i2c_buf, argp, sizeof(i2c_buf))) { dev_err(&akm->i2c->dev, "copy_from_user failed."); return -EFAULT; } break; case ECS_IOCTL_SET_MODE: if (argp == NULL) { dev_err(&akm->i2c->dev, "invalid argument."); return -EINVAL; } if (copy_from_user(&mode, argp, sizeof(mode))) { dev_err(&akm->i2c->dev, "copy_from_user failed."); return -EFAULT; } break; case ECS_IOCTL_SET_YPR: if (argp == NULL) { dev_err(&akm->i2c->dev, "invalid argument."); return -EINVAL; } if (copy_from_user(&ypr_buf, argp, sizeof(ypr_buf))) { dev_err(&akm->i2c->dev, "copy_from_user failed."); return -EFAULT; } case ECS_IOCTL_GET_INFO: case ECS_IOCTL_GET_CONF: case ECS_IOCTL_GET_DATA: case ECS_IOCTL_GET_OPEN_STATUS: case ECS_IOCTL_GET_CLOSE_STATUS: case ECS_IOCTL_GET_DELAY: case ECS_IOCTL_GET_LAYOUT: case ECS_IOCTL_GET_ACCEL: /* Check buffer pointer for writing a data later. */ if (argp == NULL) { dev_err(&akm->i2c->dev, "invalid argument."); return -EINVAL; } break; default: break; } switch (cmd) { case ECS_IOCTL_READ: dev_vdbg(&akm->i2c->dev, "IOCTL_READ called."); if ((i2c_buf[0] < 1) || (i2c_buf[0] > (AKM_RWBUF_SIZE-1))) { dev_err(&akm->i2c->dev, "invalid argument."); return -EINVAL; } ret = akm_i2c_rxdata(akm->i2c, &i2c_buf[1], i2c_buf[0]); if (ret < 0) return ret; break; case ECS_IOCTL_WRITE: dev_vdbg(&akm->i2c->dev, "IOCTL_WRITE called."); if ((i2c_buf[0] < 2) || (i2c_buf[0] > (AKM_RWBUF_SIZE-1))) { dev_err(&akm->i2c->dev, "invalid argument."); return -EINVAL; } ret = akm_i2c_txdata(akm->i2c, &i2c_buf[1], i2c_buf[0]); if (ret < 0) return ret; break; case ECS_IOCTL_RESET: dev_vdbg(&akm->i2c->dev, "IOCTL_RESET called."); ret = AKECS_Reset(akm, akm->gpio_rstn); if (ret < 0) return ret; break; case ECS_IOCTL_SET_MODE: dev_vdbg(&akm->i2c->dev, "IOCTL_SET_MODE called."); ret = AKECS_SetMode(akm, mode); if (ret < 0) return ret; break; case ECS_IOCTL_SET_YPR: dev_vdbg(&akm->i2c->dev, "IOCTL_SET_YPR called."); AKECS_SetYPR(akm, ypr_buf); break; case ECS_IOCTL_GET_DATA: dev_vdbg(&akm->i2c->dev, "IOCTL_GET_DATA called."); if (akm->i2c->irq) ret = AKECS_GetData(akm, dat_buf, AKM_SENSOR_DATA_SIZE); else ret = AKECS_GetData_Poll( akm, dat_buf, AKM_SENSOR_DATA_SIZE); if (ret < 0) return ret; break; case ECS_IOCTL_GET_OPEN_STATUS: dev_vdbg(&akm->i2c->dev, "IOCTL_GET_OPEN_STATUS called."); ret = AKECS_GetOpenStatus(akm); if (ret < 0) { dev_err(&akm->i2c->dev, "Get Open returns error (%d).", ret); return ret; } break; case ECS_IOCTL_GET_CLOSE_STATUS: dev_vdbg(&akm->i2c->dev, "IOCTL_GET_CLOSE_STATUS called."); ret = AKECS_GetCloseStatus(akm); if (ret < 0) { dev_err(&akm->i2c->dev, "Get Close returns error (%d).", ret); return ret; } break; case ECS_IOCTL_GET_DELAY: dev_vdbg(&akm->i2c->dev, "IOCTL_GET_DELAY called."); mutex_lock(&akm->val_mutex); delay[0] = ((akm->enable_flag & ACC_DATA_READY) ? akm->delay[0] : -1); delay[1] = ((akm->enable_flag & MAG_DATA_READY) ? akm->delay[1] : -1); delay[2] = ((akm->enable_flag & FUSION_DATA_READY) ? akm->delay[2] : -1); mutex_unlock(&akm->val_mutex); break; case ECS_IOCTL_GET_INFO: dev_vdbg(&akm->i2c->dev, "IOCTL_GET_INFO called."); break; case ECS_IOCTL_GET_CONF: dev_vdbg(&akm->i2c->dev, "IOCTL_GET_CONF called."); break; case ECS_IOCTL_GET_LAYOUT: dev_vdbg(&akm->i2c->dev, "IOCTL_GET_LAYOUT called."); break; case ECS_IOCTL_GET_ACCEL: dev_vdbg(&akm->i2c->dev, "IOCTL_GET_ACCEL called."); mutex_lock(&akm->accel_mutex); acc_buf[0] = akm->accel_data[0]; acc_buf[1] = akm->accel_data[1]; acc_buf[2] = akm->accel_data[2]; mutex_unlock(&akm->accel_mutex); break; default: return -ENOTTY; } switch (cmd) { case ECS_IOCTL_READ: /* +1 is for the first byte */ if (copy_to_user(argp, &i2c_buf, i2c_buf[0]+1)) { dev_err(&akm->i2c->dev, "copy_to_user failed."); return -EFAULT; } break; case ECS_IOCTL_GET_INFO: if (copy_to_user(argp, &akm->sense_info, sizeof(akm->sense_info))) { dev_err(&akm->i2c->dev, "copy_to_user failed."); return -EFAULT; } break; case ECS_IOCTL_GET_CONF: if (copy_to_user(argp, &akm->sense_conf, sizeof(akm->sense_conf))) { dev_err(&akm->i2c->dev, "copy_to_user failed."); return -EFAULT; } break; case ECS_IOCTL_GET_DATA: if (copy_to_user(argp, &dat_buf, sizeof(dat_buf))) { dev_err(&akm->i2c->dev, "copy_to_user failed."); return -EFAULT; } break; case ECS_IOCTL_GET_OPEN_STATUS: case ECS_IOCTL_GET_CLOSE_STATUS: status = atomic_read(&akm->active); if (copy_to_user(argp, &status, sizeof(status))) { dev_err(&akm->i2c->dev, "copy_to_user failed."); return -EFAULT; } break; case ECS_IOCTL_GET_DELAY: if (copy_to_user(argp, &delay, sizeof(delay))) { dev_err(&akm->i2c->dev, "copy_to_user failed."); return -EFAULT; } break; case ECS_IOCTL_GET_LAYOUT: if (copy_to_user(argp, &akm->pdata->layout, sizeof(akm->pdata->layout))) { dev_err(&akm->i2c->dev, "copy_to_user failed."); return -EFAULT; } break; case ECS_IOCTL_GET_ACCEL: if (copy_to_user(argp, &acc_buf, sizeof(acc_buf))) { dev_err(&akm->i2c->dev, "copy_to_user failed."); return -EFAULT; } break; default: break; } return 0; } static const struct file_operations AKECS_fops = { .owner = THIS_MODULE, .open = AKECS_Open, .release = AKECS_Release, .unlocked_ioctl = AKECS_ioctl, }; /***** akm sysfs functions ******************************************/ static int create_device_attributes( struct device *dev, struct device_attribute *attrs) { int i; int err = 0; for (i = 0 ; NULL != attrs[i].attr.name ; ++i) { err = device_create_file(dev, &attrs[i]); if (err) break; } if (err) { for (--i; i >= 0 ; --i) device_remove_file(dev, &attrs[i]); } return err; } static void remove_device_attributes( struct device *dev, struct device_attribute *attrs) { int i; for (i = 0 ; NULL != attrs[i].attr.name ; ++i) device_remove_file(dev, &attrs[i]); } static int create_device_binary_attributes( struct kobject *kobj, struct bin_attribute *attrs) { int i; int err = 0; err = 0; for (i = 0 ; NULL != attrs[i].attr.name ; ++i) { err = sysfs_create_bin_file(kobj, &attrs[i]); if (0 != err) break; } if (0 != err) { for (--i; i >= 0 ; --i) sysfs_remove_bin_file(kobj, &attrs[i]); } return err; } static void remove_device_binary_attributes( struct kobject *kobj, struct bin_attribute *attrs) { int i; for (i = 0 ; NULL != attrs[i].attr.name ; ++i) sysfs_remove_bin_file(kobj, &attrs[i]); } /********************************************************************* * * SysFS attribute functions * * directory : /sys/class/compass/akmXXXX/ * files : * - enable_acc [rw] [t] : enable flag for accelerometer * - enable_mag [rw] [t] : enable flag for magnetometer * - enable_fusion [rw] [t] : enable flag for fusion sensor * - delay_acc [rw] [t] : delay in nanosecond for accelerometer * - delay_mag [rw] [t] : delay in nanosecond for magnetometer * - delay_fusion [rw] [t] : delay in nanosecond for fusion sensor * * debug : * - mode [w] [t] : E-Compass mode * - bdata [r] [t] : buffered raw data * - asa [r] [t] : FUSEROM data * - regs [r] [t] : read all registers * * [b] = binary format * [t] = text format */ /***** sysfs enable *************************************************/ static void akm_compass_sysfs_update_status( struct akm_compass_data *akm) { uint32_t en; mutex_lock(&akm->val_mutex); en = akm->enable_flag; mutex_unlock(&akm->val_mutex); if (en == 0) { if (atomic_cmpxchg(&akm->active, 1, 0) == 1) { wake_up(&akm->open_wq); dev_dbg(akm->class_dev, "Deactivated"); } } else { if (atomic_cmpxchg(&akm->active, 0, 1) == 0) { wake_up(&akm->open_wq); dev_dbg(akm->class_dev, "Activated"); } } dev_dbg(&akm->i2c->dev, "Status updated: enable=0x%X, active=%d", en, atomic_read(&akm->active)); } static int akm_enable_set(struct sensors_classdev *sensors_cdev, unsigned int enable) { int ret = 0; struct akm_compass_data *akm = container_of(sensors_cdev, struct akm_compass_data, cdev); mutex_lock(&akm->val_mutex); akm->enable_flag &= ~(1<enable_flag |= ((uint32_t)(enable))<val_mutex); akm_compass_sysfs_update_status(akm); mutex_lock(&akm->op_mutex); if (enable) { ret = akm_compass_power_set(akm, true); if (ret) { dev_err(&akm->i2c->dev, "Power on fail! ret=%d\n", ret); goto exit; } } if (akm->use_poll && akm->pdata->auto_report) { if (enable) { AKECS_SetMode(akm, AKM_MODE_SNG_MEASURE | AKM8963_BIT_OP_16); queue_delayed_work(akm->data_wq, &akm->dwork, msecs_to_jiffies (akm->delay[MAG_DATA_FLAG])); } else { cancel_delayed_work_sync(&akm->dwork); AKECS_SetMode(akm, AKM_MODE_POWERDOWN); } } else { if (enable) enable_irq(akm->i2c->irq); else disable_irq(akm->i2c->irq); } if (!enable) { ret = akm_compass_power_set(akm, false); if (ret) { dev_err(&akm->i2c->dev, "Power off fail! ret=%d\n", ret); goto exit; } } exit: mutex_unlock(&akm->op_mutex); return ret; } static ssize_t akm_compass_sysfs_enable_show( struct akm_compass_data *akm, char *buf, int pos) { int flag; mutex_lock(&akm->val_mutex); flag = ((akm->enable_flag >> pos) & 1); mutex_unlock(&akm->val_mutex); return scnprintf(buf, PAGE_SIZE, "%d\n", flag); } static ssize_t akm_compass_sysfs_enable_store( struct akm_compass_data *akm, char const *buf, size_t count, int pos) { long en = 0; int ret = 0; if (NULL == buf) return -EINVAL; if (0 == count) return 0; if (strict_strtol(buf, AKM_BASE_NUM, &en)) return -EINVAL; en = en ? 1 : 0; mutex_lock(&akm->op_mutex); ret = akm_compass_power_set(akm, en); if (ret) { dev_err(&akm->i2c->dev, "Fail to configure device power!\n"); goto exit; } mutex_lock(&akm->val_mutex); akm->enable_flag &= ~(1<enable_flag |= ((uint32_t)(en))<val_mutex); akm_compass_sysfs_update_status(akm); exit: mutex_unlock(&akm->op_mutex); return ret ? ret : count; } /***** Acceleration ***/ static ssize_t akm_enable_acc_show( struct device *dev, struct device_attribute *attr, char *buf) { return akm_compass_sysfs_enable_show( dev_get_drvdata(dev), buf, ACC_DATA_FLAG); } static ssize_t akm_enable_acc_store( struct device *dev, struct device_attribute *attr, char const *buf, size_t count) { return akm_compass_sysfs_enable_store( dev_get_drvdata(dev), buf, count, ACC_DATA_FLAG); } /***** Magnetic field ***/ static ssize_t akm_enable_mag_show( struct device *dev, struct device_attribute *attr, char *buf) { return akm_compass_sysfs_enable_show( dev_get_drvdata(dev), buf, MAG_DATA_FLAG); } static ssize_t akm_enable_mag_store( struct device *dev, struct device_attribute *attr, char const *buf, size_t count) { return akm_compass_sysfs_enable_store( dev_get_drvdata(dev), buf, count, MAG_DATA_FLAG); } /***** Fusion ***/ static ssize_t akm_enable_fusion_show( struct device *dev, struct device_attribute *attr, char *buf) { return akm_compass_sysfs_enable_show( dev_get_drvdata(dev), buf, FUSION_DATA_FLAG); } static ssize_t akm_enable_fusion_store( struct device *dev, struct device_attribute *attr, char const *buf, size_t count) { return akm_compass_sysfs_enable_store( dev_get_drvdata(dev), buf, count, FUSION_DATA_FLAG); } /***** sysfs delay **************************************************/ static int akm_poll_delay_set(struct sensors_classdev *sensors_cdev, unsigned int delay_msec) { struct akm_compass_data *akm = container_of(sensors_cdev, struct akm_compass_data, cdev); mutex_lock(&akm->val_mutex); akm->delay[MAG_DATA_FLAG] = delay_msec; mutex_unlock(&akm->val_mutex); return 0; } static ssize_t akm_compass_sysfs_delay_show( struct akm_compass_data *akm, char *buf, int pos) { unsigned long val; mutex_lock(&akm->val_mutex); val = akm->delay[pos]; mutex_unlock(&akm->val_mutex); return scnprintf(buf, PAGE_SIZE, "%lu\n", val); } static ssize_t akm_compass_sysfs_delay_store( struct akm_compass_data *akm, char const *buf, size_t count, int pos) { unsigned long val = 0; if (NULL == buf) return -EINVAL; if (0 == count) return 0; if (kstrtoul(buf, AKM_BASE_NUM, &val)) return -EINVAL; mutex_lock(&akm->val_mutex); akm->delay[pos] = val; mutex_unlock(&akm->val_mutex); return count; } /***** Accelerometer ***/ static ssize_t akm_delay_acc_show( struct device *dev, struct device_attribute *attr, char *buf) { return akm_compass_sysfs_delay_show( dev_get_drvdata(dev), buf, ACC_DATA_FLAG); } static ssize_t akm_delay_acc_store( struct device *dev, struct device_attribute *attr, char const *buf, size_t count) { return akm_compass_sysfs_delay_store( dev_get_drvdata(dev), buf, count, ACC_DATA_FLAG); } /***** Magnetic field ***/ static ssize_t akm_delay_mag_show( struct device *dev, struct device_attribute *attr, char *buf) { return akm_compass_sysfs_delay_show( dev_get_drvdata(dev), buf, MAG_DATA_FLAG); } static ssize_t akm_delay_mag_store( struct device *dev, struct device_attribute *attr, char const *buf, size_t count) { return akm_compass_sysfs_delay_store( dev_get_drvdata(dev), buf, count, MAG_DATA_FLAG); } /***** Fusion ***/ static ssize_t akm_delay_fusion_show( struct device *dev, struct device_attribute *attr, char *buf) { return akm_compass_sysfs_delay_show( dev_get_drvdata(dev), buf, FUSION_DATA_FLAG); } static ssize_t akm_delay_fusion_store( struct device *dev, struct device_attribute *attr, char const *buf, size_t count) { return akm_compass_sysfs_delay_store( dev_get_drvdata(dev), buf, count, FUSION_DATA_FLAG); } /***** accel (binary) ***/ static ssize_t akm_bin_accel_write( struct file *file, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t pos, size_t size) { struct device *dev = container_of(kobj, struct device, kobj); struct akm_compass_data *akm = dev_get_drvdata(dev); int16_t *accel_data; if (size == 0) return 0; accel_data = (int16_t *)buf; mutex_lock(&akm->accel_mutex); akm->accel_data[0] = accel_data[0]; akm->accel_data[1] = accel_data[1]; akm->accel_data[2] = accel_data[2]; mutex_unlock(&akm->accel_mutex); dev_vdbg(&akm->i2c->dev, "accel:%d,%d,%d\n", accel_data[0], accel_data[1], accel_data[2]); return size; } #if AKM_DEBUG_IF static ssize_t akm_sysfs_mode_store( struct device *dev, struct device_attribute *attr, char const *buf, size_t count) { struct akm_compass_data *akm = dev_get_drvdata(dev); long mode = 0; if (NULL == buf) return -EINVAL; if (0 == count) return 0; if (strict_strtol(buf, AKM_BASE_NUM, &mode)) return -EINVAL; if (AKECS_SetMode(akm, (uint8_t)mode) < 0) return -EINVAL; return 1; } static ssize_t akm_buf_print( char *buf, uint8_t *data, size_t num) { int sz, i; char *cur; size_t cur_len; cur = buf; cur_len = PAGE_SIZE; sz = snprintf(cur, cur_len, "(HEX):"); if (sz < 0) return sz; cur += sz; cur_len -= sz; for (i = 0; i < num; i++) { sz = snprintf(cur, cur_len, "%02X,", *data); if (sz < 0) return sz; cur += sz; cur_len -= sz; data++; } sz = snprintf(cur, cur_len, "\n"); if (sz < 0) return sz; cur += sz; return (ssize_t)(cur - buf); } static ssize_t akm_sysfs_bdata_show( struct device *dev, struct device_attribute *attr, char *buf) { struct akm_compass_data *akm = dev_get_drvdata(dev); uint8_t rbuf[AKM_SENSOR_DATA_SIZE]; mutex_lock(&akm->sensor_mutex); memcpy(&rbuf, akm->sense_data, sizeof(rbuf)); mutex_unlock(&akm->sensor_mutex); return akm_buf_print(buf, rbuf, AKM_SENSOR_DATA_SIZE); } static ssize_t akm_sysfs_asa_show( struct device *dev, struct device_attribute *attr, char *buf) { struct akm_compass_data *akm = dev_get_drvdata(dev); int err; uint8_t asa[3]; err = AKECS_SetMode(akm, AKM_MODE_FUSE_ACCESS); if (err < 0) return err; asa[0] = AKM_FUSE_1ST_ADDR; err = akm_i2c_rxdata(akm->i2c, asa, 3); if (err < 0) return err; err = AKECS_SetMode(akm, AKM_MODE_POWERDOWN); if (err < 0) return err; return akm_buf_print(buf, asa, 3); } static ssize_t akm_sysfs_regs_show( struct device *dev, struct device_attribute *attr, char *buf) { /* The total number of registers depends on the device. */ struct akm_compass_data *akm = dev_get_drvdata(dev); int err; uint8_t regs[AKM_REGS_SIZE]; /* This function does not lock mutex obj */ regs[0] = AKM_REGS_1ST_ADDR; err = akm_i2c_rxdata(akm->i2c, regs, AKM_REGS_SIZE); if (err < 0) return err; return akm_buf_print(buf, regs, AKM_REGS_SIZE); } #endif static struct device_attribute akm_compass_attributes[] = { __ATTR(enable_acc, 0660, akm_enable_acc_show, akm_enable_acc_store), __ATTR(enable_mag, 0660, akm_enable_mag_show, akm_enable_mag_store), __ATTR(enable_fusion, 0660, akm_enable_fusion_show, akm_enable_fusion_store), __ATTR(delay_acc, 0660, akm_delay_acc_show, akm_delay_acc_store), __ATTR(delay_mag, 0660, akm_delay_mag_show, akm_delay_mag_store), __ATTR(delay_fusion, 0660, akm_delay_fusion_show, akm_delay_fusion_store), #if AKM_DEBUG_IF __ATTR(mode, 0220, NULL, akm_sysfs_mode_store), __ATTR(bdata, 0440, akm_sysfs_bdata_show, NULL), __ATTR(asa, 0440, akm_sysfs_asa_show, NULL), __ATTR(regs, 0440, akm_sysfs_regs_show, NULL), #endif __ATTR_NULL, }; #define __BIN_ATTR(name_, mode_, size_, private_, read_, write_) \ { \ .attr = { .name = __stringify(name_), .mode = mode_ }, \ .size = size_, \ .private = private_, \ .read = read_, \ .write = write_, \ } #define __BIN_ATTR_NULL \ { \ .attr = { .name = NULL }, \ } static struct bin_attribute akm_compass_bin_attributes[] = { __BIN_ATTR(accel, 0220, 6, NULL, NULL, akm_bin_accel_write), __BIN_ATTR_NULL }; static char const *const device_link_name = "i2c"; static dev_t const akm_compass_device_dev_t = MKDEV(MISC_MAJOR, 240); static int create_sysfs_interfaces(struct akm_compass_data *akm) { int err; if (NULL == akm) return -EINVAL; err = 0; akm->compass = class_create(THIS_MODULE, AKM_SYSCLS_NAME); if (IS_ERR(akm->compass)) { err = PTR_ERR(akm->compass); goto exit_class_create_failed; } akm->class_dev = device_create( akm->compass, NULL, akm_compass_device_dev_t, akm, AKM_SYSDEV_NAME); if (IS_ERR(akm->class_dev)) { err = PTR_ERR(akm->class_dev); goto exit_class_device_create_failed; } err = sysfs_create_link( &akm->class_dev->kobj, &akm->i2c->dev.kobj, device_link_name); if (0 > err) goto exit_sysfs_create_link_failed; err = create_device_attributes( akm->class_dev, akm_compass_attributes); if (0 > err) goto exit_device_attributes_create_failed; err = create_device_binary_attributes( &akm->class_dev->kobj, akm_compass_bin_attributes); if (0 > err) goto exit_device_binary_attributes_create_failed; return err; exit_device_binary_attributes_create_failed: remove_device_attributes(akm->class_dev, akm_compass_attributes); exit_device_attributes_create_failed: sysfs_remove_link(&akm->class_dev->kobj, device_link_name); exit_sysfs_create_link_failed: device_destroy(akm->compass, akm_compass_device_dev_t); exit_class_device_create_failed: akm->class_dev = NULL; class_destroy(akm->compass); exit_class_create_failed: akm->compass = NULL; return err; } static void remove_sysfs_interfaces(struct akm_compass_data *akm) { if (NULL == akm) return; if (NULL != akm->class_dev) { remove_device_binary_attributes( &akm->class_dev->kobj, akm_compass_bin_attributes); remove_device_attributes( akm->class_dev, akm_compass_attributes); sysfs_remove_link( &akm->class_dev->kobj, device_link_name); akm->class_dev = NULL; } if (NULL != akm->compass) { device_destroy( akm->compass, akm_compass_device_dev_t); class_destroy(akm->compass); akm->compass = NULL; } } /***** akm input device functions ***********************************/ static int akm_compass_input_init( struct input_dev **input) { int err = 0; /* Declare input device */ *input = input_allocate_device(); if (!*input) return -ENOMEM; /* Setup input device */ set_bit(EV_ABS, (*input)->evbit); /* Magnetic field (limited to 16bit) */ input_set_abs_params(*input, ABS_X, -32768, 32767, 0, 0); input_set_abs_params(*input, ABS_Y, -32768, 32767, 0, 0); input_set_abs_params(*input, ABS_Z, -32768, 32767, 0, 0); /* Set name */ (*input)->name = AKM_INPUT_DEVICE_NAME; /* Register */ err = input_register_device(*input); if (err) { input_free_device(*input); return err; } return err; } /***** akm functions ************************************************/ static irqreturn_t akm_compass_irq(int irq, void *handle) { struct akm_compass_data *akm = handle; uint8_t buffer[AKM_SENSOR_DATA_SIZE]; int err; memset(buffer, 0, sizeof(buffer)); /***** lock *****/ mutex_lock(&akm->sensor_mutex); /* Read whole data */ buffer[0] = AKM_REG_STATUS; err = akm_i2c_rxdata(akm->i2c, buffer, AKM_SENSOR_DATA_SIZE); if (err < 0) { dev_err(&akm->i2c->dev, "IRQ I2C error."); akm->is_busy = 0; mutex_unlock(&akm->sensor_mutex); /***** unlock *****/ return IRQ_HANDLED; } /* Check ST bit */ if (!(AKM_DRDY_IS_HIGH(buffer[0]))) goto work_func_none; memcpy(akm->sense_data, buffer, AKM_SENSOR_DATA_SIZE); akm->is_busy = 0; mutex_unlock(&akm->sensor_mutex); /***** unlock *****/ atomic_set(&akm->drdy, 1); wake_up(&akm->drdy_wq); dev_vdbg(&akm->i2c->dev, "IRQ handled."); return IRQ_HANDLED; work_func_none: mutex_unlock(&akm->sensor_mutex); /***** unlock *****/ dev_vdbg(&akm->i2c->dev, "IRQ not handled."); return IRQ_NONE; } static int akm_compass_suspend(struct device *dev) { struct akm_compass_data *akm = dev_get_drvdata(dev); int ret = 0; if (AKM_IS_MAG_DATA_ENABLED() && akm->use_poll && akm->pdata->auto_report) cancel_delayed_work_sync(&akm->dwork); akm->state.power_on = akm->power_enabled; if (akm->state.power_on) { akm_compass_power_set(akm, false); /* Clear status */ akm->is_busy = 0; atomic_set(&akm->drdy, 0); } ret = pinctrl_select_state(akm->pinctrl, akm->pin_sleep); if (ret) dev_err(dev, "Can't select pinctrl state\n"); dev_dbg(&akm->i2c->dev, "suspended\n"); return 0; } static int akm_compass_resume(struct device *dev) { struct akm_compass_data *akm = dev_get_drvdata(dev); int ret = 0; ret = pinctrl_select_state(akm->pinctrl, akm->pin_default); if (ret) dev_err(dev, "Can't select pinctrl state\n"); if (akm->state.power_on) { ret = akm_compass_power_set(akm, true); if (ret) { dev_err(dev, "Sensor power resume fail!\n"); goto exit; } ret = AKECS_SetMode(akm, akm->state.mode); if (ret) { dev_err(dev, "Sensor state resume fail!\n"); goto exit; } if (AKM_IS_MAG_DATA_ENABLED() && akm->use_poll && akm->pdata->auto_report) queue_delayed_work(akm->data_wq, &akm->dwork, (unsigned long)nsecs_to_jiffies64( akm->delay[MAG_DATA_FLAG])); } dev_dbg(&akm->i2c->dev, "resumed\n"); exit: return 0; } static int akm8963_i2c_check_device( struct i2c_client *client) { /* AK8963 specific function */ struct akm_compass_data *akm = i2c_get_clientdata(client); int err; akm->sense_info[0] = AK8963_REG_WIA; err = akm_i2c_rxdata(client, akm->sense_info, AKM_SENSOR_INFO_SIZE); if (err < 0) return err; /* Set FUSE access mode */ err = AKECS_SetMode(akm, AK8963_MODE_FUSE_ACCESS); if (err < 0) return err; akm->sense_conf[0] = AK8963_FUSE_ASAX; err = akm_i2c_rxdata(client, akm->sense_conf, AKM_SENSOR_CONF_SIZE); if (err < 0) return err; err = AKECS_SetMode(akm, AK8963_MODE_POWERDOWN); if (err < 0) return err; /* Check read data */ if (akm->sense_info[0] != AK8963_WIA_VALUE) { dev_err(&client->dev, "%s: The device is not AKM Compass.", __func__); return -ENXIO; } return err; } static int akm_compass_power_set(struct akm_compass_data *data, bool on) { int rc = 0; if (!on && data->power_enabled) { rc = regulator_disable(data->vdd); if (rc) { dev_err(&data->i2c->dev, "Regulator vdd disable failed rc=%d\n", rc); goto err_vdd_disable; } rc = regulator_disable(data->vio); if (rc) { dev_err(&data->i2c->dev, "Regulator vio disable failed rc=%d\n", rc); goto err_vio_disable; } data->power_enabled = false; return rc; } else if (on && !data->power_enabled) { rc = regulator_enable(data->vdd); if (rc) { dev_err(&data->i2c->dev, "Regulator vdd enable failed rc=%d\n", rc); goto err_vdd_enable; } rc = regulator_enable(data->vio); if (rc) { dev_err(&data->i2c->dev, "Regulator vio enable failed rc=%d\n", rc); goto err_vio_enable; } data->power_enabled = true; /* * The max time for the power supply rise time is 50ms. * Use 80ms to make sure it meets the requirements. */ msleep(80); return rc; } else { dev_warn(&data->i2c->dev, "Power on=%d. enabled=%d\n", on, data->power_enabled); return rc; } err_vio_enable: regulator_disable(data->vio); err_vdd_enable: return rc; err_vio_disable: if (regulator_enable(data->vdd)) dev_warn(&data->i2c->dev, "Regulator vdd enable failed\n"); err_vdd_disable: return rc; } static int akm_compass_power_init(struct akm_compass_data *data, bool on) { int rc; if (!on) { if (regulator_count_voltages(data->vdd) > 0) regulator_set_voltage(data->vdd, 0, AKM8963_VDD_MAX_UV); regulator_put(data->vdd); if (regulator_count_voltages(data->vio) > 0) regulator_set_voltage(data->vio, 0, AKM8963_VIO_MAX_UV); regulator_put(data->vio); } else { data->vdd = regulator_get(&data->i2c->dev, "vdd"); if (IS_ERR(data->vdd)) { rc = PTR_ERR(data->vdd); dev_err(&data->i2c->dev, "Regulator get failed vdd rc=%d\n", rc); return rc; } if (regulator_count_voltages(data->vdd) > 0) { rc = regulator_set_voltage(data->vdd, AKM8963_VDD_MIN_UV, AKM8963_VDD_MAX_UV); if (rc) { dev_err(&data->i2c->dev, "Regulator set failed vdd rc=%d\n", rc); goto reg_vdd_put; } } data->vio = regulator_get(&data->i2c->dev, "vio"); if (IS_ERR(data->vio)) { rc = PTR_ERR(data->vio); dev_err(&data->i2c->dev, "Regulator get failed vio rc=%d\n", rc); goto reg_vdd_set; } if (regulator_count_voltages(data->vio) > 0) { rc = regulator_set_voltage(data->vio, AKM8963_VIO_MIN_UV, AKM8963_VIO_MAX_UV); if (rc) { dev_err(&data->i2c->dev, "Regulator set failed vio rc=%d\n", rc); goto reg_vio_put; } } } return 0; reg_vio_put: regulator_put(data->vio); reg_vdd_set: if (regulator_count_voltages(data->vdd) > 0) regulator_set_voltage(data->vdd, 0, AKM8963_VDD_MAX_UV); reg_vdd_put: regulator_put(data->vdd); return rc; } #ifdef CONFIG_OF static int akm_compass_parse_dt(struct device *dev, struct akm8963_platform_data *pdata) { struct device_node *np = dev->of_node; u32 temp_val; int rc; rc = of_property_read_u32(np, "ak,layout", &temp_val); if (rc && (rc != -EINVAL)) { dev_err(dev, "Unable to read akm,layout\n"); return rc; } else { pdata->layout = temp_val; } if (of_property_read_bool(np, "ak,auto-report")) { pdata->auto_report = 1; pdata->use_int = 0; } else { pdata->auto_report = 0; if (of_property_read_bool(dev->of_node, "ak,use-interrupt")) { pdata->use_int = 1; /* check gpio_int later, if it is invalid, * just use poll */ pdata->gpio_int = of_get_named_gpio_flags(dev->of_node, "ak,gpio-int", 0, &pdata->int_flags); } else { pdata->use_int = 0; } } pdata->gpio_rstn = of_get_named_gpio_flags(dev->of_node, "ak,gpio-rstn", 0, NULL); return 0; } #else static int akm_compass_parse_dt(struct device *dev, struct akm8963_platform_data *pdata) { return -EINVAL; } #endif /* !CONFIG_OF */ static int akm8963_pinctrl_init(struct akm_compass_data *s_akm) { struct i2c_client *client = s_akm->i2c; s_akm->pinctrl = devm_pinctrl_get(&client->dev); if (IS_ERR_OR_NULL(s_akm->pinctrl)) { dev_err(&client->dev, "Failed to get pinctrl\n"); return PTR_ERR(s_akm->pinctrl); } s_akm->pin_default = pinctrl_lookup_state(s_akm->pinctrl, "ak8963_default"); if (IS_ERR_OR_NULL(s_akm->pin_default)) { dev_err(&client->dev, "Failed to look up default state\n"); return PTR_ERR(s_akm->pin_default); } s_akm->pin_sleep = pinctrl_lookup_state(s_akm->pinctrl, "ak8963_sleep"); if (IS_ERR_OR_NULL(s_akm->pin_sleep)) { dev_err(&client->dev, "Failed to look up sleep state\n"); return PTR_ERR(s_akm->pin_sleep); } return 0; } static void akm_dev_poll(struct work_struct *work) { struct akm_compass_data *akm; uint8_t dat_buf[AKM_SENSOR_DATA_SIZE];/* for GET_DATA */ int ret; int mag_x, mag_y, mag_z; int tmp; akm = container_of((struct delayed_work *)work, struct akm_compass_data, dwork); ret = AKECS_GetData_Poll(akm, dat_buf, AKM_SENSOR_DATA_SIZE); if (ret < 0) { dev_warn(&s_akm->i2c->dev, "Get data failed\n"); goto exit; } tmp = 0xF & (dat_buf[7] + dat_buf[0]); if (STATUS_ERROR(tmp)) { dev_warn(&akm->i2c->dev, "Status error(0x%x). Reset...\n", tmp); AKECS_Reset(akm, 0); goto exit; } tmp = (int)((int16_t)(dat_buf[2]<<8)+((int16_t)dat_buf[1])); tmp = tmp * akm->sense_conf[0] / 256 + tmp / 2; mag_x = tmp; tmp = (int)((int16_t)(dat_buf[4]<<8)+((int16_t)dat_buf[3])); tmp = tmp * akm->sense_conf[1] / 256 + tmp / 2; mag_y = tmp; tmp = (int)((int16_t)(dat_buf[6]<<8)+((int16_t)dat_buf[5])); tmp = tmp * akm->sense_conf[2] / 256 + tmp / 2; mag_z = tmp; switch (akm->pdata->layout) { case 0: case 1: /* Fall into the default direction */ break; case 2: tmp = mag_x; mag_x = mag_y; mag_y = -tmp; break; case 3: mag_x = -mag_x; mag_y = -mag_y; break; case 4: tmp = mag_x; mag_x = -mag_y; mag_y = tmp; break; case 5: mag_x = -mag_x; mag_z = -mag_z; break; case 6: tmp = mag_x; mag_x = mag_y; mag_y = tmp; mag_z = -mag_z; break; case 7: mag_y = -mag_y; mag_z = -mag_z; break; case 8: tmp = mag_x; mag_x = -mag_y; mag_y = -tmp; mag_z = -mag_z; break; } input_report_abs(akm->input, ABS_X, mag_x); input_report_abs(akm->input, ABS_Y, mag_y); input_report_abs(akm->input, ABS_Z, mag_z); input_sync(akm->input); dev_vdbg(&s_akm->i2c->dev, "input report: mag_x=%02x, mag_y=%02x, mag_z=%02x", mag_x, mag_y, mag_z); exit: ret = AKECS_SetMode(akm, AKM_MODE_SNG_MEASURE | AKM8963_BIT_OP_16); if (ret < 0) dev_warn(&akm->i2c->dev, "Failed to set mode\n"); if (akm->use_poll) queue_delayed_work(akm->data_wq, &akm->dwork, msecs_to_jiffies(akm->delay[MAG_DATA_FLAG])); } int akm8963_compass_probe( struct i2c_client *i2c, const struct i2c_device_id *id) { struct akm8963_platform_data *pdata; int err = 0; int i; dev_dbg(&i2c->dev, "start probing."); if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { dev_err(&i2c->dev, "%s: check_functionality failed.", __func__); err = -ENODEV; goto err_i2c_check; } /* Allocate memory for driver data */ s_akm = devm_kzalloc(&i2c->dev, sizeof(struct akm_compass_data), GFP_KERNEL); if (!s_akm) { dev_err(&i2c->dev, "Failed to allocate driver data\n"); return -ENOMEM; } /***** I2C initialization *****/ s_akm->i2c = i2c; /* set i2c data */ i2c_set_clientdata(i2c, s_akm); /**** initialize variables in akm_compass_data *****/ init_waitqueue_head(&s_akm->drdy_wq); init_waitqueue_head(&s_akm->open_wq); mutex_init(&s_akm->sensor_mutex); mutex_init(&s_akm->accel_mutex); mutex_init(&s_akm->val_mutex); mutex_init(&s_akm->op_mutex); atomic_set(&s_akm->active, 0); atomic_set(&s_akm->drdy, 0); s_akm->is_busy = 0; s_akm->enable_flag = 0; /* Set to 1G in Android coordination, AKSC format */ s_akm->accel_data[0] = 0; s_akm->accel_data[1] = 0; s_akm->accel_data[2] = 720; for (i = 0; i < AKM_NUM_SENSORS; i++) s_akm->delay[i] = 0; if (i2c->dev.of_node) { pdata = devm_kzalloc( &i2c->dev, sizeof(struct akm8963_platform_data), GFP_KERNEL); if (!pdata) { dev_err(&i2c->dev, "Failed to allcated memory\n"); err = -ENOMEM; goto err_devm; } err = akm_compass_parse_dt(&i2c->dev, pdata); if (err) { dev_err( &i2c->dev, "Unable to parse platfrom data err=%d\n", err); goto err_devm; } } else { if (i2c->dev.platform_data) { /* Copy platform data to local. */ pdata = i2c->dev.platform_data; } else { /* Platform data is not available. Layout and information should be set by each application. */ s_akm->pdata->layout = 0; s_akm->gpio_rstn = 0; dev_warn(&i2c->dev, "%s: No platform data.", __func__); } } s_akm->pdata = pdata; /* check connection */ err = akm_compass_power_init(s_akm, true); if (err < 0) goto err_devm; err = akm_compass_power_set(s_akm, true); if (err < 0) goto err_compass_pwr_init; /* Pull up the reset pin */ AKECS_Reset(s_akm, 1); err = akm8963_i2c_check_device(i2c); if (err < 0) goto err_compass_pwr_off; /***** input *****/ err = akm_compass_input_init(&s_akm->input); if (err) { dev_err(&i2c->dev, "%s: input_dev register failed", __func__); goto err_compass_pwr_off; } input_set_drvdata(s_akm->input, s_akm); /* initialize pinctrl */ if (!akm8963_pinctrl_init(s_akm)) { err = pinctrl_select_state(s_akm->pinctrl, s_akm->pin_default); if (err) { dev_err(&i2c->dev, "Can't select pinctrl state\n"); goto err_unregister_device; } } s_akm->data_wq = NULL; if ((s_akm->pdata->use_int) && gpio_is_valid(s_akm->pdata->gpio_int)) { s_akm->use_poll = false; /* configure interrupt gpio */ err = gpio_request(s_akm->pdata->gpio_int, "akm8963_gpio_int"); if (err) { dev_err( &i2c->dev, "Unable to request interrupt gpio %d\n", s_akm->pdata->gpio_int); goto err_unregister_device; } err = gpio_direction_input(s_akm->pdata->gpio_int); if (err) { dev_err( &i2c->dev, "Unable to set direction for gpio %d\n", s_akm->pdata->gpio_int); goto err_gpio_free; } i2c->irq = gpio_to_irq(s_akm->pdata->gpio_int); /***** IRQ setup *****/ s_akm->i2c->irq = i2c->irq; dev_dbg(&i2c->dev, "%s: IRQ is #%d.", __func__, s_akm->i2c->irq); err = request_threaded_irq( s_akm->i2c->irq, NULL, akm_compass_irq, IRQF_TRIGGER_HIGH|IRQF_ONESHOT, dev_name(&i2c->dev), s_akm); if (err) { dev_err(&i2c->dev, "%s: request irq failed.", __func__); goto err_gpio_free; } } else if (s_akm->pdata->auto_report) { s_akm->use_poll = true; s_akm->data_wq = create_freezable_workqueue("akm8963_data_work"); if (!s_akm->data_wq) { dev_err(&i2c->dev, "Cannot create workqueue!\n"); goto err_unregister_device; } INIT_DELAYED_WORK(&s_akm->dwork, akm_dev_poll); } /***** sysfs *****/ err = create_sysfs_interfaces(s_akm); if (0 > err) { dev_err(&i2c->dev, "%s: create sysfs failed.", __func__); goto err_destroy_workqueue; } s_akm->cdev = sensors_cdev; s_akm->cdev.sensors_enable = akm_enable_set; s_akm->cdev.sensors_poll_delay = akm_poll_delay_set; s_akm->delay[MAG_DATA_FLAG] = sensors_cdev.delay_msec; err = sensors_classdev_register(&i2c->dev, &s_akm->cdev); if (err) { dev_err(&i2c->dev, "class device create failed: %d\n", err); goto remove_sysfs; } err = akm_compass_power_set(s_akm, false); if (err) dev_err(&i2c->dev, "Fail to disable power after probe: %d\n", err); dev_info(&i2c->dev, "successfully probed."); return 0; remove_sysfs: remove_sysfs_interfaces(s_akm); err_destroy_workqueue: if (s_akm->data_wq) destroy_workqueue(s_akm->data_wq); if (s_akm->i2c->irq) free_irq(s_akm->i2c->irq, s_akm); err_gpio_free: if ((s_akm->pdata->use_int) && (gpio_is_valid(s_akm->pdata->gpio_int))) gpio_free(s_akm->pdata->gpio_int); err_unregister_device: input_unregister_device(s_akm->input); err_compass_pwr_off: akm_compass_power_set(s_akm, false); err_compass_pwr_init: akm_compass_power_init(s_akm, false); err_devm: devm_kfree(&i2c->dev, s_akm); err_i2c_check: return err; } static int akm8963_compass_remove(struct i2c_client *i2c) { struct akm_compass_data *akm = i2c_get_clientdata(i2c); if (akm_compass_power_set(akm, false)) dev_err(&i2c->dev, "power off failed.\n"); if (akm_compass_power_init(akm, false)) dev_err(&i2c->dev, "power deinit failed.\n"); remove_sysfs_interfaces(akm); if (akm->data_wq) destroy_workqueue(s_akm->data_wq); if (akm->i2c->irq) free_irq(akm->i2c->irq, akm); if ((s_akm->pdata->use_int) && (gpio_is_valid(s_akm->pdata->gpio_int))) gpio_free(s_akm->pdata->gpio_int); input_unregister_device(akm->input); devm_kfree(&i2c->dev, akm); dev_info(&i2c->dev, "successfully removed."); return 0; } static const struct i2c_device_id akm8963_compass_id[] = { {AKM_I2C_NAME, 0 }, { } }; static const struct dev_pm_ops akm_compass_pm_ops = { .suspend = akm_compass_suspend, .resume = akm_compass_resume, }; static struct of_device_id akm8963_match_table[] = { { .compatible = "ak,ak8963", }, { .compatible = "akm,akm8963", }, { }, }; static struct i2c_driver akm_compass_driver = { .probe = akm8963_compass_probe, .remove = akm8963_compass_remove, .id_table = akm8963_compass_id, .driver = { .name = AKM_I2C_NAME, .owner = THIS_MODULE, .of_match_table = akm8963_match_table, .pm = &akm_compass_pm_ops, }, }; static int __init akm_compass_init(void) { pr_info("AKM compass driver: initialize."); return i2c_add_driver(&akm_compass_driver); } static void __exit akm_compass_exit(void) { pr_info("AKM compass driver: release."); i2c_del_driver(&akm_compass_driver); } module_init(akm_compass_init); module_exit(akm_compass_exit); MODULE_AUTHOR("viral wang "); MODULE_DESCRIPTION("AKM compass driver"); MODULE_LICENSE("GPL");