diff options
Diffstat (limited to 'drivers/misc/eeprom/ee1004.c')
-rw-r--r-- | drivers/misc/eeprom/ee1004.c | 223 |
1 files changed, 90 insertions, 133 deletions
diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/misc/eeprom/ee1004.c index 252e15ba65e1..bb9c4512c968 100644 --- a/drivers/misc/eeprom/ee1004.c +++ b/drivers/misc/eeprom/ee1004.c @@ -32,16 +32,17 @@ */ #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 struct i2c_client *ee1004_set_page[EE1004_NUM_PAGES]; static unsigned int ee1004_dev_count; static int ee1004_current_page; @@ -71,40 +72,58 @@ static int ee1004_get_current_page(void) return 0; } +static int ee1004_set_current_page(struct device *dev, int page) +{ + int ret; + + if (page == ee1004_current_page) + return 0; + + /* Data is ignored */ + ret = i2c_smbus_write_byte(ee1004_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() == page) + ret = 0; + if (ret < 0) { + dev_err(dev, "Failed to select page %d (%d)\n", page, ret); + return ret; + } + + dev_dbg(dev, "Selected page %d\n", page); + ee1004_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->dev, 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); - - 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, +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct device *dev = kobj_to_dev(kobj); - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = kobj_to_i2c_client(kobj); size_t requested = count; - int page; - - if (unlikely(!count)) - return count; - - page = off >> EE1004_PAGE_SHIFT; - if (unlikely(page > 1)) - return 0; - off &= (1 << EE1004_PAGE_SHIFT) - 1; + int ret = 0; /* * Read data from chip, protecting against concurrent access to @@ -113,139 +132,85 @@ 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 == -ENXIO) { - /* - * 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 (ee1004_get_current_page() == page) - status = 0; - } - 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) { - mutex_unlock(&ee1004_bus_lock); - return status; - } - buf += status; - off += status; - count -= status; + ret = ee1004_eeprom_read(client, buf, off, count); + if (ret < 0) + goto out; - if (off == EE1004_PAGE_SIZE) { - page++; - off = 0; - } + buf += ret; + off += ret; + count -= ret; } - +out: mutex_unlock(&ee1004_bus_lock); - return requested; + return ret < 0 ? ret : requested; } -static const struct bin_attribute eeprom_attr = { - .attr = { - .name = "eeprom", - .mode = 0444, - }, - .size = EE1004_EEPROM_SIZE, - .read = ee1004_read, +static BIN_ATTR_RO(eeprom, EE1004_EEPROM_SIZE); + +static struct bin_attribute *ee1004_attrs[] = { + &bin_attr_eeprom, + NULL }; -static int ee1004_probe(struct i2c_client *client, - const struct i2c_device_id *id) +BIN_ATTRIBUTE_GROUPS(ee1004); + +static void ee1004_cleanup(int idx) +{ + if (--ee1004_dev_count == 0) + while (--idx >= 0) { + i2c_unregister_device(ee1004_set_page[idx]); + ee1004_set_page[idx] = NULL; + } +} + +static int ee1004_probe(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; - } + 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; /* 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_device(client->adapter, - EE1004_ADDR_SET_PAGE + cnr); - if (IS_ERR(ee1004_set_page[cnr])) { - dev_err(&client->dev, - "address 0x%02x unavailable\n", - EE1004_ADDR_SET_PAGE + cnr); - err = PTR_ERR(ee1004_set_page[cnr]); + 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_clients; } + ee1004_set_page[cnr] = cl; } - } else if (i2c_adapter_id(client->adapter) != - i2c_adapter_id(ee1004_set_page[0]->adapter)) { + + /* Remember current page to avoid unneeded page select */ + err = ee1004_get_current_page(); + if (err < 0) + goto err_clients; + dev_dbg(&client->dev, "Currently selected page: %d\n", err); + ee1004_current_page = err; + } else if (client->adapter != ee1004_set_page[0]->adapter) { dev_err(&client->dev, "Driver only supports devices on a single I2C bus\n"); err = -EOPNOTSUPP; goto err_clients; } - - /* Remember current page to avoid unneeded page select */ - err = ee1004_get_current_page(); - if (err < 0) - goto err_clients; - ee1004_current_page = err; - dev_dbg(&client->dev, "Currently selected page: %d\n", - ee1004_current_page); 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; - 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); return 0; - 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; - } - } + ee1004_cleanup(cnr); mutex_unlock(&ee1004_bus_lock); return err; @@ -253,18 +218,9 @@ static int ee1004_probe(struct i2c_client *client, static int ee1004_remove(struct i2c_client *client) { - int i; - - sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr); - /* 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; - } - } + ee1004_cleanup(EE1004_NUM_PAGES); mutex_unlock(&ee1004_bus_lock); return 0; @@ -275,8 +231,9 @@ static int ee1004_remove(struct i2c_client *client) static struct i2c_driver ee1004_driver = { .driver = { .name = "ee1004", + .dev_groups = ee1004_groups, }, - .probe = ee1004_probe, + .probe_new = ee1004_probe, .remove = ee1004_remove, .id_table = ee1004_ids, }; |