162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * i2c-smbus.c - SMBus extensions to the I2C protocol 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 David Brownell 662306a36Sopenharmony_ci * Copyright (C) 2010-2019 Jean Delvare <jdelvare@suse.de> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/dmi.h> 1162306a36Sopenharmony_ci#include <linux/i2c.h> 1262306a36Sopenharmony_ci#include <linux/i2c-smbus.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/property.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/workqueue.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct i2c_smbus_alert { 2162306a36Sopenharmony_ci struct work_struct alert; 2262306a36Sopenharmony_ci struct i2c_client *ara; /* Alert response address */ 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct alert_data { 2662306a36Sopenharmony_ci unsigned short addr; 2762306a36Sopenharmony_ci enum i2c_alert_protocol type; 2862306a36Sopenharmony_ci unsigned int data; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* If this is the alerting device, notify its driver */ 3262306a36Sopenharmony_cistatic int smbus_do_alert(struct device *dev, void *addrp) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct i2c_client *client = i2c_verify_client(dev); 3562306a36Sopenharmony_ci struct alert_data *data = addrp; 3662306a36Sopenharmony_ci struct i2c_driver *driver; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (!client || client->addr != data->addr) 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci if (client->flags & I2C_CLIENT_TEN) 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* 4462306a36Sopenharmony_ci * Drivers should either disable alerts, or provide at least 4562306a36Sopenharmony_ci * a minimal handler. Lock so the driver won't change. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci device_lock(dev); 4862306a36Sopenharmony_ci if (client->dev.driver) { 4962306a36Sopenharmony_ci driver = to_i2c_driver(client->dev.driver); 5062306a36Sopenharmony_ci if (driver->alert) 5162306a36Sopenharmony_ci driver->alert(client, data->type, data->data); 5262306a36Sopenharmony_ci else 5362306a36Sopenharmony_ci dev_warn(&client->dev, "no driver alert()!\n"); 5462306a36Sopenharmony_ci } else 5562306a36Sopenharmony_ci dev_dbg(&client->dev, "alert with no driver\n"); 5662306a36Sopenharmony_ci device_unlock(dev); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* Stop iterating after we find the device */ 5962306a36Sopenharmony_ci return -EBUSY; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * The alert IRQ handler needs to hand work off to a task which can issue 6462306a36Sopenharmony_ci * SMBus calls, because those sleeping calls can't be made in IRQ context. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistatic irqreturn_t smbus_alert(int irq, void *d) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct i2c_smbus_alert *alert = d; 6962306a36Sopenharmony_ci struct i2c_client *ara; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci ara = alert->ara; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci for (;;) { 7462306a36Sopenharmony_ci s32 status; 7562306a36Sopenharmony_ci struct alert_data data; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * Devices with pending alerts reply in address order, low 7962306a36Sopenharmony_ci * to high, because of slave transmit arbitration. After 8062306a36Sopenharmony_ci * responding, an SMBus device stops asserting SMBALERT#. 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Note that SMBus 2.0 reserves 10-bit addresses for future 8362306a36Sopenharmony_ci * use. We neither handle them, nor try to use PEC here. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci status = i2c_smbus_read_byte(ara); 8662306a36Sopenharmony_ci if (status < 0) 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci data.data = status & 1; 9062306a36Sopenharmony_ci data.addr = status >> 1; 9162306a36Sopenharmony_ci data.type = I2C_PROTOCOL_SMBUS_ALERT; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n", 9462306a36Sopenharmony_ci data.addr, data.data); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* Notify driver for the device which issued the alert */ 9762306a36Sopenharmony_ci device_for_each_child(&ara->adapter->dev, &data, 9862306a36Sopenharmony_ci smbus_do_alert); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return IRQ_HANDLED; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void smbalert_work(struct work_struct *work) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct i2c_smbus_alert *alert; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci alert = container_of(work, struct i2c_smbus_alert, alert); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci smbus_alert(0, alert); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* Setup SMBALERT# infrastructure */ 11562306a36Sopenharmony_cistatic int smbalert_probe(struct i2c_client *ara) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev); 11862306a36Sopenharmony_ci struct i2c_smbus_alert *alert; 11962306a36Sopenharmony_ci struct i2c_adapter *adapter = ara->adapter; 12062306a36Sopenharmony_ci int res, irq; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert), 12362306a36Sopenharmony_ci GFP_KERNEL); 12462306a36Sopenharmony_ci if (!alert) 12562306a36Sopenharmony_ci return -ENOMEM; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (setup) { 12862306a36Sopenharmony_ci irq = setup->irq; 12962306a36Sopenharmony_ci } else { 13062306a36Sopenharmony_ci irq = fwnode_irq_get_byname(dev_fwnode(adapter->dev.parent), 13162306a36Sopenharmony_ci "smbus_alert"); 13262306a36Sopenharmony_ci if (irq <= 0) 13362306a36Sopenharmony_ci return irq; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci INIT_WORK(&alert->alert, smbalert_work); 13762306a36Sopenharmony_ci alert->ara = ara; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (irq > 0) { 14062306a36Sopenharmony_ci res = devm_request_threaded_irq(&ara->dev, irq, 14162306a36Sopenharmony_ci NULL, smbus_alert, 14262306a36Sopenharmony_ci IRQF_SHARED | IRQF_ONESHOT, 14362306a36Sopenharmony_ci "smbus_alert", alert); 14462306a36Sopenharmony_ci if (res) 14562306a36Sopenharmony_ci return res; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci i2c_set_clientdata(ara, alert); 14962306a36Sopenharmony_ci dev_info(&adapter->dev, "supports SMBALERT#\n"); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* IRQ and memory resources are managed so they are freed automatically */ 15562306a36Sopenharmony_cistatic void smbalert_remove(struct i2c_client *ara) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci cancel_work_sync(&alert->alert); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic const struct i2c_device_id smbalert_ids[] = { 16362306a36Sopenharmony_ci { "smbus_alert", 0 }, 16462306a36Sopenharmony_ci { /* LIST END */ } 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, smbalert_ids); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic struct i2c_driver smbalert_driver = { 16962306a36Sopenharmony_ci .driver = { 17062306a36Sopenharmony_ci .name = "smbus_alert", 17162306a36Sopenharmony_ci }, 17262306a36Sopenharmony_ci .probe = smbalert_probe, 17362306a36Sopenharmony_ci .remove = smbalert_remove, 17462306a36Sopenharmony_ci .id_table = smbalert_ids, 17562306a36Sopenharmony_ci}; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/** 17862306a36Sopenharmony_ci * i2c_handle_smbus_alert - Handle an SMBus alert 17962306a36Sopenharmony_ci * @ara: the ARA client on the relevant adapter 18062306a36Sopenharmony_ci * Context: can't sleep 18162306a36Sopenharmony_ci * 18262306a36Sopenharmony_ci * Helper function to be called from an I2C bus driver's interrupt 18362306a36Sopenharmony_ci * handler. It will schedule the alert work, in turn calling the 18462306a36Sopenharmony_ci * corresponding I2C device driver's alert function. 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * It is assumed that ara is a valid i2c client previously returned by 18762306a36Sopenharmony_ci * i2c_new_smbus_alert_device(). 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ciint i2c_handle_smbus_alert(struct i2c_client *ara) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return schedule_work(&alert->alert); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_handle_smbus_alert); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cimodule_i2c_driver(smbalert_driver); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 20062306a36Sopenharmony_ci#define SMBUS_HOST_NOTIFY_LEN 3 20162306a36Sopenharmony_cistruct i2c_slave_host_notify_status { 20262306a36Sopenharmony_ci u8 index; 20362306a36Sopenharmony_ci u8 addr; 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int i2c_slave_host_notify_cb(struct i2c_client *client, 20762306a36Sopenharmony_ci enum i2c_slave_event event, u8 *val) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct i2c_slave_host_notify_status *status = client->dev.platform_data; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci switch (event) { 21262306a36Sopenharmony_ci case I2C_SLAVE_WRITE_RECEIVED: 21362306a36Sopenharmony_ci /* We only retrieve the first byte received (addr) 21462306a36Sopenharmony_ci * since there is currently no support to retrieve the data 21562306a36Sopenharmony_ci * parameter from the client. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci if (status->index == 0) 21862306a36Sopenharmony_ci status->addr = *val; 21962306a36Sopenharmony_ci if (status->index < U8_MAX) 22062306a36Sopenharmony_ci status->index++; 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci case I2C_SLAVE_STOP: 22362306a36Sopenharmony_ci if (status->index == SMBUS_HOST_NOTIFY_LEN) 22462306a36Sopenharmony_ci i2c_handle_smbus_host_notify(client->adapter, 22562306a36Sopenharmony_ci status->addr); 22662306a36Sopenharmony_ci fallthrough; 22762306a36Sopenharmony_ci case I2C_SLAVE_WRITE_REQUESTED: 22862306a36Sopenharmony_ci status->index = 0; 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci case I2C_SLAVE_READ_REQUESTED: 23162306a36Sopenharmony_ci case I2C_SLAVE_READ_PROCESSED: 23262306a36Sopenharmony_ci *val = 0xff; 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/** 24062306a36Sopenharmony_ci * i2c_new_slave_host_notify_device - get a client for SMBus host-notify support 24162306a36Sopenharmony_ci * @adapter: the target adapter 24262306a36Sopenharmony_ci * Context: can sleep 24362306a36Sopenharmony_ci * 24462306a36Sopenharmony_ci * Setup handling of the SMBus host-notify protocol on a given I2C bus segment. 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * Handling is done by creating a device and its callback and handling data 24762306a36Sopenharmony_ci * received via the SMBus host-notify address (0x8) 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci * This returns the client, which should be ultimately freed using 25062306a36Sopenharmony_ci * i2c_free_slave_host_notify_device(); or an ERRPTR to indicate an error. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_cistruct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct i2c_board_info host_notify_board_info = { 25562306a36Sopenharmony_ci I2C_BOARD_INFO("smbus_host_notify", 0x08), 25662306a36Sopenharmony_ci .flags = I2C_CLIENT_SLAVE, 25762306a36Sopenharmony_ci }; 25862306a36Sopenharmony_ci struct i2c_slave_host_notify_status *status; 25962306a36Sopenharmony_ci struct i2c_client *client; 26062306a36Sopenharmony_ci int ret; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci status = kzalloc(sizeof(struct i2c_slave_host_notify_status), 26362306a36Sopenharmony_ci GFP_KERNEL); 26462306a36Sopenharmony_ci if (!status) 26562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci host_notify_board_info.platform_data = status; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci client = i2c_new_client_device(adapter, &host_notify_board_info); 27062306a36Sopenharmony_ci if (IS_ERR(client)) { 27162306a36Sopenharmony_ci kfree(status); 27262306a36Sopenharmony_ci return client; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ret = i2c_slave_register(client, i2c_slave_host_notify_cb); 27662306a36Sopenharmony_ci if (ret) { 27762306a36Sopenharmony_ci i2c_unregister_device(client); 27862306a36Sopenharmony_ci kfree(status); 27962306a36Sopenharmony_ci return ERR_PTR(ret); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return client; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_new_slave_host_notify_device); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/** 28762306a36Sopenharmony_ci * i2c_free_slave_host_notify_device - free the client for SMBus host-notify 28862306a36Sopenharmony_ci * support 28962306a36Sopenharmony_ci * @client: the client to free 29062306a36Sopenharmony_ci * Context: can sleep 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * Free the i2c_client allocated via i2c_new_slave_host_notify_device 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_civoid i2c_free_slave_host_notify_device(struct i2c_client *client) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci if (IS_ERR_OR_NULL(client)) 29762306a36Sopenharmony_ci return; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci i2c_slave_unregister(client); 30062306a36Sopenharmony_ci kfree(client->dev.platform_data); 30162306a36Sopenharmony_ci i2c_unregister_device(client); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_free_slave_host_notify_device); 30462306a36Sopenharmony_ci#endif 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* 30762306a36Sopenharmony_ci * SPD is not part of SMBus but we include it here for convenience as the 30862306a36Sopenharmony_ci * target systems are the same. 30962306a36Sopenharmony_ci * Restrictions to automatic SPD instantiation: 31062306a36Sopenharmony_ci * - Only works if all filled slots have the same memory type 31162306a36Sopenharmony_ci * - Only works for DDR2, DDR3 and DDR4 for now 31262306a36Sopenharmony_ci * - Only works on systems with 1 to 4 memory slots 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_DMI) 31562306a36Sopenharmony_civoid i2c_register_spd(struct i2c_adapter *adap) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci int n, slot_count = 0, dimm_count = 0; 31862306a36Sopenharmony_ci u16 handle; 31962306a36Sopenharmony_ci u8 common_mem_type = 0x0, mem_type; 32062306a36Sopenharmony_ci u64 mem_size; 32162306a36Sopenharmony_ci const char *name; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci while ((handle = dmi_memdev_handle(slot_count)) != 0xffff) { 32462306a36Sopenharmony_ci slot_count++; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* Skip empty slots */ 32762306a36Sopenharmony_ci mem_size = dmi_memdev_size(handle); 32862306a36Sopenharmony_ci if (!mem_size) 32962306a36Sopenharmony_ci continue; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Skip undefined memory type */ 33262306a36Sopenharmony_ci mem_type = dmi_memdev_type(handle); 33362306a36Sopenharmony_ci if (mem_type <= 0x02) /* Invalid, Other, Unknown */ 33462306a36Sopenharmony_ci continue; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (!common_mem_type) { 33762306a36Sopenharmony_ci /* First filled slot */ 33862306a36Sopenharmony_ci common_mem_type = mem_type; 33962306a36Sopenharmony_ci } else { 34062306a36Sopenharmony_ci /* Check that all filled slots have the same type */ 34162306a36Sopenharmony_ci if (mem_type != common_mem_type) { 34262306a36Sopenharmony_ci dev_warn(&adap->dev, 34362306a36Sopenharmony_ci "Different memory types mixed, not instantiating SPD\n"); 34462306a36Sopenharmony_ci return; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci dimm_count++; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* No useful DMI data, bail out */ 35162306a36Sopenharmony_ci if (!dimm_count) 35262306a36Sopenharmony_ci return; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci dev_info(&adap->dev, "%d/%d memory slots populated (from DMI)\n", 35562306a36Sopenharmony_ci dimm_count, slot_count); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (slot_count > 4) { 35862306a36Sopenharmony_ci dev_warn(&adap->dev, 35962306a36Sopenharmony_ci "Systems with more than 4 memory slots not supported yet, not instantiating SPD\n"); 36062306a36Sopenharmony_ci return; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* 36462306a36Sopenharmony_ci * Memory types could be found at section 7.18.2 (Memory Device — Type), table 78 36562306a36Sopenharmony_ci * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.6.0.pdf 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci switch (common_mem_type) { 36862306a36Sopenharmony_ci case 0x12: /* DDR */ 36962306a36Sopenharmony_ci case 0x13: /* DDR2 */ 37062306a36Sopenharmony_ci case 0x18: /* DDR3 */ 37162306a36Sopenharmony_ci case 0x1B: /* LPDDR */ 37262306a36Sopenharmony_ci case 0x1C: /* LPDDR2 */ 37362306a36Sopenharmony_ci case 0x1D: /* LPDDR3 */ 37462306a36Sopenharmony_ci name = "spd"; 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci case 0x1A: /* DDR4 */ 37762306a36Sopenharmony_ci case 0x1E: /* LPDDR4 */ 37862306a36Sopenharmony_ci name = "ee1004"; 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci default: 38162306a36Sopenharmony_ci dev_info(&adap->dev, 38262306a36Sopenharmony_ci "Memory type 0x%02x not supported yet, not instantiating SPD\n", 38362306a36Sopenharmony_ci common_mem_type); 38462306a36Sopenharmony_ci return; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* 38862306a36Sopenharmony_ci * We don't know in which slots the memory modules are. We could 38962306a36Sopenharmony_ci * try to guess from the slot names, but that would be rather complex 39062306a36Sopenharmony_ci * and unreliable, so better probe all possible addresses until we 39162306a36Sopenharmony_ci * have found all memory modules. 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_ci for (n = 0; n < slot_count && dimm_count; n++) { 39462306a36Sopenharmony_ci struct i2c_board_info info; 39562306a36Sopenharmony_ci unsigned short addr_list[2]; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci memset(&info, 0, sizeof(struct i2c_board_info)); 39862306a36Sopenharmony_ci strscpy(info.type, name, I2C_NAME_SIZE); 39962306a36Sopenharmony_ci addr_list[0] = 0x50 + n; 40062306a36Sopenharmony_ci addr_list[1] = I2C_CLIENT_END; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!IS_ERR(i2c_new_scanned_device(adap, &info, addr_list, NULL))) { 40362306a36Sopenharmony_ci dev_info(&adap->dev, 40462306a36Sopenharmony_ci "Successfully instantiated SPD at 0x%hx\n", 40562306a36Sopenharmony_ci addr_list[0]); 40662306a36Sopenharmony_ci dimm_count--; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_register_spd); 41162306a36Sopenharmony_ci#endif 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ciMODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); 41462306a36Sopenharmony_ciMODULE_DESCRIPTION("SMBus protocol extensions support"); 41562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 416