162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci i2c-stub.c - I2C/SMBus chip emulator 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci Copyright (c) 2004 Mark M. Hoffman <mhoffman@lightlink.com> 662306a36Sopenharmony_ci Copyright (C) 2007-2014 Jean Delvare <jdelvare@suse.de> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci*/ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) "i2c-stub: " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/i2c.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/list.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define MAX_CHIPS 10 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * Support for I2C_FUNC_SMBUS_BLOCK_DATA is disabled by default and must 2462306a36Sopenharmony_ci * be enabled explicitly by setting the I2C_FUNC_SMBUS_BLOCK_DATA bits 2562306a36Sopenharmony_ci * in the 'functionality' module parameter. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci#define STUB_FUNC_DEFAULT \ 2862306a36Sopenharmony_ci (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \ 2962306a36Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \ 3062306a36Sopenharmony_ci I2C_FUNC_SMBUS_I2C_BLOCK) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define STUB_FUNC_ALL \ 3362306a36Sopenharmony_ci (STUB_FUNC_DEFAULT | I2C_FUNC_SMBUS_BLOCK_DATA) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic unsigned short chip_addr[MAX_CHIPS]; 3662306a36Sopenharmony_cimodule_param_array(chip_addr, ushort, NULL, S_IRUGO); 3762306a36Sopenharmony_ciMODULE_PARM_DESC(chip_addr, 3862306a36Sopenharmony_ci "Chip addresses (up to 10, between 0x03 and 0x77)"); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic unsigned long functionality = STUB_FUNC_DEFAULT; 4162306a36Sopenharmony_cimodule_param(functionality, ulong, S_IRUGO | S_IWUSR); 4262306a36Sopenharmony_ciMODULE_PARM_DESC(functionality, "Override functionality bitfield"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Some chips have banked register ranges */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic u8 bank_reg[MAX_CHIPS]; 4762306a36Sopenharmony_cimodule_param_array(bank_reg, byte, NULL, S_IRUGO); 4862306a36Sopenharmony_ciMODULE_PARM_DESC(bank_reg, "Bank register"); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic u8 bank_mask[MAX_CHIPS]; 5162306a36Sopenharmony_cimodule_param_array(bank_mask, byte, NULL, S_IRUGO); 5262306a36Sopenharmony_ciMODULE_PARM_DESC(bank_mask, "Bank value mask"); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic u8 bank_start[MAX_CHIPS]; 5562306a36Sopenharmony_cimodule_param_array(bank_start, byte, NULL, S_IRUGO); 5662306a36Sopenharmony_ciMODULE_PARM_DESC(bank_start, "First banked register"); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic u8 bank_end[MAX_CHIPS]; 5962306a36Sopenharmony_cimodule_param_array(bank_end, byte, NULL, S_IRUGO); 6062306a36Sopenharmony_ciMODULE_PARM_DESC(bank_end, "Last banked register"); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct smbus_block_data { 6362306a36Sopenharmony_ci struct list_head node; 6462306a36Sopenharmony_ci u8 command; 6562306a36Sopenharmony_ci u8 len; 6662306a36Sopenharmony_ci u8 block[I2C_SMBUS_BLOCK_MAX]; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistruct stub_chip { 7062306a36Sopenharmony_ci u8 pointer; 7162306a36Sopenharmony_ci u16 words[256]; /* Byte operations use the LSB as per SMBus 7262306a36Sopenharmony_ci specification */ 7362306a36Sopenharmony_ci struct list_head smbus_blocks; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* For chips with banks, extra registers are allocated dynamically */ 7662306a36Sopenharmony_ci u8 bank_reg; 7762306a36Sopenharmony_ci u8 bank_shift; 7862306a36Sopenharmony_ci u8 bank_mask; 7962306a36Sopenharmony_ci u8 bank_sel; /* Currently selected bank */ 8062306a36Sopenharmony_ci u8 bank_start; 8162306a36Sopenharmony_ci u8 bank_end; 8262306a36Sopenharmony_ci u16 bank_size; 8362306a36Sopenharmony_ci u16 *bank_words; /* Room for bank_mask * bank_size registers */ 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic struct stub_chip *stub_chips; 8762306a36Sopenharmony_cistatic int stub_chips_nr; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic struct smbus_block_data *stub_find_block(struct device *dev, 9062306a36Sopenharmony_ci struct stub_chip *chip, 9162306a36Sopenharmony_ci u8 command, bool create) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct smbus_block_data *b, *rb = NULL; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci list_for_each_entry(b, &chip->smbus_blocks, node) { 9662306a36Sopenharmony_ci if (b->command == command) { 9762306a36Sopenharmony_ci rb = b; 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci if (rb == NULL && create) { 10262306a36Sopenharmony_ci rb = devm_kzalloc(dev, sizeof(*rb), GFP_KERNEL); 10362306a36Sopenharmony_ci if (rb == NULL) 10462306a36Sopenharmony_ci return rb; 10562306a36Sopenharmony_ci rb->command = command; 10662306a36Sopenharmony_ci list_add(&rb->node, &chip->smbus_blocks); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci return rb; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic u16 *stub_get_wordp(struct stub_chip *chip, u8 offset) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci if (chip->bank_sel && 11462306a36Sopenharmony_ci offset >= chip->bank_start && offset <= chip->bank_end) 11562306a36Sopenharmony_ci return chip->bank_words + 11662306a36Sopenharmony_ci (chip->bank_sel - 1) * chip->bank_size + 11762306a36Sopenharmony_ci offset - chip->bank_start; 11862306a36Sopenharmony_ci else 11962306a36Sopenharmony_ci return chip->words + offset; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* Return negative errno on error. */ 12362306a36Sopenharmony_cistatic s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags, 12462306a36Sopenharmony_ci char read_write, u8 command, int size, union i2c_smbus_data *data) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci s32 ret; 12762306a36Sopenharmony_ci int i, len; 12862306a36Sopenharmony_ci struct stub_chip *chip = NULL; 12962306a36Sopenharmony_ci struct smbus_block_data *b; 13062306a36Sopenharmony_ci u16 *wordp; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Search for the right chip */ 13362306a36Sopenharmony_ci for (i = 0; i < stub_chips_nr; i++) { 13462306a36Sopenharmony_ci if (addr == chip_addr[i]) { 13562306a36Sopenharmony_ci chip = stub_chips + i; 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci if (!chip) 14062306a36Sopenharmony_ci return -ENODEV; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci switch (size) { 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci case I2C_SMBUS_QUICK: 14562306a36Sopenharmony_ci dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr); 14662306a36Sopenharmony_ci ret = 0; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci case I2C_SMBUS_BYTE: 15062306a36Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 15162306a36Sopenharmony_ci chip->pointer = command; 15262306a36Sopenharmony_ci dev_dbg(&adap->dev, 15362306a36Sopenharmony_ci "smbus byte - addr 0x%02x, wrote 0x%02x.\n", 15462306a36Sopenharmony_ci addr, command); 15562306a36Sopenharmony_ci } else { 15662306a36Sopenharmony_ci wordp = stub_get_wordp(chip, chip->pointer++); 15762306a36Sopenharmony_ci data->byte = *wordp & 0xff; 15862306a36Sopenharmony_ci dev_dbg(&adap->dev, 15962306a36Sopenharmony_ci "smbus byte - addr 0x%02x, read 0x%02x.\n", 16062306a36Sopenharmony_ci addr, data->byte); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = 0; 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci case I2C_SMBUS_BYTE_DATA: 16762306a36Sopenharmony_ci wordp = stub_get_wordp(chip, command); 16862306a36Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 16962306a36Sopenharmony_ci *wordp &= 0xff00; 17062306a36Sopenharmony_ci *wordp |= data->byte; 17162306a36Sopenharmony_ci dev_dbg(&adap->dev, 17262306a36Sopenharmony_ci "smbus byte data - addr 0x%02x, wrote 0x%02x at 0x%02x.\n", 17362306a36Sopenharmony_ci addr, data->byte, command); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Set the bank as needed */ 17662306a36Sopenharmony_ci if (chip->bank_words && command == chip->bank_reg) { 17762306a36Sopenharmony_ci chip->bank_sel = 17862306a36Sopenharmony_ci (data->byte >> chip->bank_shift) 17962306a36Sopenharmony_ci & chip->bank_mask; 18062306a36Sopenharmony_ci dev_dbg(&adap->dev, 18162306a36Sopenharmony_ci "switching to bank %u.\n", 18262306a36Sopenharmony_ci chip->bank_sel); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci } else { 18562306a36Sopenharmony_ci data->byte = *wordp & 0xff; 18662306a36Sopenharmony_ci dev_dbg(&adap->dev, 18762306a36Sopenharmony_ci "smbus byte data - addr 0x%02x, read 0x%02x at 0x%02x.\n", 18862306a36Sopenharmony_ci addr, data->byte, command); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci chip->pointer = command + 1; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci ret = 0; 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci case I2C_SMBUS_WORD_DATA: 19662306a36Sopenharmony_ci wordp = stub_get_wordp(chip, command); 19762306a36Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 19862306a36Sopenharmony_ci *wordp = data->word; 19962306a36Sopenharmony_ci dev_dbg(&adap->dev, 20062306a36Sopenharmony_ci "smbus word data - addr 0x%02x, wrote 0x%04x at 0x%02x.\n", 20162306a36Sopenharmony_ci addr, data->word, command); 20262306a36Sopenharmony_ci } else { 20362306a36Sopenharmony_ci data->word = *wordp; 20462306a36Sopenharmony_ci dev_dbg(&adap->dev, 20562306a36Sopenharmony_ci "smbus word data - addr 0x%02x, read 0x%04x at 0x%02x.\n", 20662306a36Sopenharmony_ci addr, data->word, command); 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci ret = 0; 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci case I2C_SMBUS_I2C_BLOCK_DATA: 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * We ignore banks here, because banked chips don't use I2C 21562306a36Sopenharmony_ci * block transfers 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci if (data->block[0] > 256 - command) /* Avoid overrun */ 21862306a36Sopenharmony_ci data->block[0] = 256 - command; 21962306a36Sopenharmony_ci len = data->block[0]; 22062306a36Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 22162306a36Sopenharmony_ci for (i = 0; i < len; i++) { 22262306a36Sopenharmony_ci chip->words[command + i] &= 0xff00; 22362306a36Sopenharmony_ci chip->words[command + i] |= data->block[1 + i]; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci dev_dbg(&adap->dev, 22662306a36Sopenharmony_ci "i2c block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n", 22762306a36Sopenharmony_ci addr, len, command); 22862306a36Sopenharmony_ci } else { 22962306a36Sopenharmony_ci for (i = 0; i < len; i++) { 23062306a36Sopenharmony_ci data->block[1 + i] = 23162306a36Sopenharmony_ci chip->words[command + i] & 0xff; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci dev_dbg(&adap->dev, 23462306a36Sopenharmony_ci "i2c block data - addr 0x%02x, read %d bytes at 0x%02x.\n", 23562306a36Sopenharmony_ci addr, len, command); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ret = 0; 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci case I2C_SMBUS_BLOCK_DATA: 24262306a36Sopenharmony_ci /* 24362306a36Sopenharmony_ci * We ignore banks here, because chips typically don't use both 24462306a36Sopenharmony_ci * banks and SMBus block transfers 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_ci b = stub_find_block(&adap->dev, chip, command, false); 24762306a36Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 24862306a36Sopenharmony_ci len = data->block[0]; 24962306a36Sopenharmony_ci if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) { 25062306a36Sopenharmony_ci ret = -EINVAL; 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci if (b == NULL) { 25462306a36Sopenharmony_ci b = stub_find_block(&adap->dev, chip, command, 25562306a36Sopenharmony_ci true); 25662306a36Sopenharmony_ci if (b == NULL) { 25762306a36Sopenharmony_ci ret = -ENOMEM; 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci /* Largest write sets read block length */ 26262306a36Sopenharmony_ci if (len > b->len) 26362306a36Sopenharmony_ci b->len = len; 26462306a36Sopenharmony_ci for (i = 0; i < len; i++) 26562306a36Sopenharmony_ci b->block[i] = data->block[i + 1]; 26662306a36Sopenharmony_ci /* update for byte and word commands */ 26762306a36Sopenharmony_ci chip->words[command] = (b->block[0] << 8) | b->len; 26862306a36Sopenharmony_ci dev_dbg(&adap->dev, 26962306a36Sopenharmony_ci "smbus block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n", 27062306a36Sopenharmony_ci addr, len, command); 27162306a36Sopenharmony_ci } else { 27262306a36Sopenharmony_ci if (b == NULL) { 27362306a36Sopenharmony_ci dev_dbg(&adap->dev, 27462306a36Sopenharmony_ci "SMBus block read command without prior block write not supported\n"); 27562306a36Sopenharmony_ci ret = -EOPNOTSUPP; 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci len = b->len; 27962306a36Sopenharmony_ci data->block[0] = len; 28062306a36Sopenharmony_ci for (i = 0; i < len; i++) 28162306a36Sopenharmony_ci data->block[i + 1] = b->block[i]; 28262306a36Sopenharmony_ci dev_dbg(&adap->dev, 28362306a36Sopenharmony_ci "smbus block data - addr 0x%02x, read %d bytes at 0x%02x.\n", 28462306a36Sopenharmony_ci addr, len, command); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = 0; 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci default: 29162306a36Sopenharmony_ci dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n"); 29262306a36Sopenharmony_ci ret = -EOPNOTSUPP; 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci } /* switch (size) */ 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic u32 stub_func(struct i2c_adapter *adapter) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci return STUB_FUNC_ALL & functionality; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic const struct i2c_algorithm smbus_algorithm = { 30562306a36Sopenharmony_ci .functionality = stub_func, 30662306a36Sopenharmony_ci .smbus_xfer = stub_xfer, 30762306a36Sopenharmony_ci}; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic struct i2c_adapter stub_adapter = { 31062306a36Sopenharmony_ci .owner = THIS_MODULE, 31162306a36Sopenharmony_ci .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 31262306a36Sopenharmony_ci .algo = &smbus_algorithm, 31362306a36Sopenharmony_ci .name = "SMBus stub driver", 31462306a36Sopenharmony_ci}; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int __init i2c_stub_allocate_banks(int i) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct stub_chip *chip = stub_chips + i; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci chip->bank_reg = bank_reg[i]; 32162306a36Sopenharmony_ci chip->bank_start = bank_start[i]; 32262306a36Sopenharmony_ci chip->bank_end = bank_end[i]; 32362306a36Sopenharmony_ci chip->bank_size = bank_end[i] - bank_start[i] + 1; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* We assume that all bits in the mask are contiguous */ 32662306a36Sopenharmony_ci chip->bank_mask = bank_mask[i]; 32762306a36Sopenharmony_ci while (!(chip->bank_mask & 1)) { 32862306a36Sopenharmony_ci chip->bank_shift++; 32962306a36Sopenharmony_ci chip->bank_mask >>= 1; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci chip->bank_words = kcalloc(chip->bank_mask * chip->bank_size, 33362306a36Sopenharmony_ci sizeof(u16), 33462306a36Sopenharmony_ci GFP_KERNEL); 33562306a36Sopenharmony_ci if (!chip->bank_words) 33662306a36Sopenharmony_ci return -ENOMEM; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci pr_debug("Allocated %u banks of %u words each (registers 0x%02x to 0x%02x)\n", 33962306a36Sopenharmony_ci chip->bank_mask, chip->bank_size, chip->bank_start, 34062306a36Sopenharmony_ci chip->bank_end); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic void i2c_stub_free(void) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci int i; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci for (i = 0; i < stub_chips_nr; i++) 35062306a36Sopenharmony_ci kfree(stub_chips[i].bank_words); 35162306a36Sopenharmony_ci kfree(stub_chips); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int __init i2c_stub_init(void) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci int i, ret; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!chip_addr[0]) { 35962306a36Sopenharmony_ci pr_err("Please specify a chip address\n"); 36062306a36Sopenharmony_ci return -ENODEV; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { 36462306a36Sopenharmony_ci if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) { 36562306a36Sopenharmony_ci pr_err("Invalid chip address 0x%02x\n", 36662306a36Sopenharmony_ci chip_addr[i]); 36762306a36Sopenharmony_ci return -EINVAL; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci pr_info("Virtual chip at 0x%02x\n", chip_addr[i]); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* Allocate memory for all chips at once */ 37462306a36Sopenharmony_ci stub_chips_nr = i; 37562306a36Sopenharmony_ci stub_chips = kcalloc(stub_chips_nr, sizeof(struct stub_chip), 37662306a36Sopenharmony_ci GFP_KERNEL); 37762306a36Sopenharmony_ci if (!stub_chips) 37862306a36Sopenharmony_ci return -ENOMEM; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci for (i = 0; i < stub_chips_nr; i++) { 38162306a36Sopenharmony_ci INIT_LIST_HEAD(&stub_chips[i].smbus_blocks); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* Allocate extra memory for banked register ranges */ 38462306a36Sopenharmony_ci if (bank_mask[i]) { 38562306a36Sopenharmony_ci ret = i2c_stub_allocate_banks(i); 38662306a36Sopenharmony_ci if (ret) 38762306a36Sopenharmony_ci goto fail_free; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci ret = i2c_add_adapter(&stub_adapter); 39262306a36Sopenharmony_ci if (ret) 39362306a36Sopenharmony_ci goto fail_free; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci fail_free: 39862306a36Sopenharmony_ci i2c_stub_free(); 39962306a36Sopenharmony_ci return ret; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void __exit i2c_stub_exit(void) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci i2c_del_adapter(&stub_adapter); 40562306a36Sopenharmony_ci i2c_stub_free(); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ciMODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 40962306a36Sopenharmony_ciMODULE_DESCRIPTION("I2C stub driver"); 41062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cimodule_init(i2c_stub_init); 41362306a36Sopenharmony_cimodule_exit(i2c_stub_exit); 414