18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * i2c-smbus.c - SMBus extensions to the I2C protocol 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 David Brownell 68c2ecf20Sopenharmony_ci * Copyright (C) 2010-2019 Jean Delvare <jdelvare@suse.de> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/dmi.h> 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c-smbus.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct i2c_smbus_alert { 218c2ecf20Sopenharmony_ci struct work_struct alert; 228c2ecf20Sopenharmony_ci struct i2c_client *ara; /* Alert response address */ 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct alert_data { 268c2ecf20Sopenharmony_ci unsigned short addr; 278c2ecf20Sopenharmony_ci enum i2c_alert_protocol type; 288c2ecf20Sopenharmony_ci unsigned int data; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* If this is the alerting device, notify its driver */ 328c2ecf20Sopenharmony_cistatic int smbus_do_alert(struct device *dev, void *addrp) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct i2c_client *client = i2c_verify_client(dev); 358c2ecf20Sopenharmony_ci struct alert_data *data = addrp; 368c2ecf20Sopenharmony_ci struct i2c_driver *driver; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (!client || client->addr != data->addr) 398c2ecf20Sopenharmony_ci return 0; 408c2ecf20Sopenharmony_ci if (client->flags & I2C_CLIENT_TEN) 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci /* 448c2ecf20Sopenharmony_ci * Drivers should either disable alerts, or provide at least 458c2ecf20Sopenharmony_ci * a minimal handler. Lock so the driver won't change. 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci device_lock(dev); 488c2ecf20Sopenharmony_ci if (client->dev.driver) { 498c2ecf20Sopenharmony_ci driver = to_i2c_driver(client->dev.driver); 508c2ecf20Sopenharmony_ci if (driver->alert) 518c2ecf20Sopenharmony_ci driver->alert(client, data->type, data->data); 528c2ecf20Sopenharmony_ci else 538c2ecf20Sopenharmony_ci dev_warn(&client->dev, "no driver alert()!\n"); 548c2ecf20Sopenharmony_ci } else 558c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "alert with no driver\n"); 568c2ecf20Sopenharmony_ci device_unlock(dev); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Stop iterating after we find the device */ 598c2ecf20Sopenharmony_ci return -EBUSY; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * The alert IRQ handler needs to hand work off to a task which can issue 648c2ecf20Sopenharmony_ci * SMBus calls, because those sleeping calls can't be made in IRQ context. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic irqreturn_t smbus_alert(int irq, void *d) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct i2c_smbus_alert *alert = d; 698c2ecf20Sopenharmony_ci struct i2c_client *ara; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci ara = alert->ara; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci for (;;) { 748c2ecf20Sopenharmony_ci s32 status; 758c2ecf20Sopenharmony_ci struct alert_data data; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * Devices with pending alerts reply in address order, low 798c2ecf20Sopenharmony_ci * to high, because of slave transmit arbitration. After 808c2ecf20Sopenharmony_ci * responding, an SMBus device stops asserting SMBALERT#. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Note that SMBus 2.0 reserves 10-bit addresses for future 838c2ecf20Sopenharmony_ci * use. We neither handle them, nor try to use PEC here. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci status = i2c_smbus_read_byte(ara); 868c2ecf20Sopenharmony_ci if (status < 0) 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci data.data = status & 1; 908c2ecf20Sopenharmony_ci data.addr = status >> 1; 918c2ecf20Sopenharmony_ci data.type = I2C_PROTOCOL_SMBUS_ALERT; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n", 948c2ecf20Sopenharmony_ci data.addr, data.data); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Notify driver for the device which issued the alert */ 978c2ecf20Sopenharmony_ci device_for_each_child(&ara->adapter->dev, &data, 988c2ecf20Sopenharmony_ci smbus_do_alert); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void smbalert_work(struct work_struct *work) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct i2c_smbus_alert *alert; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci alert = container_of(work, struct i2c_smbus_alert, alert); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci smbus_alert(0, alert); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* Setup SMBALERT# infrastructure */ 1158c2ecf20Sopenharmony_cistatic int smbalert_probe(struct i2c_client *ara, 1168c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev); 1198c2ecf20Sopenharmony_ci struct i2c_smbus_alert *alert; 1208c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = ara->adapter; 1218c2ecf20Sopenharmony_ci int res, irq; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert), 1248c2ecf20Sopenharmony_ci GFP_KERNEL); 1258c2ecf20Sopenharmony_ci if (!alert) 1268c2ecf20Sopenharmony_ci return -ENOMEM; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (setup) { 1298c2ecf20Sopenharmony_ci irq = setup->irq; 1308c2ecf20Sopenharmony_ci } else { 1318c2ecf20Sopenharmony_ci irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert"); 1328c2ecf20Sopenharmony_ci if (irq <= 0) 1338c2ecf20Sopenharmony_ci return irq; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci INIT_WORK(&alert->alert, smbalert_work); 1378c2ecf20Sopenharmony_ci alert->ara = ara; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (irq > 0) { 1408c2ecf20Sopenharmony_ci res = devm_request_threaded_irq(&ara->dev, irq, 1418c2ecf20Sopenharmony_ci NULL, smbus_alert, 1428c2ecf20Sopenharmony_ci IRQF_SHARED | IRQF_ONESHOT, 1438c2ecf20Sopenharmony_ci "smbus_alert", alert); 1448c2ecf20Sopenharmony_ci if (res) 1458c2ecf20Sopenharmony_ci return res; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci i2c_set_clientdata(ara, alert); 1498c2ecf20Sopenharmony_ci dev_info(&adapter->dev, "supports SMBALERT#\n"); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* IRQ and memory resources are managed so they are freed automatically */ 1558c2ecf20Sopenharmony_cistatic int smbalert_remove(struct i2c_client *ara) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci cancel_work_sync(&alert->alert); 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic const struct i2c_device_id smbalert_ids[] = { 1648c2ecf20Sopenharmony_ci { "smbus_alert", 0 }, 1658c2ecf20Sopenharmony_ci { /* LIST END */ } 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, smbalert_ids); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic struct i2c_driver smbalert_driver = { 1708c2ecf20Sopenharmony_ci .driver = { 1718c2ecf20Sopenharmony_ci .name = "smbus_alert", 1728c2ecf20Sopenharmony_ci }, 1738c2ecf20Sopenharmony_ci .probe = smbalert_probe, 1748c2ecf20Sopenharmony_ci .remove = smbalert_remove, 1758c2ecf20Sopenharmony_ci .id_table = smbalert_ids, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/** 1798c2ecf20Sopenharmony_ci * i2c_handle_smbus_alert - Handle an SMBus alert 1808c2ecf20Sopenharmony_ci * @ara: the ARA client on the relevant adapter 1818c2ecf20Sopenharmony_ci * Context: can't sleep 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * Helper function to be called from an I2C bus driver's interrupt 1848c2ecf20Sopenharmony_ci * handler. It will schedule the alert work, in turn calling the 1858c2ecf20Sopenharmony_ci * corresponding I2C device driver's alert function. 1868c2ecf20Sopenharmony_ci * 1878c2ecf20Sopenharmony_ci * It is assumed that ara is a valid i2c client previously returned by 1888c2ecf20Sopenharmony_ci * i2c_new_smbus_alert_device(). 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ciint i2c_handle_smbus_alert(struct i2c_client *ara) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return schedule_work(&alert->alert); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_handle_smbus_alert); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cimodule_i2c_driver(smbalert_driver); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SLAVE) 2018c2ecf20Sopenharmony_ci#define SMBUS_HOST_NOTIFY_LEN 3 2028c2ecf20Sopenharmony_cistruct i2c_slave_host_notify_status { 2038c2ecf20Sopenharmony_ci u8 index; 2048c2ecf20Sopenharmony_ci u8 addr; 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int i2c_slave_host_notify_cb(struct i2c_client *client, 2088c2ecf20Sopenharmony_ci enum i2c_slave_event event, u8 *val) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct i2c_slave_host_notify_status *status = client->dev.platform_data; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci switch (event) { 2138c2ecf20Sopenharmony_ci case I2C_SLAVE_WRITE_RECEIVED: 2148c2ecf20Sopenharmony_ci /* We only retrieve the first byte received (addr) 2158c2ecf20Sopenharmony_ci * since there is currently no support to retrieve the data 2168c2ecf20Sopenharmony_ci * parameter from the client. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci if (status->index == 0) 2198c2ecf20Sopenharmony_ci status->addr = *val; 2208c2ecf20Sopenharmony_ci if (status->index < U8_MAX) 2218c2ecf20Sopenharmony_ci status->index++; 2228c2ecf20Sopenharmony_ci break; 2238c2ecf20Sopenharmony_ci case I2C_SLAVE_STOP: 2248c2ecf20Sopenharmony_ci if (status->index == SMBUS_HOST_NOTIFY_LEN) 2258c2ecf20Sopenharmony_ci i2c_handle_smbus_host_notify(client->adapter, 2268c2ecf20Sopenharmony_ci status->addr); 2278c2ecf20Sopenharmony_ci fallthrough; 2288c2ecf20Sopenharmony_ci case I2C_SLAVE_WRITE_REQUESTED: 2298c2ecf20Sopenharmony_ci status->index = 0; 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci case I2C_SLAVE_READ_REQUESTED: 2328c2ecf20Sopenharmony_ci case I2C_SLAVE_READ_PROCESSED: 2338c2ecf20Sopenharmony_ci *val = 0xff; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/** 2418c2ecf20Sopenharmony_ci * i2c_new_slave_host_notify_device - get a client for SMBus host-notify support 2428c2ecf20Sopenharmony_ci * @adapter: the target adapter 2438c2ecf20Sopenharmony_ci * Context: can sleep 2448c2ecf20Sopenharmony_ci * 2458c2ecf20Sopenharmony_ci * Setup handling of the SMBus host-notify protocol on a given I2C bus segment. 2468c2ecf20Sopenharmony_ci * 2478c2ecf20Sopenharmony_ci * Handling is done by creating a device and its callback and handling data 2488c2ecf20Sopenharmony_ci * received via the SMBus host-notify address (0x8) 2498c2ecf20Sopenharmony_ci * 2508c2ecf20Sopenharmony_ci * This returns the client, which should be ultimately freed using 2518c2ecf20Sopenharmony_ci * i2c_free_slave_host_notify_device(); or an ERRPTR to indicate an error. 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_cistruct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct i2c_board_info host_notify_board_info = { 2568c2ecf20Sopenharmony_ci I2C_BOARD_INFO("smbus_host_notify", 0x08), 2578c2ecf20Sopenharmony_ci .flags = I2C_CLIENT_SLAVE, 2588c2ecf20Sopenharmony_ci }; 2598c2ecf20Sopenharmony_ci struct i2c_slave_host_notify_status *status; 2608c2ecf20Sopenharmony_ci struct i2c_client *client; 2618c2ecf20Sopenharmony_ci int ret; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci status = kzalloc(sizeof(struct i2c_slave_host_notify_status), 2648c2ecf20Sopenharmony_ci GFP_KERNEL); 2658c2ecf20Sopenharmony_ci if (!status) 2668c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci host_notify_board_info.platform_data = status; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci client = i2c_new_client_device(adapter, &host_notify_board_info); 2718c2ecf20Sopenharmony_ci if (IS_ERR(client)) { 2728c2ecf20Sopenharmony_ci kfree(status); 2738c2ecf20Sopenharmony_ci return client; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci ret = i2c_slave_register(client, i2c_slave_host_notify_cb); 2778c2ecf20Sopenharmony_ci if (ret) { 2788c2ecf20Sopenharmony_ci i2c_unregister_device(client); 2798c2ecf20Sopenharmony_ci kfree(status); 2808c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return client; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_new_slave_host_notify_device); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/** 2888c2ecf20Sopenharmony_ci * i2c_free_slave_host_notify_device - free the client for SMBus host-notify 2898c2ecf20Sopenharmony_ci * support 2908c2ecf20Sopenharmony_ci * @client: the client to free 2918c2ecf20Sopenharmony_ci * Context: can sleep 2928c2ecf20Sopenharmony_ci * 2938c2ecf20Sopenharmony_ci * Free the i2c_client allocated via i2c_new_slave_host_notify_device 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_civoid i2c_free_slave_host_notify_device(struct i2c_client *client) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(client)) 2988c2ecf20Sopenharmony_ci return; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci i2c_slave_unregister(client); 3018c2ecf20Sopenharmony_ci kfree(client->dev.platform_data); 3028c2ecf20Sopenharmony_ci i2c_unregister_device(client); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_free_slave_host_notify_device); 3058c2ecf20Sopenharmony_ci#endif 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/* 3088c2ecf20Sopenharmony_ci * SPD is not part of SMBus but we include it here for convenience as the 3098c2ecf20Sopenharmony_ci * target systems are the same. 3108c2ecf20Sopenharmony_ci * Restrictions to automatic SPD instantiation: 3118c2ecf20Sopenharmony_ci * - Only works if all filled slots have the same memory type 3128c2ecf20Sopenharmony_ci * - Only works for DDR2, DDR3 and DDR4 for now 3138c2ecf20Sopenharmony_ci * - Only works on systems with 1 to 4 memory slots 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_DMI) 3168c2ecf20Sopenharmony_civoid i2c_register_spd(struct i2c_adapter *adap) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci int n, slot_count = 0, dimm_count = 0; 3198c2ecf20Sopenharmony_ci u16 handle; 3208c2ecf20Sopenharmony_ci u8 common_mem_type = 0x0, mem_type; 3218c2ecf20Sopenharmony_ci u64 mem_size; 3228c2ecf20Sopenharmony_ci const char *name; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci while ((handle = dmi_memdev_handle(slot_count)) != 0xffff) { 3258c2ecf20Sopenharmony_ci slot_count++; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* Skip empty slots */ 3288c2ecf20Sopenharmony_ci mem_size = dmi_memdev_size(handle); 3298c2ecf20Sopenharmony_ci if (!mem_size) 3308c2ecf20Sopenharmony_ci continue; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Skip undefined memory type */ 3338c2ecf20Sopenharmony_ci mem_type = dmi_memdev_type(handle); 3348c2ecf20Sopenharmony_ci if (mem_type <= 0x02) /* Invalid, Other, Unknown */ 3358c2ecf20Sopenharmony_ci continue; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (!common_mem_type) { 3388c2ecf20Sopenharmony_ci /* First filled slot */ 3398c2ecf20Sopenharmony_ci common_mem_type = mem_type; 3408c2ecf20Sopenharmony_ci } else { 3418c2ecf20Sopenharmony_ci /* Check that all filled slots have the same type */ 3428c2ecf20Sopenharmony_ci if (mem_type != common_mem_type) { 3438c2ecf20Sopenharmony_ci dev_warn(&adap->dev, 3448c2ecf20Sopenharmony_ci "Different memory types mixed, not instantiating SPD\n"); 3458c2ecf20Sopenharmony_ci return; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci dimm_count++; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* No useful DMI data, bail out */ 3528c2ecf20Sopenharmony_ci if (!dimm_count) 3538c2ecf20Sopenharmony_ci return; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci dev_info(&adap->dev, "%d/%d memory slots populated (from DMI)\n", 3568c2ecf20Sopenharmony_ci dimm_count, slot_count); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (slot_count > 4) { 3598c2ecf20Sopenharmony_ci dev_warn(&adap->dev, 3608c2ecf20Sopenharmony_ci "Systems with more than 4 memory slots not supported yet, not instantiating SPD\n"); 3618c2ecf20Sopenharmony_ci return; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci switch (common_mem_type) { 3658c2ecf20Sopenharmony_ci case 0x13: /* DDR2 */ 3668c2ecf20Sopenharmony_ci case 0x18: /* DDR3 */ 3678c2ecf20Sopenharmony_ci case 0x1C: /* LPDDR2 */ 3688c2ecf20Sopenharmony_ci case 0x1D: /* LPDDR3 */ 3698c2ecf20Sopenharmony_ci name = "spd"; 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci case 0x1A: /* DDR4 */ 3728c2ecf20Sopenharmony_ci case 0x1E: /* LPDDR4 */ 3738c2ecf20Sopenharmony_ci name = "ee1004"; 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci default: 3768c2ecf20Sopenharmony_ci dev_info(&adap->dev, 3778c2ecf20Sopenharmony_ci "Memory type 0x%02x not supported yet, not instantiating SPD\n", 3788c2ecf20Sopenharmony_ci common_mem_type); 3798c2ecf20Sopenharmony_ci return; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* 3838c2ecf20Sopenharmony_ci * We don't know in which slots the memory modules are. We could 3848c2ecf20Sopenharmony_ci * try to guess from the slot names, but that would be rather complex 3858c2ecf20Sopenharmony_ci * and unreliable, so better probe all possible addresses until we 3868c2ecf20Sopenharmony_ci * have found all memory modules. 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ci for (n = 0; n < slot_count && dimm_count; n++) { 3898c2ecf20Sopenharmony_ci struct i2c_board_info info; 3908c2ecf20Sopenharmony_ci unsigned short addr_list[2]; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(struct i2c_board_info)); 3938c2ecf20Sopenharmony_ci strlcpy(info.type, name, I2C_NAME_SIZE); 3948c2ecf20Sopenharmony_ci addr_list[0] = 0x50 + n; 3958c2ecf20Sopenharmony_ci addr_list[1] = I2C_CLIENT_END; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (!IS_ERR(i2c_new_scanned_device(adap, &info, addr_list, NULL))) { 3988c2ecf20Sopenharmony_ci dev_info(&adap->dev, 3998c2ecf20Sopenharmony_ci "Successfully instantiated SPD at 0x%hx\n", 4008c2ecf20Sopenharmony_ci addr_list[0]); 4018c2ecf20Sopenharmony_ci dimm_count--; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_register_spd); 4068c2ecf20Sopenharmony_ci#endif 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); 4098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SMBus protocol extensions support"); 4108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 411