xref: /openbmc/linux/drivers/i2c/i2c-stub.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
1*c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
231d178bfSJean Delvare /*
331d178bfSJean Delvare     i2c-stub.c - I2C/SMBus chip emulator
431d178bfSJean Delvare 
531d178bfSJean Delvare     Copyright (c) 2004 Mark M. Hoffman <mhoffman@lightlink.com>
62408c17fSJean Delvare     Copyright (C) 2007-2014 Jean Delvare <jdelvare@suse.de>
731d178bfSJean Delvare 
831d178bfSJean Delvare */
931d178bfSJean Delvare 
106c427787SWolfram Sang #define pr_fmt(fmt) "i2c-stub: " fmt
1131d178bfSJean Delvare 
1231d178bfSJean Delvare #include <linux/errno.h>
1331d178bfSJean Delvare #include <linux/i2c.h>
149f8f53a6SWolfram Sang #include <linux/init.h>
159f8f53a6SWolfram Sang #include <linux/kernel.h>
166f16b75aSGuenter Roeck #include <linux/list.h>
179f8f53a6SWolfram Sang #include <linux/module.h>
189f8f53a6SWolfram Sang #include <linux/slab.h>
1931d178bfSJean Delvare 
2031d178bfSJean Delvare #define MAX_CHIPS 10
216f16b75aSGuenter Roeck 
226f16b75aSGuenter Roeck /*
236f16b75aSGuenter Roeck  * Support for I2C_FUNC_SMBUS_BLOCK_DATA is disabled by default and must
246f16b75aSGuenter Roeck  * be enabled explicitly by setting the I2C_FUNC_SMBUS_BLOCK_DATA bits
256f16b75aSGuenter Roeck  * in the 'functionality' module parameter.
266f16b75aSGuenter Roeck  */
276f16b75aSGuenter Roeck #define STUB_FUNC_DEFAULT \
286f16b75aSGuenter Roeck 		(I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \
2931d178bfSJean Delvare 		 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \
3031d178bfSJean Delvare 		 I2C_FUNC_SMBUS_I2C_BLOCK)
3131d178bfSJean Delvare 
326f16b75aSGuenter Roeck #define STUB_FUNC_ALL \
336f16b75aSGuenter Roeck 		(STUB_FUNC_DEFAULT | I2C_FUNC_SMBUS_BLOCK_DATA)
346f16b75aSGuenter Roeck 
3531d178bfSJean Delvare static unsigned short chip_addr[MAX_CHIPS];
3631d178bfSJean Delvare module_param_array(chip_addr, ushort, NULL, S_IRUGO);
3731d178bfSJean Delvare MODULE_PARM_DESC(chip_addr,
3831d178bfSJean Delvare 		 "Chip addresses (up to 10, between 0x03 and 0x77)");
3931d178bfSJean Delvare 
406f16b75aSGuenter Roeck static unsigned long functionality = STUB_FUNC_DEFAULT;
4131d178bfSJean Delvare module_param(functionality, ulong, S_IRUGO | S_IWUSR);
4231d178bfSJean Delvare MODULE_PARM_DESC(functionality, "Override functionality bitfield");
4331d178bfSJean Delvare 
442408c17fSJean Delvare /* Some chips have banked register ranges */
452408c17fSJean Delvare 
462408c17fSJean Delvare static u8 bank_reg[MAX_CHIPS];
472408c17fSJean Delvare module_param_array(bank_reg, byte, NULL, S_IRUGO);
482408c17fSJean Delvare MODULE_PARM_DESC(bank_reg, "Bank register");
492408c17fSJean Delvare 
502408c17fSJean Delvare static u8 bank_mask[MAX_CHIPS];
512408c17fSJean Delvare module_param_array(bank_mask, byte, NULL, S_IRUGO);
522408c17fSJean Delvare MODULE_PARM_DESC(bank_mask, "Bank value mask");
532408c17fSJean Delvare 
542408c17fSJean Delvare static u8 bank_start[MAX_CHIPS];
552408c17fSJean Delvare module_param_array(bank_start, byte, NULL, S_IRUGO);
562408c17fSJean Delvare MODULE_PARM_DESC(bank_start, "First banked register");
572408c17fSJean Delvare 
582408c17fSJean Delvare static u8 bank_end[MAX_CHIPS];
592408c17fSJean Delvare module_param_array(bank_end, byte, NULL, S_IRUGO);
602408c17fSJean Delvare MODULE_PARM_DESC(bank_end, "Last banked register");
612408c17fSJean Delvare 
626f16b75aSGuenter Roeck struct smbus_block_data {
636f16b75aSGuenter Roeck 	struct list_head node;
646f16b75aSGuenter Roeck 	u8 command;
656f16b75aSGuenter Roeck 	u8 len;
666f16b75aSGuenter Roeck 	u8 block[I2C_SMBUS_BLOCK_MAX];
676f16b75aSGuenter Roeck };
686f16b75aSGuenter Roeck 
6931d178bfSJean Delvare struct stub_chip {
7031d178bfSJean Delvare 	u8 pointer;
7131d178bfSJean Delvare 	u16 words[256];		/* Byte operations use the LSB as per SMBus
7231d178bfSJean Delvare 				   specification */
736f16b75aSGuenter Roeck 	struct list_head smbus_blocks;
742408c17fSJean Delvare 
752408c17fSJean Delvare 	/* For chips with banks, extra registers are allocated dynamically */
762408c17fSJean Delvare 	u8 bank_reg;
772408c17fSJean Delvare 	u8 bank_shift;
782408c17fSJean Delvare 	u8 bank_mask;
792408c17fSJean Delvare 	u8 bank_sel;		/* Currently selected bank */
802408c17fSJean Delvare 	u8 bank_start;
812408c17fSJean Delvare 	u8 bank_end;
822408c17fSJean Delvare 	u16 bank_size;
832408c17fSJean Delvare 	u16 *bank_words;	/* Room for bank_mask * bank_size registers */
8431d178bfSJean Delvare };
8531d178bfSJean Delvare 
8631d178bfSJean Delvare static struct stub_chip *stub_chips;
871dff5983SJean Delvare static int stub_chips_nr;
8831d178bfSJean Delvare 
stub_find_block(struct device * dev,struct stub_chip * chip,u8 command,bool create)896f16b75aSGuenter Roeck static struct smbus_block_data *stub_find_block(struct device *dev,
906f16b75aSGuenter Roeck 						struct stub_chip *chip,
916f16b75aSGuenter Roeck 						u8 command, bool create)
926f16b75aSGuenter Roeck {
936f16b75aSGuenter Roeck 	struct smbus_block_data *b, *rb = NULL;
946f16b75aSGuenter Roeck 
956f16b75aSGuenter Roeck 	list_for_each_entry(b, &chip->smbus_blocks, node) {
966f16b75aSGuenter Roeck 		if (b->command == command) {
976f16b75aSGuenter Roeck 			rb = b;
986f16b75aSGuenter Roeck 			break;
996f16b75aSGuenter Roeck 		}
1006f16b75aSGuenter Roeck 	}
1016f16b75aSGuenter Roeck 	if (rb == NULL && create) {
1026f16b75aSGuenter Roeck 		rb = devm_kzalloc(dev, sizeof(*rb), GFP_KERNEL);
1036f16b75aSGuenter Roeck 		if (rb == NULL)
1046f16b75aSGuenter Roeck 			return rb;
1056f16b75aSGuenter Roeck 		rb->command = command;
1066f16b75aSGuenter Roeck 		list_add(&rb->node, &chip->smbus_blocks);
1076f16b75aSGuenter Roeck 	}
1086f16b75aSGuenter Roeck 	return rb;
1096f16b75aSGuenter Roeck }
1106f16b75aSGuenter Roeck 
stub_get_wordp(struct stub_chip * chip,u8 offset)1112408c17fSJean Delvare static u16 *stub_get_wordp(struct stub_chip *chip, u8 offset)
1122408c17fSJean Delvare {
1132408c17fSJean Delvare 	if (chip->bank_sel &&
1142408c17fSJean Delvare 	    offset >= chip->bank_start && offset <= chip->bank_end)
1152408c17fSJean Delvare 		return chip->bank_words +
1162408c17fSJean Delvare 		       (chip->bank_sel - 1) * chip->bank_size +
1172408c17fSJean Delvare 		       offset - chip->bank_start;
1182408c17fSJean Delvare 	else
1192408c17fSJean Delvare 		return chip->words + offset;
1202408c17fSJean Delvare }
1212408c17fSJean Delvare 
12231d178bfSJean Delvare /* Return negative errno on error. */
stub_xfer(struct i2c_adapter * adap,u16 addr,unsigned short flags,char read_write,u8 command,int size,union i2c_smbus_data * data)12331d178bfSJean Delvare static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
12431d178bfSJean Delvare 	char read_write, u8 command, int size, union i2c_smbus_data *data)
12531d178bfSJean Delvare {
12631d178bfSJean Delvare 	s32 ret;
12731d178bfSJean Delvare 	int i, len;
12831d178bfSJean Delvare 	struct stub_chip *chip = NULL;
1296f16b75aSGuenter Roeck 	struct smbus_block_data *b;
1302408c17fSJean Delvare 	u16 *wordp;
13131d178bfSJean Delvare 
13231d178bfSJean Delvare 	/* Search for the right chip */
1331dff5983SJean Delvare 	for (i = 0; i < stub_chips_nr; i++) {
13431d178bfSJean Delvare 		if (addr == chip_addr[i]) {
13531d178bfSJean Delvare 			chip = stub_chips + i;
13631d178bfSJean Delvare 			break;
13731d178bfSJean Delvare 		}
13831d178bfSJean Delvare 	}
13931d178bfSJean Delvare 	if (!chip)
14031d178bfSJean Delvare 		return -ENODEV;
14131d178bfSJean Delvare 
14231d178bfSJean Delvare 	switch (size) {
14331d178bfSJean Delvare 
14431d178bfSJean Delvare 	case I2C_SMBUS_QUICK:
14531d178bfSJean Delvare 		dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr);
14631d178bfSJean Delvare 		ret = 0;
14731d178bfSJean Delvare 		break;
14831d178bfSJean Delvare 
14931d178bfSJean Delvare 	case I2C_SMBUS_BYTE:
15031d178bfSJean Delvare 		if (read_write == I2C_SMBUS_WRITE) {
15131d178bfSJean Delvare 			chip->pointer = command;
15231d178bfSJean Delvare 			dev_dbg(&adap->dev,
15331d178bfSJean Delvare 				"smbus byte - addr 0x%02x, wrote 0x%02x.\n",
15431d178bfSJean Delvare 				addr, command);
15531d178bfSJean Delvare 		} else {
1562408c17fSJean Delvare 			wordp = stub_get_wordp(chip, chip->pointer++);
1572408c17fSJean Delvare 			data->byte = *wordp & 0xff;
15831d178bfSJean Delvare 			dev_dbg(&adap->dev,
15931d178bfSJean Delvare 				"smbus byte - addr 0x%02x, read  0x%02x.\n",
16031d178bfSJean Delvare 				addr, data->byte);
16131d178bfSJean Delvare 		}
16231d178bfSJean Delvare 
16331d178bfSJean Delvare 		ret = 0;
16431d178bfSJean Delvare 		break;
16531d178bfSJean Delvare 
16631d178bfSJean Delvare 	case I2C_SMBUS_BYTE_DATA:
1672408c17fSJean Delvare 		wordp = stub_get_wordp(chip, command);
16831d178bfSJean Delvare 		if (read_write == I2C_SMBUS_WRITE) {
1692408c17fSJean Delvare 			*wordp &= 0xff00;
1702408c17fSJean Delvare 			*wordp |= data->byte;
17131d178bfSJean Delvare 			dev_dbg(&adap->dev,
17231d178bfSJean Delvare 				"smbus byte data - addr 0x%02x, wrote 0x%02x at 0x%02x.\n",
17331d178bfSJean Delvare 				addr, data->byte, command);
1742408c17fSJean Delvare 
1752408c17fSJean Delvare 			/* Set the bank as needed */
1762408c17fSJean Delvare 			if (chip->bank_words && command == chip->bank_reg) {
1772408c17fSJean Delvare 				chip->bank_sel =
1782408c17fSJean Delvare 					(data->byte >> chip->bank_shift)
1792408c17fSJean Delvare 					& chip->bank_mask;
1802408c17fSJean Delvare 				dev_dbg(&adap->dev,
1812408c17fSJean Delvare 					"switching to bank %u.\n",
1822408c17fSJean Delvare 					chip->bank_sel);
1832408c17fSJean Delvare 			}
18431d178bfSJean Delvare 		} else {
1852408c17fSJean Delvare 			data->byte = *wordp & 0xff;
18631d178bfSJean Delvare 			dev_dbg(&adap->dev,
18731d178bfSJean Delvare 				"smbus byte data - addr 0x%02x, read  0x%02x at 0x%02x.\n",
18831d178bfSJean Delvare 				addr, data->byte, command);
18931d178bfSJean Delvare 		}
19031d178bfSJean Delvare 		chip->pointer = command + 1;
19131d178bfSJean Delvare 
19231d178bfSJean Delvare 		ret = 0;
19331d178bfSJean Delvare 		break;
19431d178bfSJean Delvare 
19531d178bfSJean Delvare 	case I2C_SMBUS_WORD_DATA:
1962408c17fSJean Delvare 		wordp = stub_get_wordp(chip, command);
19731d178bfSJean Delvare 		if (read_write == I2C_SMBUS_WRITE) {
1982408c17fSJean Delvare 			*wordp = data->word;
19931d178bfSJean Delvare 			dev_dbg(&adap->dev,
20031d178bfSJean Delvare 				"smbus word data - addr 0x%02x, wrote 0x%04x at 0x%02x.\n",
20131d178bfSJean Delvare 				addr, data->word, command);
20231d178bfSJean Delvare 		} else {
2032408c17fSJean Delvare 			data->word = *wordp;
20431d178bfSJean Delvare 			dev_dbg(&adap->dev,
20531d178bfSJean Delvare 				"smbus word data - addr 0x%02x, read  0x%04x at 0x%02x.\n",
20631d178bfSJean Delvare 				addr, data->word, command);
20731d178bfSJean Delvare 		}
20831d178bfSJean Delvare 
20931d178bfSJean Delvare 		ret = 0;
21031d178bfSJean Delvare 		break;
21131d178bfSJean Delvare 
21231d178bfSJean Delvare 	case I2C_SMBUS_I2C_BLOCK_DATA:
2132408c17fSJean Delvare 		/*
2142408c17fSJean Delvare 		 * We ignore banks here, because banked chips don't use I2C
2152408c17fSJean Delvare 		 * block transfers
2162408c17fSJean Delvare 		 */
2170f6ba0d1SJean Delvare 		if (data->block[0] > 256 - command)	/* Avoid overrun */
2180f6ba0d1SJean Delvare 			data->block[0] = 256 - command;
21931d178bfSJean Delvare 		len = data->block[0];
22031d178bfSJean Delvare 		if (read_write == I2C_SMBUS_WRITE) {
22131d178bfSJean Delvare 			for (i = 0; i < len; i++) {
22231d178bfSJean Delvare 				chip->words[command + i] &= 0xff00;
22331d178bfSJean Delvare 				chip->words[command + i] |= data->block[1 + i];
22431d178bfSJean Delvare 			}
22531d178bfSJean Delvare 			dev_dbg(&adap->dev,
22631d178bfSJean Delvare 				"i2c block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n",
22731d178bfSJean Delvare 				addr, len, command);
22831d178bfSJean Delvare 		} else {
22931d178bfSJean Delvare 			for (i = 0; i < len; i++) {
23031d178bfSJean Delvare 				data->block[1 + i] =
23131d178bfSJean Delvare 					chip->words[command + i] & 0xff;
23231d178bfSJean Delvare 			}
23331d178bfSJean Delvare 			dev_dbg(&adap->dev,
23431d178bfSJean Delvare 				"i2c block data - addr 0x%02x, read  %d bytes at 0x%02x.\n",
23531d178bfSJean Delvare 				addr, len, command);
23631d178bfSJean Delvare 		}
23731d178bfSJean Delvare 
23831d178bfSJean Delvare 		ret = 0;
23931d178bfSJean Delvare 		break;
24031d178bfSJean Delvare 
2416f16b75aSGuenter Roeck 	case I2C_SMBUS_BLOCK_DATA:
2422408c17fSJean Delvare 		/*
2432408c17fSJean Delvare 		 * We ignore banks here, because chips typically don't use both
2442408c17fSJean Delvare 		 * banks and SMBus block transfers
2452408c17fSJean Delvare 		 */
2466f16b75aSGuenter Roeck 		b = stub_find_block(&adap->dev, chip, command, false);
2476f16b75aSGuenter Roeck 		if (read_write == I2C_SMBUS_WRITE) {
2486f16b75aSGuenter Roeck 			len = data->block[0];
2496f16b75aSGuenter Roeck 			if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) {
2506f16b75aSGuenter Roeck 				ret = -EINVAL;
2516f16b75aSGuenter Roeck 				break;
2526f16b75aSGuenter Roeck 			}
2536f16b75aSGuenter Roeck 			if (b == NULL) {
2546f16b75aSGuenter Roeck 				b = stub_find_block(&adap->dev, chip, command,
2556f16b75aSGuenter Roeck 						    true);
2566f16b75aSGuenter Roeck 				if (b == NULL) {
2576f16b75aSGuenter Roeck 					ret = -ENOMEM;
2586f16b75aSGuenter Roeck 					break;
2596f16b75aSGuenter Roeck 				}
2606f16b75aSGuenter Roeck 			}
2616f16b75aSGuenter Roeck 			/* Largest write sets read block length */
2626f16b75aSGuenter Roeck 			if (len > b->len)
2636f16b75aSGuenter Roeck 				b->len = len;
2646f16b75aSGuenter Roeck 			for (i = 0; i < len; i++)
2656f16b75aSGuenter Roeck 				b->block[i] = data->block[i + 1];
2666f16b75aSGuenter Roeck 			/* update for byte and word commands */
2676f16b75aSGuenter Roeck 			chip->words[command] = (b->block[0] << 8) | b->len;
2686f16b75aSGuenter Roeck 			dev_dbg(&adap->dev,
2696f16b75aSGuenter Roeck 				"smbus block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n",
2706f16b75aSGuenter Roeck 				addr, len, command);
2716f16b75aSGuenter Roeck 		} else {
2726f16b75aSGuenter Roeck 			if (b == NULL) {
2736f16b75aSGuenter Roeck 				dev_dbg(&adap->dev,
2746f16b75aSGuenter Roeck 					"SMBus block read command without prior block write not supported\n");
2756f16b75aSGuenter Roeck 				ret = -EOPNOTSUPP;
2766f16b75aSGuenter Roeck 				break;
2776f16b75aSGuenter Roeck 			}
2786f16b75aSGuenter Roeck 			len = b->len;
2796f16b75aSGuenter Roeck 			data->block[0] = len;
2806f16b75aSGuenter Roeck 			for (i = 0; i < len; i++)
2816f16b75aSGuenter Roeck 				data->block[i + 1] = b->block[i];
2826f16b75aSGuenter Roeck 			dev_dbg(&adap->dev,
2836f16b75aSGuenter Roeck 				"smbus block data - addr 0x%02x, read  %d bytes at 0x%02x.\n",
2846f16b75aSGuenter Roeck 				addr, len, command);
2856f16b75aSGuenter Roeck 		}
2866f16b75aSGuenter Roeck 
2876f16b75aSGuenter Roeck 		ret = 0;
2886f16b75aSGuenter Roeck 		break;
2896f16b75aSGuenter Roeck 
29031d178bfSJean Delvare 	default:
29131d178bfSJean Delvare 		dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n");
29231d178bfSJean Delvare 		ret = -EOPNOTSUPP;
29331d178bfSJean Delvare 		break;
29431d178bfSJean Delvare 	} /* switch (size) */
29531d178bfSJean Delvare 
29631d178bfSJean Delvare 	return ret;
29731d178bfSJean Delvare }
29831d178bfSJean Delvare 
stub_func(struct i2c_adapter * adapter)29931d178bfSJean Delvare static u32 stub_func(struct i2c_adapter *adapter)
30031d178bfSJean Delvare {
3016f16b75aSGuenter Roeck 	return STUB_FUNC_ALL & functionality;
30231d178bfSJean Delvare }
30331d178bfSJean Delvare 
30431d178bfSJean Delvare static const struct i2c_algorithm smbus_algorithm = {
30531d178bfSJean Delvare 	.functionality	= stub_func,
30631d178bfSJean Delvare 	.smbus_xfer	= stub_xfer,
30731d178bfSJean Delvare };
30831d178bfSJean Delvare 
30931d178bfSJean Delvare static struct i2c_adapter stub_adapter = {
31031d178bfSJean Delvare 	.owner		= THIS_MODULE,
31131d178bfSJean Delvare 	.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
31231d178bfSJean Delvare 	.algo		= &smbus_algorithm,
31331d178bfSJean Delvare 	.name		= "SMBus stub driver",
31431d178bfSJean Delvare };
31531d178bfSJean Delvare 
i2c_stub_allocate_banks(int i)3162408c17fSJean Delvare static int __init i2c_stub_allocate_banks(int i)
3172408c17fSJean Delvare {
3182408c17fSJean Delvare 	struct stub_chip *chip = stub_chips + i;
3192408c17fSJean Delvare 
3202408c17fSJean Delvare 	chip->bank_reg = bank_reg[i];
3212408c17fSJean Delvare 	chip->bank_start = bank_start[i];
3222408c17fSJean Delvare 	chip->bank_end = bank_end[i];
3232408c17fSJean Delvare 	chip->bank_size = bank_end[i] - bank_start[i] + 1;
3242408c17fSJean Delvare 
3252408c17fSJean Delvare 	/* We assume that all bits in the mask are contiguous */
3262408c17fSJean Delvare 	chip->bank_mask = bank_mask[i];
3272408c17fSJean Delvare 	while (!(chip->bank_mask & 1)) {
3282408c17fSJean Delvare 		chip->bank_shift++;
3292408c17fSJean Delvare 		chip->bank_mask >>= 1;
3302408c17fSJean Delvare 	}
3312408c17fSJean Delvare 
3326396bb22SKees Cook 	chip->bank_words = kcalloc(chip->bank_mask * chip->bank_size,
3336396bb22SKees Cook 				   sizeof(u16),
3346396bb22SKees Cook 				   GFP_KERNEL);
3352408c17fSJean Delvare 	if (!chip->bank_words)
3362408c17fSJean Delvare 		return -ENOMEM;
3372408c17fSJean Delvare 
3386c427787SWolfram Sang 	pr_debug("Allocated %u banks of %u words each (registers 0x%02x to 0x%02x)\n",
3392408c17fSJean Delvare 		 chip->bank_mask, chip->bank_size, chip->bank_start,
3402408c17fSJean Delvare 		 chip->bank_end);
3412408c17fSJean Delvare 
3422408c17fSJean Delvare 	return 0;
3432408c17fSJean Delvare }
3442408c17fSJean Delvare 
i2c_stub_free(void)3452408c17fSJean Delvare static void i2c_stub_free(void)
3462408c17fSJean Delvare {
3472408c17fSJean Delvare 	int i;
3482408c17fSJean Delvare 
3492408c17fSJean Delvare 	for (i = 0; i < stub_chips_nr; i++)
3502408c17fSJean Delvare 		kfree(stub_chips[i].bank_words);
3512408c17fSJean Delvare 	kfree(stub_chips);
3522408c17fSJean Delvare }
3532408c17fSJean Delvare 
i2c_stub_init(void)35431d178bfSJean Delvare static int __init i2c_stub_init(void)
35531d178bfSJean Delvare {
35631d178bfSJean Delvare 	int i, ret;
35731d178bfSJean Delvare 
35831d178bfSJean Delvare 	if (!chip_addr[0]) {
3596c427787SWolfram Sang 		pr_err("Please specify a chip address\n");
36031d178bfSJean Delvare 		return -ENODEV;
36131d178bfSJean Delvare 	}
36231d178bfSJean Delvare 
36331d178bfSJean Delvare 	for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) {
36431d178bfSJean Delvare 		if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) {
3656c427787SWolfram Sang 			pr_err("Invalid chip address 0x%02x\n",
36631d178bfSJean Delvare 			       chip_addr[i]);
36731d178bfSJean Delvare 			return -EINVAL;
36831d178bfSJean Delvare 		}
36931d178bfSJean Delvare 
3706c427787SWolfram Sang 		pr_info("Virtual chip at 0x%02x\n", chip_addr[i]);
37131d178bfSJean Delvare 	}
37231d178bfSJean Delvare 
37331d178bfSJean Delvare 	/* Allocate memory for all chips at once */
3741dff5983SJean Delvare 	stub_chips_nr = i;
3751dff5983SJean Delvare 	stub_chips = kcalloc(stub_chips_nr, sizeof(struct stub_chip),
3761dff5983SJean Delvare 			     GFP_KERNEL);
3776c427787SWolfram Sang 	if (!stub_chips)
37831d178bfSJean Delvare 		return -ENOMEM;
3796c427787SWolfram Sang 
3802408c17fSJean Delvare 	for (i = 0; i < stub_chips_nr; i++) {
3816f16b75aSGuenter Roeck 		INIT_LIST_HEAD(&stub_chips[i].smbus_blocks);
38231d178bfSJean Delvare 
3832408c17fSJean Delvare 		/* Allocate extra memory for banked register ranges */
3842408c17fSJean Delvare 		if (bank_mask[i]) {
3852408c17fSJean Delvare 			ret = i2c_stub_allocate_banks(i);
3862408c17fSJean Delvare 			if (ret)
3872408c17fSJean Delvare 				goto fail_free;
3882408c17fSJean Delvare 		}
3892408c17fSJean Delvare 	}
3902408c17fSJean Delvare 
39131d178bfSJean Delvare 	ret = i2c_add_adapter(&stub_adapter);
39231d178bfSJean Delvare 	if (ret)
3932408c17fSJean Delvare 		goto fail_free;
3942408c17fSJean Delvare 
3952408c17fSJean Delvare 	return 0;
3962408c17fSJean Delvare 
3972408c17fSJean Delvare  fail_free:
3982408c17fSJean Delvare 	i2c_stub_free();
39931d178bfSJean Delvare 	return ret;
40031d178bfSJean Delvare }
40131d178bfSJean Delvare 
i2c_stub_exit(void)40231d178bfSJean Delvare static void __exit i2c_stub_exit(void)
40331d178bfSJean Delvare {
40431d178bfSJean Delvare 	i2c_del_adapter(&stub_adapter);
4052408c17fSJean Delvare 	i2c_stub_free();
40631d178bfSJean Delvare }
40731d178bfSJean Delvare 
40831d178bfSJean Delvare MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
40931d178bfSJean Delvare MODULE_DESCRIPTION("I2C stub driver");
41031d178bfSJean Delvare MODULE_LICENSE("GPL");
41131d178bfSJean Delvare 
41231d178bfSJean Delvare module_init(i2c_stub_init);
41331d178bfSJean Delvare module_exit(i2c_stub_exit);
414