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