hwmon: Enable EPM driver

This snapshot is taken as of msm-3.18 commit dbdb6776f
(Merge "msm: camera: Add dummy sub module in sensor pipeline")

Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
This commit is contained in:
Siddartha Mohanadoss 2016-02-18 14:19:26 -08:00 committed by David Keitel
parent b07dd296fd
commit 3b820018b7
6 changed files with 765 additions and 0 deletions

View file

@ -0,0 +1,28 @@
Embedded Power Measurement(EPM) using Cypress Progammable System on a chip (PSOC)
The EPM using the PSoC5 is used by clients to measure on target power
measurement on supported channels. The PSoC5 is a microcontroller
that is communicated over the SPI from the MSM. Primary configuration
supports upto 31 channels and the scope of the driver is to support
userspace clients.
EPM node
Required properties:
- compatible : should be "cy,epm-adc-cy8c5568lti-114" for EPM using PSoC5.
- reg : chip select for the device.
- interrupt-parent : should be phandle of the interrupt controller
servicing the interrupt for this device.
- spi-max-frequency : existing support is set for 960kHz.
- qcom,channels : The number of voltage and current channels that
are supported.
- qcom,gain : The gain for each of the supported channels.
- qcom,rsense : The rsense value for each channel. The current channels
rsense values units are in milliohms. The voltage channels
rsense value is 1.
- qcom,channel-type : Bitmak of channels to set as voltage and current.
These are platform dependent and the appropriate scaling
functions are used for returning voltage and current.
- qcom,<gpio-name>-gpio : Handle to the GPIO node, see "gpios property" in
Documentation/devicetree/bindings/gpio/gpio.txt.
"gpio-name" can be "epm-enable" which is the EPM global enable GPIO for powering up the PSoC.

View file

@ -1200,6 +1200,14 @@ config SENSORS_PCF8591
These devices are hard to detect and rarely found on mainstream
hardware. If unsure, say N.
config SENSORS_EPM_ADC
tristate "EPM ADC Driver for power measurement"
depends on I2C && SPI_MASTER
default n
help
Provides interface for measuring the current on specific power rails
through the channels on ADC1158 ADC
config SENSORS_QPNP_ADC_VOLTAGE
tristate "Support for Qualcomm QPNP Voltage ADC"
depends on SPMI

View file

@ -160,6 +160,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
obj-$(CONFIG_SENSORS_EPM_ADC) += epm_adc.o
obj-$(CONFIG_SENSORS_QPNP_ADC_VOLTAGE) += qpnp-adc-voltage.o qpnp-adc-common.o
obj-$(CONFIG_SENSORS_QPNP_ADC_CURRENT) += qpnp-adc-current.o qpnp-adc-common.o

496
drivers/hwmon/epm_adc.c Normal file
View file

@ -0,0 +1,496 @@
/* Copyright (c) 2012-2015, 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/hwmon.h>
#include <linux/delay.h>
#include <linux/epm_adc.h>
#include <linux/uaccess.h>
#include <linux/spi/spi.h>
#include <linux/hwmon-sysfs.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#define EPM_ADC_DRIVER_NAME "epm_adc"
#define EPM_ADC_MAX_FNAME 20
#define EPM_ADC_CONVERSION_DELAY 100 /* milliseconds */
#define EPM_ADC_SPI_BITS_PER_WORD 8
#define GPIO_EPM_GLOBAL_ENABLE 86
#define GPIO_EPM_MARKER1 96
#define GPIO_EPM_MARKER2 85
#define EPM_ADC_CONVERSION_TIME_MIN 50000
#define EPM_ADC_CONVERSION_TIME_MAX 51000
/* PSoc Commands */
#define EPM_PSOC_GLOBAL_ENABLE 81
#define EPM_PSOC_VREF_VOLTAGE 2048
#define EPM_PSOC_MAX_ADC_CODE_15_BIT 32767
#define EPM_PSOC_MAX_ADC_CODE_12_BIT 4096
#define EPM_GLOBAL_ENABLE_MIN_DELAY 5000
#define EPM_GLOBAL_ENABLE_MAX_DELAY 5100
struct epm_adc_drv {
struct platform_device *pdev;
struct device *hwmon;
struct spi_device *epm_spi_client;
struct mutex conv_lock;
uint32_t bus_id;
struct miscdevice misc;
uint32_t channel_mask;
uint32_t epm_global_en_gpio;
struct epm_chan_properties epm_psoc_ch_prop[0];
};
static struct epm_adc_drv *epm_adc_drv;
static int epm_adc_psoc_gpio_init(struct epm_adc_drv *epm_adc,
bool enable)
{
int rc = 0;
if (enable) {
rc = gpio_request(epm_adc->epm_global_en_gpio,
"EPM_PSOC_GLOBAL_EN");
if (!rc) {
gpio_direction_output(epm_adc->epm_global_en_gpio, 1);
} else {
pr_err("%s: Configure EPM_GLOBAL_EN Failed\n",
__func__);
return rc;
}
} else {
gpio_direction_output(epm_adc->epm_global_en_gpio, 0);
gpio_free(epm_adc->epm_global_en_gpio);
}
return 0;
}
static int epm_request_marker1(void)
{
int rc = 0;
rc = gpio_request(GPIO_EPM_MARKER1, "EPM_MARKER1");
if (!rc) {
gpio_direction_output(GPIO_EPM_MARKER1, 1);
} else {
pr_err("%s: Configure MARKER1 GPIO Failed\n",
__func__);
return rc;
}
return 0;
}
static int epm_set_marker1(struct epm_marker_level *marker_init)
{
gpio_set_value(GPIO_EPM_MARKER1, marker_init->level);
return 0;
}
static int epm_request_marker2(void)
{
int rc = 0;
rc = gpio_request(GPIO_EPM_MARKER2, "EPM_MARKER2");
if (!rc) {
gpio_direction_output(GPIO_EPM_MARKER2, 1);
} else {
pr_err("%s: Configure MARKER2 GPIO Failed\n",
__func__);
return rc;
}
return 0;
}
static int epm_set_marker2(struct epm_marker_level *marker_init)
{
gpio_set_value(GPIO_EPM_MARKER2, marker_init->level);
return 0;
}
static int epm_marker1_release(void)
{
gpio_free(GPIO_EPM_MARKER1);
return 0;
}
static int epm_marker2_release(void)
{
gpio_free(GPIO_EPM_MARKER2);
return 0;
}
static int epm_psoc_generic_request(struct epm_adc_drv *epm_adc,
struct epm_generic_request *psoc_get_data)
{
struct spi_message m;
struct spi_transfer t;
char tx_buf[64], rx_buf[64];
int rc = 0, data_loop = 0;
spi_setup(epm_adc->epm_spi_client);
memset(&t, 0, sizeof(t));
memset(tx_buf, 0, sizeof(tx_buf));
memset(rx_buf, 0, sizeof(tx_buf));
t.tx_buf = tx_buf;
t.rx_buf = rx_buf;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
for (data_loop = 0; data_loop < 64; data_loop++)
tx_buf[data_loop] = psoc_get_data->buf[data_loop];
t.len = sizeof(tx_buf);
t.bits_per_word = EPM_ADC_SPI_BITS_PER_WORD;
rc = spi_sync(epm_adc->epm_spi_client, &m);
if (rc)
return rc;
for (data_loop = 0; data_loop < 64; data_loop++)
psoc_get_data->buf[data_loop] = rx_buf[data_loop];
return rc;
}
static long epm_adc_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct epm_adc_drv *epm_adc = epm_adc_drv;
switch (cmd) {
case EPM_MARKER1_REQUEST:
{
uint32_t result;
result = epm_request_marker1();
if (copy_to_user((void __user *)arg, &result,
sizeof(uint32_t)))
return -EFAULT;
break;
}
case EPM_MARKER2_REQUEST:
{
uint32_t result;
result = epm_request_marker2();
if (copy_to_user((void __user *)arg, &result,
sizeof(uint32_t)))
return -EFAULT;
break;
}
case EPM_MARKER1_SET_LEVEL:
{
struct epm_marker_level marker_init;
uint32_t result;
if (copy_from_user(&marker_init, (void __user *)arg,
sizeof(struct epm_marker_level)))
return -EFAULT;
result = epm_set_marker1(&marker_init);
if (copy_to_user((void __user *)arg, &result,
sizeof(uint32_t)))
return -EFAULT;
break;
}
case EPM_MARKER2_SET_LEVEL:
{
struct epm_marker_level marker_init;
uint32_t result;
if (copy_from_user(&marker_init, (void __user *)arg,
sizeof(struct epm_marker_level)))
return -EFAULT;
result = epm_set_marker2(&marker_init);
if (copy_to_user((void __user *)arg, &result,
sizeof(uint32_t)))
return -EFAULT;
break;
}
case EPM_MARKER1_RELEASE:
{
uint32_t result;
result = epm_marker1_release();
if (copy_to_user((void __user *)arg, &result,
sizeof(uint32_t)))
return -EFAULT;
break;
}
case EPM_MARKER2_RELEASE:
{
uint32_t result;
result = epm_marker2_release();
if (copy_to_user((void __user *)arg, &result,
sizeof(uint32_t)))
return -EFAULT;
break;
}
case EPM_PSOC_ADC_INIT:
{
int rc;
rc = epm_adc_psoc_gpio_init(epm_adc, true);
if (rc)
pr_err("GPIO init failed with %d\n", rc);
if (copy_to_user((void __user *)arg, &rc,
sizeof(int)))
return -EFAULT;
break;
}
case EPM_PSOC_ADC_DEINIT:
{
int rc;
rc = epm_adc_psoc_gpio_init(epm_adc, false);
if (copy_to_user((void __user *)arg, &rc,
sizeof(int)))
return -EFAULT;
break;
}
case EPM_PSOC_GENERIC_REQUEST:
{
struct epm_generic_request psoc_get_data;
int rc;
if (copy_from_user(&psoc_get_data,
(void __user *)arg,
sizeof(struct
epm_generic_request)))
return -EFAULT;
rc = epm_psoc_generic_request(epm_adc, &psoc_get_data);
if (rc)
pr_err("Generic request failed\n");
if (copy_to_user((void __user *)arg, &psoc_get_data,
sizeof(struct
epm_generic_request)))
return -EFAULT;
break;
}
default:
return -EINVAL;
}
return 0;
}
#ifdef CONFIG_COMPAT
static long epm_adc_compat_ioctl_process(struct file *filep,
unsigned int cmd, unsigned long arg)
{
arg = (unsigned long)compat_ptr(arg);
return epm_adc_ioctl(filep, cmd, arg);
}
#endif /* CONFIG_COMPAT */
const struct file_operations epm_adc_fops = {
.unlocked_ioctl = epm_adc_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = epm_adc_compat_ioctl_process,
#endif /* CONFIG_COMPAT */
};
static int get_device_tree_data(struct spi_device *spi)
{
const struct device_node *node = spi->dev.of_node;
struct epm_adc_drv *epm_adc;
u32 *epm_ch_gain, *epm_ch_rsense;
u32 rc = 0, epm_num_channels, i, channel_mask, epm_gpio_num;
if (!node)
return -EINVAL;
rc = of_property_read_u32(node,
"qcom,channels", &epm_num_channels);
if (rc) {
dev_err(&spi->dev, "missing channel numbers\n");
return -ENODEV;
}
epm_ch_gain = devm_kzalloc(&spi->dev,
epm_num_channels * sizeof(u32), GFP_KERNEL);
if (!epm_ch_gain) {
dev_err(&spi->dev, "cannot allocate gain\n");
return -ENOMEM;
}
epm_ch_rsense = devm_kzalloc(&spi->dev,
epm_num_channels * sizeof(u32), GFP_KERNEL);
if (!epm_ch_rsense) {
dev_err(&spi->dev, "cannot allocate rsense\n");
return -ENOMEM;
}
rc = of_property_read_u32_array(node,
"qcom,gain", epm_ch_gain, epm_num_channels);
if (rc) {
dev_err(&spi->dev, "invalid gain property:%d\n", rc);
return rc;
}
rc = of_property_read_u32_array(node,
"qcom,rsense", epm_ch_rsense, epm_num_channels);
if (rc) {
dev_err(&spi->dev, "invalid rsense property:%d\n", rc);
return rc;
}
rc = of_property_read_u32(node,
"qcom,channel-type", &channel_mask);
if (rc) {
dev_err(&spi->dev, "missing channel mask\n");
return -ENODEV;
}
epm_gpio_num = of_get_named_gpio(spi->dev.of_node,
"qcom,epm-enable-gpio", 0);
if (epm_gpio_num < 0) {
dev_err(&spi->dev, "missing global en gpio num\n");
return -ENODEV;
}
epm_adc = devm_kzalloc(&spi->dev,
sizeof(struct epm_adc_drv) +
(epm_num_channels *
sizeof(struct epm_chan_properties)),
GFP_KERNEL);
if (!epm_adc) {
dev_err(&spi->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
for (i = 0; i < epm_num_channels; i++) {
epm_adc->epm_psoc_ch_prop[i].resistorvalue =
epm_ch_rsense[i];
epm_adc->epm_psoc_ch_prop[i].gain =
epm_ch_gain[i];
}
epm_adc->channel_mask = channel_mask;
epm_adc->epm_global_en_gpio = epm_gpio_num;
epm_adc_drv = epm_adc;
return 0;
}
static int epm_adc_psoc_spi_probe(struct spi_device *spi)
{
struct epm_adc_drv *epm_adc;
struct device_node *node = spi->dev.of_node;
int rc = 0;
if (node) {
rc = get_device_tree_data(spi);
if (rc)
return rc;
} else {
epm_adc = epm_adc_drv;
epm_adc_drv->epm_spi_client = spi;
epm_adc_drv->epm_spi_client->bits_per_word =
EPM_ADC_SPI_BITS_PER_WORD;
return rc;
}
epm_adc = epm_adc_drv;
epm_adc->misc.name = EPM_ADC_DRIVER_NAME;
epm_adc->misc.minor = MISC_DYNAMIC_MINOR;
if (node) {
epm_adc->misc.fops = &epm_adc_fops;
if (misc_register(&epm_adc->misc)) {
pr_err("Unable to register misc device!\n");
return -EFAULT;
}
}
epm_adc_drv->epm_spi_client = spi;
epm_adc_drv->epm_spi_client->bits_per_word =
EPM_ADC_SPI_BITS_PER_WORD;
epm_adc->hwmon = hwmon_device_register(&spi->dev);
if (IS_ERR(epm_adc->hwmon)) {
dev_err(&spi->dev, "hwmon_device_register failed\n");
return rc;
}
mutex_init(&epm_adc->conv_lock);
return rc;
}
static int epm_adc_psoc_spi_remove(struct spi_device *spi)
{
epm_adc_drv->epm_spi_client = NULL;
return 0;
}
static const struct of_device_id epm_adc_psoc_match_table[] = {
{ .compatible = "cy,epm-adc-cy8c5568lti-114",
},
{}
};
static struct spi_driver epm_spi_driver = {
.probe = epm_adc_psoc_spi_probe,
.remove = epm_adc_psoc_spi_remove,
.driver = {
.name = EPM_ADC_DRIVER_NAME,
.of_match_table = epm_adc_psoc_match_table,
},
};
static int __init epm_adc_init(void)
{
int ret = 0;
ret = spi_register_driver(&epm_spi_driver);
if (ret)
pr_err("%s: spi register failed: rc=%d\n", __func__, ret);
return ret;
}
static void __exit epm_adc_exit(void)
{
spi_unregister_driver(&epm_spi_driver);
}
module_init(epm_adc_init);
module_exit(epm_adc_exit);
MODULE_DESCRIPTION("EPM ADC Driver");
MODULE_ALIAS("platform:epm_adc");
MODULE_LICENSE("GPL v2");

17
include/linux/epm_adc.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef __EPM_ADC_H
#define __EPM_ADC_H
#include <linux/i2c.h>
#include <uapi/linux/epm_adc.h>
struct epm_adc_platform_data {
struct epm_chan_properties *channel;
uint32_t num_channels;
uint32_t num_adc;
uint32_t chan_per_adc;
uint32_t chan_per_mux;
struct i2c_board_info epm_i2c_board_info;
uint32_t bus_id;
uint32_t gpio_expander_base_addr;
};
#endif /* __EPM_ADC_H */

View file

@ -0,0 +1,215 @@
#ifndef _UAPI_EPM_ADC_H
#define _UAPI_EPM_ADC_H
struct epm_chan_request {
/* EPM ADC device index. 0 - ADC1, 1 - ADC2 */
uint32_t device_idx;
/* Channel number within the EPM ADC device */
uint32_t channel_idx;
/* The data meaningful for each individual channel whether it is
* voltage, current etc. */
int32_t physical;
};
struct epm_psoc_init_resp {
uint8_t cmd;
uint8_t version;
uint8_t compatible_ver;
uint8_t firm_ver[3];
uint8_t num_dev;
uint8_t num_channel;
};
struct epm_psoc_channel_configure {
uint8_t cmd;
uint8_t device_num;
uint32_t channel_num;
};
struct epm_psoc_set_avg {
uint8_t cmd;
uint8_t avg_period;
uint8_t return_code;
};
struct epm_psoc_get_data {
uint8_t cmd;
uint8_t dev_num;
uint8_t chan_num;
uint32_t timestamp_resp_value;
int16_t reading_raw;
int32_t reading_value;
};
struct epm_psoc_get_buffered_data {
uint8_t cmd;
uint8_t dev_num;
uint8_t status_mask;
uint8_t chan_idx;
uint32_t chan_mask;
uint32_t timestamp_start;
uint32_t timestamp_end;
uint8_t buff_data[48];
};
struct epm_psoc_system_time_stamp {
uint8_t cmd;
uint32_t timestamp;
};
struct epm_psoc_set_channel {
uint8_t cmd;
uint8_t dev_num;
uint32_t channel_mask;
};
struct result_buffer {
uint32_t channel;
uint32_t avg_buffer_sample;
uint32_t result;
};
struct epm_psoc_get_avg_buffered_switch_data {
uint8_t cmd;
uint8_t status;
uint32_t timestamp_start;
uint32_t channel_mask;
uint8_t avg_data[54];
struct result_buffer data[54];
};
struct epm_psoc_set_channel_switch {
uint8_t cmd;
uint8_t dev;
uint32_t delay;
};
struct epm_psoc_set_vadc {
uint8_t cmd;
uint8_t vadc_dev;
uint32_t vadc_voltage;
};
struct epm_chan_properties {
uint32_t resistorvalue;
uint32_t gain;
};
struct epm_marker_level {
uint8_t level;
};
struct epm_gpio_buffer_request {
uint8_t cmd;
uint8_t bitmask_monitor_pin;
uint8_t status;
};
struct epm_get_gpio_buffer_resp {
uint8_t cmd;
uint8_t status;
uint8_t bitmask_monitor_pin;
uint32_t timestamp;
};
struct epm_get_high_res_avg_data {
uint8_t cmd;
uint8_t status;
uint32_t channel_mask;
uint32_t timestamp;
uint8_t buf_data[54];
};
struct epm_generic_request {
uint8_t buf[64];
};
#define EPM_ADC_IOCTL_CODE 0x91
#define EPM_ADC_REQUEST _IOWR(EPM_ADC_IOCTL_CODE, 1, \
struct epm_chan_request)
#define EPM_ADC_INIT _IOR(EPM_ADC_IOCTL_CODE, 2, \
uint32_t)
#define EPM_ADC_DEINIT _IOR(EPM_ADC_IOCTL_CODE, 3, \
uint32_t)
#define EPM_MARKER1_REQUEST _IOR(EPM_ADC_IOCTL_CODE, 90, \
uint32_t)
#define EPM_MARKER1_RELEASE _IOR(EPM_ADC_IOCTL_CODE, 91, \
uint32_t)
#define EPM_MARKER1_SET_LEVEL _IOWR(EPM_ADC_IOCTL_CODE, 92, \
uint32_t)
#define EPM_MARKER2_REQUEST _IOR(EPM_ADC_IOCTL_CODE, 93, \
uint32_t)
#define EPM_MARKER2_SET_LEVEL _IOWR(EPM_ADC_IOCTL_CODE, 94, \
uint32_t)
#define EPM_MARKER2_RELEASE _IOR(EPM_ADC_IOCTL_CODE, 95, \
uint32_t)
#define EPM_PSOC_ADC_INIT _IOWR(EPM_ADC_IOCTL_CODE, 4, \
struct epm_psoc_init_resp)
#define EPM_PSOC_ADC_CHANNEL_ENABLE _IOWR(EPM_ADC_IOCTL_CODE, 5, \
struct epm_psoc_channel_configure)
#define EPM_PSOC_ADC_CHANNEL_DISABLE _IOWR(EPM_ADC_IOCTL_CODE, 6, \
struct epm_psoc_channel_configure)
#define EPM_PSOC_ADC_SET_AVERAGING _IOWR(EPM_ADC_IOCTL_CODE, 7, \
struct epm_psoc_set_avg)
#define EPM_PSOC_ADC_GET_LAST_MEASUREMENT _IOWR(EPM_ADC_IOCTL_CODE, 8, \
struct epm_psoc_get_data)
#define EPM_PSOC_ADC_GET_BUFFERED_DATA _IOWR(EPM_ADC_IOCTL_CODE, 9, \
struct epm_psoc_get_buffered_data)
#define EPM_PSOC_ADC_GET_SYSTEM_TIMESTAMP _IOWR(EPM_ADC_IOCTL_CODE, 10, \
struct epm_psoc_system_time_stamp)
#define EPM_PSOC_ADC_SET_SYSTEM_TIMESTAMP _IOWR(EPM_ADC_IOCTL_CODE, 11, \
struct epm_psoc_system_time_stamp)
#define EPM_PSOC_ADC_GET_AVERAGE_DATA _IOWR(EPM_ADC_IOCTL_CODE, 12, \
struct epm_psoc_get_avg_buffered_switch_data)
#define EPM_PSOC_SET_CHANNEL_SWITCH _IOWR(EPM_ADC_IOCTL_CODE, 13, \
struct epm_psoc_set_channel_switch)
#define EPM_PSOC_CLEAR_BUFFER _IOWR(EPM_ADC_IOCTL_CODE, 14, \
uint32_t)
#define EPM_PSOC_ADC_SET_VADC_REFERENCE _IOWR(EPM_ADC_IOCTL_CODE, 15, \
struct epm_psoc_set_vadc)
#define EPM_PSOC_ADC_DEINIT _IOWR(EPM_ADC_IOCTL_CODE, 16, \
uint32_t)
#define EPM_PSOC_GPIO_BUFFER_REQUEST _IOWR(EPM_ADC_IOCTL_CODE, 17, \
struct epm_gpio_buffer_request)
#define EPM_PSOC_GET_GPIO_BUFFER_DATA _IOWR(EPM_ADC_IOCTL_CODE, 18, \
struct epm_get_gpio_buffer_resp)
#define EPM_PSOC_PAUSE_CONVERSION_REQUEST _IOWR(EPM_ADC_IOCTL_CODE, 19, \
uint32_t)
#define EPM_PSOC_UNPAUSE_CONVERSION_REQUEST _IOWR(EPM_ADC_IOCTL_CODE, 20, \
uint32_t)
#define EPM_PSOC_16_BIT_AVERAGED_REQUEST _IOWR(EPM_ADC_IOCTL_CODE, 21, \
struct epm_get_high_res_avg_data)
#define EPM_PSOC_GENERIC_REQUEST _IOWR(EPM_ADC_IOCTL_CODE, 22, \
struct epm_generic_request)
#endif /* _UAPI_EPM_ADC_H */