diff options
Diffstat (limited to 'drivers/misc/eeprom/at24.c')
| -rw-r--r-- | drivers/misc/eeprom/at24.c | 92 | 
1 files changed, 56 insertions, 36 deletions
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 559b0b3c16c..d87f77f790d 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -20,8 +20,9 @@  #include <linux/log2.h>  #include <linux/bitops.h>  #include <linux/jiffies.h> +#include <linux/of.h>  #include <linux/i2c.h> -#include <linux/i2c/at24.h> +#include <linux/platform_data/at24.h>  /*   * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. @@ -427,6 +428,9 @@ static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,  {  	struct at24_data *at24; +	if (unlikely(off >= attr->size)) +		return -EFBIG; +  	at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));  	return at24_write(at24, buf, off, count);  } @@ -457,6 +461,27 @@ static ssize_t at24_macc_write(struct memory_accessor *macc, const char *buf,  /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_OF +static void at24_get_ofdata(struct i2c_client *client, +		struct at24_platform_data *chip) +{ +	const __be32 *val; +	struct device_node *node = client->dev.of_node; + +	if (node) { +		if (of_get_property(node, "read-only", NULL)) +			chip->flags |= AT24_FLAG_READONLY; +		val = of_get_property(node, "pagesize", NULL); +		if (val) +			chip->page_size = be32_to_cpup(val); +	} +} +#else +static void at24_get_ofdata(struct i2c_client *client, +		struct at24_platform_data *chip) +{ } +#endif /* CONFIG_OF */ +  static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)  {  	struct at24_platform_data chip; @@ -470,10 +495,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)  	if (client->dev.platform_data) {  		chip = *(struct at24_platform_data *)client->dev.platform_data;  	} else { -		if (!id->driver_data) { -			err = -ENODEV; -			goto err_out; -		} +		if (!id->driver_data) +			return -ENODEV; +  		magic = id->driver_data;  		chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));  		magic >>= AT24_SIZE_BYTELEN; @@ -485,6 +509,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)  		 */  		chip.page_size = 1; +		/* update chipdata if OF is present */ +		at24_get_ofdata(client, &chip); +  		chip.setup = NULL;  		chip.context = NULL;  	} @@ -492,16 +519,19 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)  	if (!is_power_of_2(chip.byte_len))  		dev_warn(&client->dev,  			"byte_len looks suspicious (no power of 2)!\n"); +	if (!chip.page_size) { +		dev_err(&client->dev, "page_size must not be 0!\n"); +		return -EINVAL; +	}  	if (!is_power_of_2(chip.page_size))  		dev_warn(&client->dev,  			"page_size looks suspicious (no power of 2)!\n");  	/* Use I2C operations unless we're stuck with SMBus extensions. */  	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { -		if (chip.flags & AT24_FLAG_ADDR16) { -			err = -EPFNOSUPPORT; -			goto err_out; -		} +		if (chip.flags & AT24_FLAG_ADDR16) +			return -EPFNOSUPPORT; +  		if (i2c_check_functionality(client->adapter,  				I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {  			use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; @@ -512,8 +542,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)  				I2C_FUNC_SMBUS_READ_BYTE_DATA)) {  			use_smbus = I2C_SMBUS_BYTE_DATA;  		} else { -			err = -EPFNOSUPPORT; -			goto err_out; +			return -EPFNOSUPPORT;  		}  	} @@ -523,12 +552,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)  		num_addresses =	DIV_ROUND_UP(chip.byte_len,  			(chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256); -	at24 = kzalloc(sizeof(struct at24_data) + +	at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) +  		num_addresses * sizeof(struct i2c_client *), GFP_KERNEL); -	if (!at24) { -		err = -ENOMEM; -		goto err_out; -	} +	if (!at24) +		return -ENOMEM;  	mutex_init(&at24->lock);  	at24->use_smbus = use_smbus; @@ -566,11 +593,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)  			at24->write_max = write_max;  			/* buffer (data + address at the beginning) */ -			at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL); -			if (!at24->writebuf) { -				err = -ENOMEM; -				goto err_struct; -			} +			at24->writebuf = devm_kzalloc(&client->dev, +				write_max + 2, GFP_KERNEL); +			if (!at24->writebuf) +				return -ENOMEM;  		} else {  			dev_warn(&client->dev,  				"cannot write due to controller restrictions."); @@ -597,19 +623,15 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)  	i2c_set_clientdata(client, at24); -	dev_info(&client->dev, "%zu byte %s EEPROM %s\n", +	dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",  		at24->bin.size, client->name, -		writable ? "(writable)" : "(read-only)"); +		writable ? "writable" : "read-only", at24->write_max);  	if (use_smbus == I2C_SMBUS_WORD_DATA ||  	    use_smbus == I2C_SMBUS_BYTE_DATA) {  		dev_notice(&client->dev, "Falling back to %s reads, "  			   "performance will suffer\n", use_smbus ==  			   I2C_SMBUS_WORD_DATA ? "word" : "byte");  	} -	dev_dbg(&client->dev, -		"page_size %d, num_addresses %d, write_max %d, use_smbus %d\n", -		chip.page_size, num_addresses, -		at24->write_max, use_smbus);  	/* export data to kernel code */  	if (chip.setup) @@ -622,15 +644,10 @@ err_clients:  		if (at24->client[i])  			i2c_unregister_device(at24->client[i]); -	kfree(at24->writebuf); -err_struct: -	kfree(at24); -err_out: -	dev_dbg(&client->dev, "probe error %d\n", err);  	return err;  } -static int __devexit at24_remove(struct i2c_client *client) +static int at24_remove(struct i2c_client *client)  {  	struct at24_data *at24;  	int i; @@ -641,8 +658,6 @@ static int __devexit at24_remove(struct i2c_client *client)  	for (i = 1; i < at24->num_addresses; i++)  		i2c_unregister_device(at24->client[i]); -	kfree(at24->writebuf); -	kfree(at24);  	return 0;  } @@ -654,12 +669,17 @@ static struct i2c_driver at24_driver = {  		.owner = THIS_MODULE,  	},  	.probe = at24_probe, -	.remove = __devexit_p(at24_remove), +	.remove = at24_remove,  	.id_table = at24_ids,  };  static int __init at24_init(void)  { +	if (!io_limit) { +		pr_err("at24: io_limit must not be 0!\n"); +		return -EINVAL; +	} +  	io_limit = rounddown_pow_of_two(io_limit);  	return i2c_add_driver(&at24_driver);  }  | 
