/*
* 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/slab.h>
struct nvmem_device {
const char *name;
struct module *owner;
struct device dev;
int stride;
int word_size;
int ncells;
int id;
int users;
size_t size;
bool read_only;
int flags;
struct bin_attribute eeprom;
struct device *base_dev;
nvmem_reg_read_t reg_read;
nvmem_reg_write_t reg_write;
void *priv;
};
#define FLAG_COMPAT BIT(0)
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);
#ifdef CONFIG_DEBUG_LOCK_ALLOC
static struct lock_class_key eeprom_lock_key;
#endif
#define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
static int nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
void *val, size_t bytes)
{
if (nvmem->reg_read)
return nvmem->reg_read(nvmem->priv, offset, val, bytes);
return -EINVAL;
}
static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
void *val, size_t bytes)
{
if (nvmem->reg_write)
return nvmem->reg_write(nvmem->priv, offset, val, bytes);
return -EINVAL;
}
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;
struct nvmem_device *nvmem;
int rc;
if (attr->private)
dev = attr->private;
else
dev = container_of(kobj, struct device, kobj);
nvmem = to_nvmem_device(dev);
/* Stop the user from reading */
if (pos >= nvmem->size)
return 0;
if (count < nvmem->word_size)
return -EINVAL;
if (pos + count > nvmem->size)
count = nvmem->size - pos;
count = round_down(count, nvmem->word_size);
rc = nvmem_reg_read(nvmem, pos, buf, count);
if (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;
struct nvmem_device *nvmem;
int rc;
if (attr->private)
dev = attr->private;
else
dev = container_of(kobj, struct device, kobj);
nvmem = to_nvmem_device(dev);
/* Stop the user from writing */
if (pos >=