blob: 4901f91006f7e8a630de7dfbb641425d512b2eb8 [file] [log] [blame]
/*
* Copyright (c) 2011 Quantenna Communications, Inc.
* All rights reserved.
*
* Bootcfg storage with AT24C64D EEPROMs
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#include "bootcfg_drv.h"
#include <qtn/bootcfg.h>
#include <common/ruby_partitions.h>
#include <common/ruby_version.h>
#include <linux/i2c.h>
#include <linux/sched.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
#include <linux/nvmem-provider.h>
#include <linux/platform_data/at24.h>
#include <linux/moduleparam.h>
#else
#include <linux/i2c/at24.h>
#endif
#include <linux/sched.h>
#define I2C_EEPROM_ADAPTER_NUM 0x0
#define I2C_EEPROM_DEVICE_ADDR 0x50 /* first bits are 1010, then 3 chip select pins; 0x50 - 0x57 */
#define I2C_EEPROM_SIZE_BITS (64 * 1024)
#define I2C_EEPROM_NBBY 8
#define I2C_EEPROM_SIZE_BYTES (I2C_EEPROM_SIZE_BITS / I2C_EEPROM_NBBY)
#define I2C_EEPROM_PAGE_BYTES 32
static long i2c_devaddr = I2C_EEPROM_DEVICE_ADDR;
module_param(i2c_devaddr, long, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
MODULE_PARM_DESC(i2c_devaddr, "I2C device address of EEPROM");
static struct i2c_client *dev_client = NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
static struct nvmem_device *eeprom_nvmem_dev = NULL;
static spinlock_t g_eeprom_lock;
static void at24_setup(struct nvmem_device *nvmem, void *context)
{
eeprom_nvmem_dev = nvmem;
}
#else
static struct memory_accessor *eeprom_mem_acc = NULL;
static spinlock_t g_eeprom_lock;
static void at24_setup(struct memory_accessor *mem_acc, void *context)
{
eeprom_mem_acc = mem_acc;
}
#endif
static struct at24_platform_data at24c64d_plat = {
.byte_len = I2C_EEPROM_SIZE_BYTES,
.page_size = I2C_EEPROM_PAGE_BYTES,
.flags = AT24_FLAG_ADDR16,
.setup = &at24_setup,
};
static struct i2c_board_info eeprom_info = {
I2C_BOARD_INFO("at24", I2C_EEPROM_DEVICE_ADDR),
.platform_data = &at24c64d_plat,
};
int __init bootcfg_eeprom_init(struct bootcfg_store_ops *ops, size_t *store_limit)
{
spin_lock_init(&g_eeprom_lock);
eeprom_info.addr = i2c_devaddr;
printk("%s i2c_devaddr : 0x%x\n", __FUNCTION__, eeprom_info.addr);
dev_client = i2c_new_device(i2c_get_adapter(I2C_EEPROM_ADAPTER_NUM), &eeprom_info);
if (!dev_client) {
printk(KERN_ERR "%s: error instantiating i2c device\n",
__FUNCTION__);
goto device_client_fail;
}
*store_limit = I2C_EEPROM_SIZE_BYTES;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
if (eeprom_nvmem_dev == NULL) {
return -ENODEV;
}
#else
if (eeprom_mem_acc == NULL) {
return -ENODEV;
}
#endif
return 0;
device_client_fail:
return -1;
}
void __exit bootcfg_eeprom_exit(struct bootcfg_store_ops *ops)
{
i2c_unregister_device(dev_client);
}
static int bootcfg_eeprom_read(struct bootcfg_store_ops *ops, void* buf, const size_t bytes)
{
int ret;
spin_lock(&g_eeprom_lock);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
ret = nvmem_device_read(eeprom_nvmem_dev, 0, bytes, buf);
#else
ret = eeprom_mem_acc->read(eeprom_mem_acc, buf, 0, bytes);
#endif
spin_unlock(&g_eeprom_lock);
if (ret == bytes) {
ret = 0;
}
return ret;
}
static int bootcfg_eeprom_write(struct bootcfg_store_ops *ops, const void* buf, const size_t bytes)
{
int ret;
spin_lock(&g_eeprom_lock);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
ret = nvmem_device_write(eeprom_nvmem_dev, 0, bytes, buf);
#else
ret = eeprom_mem_acc->write(eeprom_mem_acc, buf, 0, bytes);
#endif
spin_unlock(&g_eeprom_lock);
if (ret == bytes) {
ret = 0;
}
return ret;
}
static struct bootcfg_store_ops eeprom_store_ops = {
.read = bootcfg_eeprom_read,
.write = bootcfg_eeprom_write,
.init = bootcfg_eeprom_init,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
.exit = bootcfg_eeprom_exit,
#else
.exit = __devexit_p(bootcfg_eeprom_exit),
#endif
};
struct bootcfg_store_ops * __init bootcfg_eeprom_get_ops(void)
{
return &eeprom_store_ops;
}