/*
* nvmem framework core.
*
* Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
* Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
*
* 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/device.h>
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/idr.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/nvmem-provider.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/slab.h>
struct nvmem_device {
const char *name;
struct regmap *regmap;
struct module *owner;
struct device dev;
int stride;
int word_size;
int ncells;
int id;
int users;
size_t size;
bool read_only;
};
struct nvmem_cell {
const char *name;
int offset;
int bytes;
int bit_offset;
int nbits;
struct nvmem_device *nvmem;
struct list_head node;
};
static DEFINE_MUTEX(nvmem_mutex);
static DEFINE_IDA(nvmem_ida);
static LIST_HEAD(nvmem_cells);
static DEFINE_MUTEX(nvmem_cells_mutex);
#define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t pos, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct nvmem_device *nvmem = to_nvmem_device(dev);
int rc;
/* Stop the user from reading */
if (pos >= nvmem->size)
return 0;
if (pos + count > nvmem->size)
count = nvmem->size - pos;
count = round_down(count, nvmem->word_size);
rc = regmap_raw_read(nvmem->regmap, pos, buf, count);
if (IS_ERR_VALUE(rc))
return rc;
return count;
}
static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t pos, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct nvmem_device *nvmem = to_nvmem_device(dev);
int rc;
/* Stop the user from writing */
if (pos >= nvmem->size)
return 0;
if (pos + count > nvmem->size)
count = nvmem->size - pos;
count = round_down(count, nvmem->word_size);
rc = regmap_raw_write(nvmem->regmap, pos, buf, count);
if (IS_ERR_VALUE(rc))
return rc;
return count;
}
/* default read/write permissions */
static struct bin_attribute bin_attr_rw_nvmem = {
.attr = {
.name = "nvmem",
.mode = S_IWUSR | S_IRUGO,
},
.read = bin_attr_nvmem_read,
.write = bin_attr_nvmem_write,
};
static struct bin_attribute *nvmem_bin_rw_attributes[] = {
&bin_attr_rw_nvmem,
NULL,
};
static const struct attribute_group nvmem_bin_rw_group = {
.bin_attrs = nvmem_bin_rw_attributes,
};
static const struct attribute_group *nvmem_rw_dev_groups[] = {
&nvmem_bin_rw_group,
NULL,
};
/* read only permission */
static struct bin_attribute bin_attr_ro_nvmem = {
.attr = {
.name =