Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a7b637ad authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "power: batterydata-lib: Add IOCTLs to read battery-profile"

parents 721f5b39 67158585
Loading
Loading
Loading
Loading
+219 −0
Original line number Diff line number Diff line
/* Copyright (c) 2014, The 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 version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * 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 pr_fmt(fmt)	"BATTERY: %s: " fmt, __func__
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/uaccess.h>
#include <linux/batterydata-lib.h>
#include <linux/batterydata-interface.h>

struct battery_data {
	dev_t				dev_no;
	struct class			*battery_class;
	struct device			*battery_device;
	struct cdev			battery_cdev;
	struct bms_battery_data		*profile;
};
static struct battery_data *the_battery;

static int battery_data_open(struct inode *inode, struct file *file)
{
	struct battery_data *battery = container_of(inode->i_cdev,
				struct battery_data, battery_cdev);

	pr_debug("battery_data device opened\n");

	file->private_data = battery;

	return 0;
}

static long battery_data_ioctl(struct file *file, unsigned int cmd,
						unsigned long arg)
{
	struct battery_data *battery = file->private_data;
	struct battery_params __user *bp_user =
				(struct battery_params __user *)arg;
	struct battery_params bp;
	int soc, rbatt_sf, slope, fcc_mah;
	int rc = 0;

	if (!battery->profile) {
		pr_err("Battery data not set!\n");
		return -EINVAL;
	}
	if (copy_from_user(&bp, bp_user, sizeof(bp))) {
		pr_err("copy_from_user failed\n");
		return -EFAULT;
	}

	switch (cmd) {
	case BPIOCXSOC:
		soc = interpolate_pc(battery->profile->pc_temp_ocv_lut,
					bp.batt_temp, bp.ocv_uv / 1000);
		rc = put_user(soc, &bp_user->soc);
		if (rc) {
			pr_err("BPIOCXSOC: Failed to 'put_user' rc=%d\n", rc);
			goto ret_err;
		}
		pr_debug("BPIOCXSOC: ocv=%d batt_temp=%d soc=%d\n",
				bp.ocv_uv / 1000, bp.batt_temp, soc);
		break;
	case BPIOCXRBATT:
		rbatt_sf = interpolate_scalingfactor(
				battery->profile->rbatt_sf_lut,
				bp.batt_temp, bp.soc);
		rc = put_user(rbatt_sf, &bp_user->rbatt_sf);
		if (rc) {
			pr_err("BPIOCXRBATT: Failed to 'put_user' rc=%d\n", rc);
			goto ret_err;
		}
		pr_debug("BPIOCXRBATT: soc=%d batt_temp=%d rbatt_sf=%d\n",
					bp.soc, bp.batt_temp, rbatt_sf);
		break;
	case BPIOCXSLOPE:
		slope = interpolate_slope(battery->profile->pc_temp_ocv_lut,
							bp.batt_temp, bp.soc);
		rc = put_user(slope, &bp_user->slope);
		if (rc) {
			pr_err("BPIOCXSLOPE: Failed to 'put_user' rc=%d\n", rc);
			goto ret_err;
		}
		pr_debug("BPIOCXSLOPE: soc=%d batt_temp=%d slope=%d\n",
					bp.soc, bp.batt_temp, slope);
		break;
	case BPIOCXFCC:
		fcc_mah = interpolate_fcc(battery->profile->fcc_temp_lut,
							bp.batt_temp);
		rc = put_user(fcc_mah, &bp_user->fcc_mah);
		if (rc) {
			pr_err("BPIOCXFCC: Failed to 'put_user' rc=%d\n", rc);
			goto ret_err;
		}
		pr_debug("BPIOCXFCC: batt_temp=%d fcc_mah=%d\n",
					bp.batt_temp, fcc_mah);
		break;
	default:
		pr_err("IOCTL %d not supported\n", cmd);
		rc = -EINVAL;

	}
ret_err:
	return rc;
}

static int battery_data_release(struct inode *inode, struct file *file)
{
	pr_debug("battery_data device closed\n");

	return 0;
}

static const struct file_operations battery_data_fops = {
	.owner = THIS_MODULE,
	.open = battery_data_open,
	.unlocked_ioctl	= battery_data_ioctl,
	.release = battery_data_release,
};

int config_battery_data(struct bms_battery_data *profile)
{
	if (!the_battery) {
		pr_err("Battery data not intialized\n");
		return -ENODEV;
	}

	the_battery->profile = profile;

	pr_debug("Battery profile set - %s\n",
			the_battery->profile->battery_type);

	return 0;
}

int batterydata_init(void)
{
	int rc;
	struct battery_data *battery;

	battery = kzalloc(sizeof(*battery), GFP_KERNEL);
	if (!battery) {
		pr_err("Unable to allocate memory\n");
		return -ENOMEM;
	}

	/* character device to access the battery-data from userspace */
	rc = alloc_chrdev_region(&battery->dev_no, 0, 1, "battery_data");
	if (rc) {
		pr_err("Unable to allocate chrdev rc=%d\n", rc);
		return rc;
	}
	cdev_init(&battery->battery_cdev, &battery_data_fops);
	rc = cdev_add(&battery->battery_cdev, battery->dev_no, 1);
	if (rc) {
		pr_err("Unable to add battery_cdev rc=%d\n", rc);
		goto unregister_chrdev;
	}

	battery->battery_class = class_create(THIS_MODULE, "battery_data");
	if (IS_ERR_OR_NULL(battery->battery_class)) {
		pr_err("Fail to create battery class\n");
		rc = -ENODEV;
		goto delete_cdev;
	}

	battery->battery_device = device_create(battery->battery_class,
					NULL, battery->dev_no,
					NULL, "battery_data");
	if (IS_ERR(battery->battery_device)) {
		pr_err("Fail to create battery_device device\n");
		rc = -ENODEV;
		goto delete_cdev;
	}

	the_battery = battery;

	pr_info("Battery-data device created!\n");

	return 0;

delete_cdev:
	cdev_del(&battery->battery_cdev);
unregister_chrdev:
	unregister_chrdev_region(battery->dev_no, 1);
	the_battery = NULL;
	return rc;
}
subsys_initcall(batterydata_init);

static void batterydata_exit(void)
{
	if (the_battery) {
		device_destroy(the_battery->battery_class, the_battery->dev_no);
		cdev_del(&the_battery->battery_cdev);
		unregister_chrdev_region(the_battery->dev_no, 1);
	}
	kfree(the_battery);
	the_battery = NULL;
}
module_exit(batterydata_exit);

MODULE_DESCRIPTION("Battery-data Interface driver");
MODULE_LICENSE("GPL v2");
+15 −0
Original line number Diff line number Diff line
/* Copyright (c) 2014, The 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 version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <uapi/linux/batterydata-interface.h>

int config_battery_data(struct bms_battery_data *profile);
+1 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ header-y += auxvec.h
header-y += avtimer.h
header-y += ax25.h
header-y += b1lli.h
header-y += batterydata-interface.h
header-y += baycom.h
header-y += bfs_fs.h
header-y += binfmts.h
+30 −0
Original line number Diff line number Diff line
#ifndef __BATTERYDATA_LIB_H__
#define __BATTERYDATA_LIB_H__

#include <linux/ioctl.h>

/**
 * struct battery_params - Battery profile data to be exchanged.
 * @soc:	SOC (state of charge) of the battery
 * @ocv_uv:	OCV (open circuit voltage) of the battery
 * @rbatt_sf:	RBATT scaling factor
 * @batt_temp:	Battery temperature in deci-degree.
 * @slope:	Slope of the OCV-SOC curve.
 * @fcc_mah:	FCC (full charge capacity) of the battery.
 */
struct battery_params {
	int soc;
	int ocv_uv;
	int rbatt_sf;
	int batt_temp;
	int slope;
	int fcc_mah;
};

/*  IOCTLs to query battery profile data */
#define BPIOCXSOC	_IOWR('B', 0x01, struct battery_params) /* SOC */
#define BPIOCXRBATT	_IOWR('B', 0x02, struct battery_params) /* RBATT SF */
#define BPIOCXSLOPE	_IOWR('B', 0x03, struct battery_params) /* SLOPE */
#define BPIOCXFCC	_IOWR('B', 0x04, struct battery_params) /* FCC */

#endif