drivers: misc: Import AKM09970 driver

* From dagu-s-oss
* Run clang-format on source files

Change-Id: Ib9b9e82a3988116a9bf1121b50e006820d197ac1
This commit is contained in:
Sebastiano Barezzi
2022-11-15 16:13:14 +01:00
committed by Sebastiano Barezzi
parent a8c0b9f5ce
commit 194a28afb5
4 changed files with 1022 additions and 0 deletions

View File

@@ -631,6 +631,11 @@ config KINECTICS_XR_NORDIC
this also parses the gpios and interrupts from device tree and sets
the gpios and interrupt handler for handling the interrupt.
config HALL_AKM09970
tristate "AKM09970 HALL sensor driver"
help
Say Y here if you want to enable AKM09970 HALL sensor driver.
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"

View File

@@ -78,3 +78,5 @@ obj-$(CONFIG_QTI_XR_SMRTVWR_MISC) += qxr-stdalonevwr.o
obj-$(CONFIG_FPR_FPC) += fpr_FingerprintCard/
obj-y += qrc/
obj-$(CONFIG_KINECTICS_XR_NORDIC) += kxrctrl/
obj-$(CONFIG_HALL_AKM09970) += akm09970.o

918
drivers/misc/akm09970.c Normal file
View File

@@ -0,0 +1,918 @@
/* drivers/intput/misc/akm09970.c - akm09970 compass driver
*
* Copyright (c) 2018-2019, Linux Foundation. All rights reserved.
*
* 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 pr_fmt(fmt) "akm09970: %s: %d " fmt, __func__, __LINE__
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/regmap.h>
#include <linux/atomic.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/unistd.h>
#include <linux/initrd.h>
#include <linux/init.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/hrtimer.h>
#include <linux/regulator/consumer.h>
#include "linux/akm09970.h"
#define CLEAR_IRQ_TIME 500
static DECLARE_WAIT_QUEUE_HEAD(poll_wait_queue);
struct akm09970_soc_ctrl {
uint8_t chip_info[AKM_SENSOR_INFO_SIZE];
uint8_t chip_data[AKM_SENSOR_DATA_SIZE];
uint8_t measure_range;
uint32_t measure_freq_hz;
int gpio_reset;
int gpio_irq;
int irq;
bool read_flag;
atomic_t power_enabled;
atomic_t data_ready;
dev_t dev_num;
struct device *dev;
struct cdev cdev;
struct class *chr_class;
struct device *chr_dev;
struct device_node *of_node;
struct i2c_client *client;
struct pinctrl *pinctrl;
struct pinctrl_state *gpio_state_active;
struct pinctrl_state *gpio_state_suspend;
struct regulator *vdd;
struct hrtimer timer;
struct work_struct report_work;
struct workqueue_struct *work_queue;
struct akm09970_platform_data pdata;
};
static int akm09970_write_byte(struct i2c_client *client, u8 reg, u8 val)
{
int rc;
if (client->irq)
disable_irq_nosync(client->irq);
rc = i2c_smbus_write_byte_data(client, reg, val);
if (client->irq)
enable_irq(client->irq);
return rc;
}
static int akm09970_set_reg_bits(struct i2c_client *client, int val, int shift,
u8 mask, u8 reg)
{
int data;
data = i2c_smbus_read_byte_data(client, reg);
if (data < 0)
return data;
data = (data & ~mask) | ((val << shift) & mask);
pr_debug("reg: 0x%x, data: 0x%x\n", reg, data);
return akm09970_write_byte(client, reg, data);
}
static int akm09970_set_mode(struct akm09970_soc_ctrl *c_ctrl, uint8_t mode)
{
int rc;
rc = akm09970_set_reg_bits(c_ctrl->client, mode, AK09970_MODE_POS,
AK09970_MODE_MSK, AK09970_MODE_REG);
return rc;
}
static enum hrtimer_restart hrtimer_handler(struct hrtimer *timer_t)
{
struct akm09970_soc_ctrl *c_ctrl =
container_of(timer_t, struct akm09970_soc_ctrl, timer);
if (false == c_ctrl->read_flag) {
queue_work(c_ctrl->work_queue, &c_ctrl->report_work);
hrtimer_forward_now(&c_ctrl->timer,
ktime_set(CLEAR_IRQ_TIME / MSEC_PER_SEC,
(CLEAR_IRQ_TIME % MSEC_PER_SEC) *
NSEC_PER_MSEC));
return HRTIMER_RESTART;
} else {
return HRTIMER_NORESTART;
}
}
static void akm09970_reset(struct akm09970_soc_ctrl *c_ctrl)
{
gpio_set_value(c_ctrl->gpio_reset, 0);
udelay(50);
gpio_set_value(c_ctrl->gpio_reset, 1);
udelay(100);
return;
}
static int akm09970_power_down(struct akm09970_soc_ctrl *c_ctrl)
{
int rc = 0;
if (atomic_read(&c_ctrl->power_enabled)) {
gpio_set_value(c_ctrl->gpio_reset, 0);
rc = regulator_disable(c_ctrl->vdd);
if (rc) {
pr_err("Regulator vdd disable failed rc=%d\n", rc);
}
atomic_set(&c_ctrl->power_enabled, 0);
pr_err("Power down successfully");
}
return rc;
}
static int akm09970_power_up(struct akm09970_soc_ctrl *c_ctrl)
{
int rc = 0;
if (!atomic_read(&c_ctrl->power_enabled)) {
rc = regulator_enable(c_ctrl->vdd);
if (rc) {
pr_err("Regulator vdd enable failed rc=%d\n", rc);
return rc;
}
udelay(20);
akm09970_reset(c_ctrl);
atomic_set(&c_ctrl->power_enabled, 1);
pr_err("Power up successfully");
}
return rc;
}
static int akm09970_active(struct akm09970_soc_ctrl *c_ctrl, bool on)
{
int rc = 0;
uint8_t mode = 0x00;
pr_info("akm sensor %s\n", on ? "on" : "off");
if (!atomic_read(&c_ctrl->power_enabled) && on) {
rc = akm09970_power_up(c_ctrl);
if (rc) {
pr_err("Sensor power up fail!\n");
return rc;
}
if (c_ctrl->measure_freq_hz >= 100)
mode = AK09970_MODE_CONTINUOUS_100HZ;
else if (c_ctrl->measure_freq_hz >= 50 &&
c_ctrl->measure_freq_hz < 100)
mode = AK09970_MODE_CONTINUOUS_50HZ;
else if (c_ctrl->measure_freq_hz >= 20 &&
c_ctrl->measure_freq_hz < 50)
mode = AK09970_MODE_CONTINUOUS_20HZ;
else
mode = AK09970_MODE_CONTINUOUS_100HZ;
c_ctrl->measure_range = 0;
rc = akm09970_write_byte(c_ctrl->client, AK09970_MODE_REG,
(mode | c_ctrl->measure_range));
if (rc < 0) {
pr_err("Failed to set mode and smr\n");
akm09970_power_down(c_ctrl);
return rc;
}
pr_debug("reg: 0x%x, data: 0x%x\n", AK09970_MODE_REG,
(mode | c_ctrl->measure_range));
enable_irq(c_ctrl->irq);
hrtimer_start(&c_ctrl->timer,
ktime_set(CLEAR_IRQ_TIME / MSEC_PER_SEC,
(CLEAR_IRQ_TIME % MSEC_PER_SEC) *
NSEC_PER_MSEC),
HRTIMER_MODE_REL);
c_ctrl->read_flag = false;
pr_debug("Enable irq successfully\n");
} else if (atomic_read(&c_ctrl->power_enabled) && !on) {
disable_irq_nosync(c_ctrl->irq);
cancel_work_sync(&c_ctrl->report_work);
c_ctrl->read_flag = false;
pr_debug("Disable irq successfully\n");
rc = akm09970_set_mode(c_ctrl, AK09970_MODE_POWERDOWN);
if (rc)
pr_err("Failed to set to POWERDOWN mode\n");
akm09970_power_down(c_ctrl);
hrtimer_cancel(&c_ctrl->timer);
} else {
pr_info("The same power state, do nothing!\n");
}
return 0;
}
static int akm09970_read_data(struct akm09970_soc_ctrl *c_ctrl)
{
int rc = 0;
rc = i2c_smbus_read_i2c_block_data(c_ctrl->client, AK09970_REG_ST_XYZ,
AKM_SENSOR_DATA_SIZE,
c_ctrl->chip_data);
if (rc < 0) {
pr_err("read data failed!\n");
return rc;
}
if (AKM_ERRADC_IS_HIGH(c_ctrl->chip_data[0])) {
pr_err("ADC over run!\n");
rc = -EIO;
}
if (AKM_ERRXY_IS_HIGH(c_ctrl->chip_data[1])) {
pr_err("Errxy over run!\n");
rc = -EIO;
}
return 0;
}
static void akm09970_dev_work_queue(struct work_struct *work)
{
int rc = 0;
struct akm09970_soc_ctrl *c_ctrl =
container_of(work, struct akm09970_soc_ctrl, report_work);
rc = akm09970_read_data(c_ctrl);
if (rc < 0) {
atomic_set(&c_ctrl->data_ready, 0);
pr_warn("Failed to read data\n");
} else {
atomic_set(&c_ctrl->data_ready, 1);
wake_up(&poll_wait_queue);
c_ctrl->read_flag = true;
}
}
static irqreturn_t akm09970_irq_handler(int irq, void *dev_id)
{
struct akm09970_soc_ctrl *c_ctrl = dev_id;
queue_work(c_ctrl->work_queue, &c_ctrl->report_work);
return IRQ_HANDLED;
}
static int akm09970_release(struct inode *inp, struct file *filp)
{
int rc = 0;
return rc;
}
static int akm09970_open(struct inode *inp, struct file *filp)
{
int rc = 0;
struct akm09970_soc_ctrl *c_ctrl =
container_of(inp->i_cdev, struct akm09970_soc_ctrl, cdev);
filp->private_data = c_ctrl;
pr_debug("Open enter, irq = %d\n", c_ctrl->irq);
return rc;
}
static ssize_t akm09970_write(struct file *filp, const char *buf, size_t len,
loff_t *fseek)
{
int rc = 0;
return rc;
}
static ssize_t akm09970_read(struct file *filp, char *buf, size_t len,
loff_t *fseek)
{
int rc = 0;
return rc;
}
static unsigned int akm09970_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
struct akm09970_soc_ctrl *c_ctrl = filp->private_data;
pr_debug("Poll enter\n");
poll_wait(filp, &poll_wait_queue, wait);
if (atomic_read(&c_ctrl->data_ready)) {
atomic_set(&c_ctrl->data_ready, 0);
mask = POLLIN | POLLRDNORM;
}
return mask;
}
static long akm09970_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int rc = 0;
int i = 0;
struct akm09970_soc_ctrl *c_ctrl = filp->private_data;
if (NULL == c_ctrl) {
pr_err("Invalid data\n");
return -EFAULT;
}
if (_IOC_TYPE(cmd) != AKM_IOC_MAGIC) {
pr_err("CMD magic number is worng\n");
return -ENODEV;
}
if (_IOC_DIR(cmd) & _IOC_READ)
rc = !access_ok(VERIFY_WRITE, (void __user *)arg,
_IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
rc = !access_ok(VERIFY_READ, (void __user *)arg,
_IOC_SIZE(cmd));
if (rc) {
pr_err("CMD access failed\n");
return -EFAULT;
}
if (_IOC_DIR(cmd) & _IOC_WRITE) {
if (copy_from_user(&c_ctrl->pdata, (void __user *)arg,
sizeof(struct akm09970_platform_data))) {
pr_err("Copy data from user space failed\n");
return -EFAULT;
}
}
switch (cmd) {
case AKM_IOC_SET_ACTIVE:
pr_debug("c_ctrl->pdata.sensor_state = %d\n",
c_ctrl->pdata.sensor_state);
rc = akm09970_active(c_ctrl, c_ctrl->pdata.sensor_state);
break;
case AKM_IOC_SET_MODE:
pr_debug("c_ctrl->pdata.sensor_mode = %d\n",
c_ctrl->pdata.sensor_mode);
c_ctrl->measure_freq_hz = c_ctrl->pdata.sensor_mode;
//rc = akm09970_set_mode(pctrl, mode);
break;
case AKM_IOC_GET_SENSSMR:
pr_debug("c_ctrl->measure_range = %d\n", c_ctrl->measure_range);
c_ctrl->pdata.sensor_smr = c_ctrl->measure_range;
break;
case AKM_IOC_GET_SENSEDATA:
for (i = 0; i < AKM_SENSOR_DATA_SIZE; i++) {
if (c_ctrl->read_flag)
c_ctrl->pdata.data[i] = c_ctrl->chip_data[i];
else
c_ctrl->pdata.data[i] = 0x00;
pr_debug("data%d = %d\n", i, c_ctrl->chip_data[i]);
}
break;
default:
pr_warn("Unsupport cmd:0x%x\n", cmd);
break;
}
if (_IOC_DIR(cmd) & _IOC_READ) {
if (copy_to_user((void __user *)arg, &c_ctrl->pdata,
sizeof(struct akm09970_platform_data))) {
pr_err("Copy data to user space failed\n");
return -EFAULT;
}
}
return rc;
}
#ifdef CONFIG_COMPAT
static long akm09970_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
return akm09970_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#endif
static const struct file_operations akm09970_fops = {
.owner = THIS_MODULE,
.open = akm09970_open,
.release = akm09970_release,
.read = akm09970_read,
.write = akm09970_write,
.unlocked_ioctl = akm09970_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = akm09970_compat_ioctl,
#endif
.poll = akm09970_poll,
};
static int akm09970_check_device_id(struct akm09970_soc_ctrl *c_ctrl)
{
int rc = 0;
rc = akm09970_power_up(c_ctrl);
if (rc < 0)
return rc;
rc = i2c_smbus_read_i2c_block_data(c_ctrl->client, AK09970_REG_WIA,
AKM_SENSOR_INFO_SIZE,
c_ctrl->chip_info);
if (rc < 0)
goto exit;
if ((c_ctrl->chip_info[0] != AK09970_WIA1_VALUE) ||
(c_ctrl->chip_info[1] != AK09970_WIA2_VALUE)) {
pr_err("Check device id failed\n");
rc = -ENXIO;
}
exit:
akm09970_power_down(c_ctrl);
return rc;
}
static ssize_t akm09970_chip_rev_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct akm09970_soc_ctrl *c_ctrl = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%x,%x\n", (char)c_ctrl->chip_info[0],
(char)c_ctrl->chip_info[1]);
}
static DEVICE_ATTR(chip_rev, S_IRUGO, akm09970_chip_rev_show, NULL);
static ssize_t akm09970_debug_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct akm09970_soc_ctrl *c_ctrl = dev_get_drvdata(dev);
short hall_data[3];
hall_data[0] =
(short)((c_ctrl->chip_data[2] << 8) | c_ctrl->chip_data[3]);
hall_data[1] =
(short)((c_ctrl->chip_data[4] << 8) | c_ctrl->chip_data[5]);
hall_data[2] =
(short)((c_ctrl->chip_data[6] << 8) | c_ctrl->chip_data[7]);
return snprintf(buf, PAGE_SIZE, "X:%d, Y:%d, Z:%d\n", hall_data[0],
hall_data[1], hall_data[2]);
}
static ssize_t akm09970_debug_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int enable;
int ret = 0;
struct akm09970_soc_ctrl *c_ctrl = dev_get_drvdata(dev);
ret = sscanf(buf, "%d", &enable);
if (0 == ret)
pr_err("Input %d\n", enable);
if (10 == enable)
akm09970_active(c_ctrl, true);
else
akm09970_active(c_ctrl, false);
return count;
}
static DEVICE_ATTR(debug, S_IRUGO | S_IWUSR, akm09970_debug_show,
akm09970_debug_store);
static struct device_attribute *akm_attrs[] = {
&dev_attr_chip_rev,
&dev_attr_debug,
};
static int akm09970_regulator_init(struct akm09970_soc_ctrl *c_ctrl, bool on)
{
int rc = 0;
if (on) {
c_ctrl->vdd = regulator_get(c_ctrl->dev, "vdd");
if (IS_ERR(c_ctrl->vdd)) {
rc = PTR_ERR(c_ctrl->vdd);
pr_err("Regulator get failed vdd rc=%d\n", rc);
return rc;
}
if (regulator_count_voltages(c_ctrl->vdd) > 0) {
rc = regulator_set_voltage(c_ctrl->vdd,
AKM09970_VDD_MIN_UV,
AKM09970_VDD_MAX_UV);
if (rc) {
pr_err("Regulator set failed vdd rc=%d\n", rc);
regulator_put(c_ctrl->vdd);
return rc;
}
}
} else {
if (regulator_count_voltages(c_ctrl->vdd) > 0)
regulator_set_voltage(c_ctrl->vdd, 0,
AKM09970_VDD_MAX_UV);
regulator_put(c_ctrl->vdd);
}
return 0;
}
static int akm09970_gpio_config(struct akm09970_soc_ctrl *c_ctrl)
{
int32_t rc = 0;
rc = gpio_request_one(c_ctrl->gpio_reset, GPIOF_OUT_INIT_LOW,
"akm09970-reset");
if (rc < 0) {
pr_err("Failed to request power enable GPIO %d\n",
c_ctrl->gpio_reset);
goto reset_gpio_req_err;
}
gpio_direction_output(c_ctrl->gpio_reset, 0);
rc = gpio_request(c_ctrl->gpio_irq, "akm09970-irq");
if (rc < 0) {
pr_err("Failed to request power enable GPIO %d\n",
c_ctrl->gpio_irq);
goto irq_gpio_req_err;
} else {
gpio_direction_input(c_ctrl->gpio_irq);
c_ctrl->irq = gpio_to_irq(c_ctrl->gpio_irq);
rc = request_threaded_irq(c_ctrl->irq, NULL,
akm09970_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"akm09970_irq", c_ctrl);
if (rc < 0) {
pr_err("Unable to request irq\n");
goto irq_req_err;
}
disable_irq_nosync(c_ctrl->irq);
}
return 0;
irq_req_err:
if (gpio_is_valid(c_ctrl->gpio_irq)) {
if (c_ctrl->irq)
free_irq(c_ctrl->irq, c_ctrl);
gpio_free(c_ctrl->gpio_irq);
}
irq_gpio_req_err:
if (gpio_is_valid(c_ctrl->gpio_reset))
gpio_free(c_ctrl->gpio_reset);
reset_gpio_req_err:
return rc;
}
static int akm09970_pinctrl_select(struct akm09970_soc_ctrl *c_ctrl, bool state)
{
int rc = 0;
struct pinctrl_state *pins_state = state ? (c_ctrl->gpio_state_active) :
(c_ctrl->gpio_state_suspend);
rc = pinctrl_select_state(c_ctrl->pinctrl, pins_state);
if (rc < 0)
pr_err("Failed to select pins state %s\n",
state ? "active" : "suspend");
return rc;
}
static int akm09970_pinctrl_init(struct akm09970_soc_ctrl *c_ctrl)
{
int rc = 0;
struct device *dev = c_ctrl->dev;
c_ctrl->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR_OR_NULL(c_ctrl->pinctrl)) {
rc = PTR_ERR(c_ctrl->pinctrl);
pr_err("Unable to acquire pinctrl %d\n", rc);
goto err_pinctrl_get;
}
c_ctrl->gpio_state_active =
pinctrl_lookup_state(c_ctrl->pinctrl, "akm09970_gpio_active");
if (IS_ERR_OR_NULL(c_ctrl->gpio_state_active)) {
pr_err("Cannot lookup active pinctrl state\n");
rc = PTR_ERR(c_ctrl->gpio_state_active);
goto err_pinctrl_lookup;
}
c_ctrl->gpio_state_suspend =
pinctrl_lookup_state(c_ctrl->pinctrl, "akm09970_gpio_suspend");
if (IS_ERR_OR_NULL(c_ctrl->gpio_state_suspend)) {
pr_err("Cannot lookup suspend pinctrl state\n");
rc = PTR_ERR(c_ctrl->gpio_state_suspend);
goto err_pinctrl_lookup;
}
return 0;
err_pinctrl_lookup:
devm_pinctrl_put(c_ctrl->pinctrl);
err_pinctrl_get:
c_ctrl->pinctrl = NULL;
return rc;
}
static int akm09970_parse_dt(struct device *dev,
struct akm09970_soc_ctrl *c_ctrl)
{
int rc = 0;
c_ctrl->gpio_reset = of_get_named_gpio_flags(dev->of_node,
"akm,gpio-reset", 0, NULL);
if (!gpio_is_valid(c_ctrl->gpio_reset)) {
pr_err("Invalid gpio reset pin %d\n", c_ctrl->gpio_reset);
return -EINVAL;
}
c_ctrl->gpio_irq =
of_get_named_gpio_flags(dev->of_node, "akm,gpio-irq", 0, NULL);
if (!gpio_is_valid(c_ctrl->gpio_irq)) {
pr_err("Invalid gpio irq pin %d\n", c_ctrl->gpio_irq);
return -EINVAL;
}
pr_err("gpio_reset = %d, gpio_irq = %d\n", c_ctrl->gpio_reset,
c_ctrl->gpio_irq);
return rc;
}
static int akm09970_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int rc = 0;
struct akm09970_soc_ctrl *c_ctrl = NULL;
pr_info("Probe enter\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
pr_err("Check_functionality failed\n");
return -ENODEV;
}
/* Allocate memory for driver data */
c_ctrl = devm_kzalloc(&client->dev, sizeof(struct akm09970_soc_ctrl),
GFP_KERNEL);
if (!c_ctrl) {
pr_err("Alloc memory failed\n");
return -ENOMEM;
}
pr_debug("Start parse device tree\n");
if (client->dev.of_node) {
rc = akm09970_parse_dt(&client->dev, c_ctrl);
if (rc < 0) {
pr_err("Unable to parse platfrom data rc=%d\n", rc);
goto exit;
}
}
c_ctrl->dev = &client->dev;
c_ctrl->client = client;
i2c_set_clientdata(client, c_ctrl);
rc = akm09970_pinctrl_init(c_ctrl);
if (rc) {
pr_err("Failed to initialize pinctrl\n");
goto exit;
} else {
if (c_ctrl->pinctrl) {
rc = akm09970_pinctrl_select(c_ctrl, true);
if (rc < 0) {
pr_err("Failed to select default pinstate %d\n",
rc);
goto err_select_pinctrl;
}
}
}
rc = akm09970_gpio_config(c_ctrl);
if (rc < 0) {
pr_err("Failed to config gpio\n");
goto err_select_pinctrl;
}
rc = akm09970_regulator_init(c_ctrl, true);
if (rc < 0)
goto err_regulator_init;
rc = akm09970_check_device_id(c_ctrl);
if (rc < 0)
goto err_check_device;
atomic_set(&c_ctrl->power_enabled, 0);
pr_err("IRQ is #%d\n", c_ctrl->irq);
hrtimer_init(&c_ctrl->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
c_ctrl->timer.function = hrtimer_handler;
c_ctrl->work_queue =
alloc_workqueue("akm09970_poll_work_queue",
WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1);
INIT_WORK(&c_ctrl->report_work, akm09970_dev_work_queue);
c_ctrl->chr_class = class_create(THIS_MODULE, AKM09970_CLASS_NAME);
if (c_ctrl->chr_class == NULL) {
pr_err("Failed to create class\n");
rc = -ENODEV;
goto err_check_device;
}
rc = alloc_chrdev_region(&c_ctrl->dev_num, 0, 1, AKM09970_DRV_NAME);
if (rc < 0) {
pr_err("Failed to allocate chrdev region\n");
goto err_destroy_class;
}
c_ctrl->chr_dev =
device_create(c_ctrl->chr_class, NULL, c_ctrl->dev_num, c_ctrl,
AKM09970_DRV_NAME);
if (IS_ERR(c_ctrl->chr_dev)) {
pr_err("Failed to create char device\n");
rc = PTR_ERR(c_ctrl->chr_dev);
goto err_unregister_chrdev;
}
cdev_init(&(c_ctrl->cdev), &akm09970_fops);
c_ctrl->cdev.owner = THIS_MODULE;
rc = cdev_add(&(c_ctrl->cdev), c_ctrl->dev_num, 1);
if (rc < 0) {
pr_err("Failed to add cdev\n");
goto err_destroy_device;
}
rc = device_create_file(c_ctrl->chr_dev, akm_attrs[0]);
if (rc < 0)
pr_err("Failed to create debug file: %d\n", rc);
rc = device_create_file(c_ctrl->chr_dev, akm_attrs[1]);
if (rc < 0)
pr_err("Failed to create debug file: %d\n", rc);
pr_info("Probe exit\n");
return 0;
err_destroy_device:
if (c_ctrl->chr_dev)
device_destroy(c_ctrl->chr_class, c_ctrl->dev_num);
err_unregister_chrdev:
unregister_chrdev_region(c_ctrl->dev_num, 1);
err_destroy_class:
if (c_ctrl->chr_class)
class_destroy(c_ctrl->chr_class);
err_check_device:
akm09970_regulator_init(c_ctrl, false);
err_regulator_init:
if (gpio_is_valid(c_ctrl->gpio_irq)) {
if (c_ctrl->irq)
free_irq(c_ctrl->irq, c_ctrl);
gpio_free(c_ctrl->gpio_irq);
}
if (gpio_is_valid(c_ctrl->gpio_reset))
gpio_free(c_ctrl->gpio_reset);
err_select_pinctrl:
if (c_ctrl->pinctrl) {
devm_pinctrl_put(c_ctrl->pinctrl);
c_ctrl->pinctrl = NULL;
}
exit:
return rc;
}
static int akm09970_remove(struct i2c_client *client)
{
struct akm09970_soc_ctrl *c_ctrl = i2c_get_clientdata(client);
cancel_work_sync(&c_ctrl->report_work);
destroy_workqueue(c_ctrl->work_queue);
if (&(c_ctrl->cdev))
cdev_del(&(c_ctrl->cdev));
if (c_ctrl->chr_dev)
device_destroy(c_ctrl->chr_class, c_ctrl->dev_num);
unregister_chrdev_region(c_ctrl->dev_num, 1);
if (c_ctrl->chr_class)
class_destroy(c_ctrl->chr_class);
akm09970_power_down(c_ctrl);
akm09970_regulator_init(c_ctrl, false);
if (gpio_is_valid(c_ctrl->gpio_irq)) {
if (c_ctrl->irq)
free_irq(c_ctrl->irq, c_ctrl);
gpio_free(c_ctrl->gpio_irq);
}
if (gpio_is_valid(c_ctrl->gpio_reset))
gpio_free(c_ctrl->gpio_reset);
if (c_ctrl->pinctrl) {
devm_pinctrl_put(c_ctrl->pinctrl);
c_ctrl->pinctrl = NULL;
}
pr_info("Removed exit\n");
return 0;
}
static const struct i2c_device_id akm09970_id[] = {
{ AKM09970_DRV_NAME, 0 },
{},
};
MODULE_DEVICE_TABLE(i2c, akm09970_id);
static struct of_device_id akm09970_match_table[] = {
{
.compatible = "akm,akm09970",
},
{},
};
static struct i2c_driver akm09970_driver = {
.driver = {
.name = AKM09970_DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = akm09970_match_table,
},
.probe = akm09970_probe,
.remove = akm09970_remove,
.id_table = akm09970_id,
};
module_i2c_driver(akm09970_driver);
MODULE_DESCRIPTION("AKM compass driver");
MODULE_AUTHOR("Zhu Nengjin <zhunengjin@xiaomi.com>");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("2.0");

View File

@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
/*
* Copyright (c) 2018-2019, Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef __AKM09970_H__
#define __AKM09970_H__
#include <linux/types.h>
#include <linux/ioctl.h>
//#define pr_fmt(fmt) "akm09970: %s: %d " fmt, __func__, __LINE__
#define AKM09970_DRV_NAME "akm09970"
#define AKM09970_CLASS_NAME "akm"
/* AKM09970 Driver Magic Number */
#define AKM_IOC_MAGIC 'M'
#define AKM_PRIVATE 109
/* Device specific constant values */
#define AK09970_REG_WIA 0x00
#define AK09970_REG_ST_XYZ 0x17
#define AK09970_REG_CNTL1 0x20
#define AK09970_REG_CNTL2 0x21
#define AK09970_REG_RESET 0x30
#define AK09970_RESET_DATA 0x01
#define AK09970_WIA1_VALUE 0x48
#define AK09970_WIA2_VALUE 0xC0
#define AK09970_MODE_POWERDOWN 0x00
#define AK09970_MODE_CONTINUOUS_10HZ 0x08 /* 10Hz */
#define AK09970_MODE_CONTINUOUS_20HZ 0x0A /* 20Hz */
#define AK09970_MODE_CONTINUOUS_50HZ 0x0C /* 50Hz */
#define AK09970_MODE_CONTINUOUS_100HZ 0x0E /* 100Hz */
#define AKM_SENSOR_INFO_SIZE 2
#define AKM_SENSOR_CONF_SIZE 3
#define AKM_SENSOR_DATA_SIZE 8
#define AK09970_MODE_POS 0
#define AK09970_MODE_MSK 0x0F
#define AK09970_MODE_REG AK09970_REG_CNTL2
#define AK09970_SDR_MODE_POS 4
#define AK09970_SDR_MODE_MSK 0x10
#define AK09970_SDR_MODE_REG AK09970_REG_CNTL2
#define AK09970_SMR_MODE_POS 5
#define AK09970_SMR_MODE_MSK 0x20
#define AK09970_SMR_MODE_REG AK09970_REG_CNTL2
#define AKM_DRDY_IS_HIGH(x) ((x)&0x01)
#define AKM_DOR_IS_HIGH(x) ((x)&0x02)
#define AKM_ERRADC_IS_HIGH(x) ((x)&0x01)
#define AKM_ERRXY_IS_HIGH(x) ((x)&0x80)
#define AK09970_SENS_Q16 ((int32_t)(72090)) /* 1.1uT in Q16 format */
#define AKM_DRDY_TIMEOUT_MS 100
#define AKM_DEFAULT_MEASURE_HZ 10
/* POWER SUPPLY VOLTAGE RANGE */
#define AKM09970_VDD_MIN_UV 1800000
#define AKM09970_VDD_MAX_UV 1800000
#define PWM_PERIOD_DEFAULT_NS 1000000
struct akm09970_platform_data {
uint8_t sensor_smr;
uint8_t sensor_mode;
uint8_t sensor_state;
uint8_t data[AKM_SENSOR_DATA_SIZE];
};
/* IOC CMD */
#define AKM_IOC_SET_ACTIVE \
_IOW(AKM_IOC_MAGIC, AKM_PRIVATE + 1, struct akm09970_platform_data)
#define AKM_IOC_SET_MODE \
_IOW(AKM_IOC_MAGIC, AKM_PRIVATE + 2, struct akm09970_platform_data)
#define AKM_IOC_GET_SENSEDATA \
_IOR(AKM_IOC_MAGIC, AKM_PRIVATE + 4, struct akm09970_platform_data)
#define AKM_IOC_GET_SENSSMR \
_IOR(AKM_IOC_MAGIC, AKM_PRIVATE + 5, struct akm09970_platform_data)
#endif /* __AKM09970_H__ */