Received: by 2002:a05:6a10:8c0a:0:0:0:0 with SMTP id go10csp3293105pxb; Fri, 12 Feb 2021 14:39:02 -0800 (PST) X-Google-Smtp-Source: ABdhPJw6+b934McK6vJdHbHfIP25ePE4A1d5DzSmOC+enEkO81H4NztgH6q/O7cJCXOezdn+Feu6 X-Received: by 2002:a17:906:a1c2:: with SMTP id bx2mr5252699ejb.138.1613169541967; Fri, 12 Feb 2021 14:39:01 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1613169541; cv=none; d=google.com; s=arc-20160816; b=0vqzW+/acVQpy7PaKy7EjjBgR+MQGX2X+R4Wwx+zYp6iZ6Vw6hH8i5RaUlCTFrnBI/ /qT/c33YODR0PvRxeFJU2gBblikofl8CQsn93bsimLM6dMSj1gTgVSN2nwlee/yWWegn nU8X0sofIF5i8fHd7i+tS92EV/pR+sLCYII8lHvKrH7Afdh1ilnvzxFr/3cpIlMowHiv VeC59I63ejgaJnL2x4YukqSbb+88nrTX4rG8Ave0iAl+8YmO1HGAZ8ZFu2L77jm+zbHN JXfmate6Fv8ucHrhBwBBT/ZP554iiElEvlQe2zP1oyABcO6uJtQLBMPgmR+IE7CmZAE/ gCaw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:references:in-reply-to:message-id:date:subject :cc:to:from:ironport-sdr:ironport-sdr; bh=FcKI2svNLhk2qXDZOEJnpiPczyhVlzLi8ApxBGdyOr4=; b=EysAqIwgcs2bpCMOI8z6O1mutXkDZaL8DOGrWkw1cvr7WS69agZIQALl+Yn1lpCvAM LaJeA53ou2qKRuMWIOv5C63E6JaPoz5vcVOJLXDmK2urA/QxRp9RxyH2IIvSWKgFW1Ow JXl6fzJn5WIllfVwQ+k6f3ei6+/0U49yKLsIcvEY3XWKvtq8wpSKum5SLxnQKcO7gOz9 TlPfB5HEC7zr+yiZ+PI+fY5w0l2XZRF+Fpktpl74DHRIsAi6kjbIGaTUWBEzTEIkpF5l nH0rFL24VaNYKz3ZlTBE8QyK1GaB6PuSohwJRR6mRVwV1ixV4BE1Bj8B9HyVt7kgvWde Vftg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id b4si7449098edr.261.2021.02.12.14.38.37; Fri, 12 Feb 2021 14:39:01 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232346AbhBLWiC (ORCPT + 99 others); Fri, 12 Feb 2021 17:38:02 -0500 Received: from mga12.intel.com ([192.55.52.136]:39762 "EHLO mga12.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232157AbhBLW2L (ORCPT ); Fri, 12 Feb 2021 17:28:11 -0500 IronPort-SDR: GTVMSQHb70Ki4cXt2p8ea62IcBr+TczWLcSjhkJjug3xqqiTjREcAhvFTHAEsFVLlDuu0JhHII pRdgLeKava6Q== X-IronPort-AV: E=McAfee;i="6000,8403,9893"; a="161628120" X-IronPort-AV: E=Sophos;i="5.81,174,1610438400"; d="scan'208";a="161628120" Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga106.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Feb 2021 14:23:09 -0800 IronPort-SDR: sugO/HL3vN1D/0BxDCjBjXnoqX0I2S4EZjQkVYDBIt6ptToF752uGyxe4ssAwQSPgJ4nUYvefW xxnUXWoY7mmA== X-IronPort-AV: E=Sophos;i="5.81,174,1610438400"; d="scan'208";a="362963885" Received: from smtp.ostc.intel.com ([10.54.29.231]) by orsmga006-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Feb 2021 14:23:09 -0800 Received: from mtg-dev.jf.intel.com (mtg-dev.jf.intel.com [10.54.74.10]) by smtp.ostc.intel.com (Postfix) with ESMTP id 1BE80636E; Fri, 12 Feb 2021 14:23:09 -0800 (PST) Received: by mtg-dev.jf.intel.com (Postfix, from userid 1000) id 1551636370D; Fri, 12 Feb 2021 14:23:09 -0800 (PST) From: mgross@linux.intel.com To: markgross@kernel.org, mgross@linux.intel.com, arnd@arndb.de, bp@suse.de, damien.lemoal@wdc.com, dragan.cvetic@xilinx.com, gregkh@linuxfoundation.org, corbet@lwn.net, palmerdabbelt@google.com, paul.walmsley@sifive.com, peng.fan@nxp.com, robh+dt@kernel.org, shawnguo@kernel.org, jassisinghbrar@gmail.com Cc: linux-kernel@vger.kernel.org, Ramya P Karanth Subject: [PATCH v6 31/34] Intel Keembay XLink SMBus driver Date: Fri, 12 Feb 2021 14:23:01 -0800 Message-Id: <20210212222304.110194-32-mgross@linux.intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210212222304.110194-1-mgross@linux.intel.com> References: <20210212222304.110194-1-mgross@linux.intel.com> Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Ramya P Karanth Adds XLink SMBus driver for Intel Keem Bay SoC. Xlink-smbus driver is a logical SMBus adapter which uses Xlink (xlink-pcie) protocol as an interface. Keem Bay(s) vision accelerators are connected to the server via PCI interface. The Server needs to know the temperature of the SoC and the source to get the temperature can be either on board sensors or on chip sensors. The sensors are ideally connected over i2c bus of the SoC and the server does not have access to sensors present in the PCB. With this xlink-smbus interfaces, server access the on board/on chip sensors via xlink smbus adapter. Signed-off-by: Ramya P Karanth Signed-off-by: Mark Gross --- Documentation/i2c/busses/index.rst | 1 + .../i2c/busses/intel-xlink-smbus.rst | 71 +++ drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/xlink-smbus/Kconfig | 25 + drivers/misc/xlink-smbus/Makefile | 5 + drivers/misc/xlink-smbus/xlink-smbus.c | 467 ++++++++++++++++++ 7 files changed, 571 insertions(+) create mode 100644 Documentation/i2c/busses/intel-xlink-smbus.rst create mode 100644 drivers/misc/xlink-smbus/Kconfig create mode 100644 drivers/misc/xlink-smbus/Makefile create mode 100644 drivers/misc/xlink-smbus/xlink-smbus.c diff --git a/Documentation/i2c/busses/index.rst b/Documentation/i2c/busses/index.rst index 5e4077b08d86..6ce4a740f616 100644 --- a/Documentation/i2c/busses/index.rst +++ b/Documentation/i2c/busses/index.rst @@ -29,4 +29,5 @@ I2C Bus Drivers i2c-taos-evm i2c-viapro i2c-via + intel-xlink-smbus.rst scx200_acb diff --git a/Documentation/i2c/busses/intel-xlink-smbus.rst b/Documentation/i2c/busses/intel-xlink-smbus.rst new file mode 100644 index 000000000000..ab87d18051b4 --- /dev/null +++ b/Documentation/i2c/busses/intel-xlink-smbus.rst @@ -0,0 +1,71 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================== +Kernel driver: xlink_smbus +========================== + +Supported chips: + * Intel Edge.AI Computer Vision platforms: Keem Bay + + Sufix: Bay + + Slave address: The address is selectable by device-tree. (TBD) + +Authors: + - Raja Subramanian, Lakshmi Bai + - Thalaiappan, Rathina + - Karanth, Ramya P + +Description +=========== +The Intel Edge.AI Computer Vision platforms have to be monitored using platform +devices like sensors, fan controller, IO expander etc. Some of these devices +are memory mapped and some are i2c based. Either of these devices are not +directly accessible to the host. + +The host here refers to the server to which the vision accelerators are +connected over PCIe Interface. The Host needs to do a consolidated action based +on the parameters of platform devices. In general, most of the standard devices +(includes sensors, fan controller, IO expander etc) are I2C/SMBus based and are +used to provide the status of the accelerator. Standard drivers for these +devices are available based on i2c/smbus APIs. + +Instead of changing the sensor drivers to adapt to PCIe interface, a generic +i2c adapter "xlink-smbus" which underneath uses xlink as physical medium is +used. With xlink-smbus, the drivers for the platform devices doesn't need to +undergo any interface change. + +High-level architecture +======================= + +Accessing Onchip devices:: + + ------------------- ------------------- + | Remote Host | | Local Host | + | IA CPU | | Vision platforms| + ------------------- ------------------- + | Onchip | | i2c slave | ==> Access the device + | sensor driver | | handler | ==> which is mmio based + ------------------- ------------------- + |Intel XLINK_SMBUS| |Intel XLINK_SMBUS| + | adpater | | adapter | + | (Master) | | (I2C_SLAVE) | + ------------------- ------------------- + | XLINK | <==========> | XLINK | + ------------------- PCIE ------------------- + +Accessing Onboard devices:: + + ------------------- ---------------------- + | Remote Host | | Local Host | + | IA CPU | | Vision platforms | + ------------------- ---------------------- + | On board | | i2c smbus | ==> Access the device + | sensor driver | | xfer [synopsys] | ==> which is on i2c bus + ------------------- ---------------------- + |Intel XLINK_SMBUS| | Intel XLINK_SMBUS | + | adpater | | adapter | + | (Master) | |(SMBUS_PROXY Master)| + ------------------- ---------------------- + | XLINK | <==========> | XLINK | + ------------------- PCIE ---------------------- diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index aed3ef61897c..f6229dd8ba9e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -486,4 +486,5 @@ source "drivers/misc/xlink-ipc/Kconfig" source "drivers/misc/xlink-core/Kconfig" source "drivers/misc/vpumgr/Kconfig" source "drivers/misc/intel_tsens/Kconfig" +source "drivers/misc/xlink-smbus/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c08502b22778..0ed8a62cbb20 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -62,3 +62,4 @@ obj-$(CONFIG_XLINK_IPC) += xlink-ipc/ obj-$(CONFIG_XLINK_CORE) += xlink-core/ obj-$(CONFIG_VPUMGR) += vpumgr/ obj-y += intel_tsens/ +obj-$(CONFIG_XLINK_SMBUS) += xlink-smbus/ diff --git a/drivers/misc/xlink-smbus/Kconfig b/drivers/misc/xlink-smbus/Kconfig new file mode 100644 index 000000000000..8d2451f8d1be --- /dev/null +++ b/drivers/misc/xlink-smbus/Kconfig @@ -0,0 +1,25 @@ +# Copyright (C) 2020 Intel Corporation +# SPDX-License-Identifier: GPL-2.0-only + +config XLINK_SMBUS + tristate "Enable SMBUS interface over Xlink PCIe" + depends on XLINK_CORE + depends on HDDL_DEVICE_CLIENT || HDDL_DEVICE_SERVER + help + Enable xlink-pcie as I2C adapter both slave and master. The server + (Remote Host) will use this interface to get sensor data from the SoC + (vision accelerator - Local Host) which is connected over PCIe. + This driver is loaded on both Remote Host and Local Host. + Select M to compile the driver as a module, name is xlink-smbus. + If unsure, select N. + +config XLINK_SMBUS_PROXY + tristate "Enable SMBUS adapter as proxy for I2C controller" + depends on XLINK_CORE + depends on XLINK_SMBUS + help + Enable this config when SMBUS adapter is acting as proxy for + another I2C controller. + Select M or Y if building for Intel Vision Processing Unit (VPU) + Local Host core. + Select N, if building for a Remote Host kernel. diff --git a/drivers/misc/xlink-smbus/Makefile b/drivers/misc/xlink-smbus/Makefile new file mode 100644 index 000000000000..27369dfa488c --- /dev/null +++ b/drivers/misc/xlink-smbus/Makefile @@ -0,0 +1,5 @@ +# Copyright (C) 2020 Intel Corporation +# SPDX-License-Identifier: GPL-2.0-only +# Makefile for Xlink SMBus +# +obj-$(CONFIG_XLINK_SMBUS) += xlink-smbus.o diff --git a/drivers/misc/xlink-smbus/xlink-smbus.c b/drivers/misc/xlink-smbus/xlink-smbus.c new file mode 100644 index 000000000000..fc652e6c96bb --- /dev/null +++ b/drivers/misc/xlink-smbus/xlink-smbus.c @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel Xlink SMBus Driver + * + * Copyright (C) 2020 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct xlink_msg { + u16 addr; + u16 flags; + u8 read_write; + u8 command; + u16 padding; + u32 protocol; + union i2c_smbus_data data; + s32 status; + struct list_head node; +}; + +struct xlink_adapter_data { + struct xlink_handle *xhandle; + struct completion work; + struct task_struct *task_recv; + struct i2c_client *slave; + struct list_head head; + struct i2c_adapter *adap; + u32 channel; +}; + +#if defined(CONFIG_XLINK_SMBUS_PROXY) +/* + * PROXY the commands using existing adapter + * I2C2 is fixed for Keem Bay, it has all sensors connected + */ +#define proxy_i2c_adapter_info() i2c_get_adapter(2) +#else +/* + * This is an adapter by itself + * It doesn't proxy transfer on another adapter + */ +#define proxy_i2c_adapter_info() ((void *)0) +#endif + +#if IS_ENABLED(CONFIG_I2C_SLAVE) +/* + * The complete slave protocol is implemented in one shot here as + * the whole chunk of data is transferred or received via xlink, + * not byte-by-byte + * Refer https://lwn.net/Articles/640346/ for protocol + */ +static s32 handle_slave_mode(struct i2c_client *slave, struct xlink_msg *msg) +{ + struct device *dev = &slave->dev; + u8 temp; + + /* Send the command as first write */ + i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, NULL); + i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &msg->command); + + /* Now handle specifics to read/write */ + if (msg->read_write == I2C_SMBUS_WRITE) { + if (msg->protocol == I2C_SMBUS_BYTE_DATA) { + i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, + &msg->data.byte); + } else if (msg->protocol == I2C_SMBUS_WORD_DATA) { + temp = msg->data.word & 0xFF; + i2c_slave_event(slave, + I2C_SLAVE_WRITE_RECEIVED, + &temp); + temp = (msg->data.word >> 8) & 0xFF; + i2c_slave_event(slave, + I2C_SLAVE_WRITE_RECEIVED, + &temp); + } else if (msg->protocol == I2C_SMBUS_BLOCK_DATA) { + int i; + + if (msg->data.block[0] > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + + for (i = 1; (i < msg->data.block[0] || + i <= I2C_SMBUS_BLOCK_MAX); ++i) { + i2c_slave_event(slave, + I2C_SLAVE_WRITE_RECEIVED, + &msg->data.block[i]); + } + } else { + dev_err(dev, + "unknown protocol (%d) received in %s\n", + msg->protocol, + __func__ + ); + return -EOPNOTSUPP; + } + } else { + if (msg->protocol == I2C_SMBUS_BYTE_DATA) { + i2c_slave_event(slave, + I2C_SLAVE_READ_REQUESTED, + &msg->data.byte); + } else if (msg->protocol == I2C_SMBUS_WORD_DATA) { + i2c_slave_event(slave, + I2C_SLAVE_READ_REQUESTED, + &temp); + msg->data.word = temp << 8; + i2c_slave_event(slave, + I2C_SLAVE_READ_REQUESTED, + &temp); + msg->data.word |= temp; + } else if (msg->protocol == I2C_SMBUS_BLOCK_DATA) { + int i; + + if (msg->data.block[0] > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + + for (i = 1; (i < msg->data.block[0] || + i <= I2C_SMBUS_BLOCK_MAX); ++i) { + i2c_slave_event(slave, + I2C_SLAVE_READ_REQUESTED, + &msg->data.block[i]); + } + } else { + dev_err(dev, + "unknown protocol (%d) received in %s\n", + msg->protocol, + __func__); + return -EOPNOTSUPP; + } + i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &temp); + } + i2c_slave_event(slave, I2C_SLAVE_STOP, NULL); + return 0; +} +#endif /* CONFIG_I2C_SLAVE */ + +static s32 xlink_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int protocol, + union i2c_smbus_data *data) +{ + struct xlink_adapter_data *adapt_data = NULL; + struct device *dev = NULL; + struct xlink_msg tx_msg, *rx_msg; + enum xlink_error xerr; + s32 rc = 0; + + if (!adap) + return -ENODEV; + adapt_data = i2c_get_adapdata(adap); + dev = &adapt_data->adap->dev; + + if (!data) + return -EINVAL; + + tx_msg.addr = addr; + tx_msg.flags = flags; + tx_msg.read_write = read_write; + tx_msg.command = command; + tx_msg.protocol = protocol; + tx_msg.data = *data; + tx_msg.status = 0; + + xerr = xlink_write_data(adapt_data->xhandle, adapt_data->channel, + (u8 *)&tx_msg, + sizeof(struct xlink_msg)); + + if (xerr != X_LINK_SUCCESS) { + dev_err_ratelimited(dev, + "xlink_write_data failed (%d) dropping packet.\n", + xerr); + return -EIO; + } + + /* + * wait for getting the response from the peer host device + * message is received by xlinki2c_receive_thread + * and notified here through completion trigger + */ + if (wait_for_completion_interruptible_timeout(&adapt_data->work, + 4 * HZ) > 0) { + rx_msg = list_first_entry(&adapt_data->head, + struct xlink_msg, + node); + list_del(&rx_msg->node); + + /* Update the data and status from the xlink message received */ + *data = rx_msg->data; + rc = rx_msg->status; + + /* free the response received from Proxy */ + kfree(rx_msg); + } else { + WARN_ONCE(1, "VPU not responding"); + rc = -ETIMEDOUT; + } + + return rc; +} + +static int xlinki2c_receive_thread(void *param) +{ + struct xlink_adapter_data *adapt_data = param; + struct device *dev = &adapt_data->adap->dev; + struct i2c_adapter *adap; + enum xlink_error xerr; + struct xlink_msg *msg; + u32 size; + + while (!kthread_should_stop()) { + /* msg will be freed in this context or other */ + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + /* Wait to receive xlink message from the peer device */ + xerr = xlink_read_data_to_buffer(adapt_data->xhandle, + adapt_data->channel, + (uint8_t *)msg, &size); + if (xerr != X_LINK_SUCCESS) { + if (xerr != X_LINK_TIMEOUT) { + dev_warn_ratelimited(dev, + "[%d] Error (%d) dropping packet.\n", + adapt_data->adap->nr, xerr); + } + kfree(msg); + continue; + } + xlink_release_data(adapt_data->xhandle, adapt_data->channel, + NULL); + adap = proxy_i2c_adapter_info(); + + if (adap) { +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (adapt_data->slave) { + msg->status = handle_slave_mode + (adapt_data->slave, msg); + goto send_resp; + } +#endif + /* + * This is a proxy for an existing adapter. + * call the local adapter to receive the data + * from the hardware. + */ + msg->status = i2c_smbus_xfer(adap, + msg->addr, + msg->flags, + msg->read_write, + msg->command, + msg->protocol, + &msg->data); + + /* + * Send back the complete message that + * carries status, back to sender which is + * waiting on xlinki2c_receive_thread + */ +#if IS_ENABLED(CONFIG_I2C_SLAVE) +send_resp: +#endif + xlink_write_data(adapt_data->xhandle, + adapt_data->channel, (u8 *)msg, + sizeof(struct xlink_msg)); + kfree(msg); + } else { + /* + * This is an adapter on its own. + * Receives the status and data over xlink (msg). + * Indicate the data received to the component + * which is waiting in xlink_smbus_xfer + */ + list_add_tail(&msg->node, &adapt_data->head); + complete(&adapt_data->work); + } + } /* thread loop */ + dev_dbg(dev, "[%d] %s stopped\n", adapt_data->adap->nr, __func__); + + return 0; +} + +static inline u32 xlink_smbus_func(struct i2c_adapter *adapter) +{ + u32 func = I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; + + return func; +} + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + +/* + * This will be called when slave client driver + * register itself to an adapter + */ +static int xlink_smbus_reg_slave(struct i2c_client *slave) +{ + struct xlink_adapter_data *adapt_data = + i2c_get_adapdata(slave->adapter); + + adapt_data->slave = slave; + + return 0; +} + +static int xlink_smbus_unreg_slave(struct i2c_client *slave) +{ + struct xlink_adapter_data *adapt_data = + i2c_get_adapdata(slave->adapter); + + adapt_data->slave = NULL; + + return 0; +} +#endif + +static struct i2c_algorithm xlink_algorithm = { + .smbus_xfer = xlink_smbus_xfer, + .functionality = xlink_smbus_func, +#if IS_ENABLED(CONFIG_I2C_SLAVE) + .reg_slave = xlink_smbus_reg_slave, + .unreg_slave = xlink_smbus_unreg_slave, +#endif +}; + +static int xlink_i2c_probe(struct platform_device *pdev) +{ + struct intel_hddl_clients *c = pdev->dev.platform_data; + struct xlink_handle *devhandle = &c->xlink_dev; + struct xlink_adapter_data *adapt_data; + struct device *dev = &pdev->dev; + struct i2c_adapter *adap; + u32 rc; + + dev_dbg(dev, "Registering xlink SMBus adapter...\n"); + + adap = kzalloc(sizeof(*adap), GFP_KERNEL); + if (!adap) + return -ENOMEM; + + c->adap[pdev->id & 0x3] = adap; + memset(adap, 0, sizeof(struct i2c_adapter)); + adap->owner = THIS_MODULE; + adap->algo = &xlink_algorithm; + strcpy(adap->name, "xlink adapter"); + platform_set_drvdata(pdev, adap); + + adapt_data = kzalloc(sizeof(*adapt_data), GFP_KERNEL); + if (!adapt_data) { + kfree(adap); + return -ENOMEM; + } + + init_completion(&adapt_data->work); + + INIT_LIST_HEAD(&adapt_data->head); + adapt_data->channel = c->xlink_i2c_ch[pdev->id & 0x3]; + adapt_data->xhandle = devhandle; + adapt_data->adap = adap; + + rc = xlink_open_channel(devhandle, + adapt_data->channel, + RXB_TXB, /* mode */ + 64 * 1024, + 100); /* timeout */ + if (rc != X_LINK_SUCCESS) { + dev_err(dev, "xlink_open_channel failed[%d][%d][%p]\n", rc, + adapt_data->channel, + adapt_data->xhandle); + goto err_kfree; + } + + i2c_set_adapdata(adap, adapt_data); + + rc = i2c_add_adapter(adap); + if (rc) + goto err_exit; + + /* Create receiver thread */ + adapt_data->task_recv = kthread_run(xlinki2c_receive_thread, + adapt_data, + "xlinki2c_receive_thread"); + if (!adapt_data->task_recv) { + dev_err(dev, "%s Thread creation failed", __func__); + i2c_del_adapter(adapt_data->adap); + goto err_exit; + } + return 0; + +err_exit: + xlink_close_channel(adapt_data->xhandle, adapt_data->channel); +err_kfree: + kfree(adap); + kfree(adapt_data); + return rc; +} + +static int xlink_i2c_remove(struct platform_device *pdev) +{ + struct i2c_adapter *adap = platform_get_drvdata(pdev); + struct xlink_adapter_data *adapt_data = i2c_get_adapdata(adap); + + kthread_stop(adapt_data->task_recv); + + dev_info(&adap->dev, "Delete the adapter[%d]\n", adap->nr); + /* Close the channel and disconnect */ + xlink_close_channel(adapt_data->xhandle, adapt_data->channel); + /* This will block the dynamic registration */ + i2c_del_adapter(adapt_data->adap); + kfree(adapt_data); + + return 0; +} + +static struct platform_driver xlink_i2c_driver = { + .probe = xlink_i2c_probe, + .remove = xlink_i2c_remove, + .driver = { + .name = "i2c_xlink" + } +}; + +/* Define the xlink debug device structures to be used with dev_dbg() et al */ + +static struct device_driver dbg_name = { + .name = "xlink_i2c_dbg" +}; + +static struct device dbg_subname = { + .init_name = "xlink_i2c_dbg", + .driver = &dbg_name +}; + +static struct device *dbgxi2c = &dbg_subname; + +static void __exit xlink_adapter_exit(void) +{ + dev_dbg(dbgxi2c, "Unloading XLink I2C module...\n"); + platform_driver_unregister(&xlink_i2c_driver); +} + +static int __init xlink_adapter_init(void) +{ + dev_dbg(dbgxi2c, "Loading XLink I2C module...\n"); + platform_driver_register(&xlink_i2c_driver); + return 0; +} + +module_init(xlink_adapter_init); +module_exit(xlink_adapter_exit); + +MODULE_AUTHOR("Raja Subramanian, Lakshmi Bai "); +MODULE_AUTHOR("Thalaiappan, Rathina "); +MODULE_AUTHOR("Karanth, Ramya P "); +MODULE_DESCRIPTION("xlink i2c adapter"); +MODULE_LICENSE("GPL"); -- 2.17.1