diff options
Diffstat (limited to 'drivers/misc/eeprom/ee1004.c')
| -rw-r--r-- | drivers/misc/eeprom/ee1004.c | 395 |
1 files changed, 233 insertions, 162 deletions
diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/misc/eeprom/ee1004.c index 276c1690ea1b..e13f9fdd9d7b 100644 --- a/drivers/misc/eeprom/ee1004.c +++ b/drivers/misc/eeprom/ee1004.c @@ -1,24 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * ee1004 - driver for DDR4 SPD EEPROMs * - * Copyright (C) 2017 Jean Delvare + * Copyright (C) 2017-2019 Jean Delvare * * Based on the at24 driver: * Copyright (C) 2005-2007 David Brownell * Copyright (C) 2008 Wolfram Sang, Pengutronix - * - * 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. */ +#include <linux/device.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/nvmem-provider.h> /* * DDR4 memory modules use special EEPROMs following the Jedec EE1004 @@ -35,62 +33,130 @@ * over performance. */ +#define EE1004_MAX_BUSSES 8 #define EE1004_ADDR_SET_PAGE 0x36 -#define EE1004_EEPROM_SIZE 512 +#define EE1004_NUM_PAGES 2 #define EE1004_PAGE_SIZE 256 #define EE1004_PAGE_SHIFT 8 +#define EE1004_EEPROM_SIZE (EE1004_PAGE_SIZE * EE1004_NUM_PAGES) /* * Mutex protects ee1004_set_page and ee1004_dev_count, and must be held * from page selection to end of read. */ static DEFINE_MUTEX(ee1004_bus_lock); -static struct i2c_client *ee1004_set_page[2]; -static unsigned int ee1004_dev_count; -static int ee1004_current_page; + +static struct ee1004_bus_data { + struct i2c_adapter *adap; + struct i2c_client *set_page[EE1004_NUM_PAGES]; + unsigned int dev_count; + int current_page; +} ee1004_bus_data[EE1004_MAX_BUSSES]; static const struct i2c_device_id ee1004_ids[] = { - { "ee1004", 0 }, + { "ee1004" }, { } }; MODULE_DEVICE_TABLE(i2c, ee1004_ids); /*-------------------------------------------------------------------------*/ +static struct ee1004_bus_data *ee1004_get_bus_data(struct i2c_adapter *adap) +{ + int i; + + for (i = 0; i < EE1004_MAX_BUSSES; i++) + if (ee1004_bus_data[i].adap == adap) + return ee1004_bus_data + i; + + /* If not existent yet, create new entry */ + for (i = 0; i < EE1004_MAX_BUSSES; i++) + if (!ee1004_bus_data[i].adap) { + ee1004_bus_data[i].adap = adap; + return ee1004_bus_data + i; + } + + return NULL; +} + +static int ee1004_get_current_page(struct ee1004_bus_data *bd) +{ + int err; + + err = i2c_smbus_read_byte(bd->set_page[0]); + if (err == -ENXIO) { + /* Nack means page 1 is selected */ + return 1; + } + if (err < 0) { + /* Anything else is a real error, bail out */ + return err; + } + + /* Ack means page 0 is selected, returned value meaningless */ + return 0; +} + +static int ee1004_set_current_page(struct i2c_client *client, int page) +{ + struct ee1004_bus_data *bd = i2c_get_clientdata(client); + int ret; + + if (page == bd->current_page) + return 0; + + /* Data is ignored */ + ret = i2c_smbus_write_byte(bd->set_page[page], 0x00); + /* + * Don't give up just yet. Some memory modules will select the page + * but not ack the command. Check which page is selected now. + */ + if (ret == -ENXIO && ee1004_get_current_page(bd) == page) + ret = 0; + if (ret < 0) { + dev_err(&client->dev, "Failed to select page %d (%d)\n", page, ret); + return ret; + } + + dev_dbg(&client->dev, "Selected page %d\n", page); + bd->current_page = page; + + return 0; +} + static ssize_t ee1004_eeprom_read(struct i2c_client *client, char *buf, unsigned int offset, size_t count) { - int status; + int status, page; + + page = offset >> EE1004_PAGE_SHIFT; + offset &= (1 << EE1004_PAGE_SHIFT) - 1; + + status = ee1004_set_current_page(client, page); + if (status) + return status; - if (count > I2C_SMBUS_BLOCK_MAX) - count = I2C_SMBUS_BLOCK_MAX; /* Can't cross page boundaries */ - if (unlikely(offset + count > EE1004_PAGE_SIZE)) + if (offset + count > EE1004_PAGE_SIZE) count = EE1004_PAGE_SIZE - offset; - status = i2c_smbus_read_i2c_block_data_or_emulated(client, offset, - count, buf); - dev_dbg(&client->dev, "read %zu@%d --> %d\n", count, offset, status); + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; - return status; + return i2c_smbus_read_i2c_block_data_or_emulated(client, offset, count, buf); } -static ssize_t ee1004_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static int ee1004_read(void *priv, unsigned int off, void *val, size_t count) { - struct device *dev = kobj_to_dev(kobj); - struct i2c_client *client = to_i2c_client(dev); - size_t requested = count; - int page; + struct i2c_client *client = priv; + char *buf = val; + int ret; if (unlikely(!count)) return count; - page = off >> EE1004_PAGE_SHIFT; - if (unlikely(page > 1)) - return 0; - off &= (1 << EE1004_PAGE_SHIFT) - 1; + if (off + count > EE1004_EEPROM_SIZE) + return -EINVAL; /* * Read data from chip, protecting against concurrent access to @@ -99,157 +165,174 @@ static ssize_t ee1004_read(struct file *filp, struct kobject *kobj, mutex_lock(&ee1004_bus_lock); while (count) { - int status; - - /* Select page */ - if (page != ee1004_current_page) { - /* Data is ignored */ - status = i2c_smbus_write_byte(ee1004_set_page[page], - 0x00); - if (status < 0) { - dev_err(dev, "Failed to select page %d (%d)\n", - page, status); - mutex_unlock(&ee1004_bus_lock); - return status; - } - dev_dbg(dev, "Selected page %d\n", page); - ee1004_current_page = page; - } - - status = ee1004_eeprom_read(client, buf, off, count); - if (status < 0) { + ret = ee1004_eeprom_read(client, buf, off, count); + if (ret < 0) { mutex_unlock(&ee1004_bus_lock); - return status; + return ret; } - buf += status; - off += status; - count -= status; - if (off == EE1004_PAGE_SIZE) { - page++; - off = 0; - } + buf += ret; + off += ret; + count -= ret; } mutex_unlock(&ee1004_bus_lock); - return requested; + return 0; } -static const struct bin_attribute eeprom_attr = { - .attr = { - .name = "eeprom", - .mode = 0444, - }, - .size = EE1004_EEPROM_SIZE, - .read = ee1004_read, -}; - -static int ee1004_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static void ee1004_probe_temp_sensor(struct i2c_client *client) { - int err, cnr = 0; - const char *slow = NULL; - - /* Make sure we can operate on this adapter */ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE | - I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { - if (i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE | - I2C_FUNC_SMBUS_READ_WORD_DATA)) - slow = "word"; - else if (i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE | - I2C_FUNC_SMBUS_READ_BYTE_DATA)) - slow = "byte"; - else - return -EPFNOSUPPORT; + struct i2c_board_info info = { .type = "jc42" }; + unsigned short addr = 0x18 | (client->addr & 7); + unsigned short addr_list[] = { addr, I2C_CLIENT_END }; + u8 data[2]; + int ret; + + /* byte 14, bit 7 is set if temp sensor is present */ + ret = ee1004_eeprom_read(client, data, 14, 1); + if (ret != 1) + return; + + if (!(data[0] & BIT(7))) { + /* + * If the SPD data suggests that there is no temperature + * sensor, it may still be there for SPD revision 1.0. + * See SPD Annex L, Revision 1 and 2, for details. + * Check DIMM type and SPD revision; if it is a DDR4 + * with SPD revision 1.0, check the thermal sensor address + * and instantiate the jc42 driver if a chip is found at + * that address. + * It is not necessary to check if there is a chip at the + * temperature sensor address since i2c_new_scanned_device() + * will do that and return silently if no chip is found. + */ + ret = ee1004_eeprom_read(client, data, 1, 2); + if (ret != 2 || data[0] != 0x10 || data[1] != 0x0c) + return; } + i2c_new_scanned_device(client->adapter, &info, addr_list, NULL); +} - /* Use 2 dummy devices for page select command */ - mutex_lock(&ee1004_bus_lock); - if (++ee1004_dev_count == 1) { - for (cnr = 0; cnr < 2; cnr++) { - ee1004_set_page[cnr] = i2c_new_dummy(client->adapter, - EE1004_ADDR_SET_PAGE + cnr); - if (!ee1004_set_page[cnr]) { - dev_err(&client->dev, - "address 0x%02x unavailable\n", - EE1004_ADDR_SET_PAGE + cnr); - err = -EADDRINUSE; - goto err_clients; - } - } - } else if (i2c_adapter_id(client->adapter) != - i2c_adapter_id(ee1004_set_page[0]->adapter)) { - dev_err(&client->dev, - "Driver only supports devices on a single I2C bus\n"); - err = -EOPNOTSUPP; - goto err_clients; +static void ee1004_cleanup(int idx, struct ee1004_bus_data *bd) +{ + if (--bd->dev_count == 0) { + while (--idx >= 0) + i2c_unregister_device(bd->set_page[idx]); + memset(bd, 0, sizeof(struct ee1004_bus_data)); } +} - /* Remember current page to avoid unneeded page select */ - err = i2c_smbus_read_byte(ee1004_set_page[0]); - if (err == -ENXIO) { - /* Nack means page 1 is selected */ - ee1004_current_page = 1; - } else if (err < 0) { - /* Anything else is a real error, bail out */ - goto err_clients; - } else { - /* Ack means page 0 is selected, returned value meaningless */ - ee1004_current_page = 0; - } - dev_dbg(&client->dev, "Currently selected page: %d\n", - ee1004_current_page); +static void ee1004_cleanup_bus_data(void *data) +{ + struct ee1004_bus_data *bd = data; + + /* Remove page select clients if this is the last device */ + mutex_lock(&ee1004_bus_lock); + ee1004_cleanup(EE1004_NUM_PAGES, bd); mutex_unlock(&ee1004_bus_lock); +} - /* Create the sysfs eeprom file */ - err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr); - if (err) - goto err_clients_lock; +static int ee1004_init_bus_data(struct i2c_client *client) +{ + struct ee1004_bus_data *bd; + int err, cnr = 0; - dev_info(&client->dev, - "%u byte EE1004-compliant SPD EEPROM, read-only\n", - EE1004_EEPROM_SIZE); - if (slow) - dev_notice(&client->dev, - "Falling back to %s reads, performance will suffer\n", - slow); + bd = ee1004_get_bus_data(client->adapter); + if (!bd) + return dev_err_probe(&client->dev, -ENOSPC, "Only %d busses supported", + EE1004_MAX_BUSSES); - return 0; + i2c_set_clientdata(client, bd); - err_clients_lock: - mutex_lock(&ee1004_bus_lock); - err_clients: - if (--ee1004_dev_count == 0) { - for (cnr--; cnr >= 0; cnr--) { - i2c_unregister_device(ee1004_set_page[cnr]); - ee1004_set_page[cnr] = NULL; + if (++bd->dev_count == 1) { + /* Use 2 dummy devices for page select command */ + for (cnr = 0; cnr < EE1004_NUM_PAGES; cnr++) { + struct i2c_client *cl; + + cl = i2c_new_dummy_device(client->adapter, EE1004_ADDR_SET_PAGE + cnr); + if (IS_ERR(cl)) { + err = PTR_ERR(cl); + goto err_out; + } + + bd->set_page[cnr] = cl; } + + /* Remember current page to avoid unneeded page select */ + err = ee1004_get_current_page(bd); + if (err < 0) + goto err_out; + + dev_dbg(&client->dev, "Currently selected page: %d\n", err); + bd->current_page = err; } - mutex_unlock(&ee1004_bus_lock); + + return 0; + +err_out: + ee1004_cleanup(cnr, bd); return err; } -static int ee1004_remove(struct i2c_client *client) +static int ee1004_probe(struct i2c_client *client) { - int i; + struct nvmem_config config = { + .dev = &client->dev, + .name = dev_name(&client->dev), + .id = NVMEM_DEVID_NONE, + .owner = THIS_MODULE, + .type = NVMEM_TYPE_EEPROM, + .read_only = true, + .root_only = false, + .reg_read = ee1004_read, + .size = EE1004_EEPROM_SIZE, + .word_size = 1, + .stride = 1, + .priv = client, + .compat = true, + .base_dev = &client->dev, + }; + struct nvmem_device *ndev; + int err; - sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr); + /* Make sure we can operate on this adapter */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_READ_I2C_BLOCK) && + !i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA)) + return -EPFNOSUPPORT; + + err = i2c_smbus_read_byte(client); + if (err < 0) + return -ENODEV; - /* Remove page select clients if this is the last device */ mutex_lock(&ee1004_bus_lock); - if (--ee1004_dev_count == 0) { - for (i = 0; i < 2; i++) { - i2c_unregister_device(ee1004_set_page[i]); - ee1004_set_page[i] = NULL; - } + + err = ee1004_init_bus_data(client); + if (err < 0) { + mutex_unlock(&ee1004_bus_lock); + return err; } + + ee1004_probe_temp_sensor(client); + mutex_unlock(&ee1004_bus_lock); + err = devm_add_action_or_reset(&client->dev, ee1004_cleanup_bus_data, + i2c_get_clientdata(client)); + if (err < 0) + return err; + + ndev = devm_nvmem_register(&client->dev, &config); + if (IS_ERR(ndev)) + return PTR_ERR(ndev); + + dev_info(&client->dev, + "%u byte EE1004-compliant SPD EEPROM, read-only\n", + EE1004_EEPROM_SIZE); + return 0; } @@ -260,21 +343,9 @@ static struct i2c_driver ee1004_driver = { .name = "ee1004", }, .probe = ee1004_probe, - .remove = ee1004_remove, .id_table = ee1004_ids, }; - -static int __init ee1004_init(void) -{ - return i2c_add_driver(&ee1004_driver); -} -module_init(ee1004_init); - -static void __exit ee1004_exit(void) -{ - i2c_del_driver(&ee1004_driver); -} -module_exit(ee1004_exit); +module_i2c_driver(ee1004_driver); MODULE_DESCRIPTION("Driver for EE1004-compliant DDR4 SPD EEPROMs"); MODULE_AUTHOR("Jean Delvare"); |
