[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20210205225204.32902-34-mgross@linux.intel.com>
Date: Fri, 5 Feb 2021 14:52:03 -0800
From: mgross@...ux.intel.com
To: markgross@...nel.org, mgross@...ux.intel.com, arnd@...db.de,
bp@...e.de, damien.lemoal@....com, dragan.cvetic@...inx.com,
gregkh@...uxfoundation.org, corbet@....net,
palmerdabbelt@...gle.com, paul.walmsley@...ive.com,
peng.fan@....com, robh+dt@...nel.org, shawnguo@...nel.org,
jassisinghbrar@...il.com
Cc: linux-kernel@...r.kernel.org,
"C, Udhayakumar" <udhayakumar.c@...el.com>
Subject: [PATCH v5 33/34] misc: Hddl device management for local host
From: "C, Udhayakumar" <udhayakumar.c@...el.com>
Add local host hddl device management for Intel Edge.AI Computer Vision
platforms.
About Intel Edge.AI Computer Vision platforms:
---------------------------------------------
The Intel Edge.AI Computer Vision platforms are vision processing systems
targeting machine vision applications for connected devices.
They are based on ARM A53 CPU running Linux and acts as a PCIe
endpoint device.
High-level architecture:
------------------------
Remote Host IA CPU Local Host ARM CPU
------------------------------- ----------------------------
| * Send time as xlink packet | |* Sync time with IA host |
| * receive sensor details | |* Prepare and share sensor|
| and register as i2c or | | details to IA host as |
| xlink smbus slaves | | xlink packets |
------------------------------- ----------------------------
| hddl server | <=====> | hddl client |
------------------------------- xlink ----------------------------
hddl device module:
-------------------
The HDDL client driver acts as an software RTC to sync with network
time. It abstracts xlink protocol to communicate with remote host.
This driver exports the details about sensors available in the
platform to remote host as xlink packets.
This driver also handles device connect/disconnect events and
identifies board id and soc id using gpio's, based on platform
configuration.
- Local Host driver
* Intended for ARM CPU
* It is based on xlink Framework
* Driver path:
{tree}/drivers/misc/hddl_device/hddl_device_client.c
Local arm host and Remote IA host drivers communicates using
XLINK protocol.
Signed-off-by: C Udhayakumar <udhayakumar.c@...el.com>
Signed-off-by: Mark Gross <mgross@...ux.intel.com>
---
.../misc-devices/hddl_device_client.rst | 212 +++++
Documentation/misc-devices/index.rst | 1 +
Documentation/vpu/index.rst | 1 +
MAINTAINERS | 1 +
drivers/misc/Kconfig | 1 +
drivers/misc/Makefile | 1 +
drivers/misc/hddl_device/Kconfig | 14 +
drivers/misc/hddl_device/Makefile | 5 +
drivers/misc/hddl_device/hddl_device.c | 565 +++++++++++++
drivers/misc/hddl_device/hddl_device_lh.c | 764 ++++++++++++++++++
drivers/misc/hddl_device/hddl_device_util.h | 52 ++
11 files changed, 1617 insertions(+)
create mode 100644 Documentation/misc-devices/hddl_device_client.rst
create mode 100644 drivers/misc/hddl_device/Kconfig
create mode 100644 drivers/misc/hddl_device/Makefile
create mode 100644 drivers/misc/hddl_device/hddl_device.c
create mode 100644 drivers/misc/hddl_device/hddl_device_lh.c
create mode 100644 drivers/misc/hddl_device/hddl_device_util.h
diff --git a/Documentation/misc-devices/hddl_device_client.rst b/Documentation/misc-devices/hddl_device_client.rst
new file mode 100644
index 000000000000..413643b6b500
--- /dev/null
+++ b/Documentation/misc-devices/hddl_device_client.rst
@@ -0,0 +1,212 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================================
+Kernel driver: hddl_device_client
+=================================
+
+Supported chips:
+ * Intel Edge.AI Computer Vision platforms: Keem Bay
+
+Authors:
+ - Thalaiappan, Rathina <rathina.thalaiappan@...el.com>
+ - Udhayakumar C <udhayakumar.c@...el.com>
+
+
+Overview
+========
+
+This driver supports hddl device management for Intel Edge.AI Computer Vision
+platforms.
+
+This driver supports the following features:
+
+ - Exports deatils of temperature sensor, current sensor and fan controller
+ present in Intel Edge.AI Computer Vision platforms to IA host.
+ - Enable Time sync of Intel Edge.AI Computer Vision platform with IA host.
+ - Handles device connect and disconnect events.
+ - Receives slave address from the IA host for memory mapped thermal sensors
+ present in SoC (Documentation/hwmon/intel_tsens_sensors.rst).
+ - Registers i2c slave device for slaves present in Intel Edge.AI Computer
+ Vision platform
+
+Keem Bay platform has
+Onchip sensors:
+
+ - Media Subsystem (mss) temperature sensor
+ - NN subsystem (nce) temperature sensor
+ - Compute subsystem (cse) temperature sensor
+ - SOC(Maximum of mss, nce and cse).
+
+Onboard sensors:
+
+ - two lm75 temperature sensors
+ - emc2103 fan controller
+ - ina3221 current sensor
+
+High-level architecture
+=======================
+::
+
+ Remote Host IA CPU Local Host ARM CPU
+ ------------------------------- ----------------------------
+ | * Send time as xlink packet | |* Sync time with IA host |
+ | * receive sensor details | |* Prepare and share sensor|
+ | and register as i2c or | | details to IA host as |
+ | xlink smbus slaves | | xlink packets |
+ ------------------------------- ----------------------------
+ | hddl server | <=====> | hddl client |
+ ------------------------------- xlink ----------------------------
+
+Driver Structure
+================
+
+The driver provides a platform device where the ``probe`` and ``remove``
+operations are provided.
+
+ - probe: Gets list of external sensors from device-tree entries, identify
+ board id and soc id based on configuration from device-tree entries and
+ spawn kernel thread to monitor new PCIE devices.
+
+ - init task: Poll for new PCIE device with time interval of 5 seconds and
+ creates connect task to setup new device.
+
+ - connect task: Connect task is the main entity which connects to hddl
+ device server using xlink and does the basic initialisation and handshaking.
+ Additionally it also monitors the hddl device server link down/link up
+ events and reinitialise the drivers accordingly in the client side.
+
+ - remove: unregister i2c client devices, i2c adapters and close xlink
+ channel.
+
+HDDL Client Sequence – Basic Setup and handshaking with HDDL Device Server
+==========================================================================
+::
+
+ ,-----. ,---------. ,------------. ,------------------.
+ |probe| |Init task| |connect task| |hddl device server|
+ `--+--' `----+----' `-----+------' `--------+---------'
+ ----. | | |
+ | "Parse DT" | | |
+ <---' | | |
+ | | | |
+ | ,------------------!. | | |
+ | |Get sensor details|_\ | | |
+ | |from device tree | | | |
+ | `--------------------' | | |
+ ----. | | |
+ | "Identify Board Id"| | |
+ <---' | | |
+ | | | |
+ | "Creates kthread" | | |
+ |----------------------->| | |
+ | | | |
+ | ,-----------------------!. | |
+ | |creates kernel thread |_\ | |
+ | |to check for new device | | |
+ | `-------------------------' | |
+ ,---------------------!. ----. | |
+ |check for new device |_\ | | |
+ |with time interval of | <---' | |
+ |5 seconds | | | |
+ `-----------------------' | | |
+ ,---------------------!. | | |
+ |if new device found?.|_\ | | |
+ |creates connect task | |-------------------->| |
+ |to setup new device | | | |
+ `-----------------------' | | |
+ | ,-------------------!. |----. |
+ | |setup xlink channel|_\| | |
+ | |to communicate with ||<---' |
+ | |server || |
+ | `---------------------'| |
+ | | | Get time data |
+ | | | from server |
+ | | | <--------------------------
+ | | | |
+ | | | share board id |
+ | | | -------------------------->
+ | | | |
+ | | | share total number of |
+ | | | sensors available in SoC |
+ | | | -------------------------->
+ | | | |
+ | ,-----------------------!. | |
+ | |For each sensors share |_\| |
+ | |sensor type, name, trip || -------------------------->
+ | |temp, trip type || |
+ | `-------------------------'| |
+ | | | Receives Send complete. |
+ | | | <--------------------------
+ | | | |
+ | | |----. |
+ | | | | Register xlink i2c |
+ | | |<---' adapters. |
+ | | | |
+ | | | |
+ | | | Receives slave addr for |
+ | | | each salve in SoC |
+ | | | <--------------------------
+ | | | |
+ | | |----. |
+ | | | | Register i2c clients.|
+ | | |<---' |
+ | | | |
+ | | |----.
+ | | | | poll for device status
+ | | |<---'
+ ,--+--. ,----+----. ,-----+------. ,--------+---------.
+ |probe| |Init task| |connect task| |hddl device server|
+ `-----' `---------' `------------' `------------------'
+
+
+XLINK i2c sequence:
+===================
+::
+
+ ,-----------------. ,--------. ,-----. ,---------.
+ |xlink-i2c-adapter| |I2C core| |xlink| |i2c-slave|
+ `--------+--------' `---+----' `--+--' `----+----'
+ | | | |
+ |---------------------->| | |
+ | | | |
+ | ,--------------------------!. | |
+ | |Initialize xlink based i2c|_\ | |
+ | |adapters. | | |
+ | `----------------------------' | |
+ | | | |
+ |<-----------------------------------------| |
+ | | | |
+ | ,--------------------------------!. | |
+ | |I2C request is received as xlink|_\ | |
+ | |packet from IA host | | |
+ | `----------------------------------' | |
+ | | | |
+ |---------------------->| | |
+ | | | |
+ | | ,---------------------------------!.
+ | | |xlink I2C request is converted to|_\
+ | | |standard i2c request |
+ | | `-----------------------------------'
+ | | | |
+ | | ----------------------------------->|
+ | | | |
+ | | ,----------------------!. |
+ | | |Linux i2c slave device|_\ |
+ | | |standard request | |
+ | | `------------------------' |
+ | | | |
+ | | <-----------------------------------|
+ | | | |
+ | | ,----------------------!. |
+ | | |Linux i2c slave device|_\ |
+ | | |standard response | |
+ | | `------------------------' |
+ | I2C response | | |
+ |<----------------------| | |
+ | | | |
+ | | | ,-------------------------!.
+ |----------------------------------------->| |I2C response is converted|_\
+ | | | |to xlink packet |
+ ,--------+--------. ,---+----. ,--+-`---------------------------'
+ |xlink-i2c-adapter| |I2C core| |xlink| |i2c-slave|
+ `-----------------' `--------' `-----' `---------'
diff --git a/Documentation/misc-devices/index.rst b/Documentation/misc-devices/index.rst
index 64420b3314fe..102f7f9dea87 100644
--- a/Documentation/misc-devices/index.rst
+++ b/Documentation/misc-devices/index.rst
@@ -19,6 +19,7 @@ fit into other categories.
bh1770glc
eeprom
c2port
+ hddl_device_client.rst
ibmvmc
ics932s401
isl29003
diff --git a/Documentation/vpu/index.rst b/Documentation/vpu/index.rst
index cd4272e089ec..b50b1376b591 100644
--- a/Documentation/vpu/index.rst
+++ b/Documentation/vpu/index.rst
@@ -17,3 +17,4 @@ This documentation contains information for the Intel VPU stack.
xlink-pcie
xlink-ipc
xlink-core
+ hddl_device_client
diff --git a/MAINTAINERS b/MAINTAINERS
index 61d7e07be721..596c770231cc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1988,6 +1988,7 @@ F: drivers/misc/intel_tsens/
ARM/INTEL TSENS SUPPORT
M: Udhayakumar C <udhayakumar.c@...el.com>
S: Supported
+F: drivers/misc/hddl_device/
F: drivers/misc/intel_tsens/
ARM/INTEL RESEARCH IMOTE/STARGATE 2 MACHINE SUPPORT
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f6229dd8ba9e..f9f3d4d89e7b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -487,4 +487,5 @@ source "drivers/misc/xlink-core/Kconfig"
source "drivers/misc/vpumgr/Kconfig"
source "drivers/misc/intel_tsens/Kconfig"
source "drivers/misc/xlink-smbus/Kconfig"
+source "drivers/misc/hddl_device/Kconfig"
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 0ed8a62cbb20..350618dbe678 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -63,3 +63,4 @@ obj-$(CONFIG_XLINK_CORE) += xlink-core/
obj-$(CONFIG_VPUMGR) += vpumgr/
obj-y += intel_tsens/
obj-$(CONFIG_XLINK_SMBUS) += xlink-smbus/
+obj-y += hddl_device/
diff --git a/drivers/misc/hddl_device/Kconfig b/drivers/misc/hddl_device/Kconfig
new file mode 100644
index 000000000000..e1ae81fdf177
--- /dev/null
+++ b/drivers/misc/hddl_device/Kconfig
@@ -0,0 +1,14 @@
+# Copyright (C) 2020 Intel Corporation
+# SPDX-License-Identifier: GPL-2.0-only
+
+config HDDL_DEVICE_CLIENT
+ tristate "Support for hddl device client"
+ depends on XLINK_CORE && INTEL_TSENS_LOCAL_HOST
+ help
+ This option enables HDDL device client module.
+
+ This driver is used for sharing time sync data to local host and
+ retrives the sensors available on the platform. This also handles
+ the device connect/disconnect programming sequence.
+ Say Y if using a processor that includes the Intel VPU such as
+ Keem Bay. If unsure, say N.
diff --git a/drivers/misc/hddl_device/Makefile b/drivers/misc/hddl_device/Makefile
new file mode 100644
index 000000000000..dca381660baa
--- /dev/null
+++ b/drivers/misc/hddl_device/Makefile
@@ -0,0 +1,5 @@
+# Copyright (C) 2020 Intel Corporation
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_HDDL_DEVICE_CLIENT) += hddl_device_client.o
+hddl_device_client-objs += hddl_device_lh.o hddl_device.o
diff --git a/drivers/misc/hddl_device/hddl_device.c b/drivers/misc/hddl_device/hddl_device.c
new file mode 100644
index 000000000000..89e22adc3a03
--- /dev/null
+++ b/drivers/misc/hddl_device/hddl_device.c
@@ -0,0 +1,565 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * High Density Deep Learning HELPER module.
+ *
+ * Copyright (C) 2020 Intel Corporation
+ *
+ */
+
+#include <asm/page.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hddl_device.h>
+#include <linux/i2c.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/sched.h>
+#include <linux/sched/mm.h>
+#include <linux/sched/task.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/xlink.h>
+#include <uapi/linux/stat.h>
+#include "hddl_device_util.h"
+
+#define HDDL_XLINK_OPEN_TIMEOUT 1000
+#define HDDL_I2C_CLIENT_INIT_TIME 1000
+
+enum hddl_device_event_type {
+ HDDL_NOTIFY_DEVICE_DISCONNECTED,
+ HDDL_NOTIFY_DEVICE_CONNECTED,
+ HDDL_NUM_EVENT_TYPE,
+};
+
+/*
+ * Register callback for device
+ * connect and disconnect events.
+ */
+static u32 hddl_device_events[] = {
+ HDDL_NOTIFY_DEVICE_DISCONNECTED,
+ HDDL_NOTIFY_DEVICE_CONNECTED,
+ HDDL_NUM_EVENT_TYPE
+};
+
+/**
+ * intel_hddl_new_device - check for new hddl device
+ * @dev: The hddl device.
+ * @hddl_clients: list of existing client devices.
+ * @sw_device_id_list: list of software device id's to check for new device.
+ * @num_devices: number of id's present in sw_device_id_list.
+ * @n_devs: number of existing client devices in hddl_clients list.
+ *
+ * Returns list of client devices by comapring the device id's with device id's
+ * present in existing client devices list. If any software id which is not
+ * matching with id's in existing client devices, it allocates new list to
+ * accommodate new client device and copies the existing client devices to
+ * new list and free's up the older list.
+ * If no new device id found in sw_device_id_list, it returns list of existing
+ * client devices.
+ */
+static struct intel_hddl_clients **
+ intel_hddl_new_device(struct device *dev,
+ struct intel_hddl_clients **hddl_clients,
+ u32 *sw_device_id_list,
+ u32 num_devices,
+ u32 *n_devs)
+{
+ struct intel_hddl_clients **cls;
+ bool match_found, new_dev = false;
+ int i, j;
+
+ /*
+ * Check is there any new device by comparing id's with
+ * existing list.
+ */
+ for (i = 0; i < num_devices; i++) {
+ match_found = false;
+ for (j = 0; j < (*n_devs); j++) {
+ if (sw_device_id_list[i] ==
+ hddl_clients[j]->xlink_dev.sw_device_id) {
+ match_found = true;
+ break;
+ }
+ }
+ if (!match_found) {
+ new_dev = true;
+ break;
+ }
+ }
+ if (!new_dev)
+ return hddl_clients;
+ /*
+ * Allocate memory for new list
+ */
+ cls = kcalloc(num_devices,
+ sizeof(struct intel_hddl_clients *),
+ GFP_KERNEL);
+ if (!cls)
+ return hddl_clients;
+ /*
+ * copy hddl client devices to new list.
+ */
+ for (i = 0; i < num_devices; i++) {
+ for (j = 0; j < *n_devs; j++) {
+ if (sw_device_id_list[i] ==
+ hddl_clients[j]->xlink_dev.sw_device_id) {
+ cls[i] = hddl_clients[j];
+ break;
+ }
+ }
+ }
+ /*
+ * update number of devices to include new device and free up existing
+ * list.
+ */
+ *n_devs = num_devices;
+ kfree(hddl_clients);
+ return cls;
+}
+
+/**
+ * intel_hddl_setup_device - Initialize new client device
+ * @dev: The hddl device.
+ * @task: Thread function to setup and connect to host/device.
+ * @n_devs: number of client devices in hddl_clients list.
+ * @hddl_clients: list of existing client devices.
+ * @pdata: platform data.
+ *
+ * Returns list of client devices. It also initialize the device and creates
+ * kernel thread to initiate communication over xlink.
+ */
+
+struct intel_hddl_clients **
+ intel_hddl_setup_device(struct device *dev,
+ intel_hddl_connect_task task, u32 *n_devs,
+ struct intel_hddl_clients **hddl_clients,
+ void *pdata)
+{
+ u32 sw_device_id_list[XLINK_MAX_DEVICE_LIST_SIZE];
+ char device_name[XLINK_MAX_DEVICE_NAME_SIZE];
+ struct intel_hddl_clients **cls;
+ u32 num_devices = 0;
+ u32 i = 0;
+
+ xlink_get_device_list(sw_device_id_list, &num_devices);
+ if (num_devices == 0) {
+ dev_err(dev, "HDDL:No devices found\n");
+ return NULL;
+ }
+
+ /*
+ * If list available, add new device to the existing client devices
+ * list, if list is not available create new list.
+ */
+ if (hddl_clients) {
+ cls = intel_hddl_new_device(dev,
+ hddl_clients,
+ sw_device_id_list,
+ num_devices, n_devs);
+ if (!cls)
+ return NULL;
+ } else {
+ cls = devm_kcalloc(dev, num_devices,
+ sizeof(struct intel_hddl_clients *),
+ GFP_KERNEL);
+ if (!cls)
+ return NULL;
+ /*
+ * update number of devices in client devices list
+ */
+ *n_devs = num_devices;
+ }
+ hddl_clients = cls;
+ for (i = 0; i < num_devices; i++) {
+ struct intel_hddl_clients *c = hddl_clients[i];
+ int rc;
+
+ /*
+ * Initialize new client device.
+ */
+ if (c)
+ continue;
+ c = devm_kzalloc(dev,
+ sizeof(struct intel_hddl_clients),
+ GFP_KERNEL);
+ if (!c)
+ return hddl_clients;
+ c->pdata = pdata;
+ c->xlink_dev.dev_type = HOST_DEVICE;
+ c->xlink_dev.sw_device_id = sw_device_id_list[i];
+ rc = xlink_get_device_name((&c->xlink_dev),
+ device_name,
+ XLINK_MAX_DEVICE_NAME_SIZE);
+ if (rc > 0) {
+ dev_err(dev,
+ "HDDL:Failed to get device name [EC%d] %x\n",
+ rc, c->xlink_dev.sw_device_id);
+ return hddl_clients;
+ }
+ dev_info(dev, "HDDL:Device name: %x %s\n",
+ c->xlink_dev.sw_device_id, device_name);
+ if (GET_INTERFACE_FROM_SW_DEVICE_ID(sw_device_id_list[i]) ==
+ SW_DEVICE_ID_PCIE_INTERFACE) {
+ /*
+ * Start kernel thread to initialize
+ * xlink communication.
+ */
+ c->hddl_dev_connect_task = kthread_run(task,
+ (void *)c,
+ device_name);
+ if (!c->hddl_dev_connect_task) {
+ dev_err(dev, "failed to create thread\n");
+ return hddl_clients;
+ }
+ c->task = (void *)task;
+ }
+ hddl_clients[i] = c;
+ }
+
+ return hddl_clients;
+}
+
+int intel_hddl_xlink_remove_i2c_adap(struct device *dev,
+ struct intel_hddl_clients *c)
+{
+ int i;
+
+ for (i = 0; i < HDDL_XLINK_I2C_END; i++) {
+ if (c->xlink_i2c_plt_dev[i]) {
+ dev_info(dev,
+ "HDDL : platform_device_unregister = %d\n",
+ i);
+ platform_device_unregister(c->xlink_i2c_plt_dev[i]);
+ c->xlink_i2c_plt_dev[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+static void hddl_register_remote_smbus_client(struct device *dev,
+ struct intel_hddl_clients *c,
+ struct intel_hddl_i2c_devs *i2c)
+{
+ struct platform_device *xlink_pdev =
+ c->xlink_i2c_plt_dev[HDDL_XLINK_I2C_MASTER];
+ struct i2c_adapter *adap;
+
+ if (!xlink_pdev)
+ return;
+ adap = (struct i2c_adapter *)platform_get_drvdata(xlink_pdev);
+ if (!adap)
+ return;
+ c->adap[HDDL_XLINK_I2C_MASTER] = adap;
+ i2c->smbus_client =
+ i2c_new_client_device(adap,
+ &i2c->board_info);
+ msleep_interruptible(HDDL_I2C_CLIENT_INIT_TIME);
+}
+
+static void hddl_register_remote_i2c_client(struct device *dev,
+ struct intel_hddl_clients *c,
+ struct intel_hddl_i2c_devs *i2c)
+{
+ if (c->smbus_adap) {
+ i2c->board_info.platform_data = c;
+ i2c->i2c_client = i2c_new_client_device(c->smbus_adap,
+ &i2c->board_info);
+ msleep_interruptible(HDDL_I2C_CLIENT_INIT_TIME);
+ }
+}
+
+static void hddl_register_remote_xlink_client(struct device *dev,
+ struct intel_hddl_clients *c,
+ struct intel_hddl_i2c_devs *i2c)
+{
+ struct platform_device *xlink_pdev =
+ c->xlink_i2c_plt_dev[HDDL_XLINK_I2C_SLAVE];
+ struct i2c_adapter *adap;
+
+ if (!xlink_pdev)
+ return;
+ adap = (struct i2c_adapter *)platform_get_drvdata(xlink_pdev);
+ if (!adap)
+ return;
+ c->adap[HDDL_XLINK_I2C_SLAVE] = adap;
+ i2c->board_info.platform_data = c;
+ i2c->xlk_client = i2c_new_client_device(adap, &i2c->board_info);
+ msleep_interruptible(HDDL_I2C_CLIENT_INIT_TIME);
+}
+
+static void intel_hddl_add_remote_clients(struct device *dev,
+ struct intel_hddl_clients *c,
+ struct intel_hddl_i2c_devs *i2c_devs)
+{
+ if (!i2c_devs->enabled)
+ return;
+ /*
+ * Register this device as xlink i2c client.
+ */
+ if (i2c_devs->remote_host & HDDL_XLINK_CLIENT)
+ hddl_register_remote_xlink_client(dev, c, i2c_devs);
+ /*
+ * Register this device as i2c smbbus client.
+ */
+ if (i2c_devs->remote_host & HDDL_I2C_CLIENT)
+ hddl_register_remote_i2c_client(dev, c, i2c_devs);
+ /*
+ * Register this device as xlink smbus i2c client.
+ * Based on the bit mask of remote_host, It is possible that same
+ * device can be registered as xlink i2c client and smbus i2c client.
+ */
+ if (i2c_devs->remote_host & HDDL_XLINK_SMBUS_CLIENT)
+ hddl_register_remote_smbus_client(dev, c, i2c_devs);
+}
+
+static void intel_hddl_add_localhost_clients(struct device *dev,
+ struct intel_hddl_clients *c,
+ struct intel_hddl_i2c_devs *i2c)
+{
+ if (!i2c->enabled)
+ return;
+ /*
+ * Register this device as xlink i2c client.
+ */
+ if (i2c->local_host & HDDL_XLINK_CLIENT) {
+ struct platform_device *xlink_pdev =
+ c->xlink_i2c_plt_dev[HDDL_XLINK_I2C_SLAVE];
+ struct i2c_adapter *adap;
+
+ if (!xlink_pdev)
+ return;
+ adap = (struct i2c_adapter *)platform_get_drvdata(xlink_pdev);
+ if (!adap)
+ return;
+ c->adap[HDDL_XLINK_I2C_SLAVE] = adap;
+ i2c->xlk_client =
+ i2c_new_client_device(adap,
+ &i2c->board_info);
+ msleep_interruptible(HDDL_I2C_CLIENT_INIT_TIME);
+ }
+ /*
+ * Register this device as smbus i2c client.
+ * Based on the bit mask of local_host, It is possible that same
+ * device can be registered as xlink i2c client and smbus i2c client.
+ */
+ if (i2c->local_host & HDDL_I2C_CLIENT) {
+ i2c->i2c_client =
+ i2c_new_client_device(i2c_get_adapter(i2c->bus),
+ &i2c->board_info);
+ msleep_interruptible(HDDL_I2C_CLIENT_INIT_TIME);
+ }
+}
+
+void intel_hddl_add_xlink_i2c_clients(struct device *dev,
+ struct intel_hddl_clients *c,
+ struct intel_hddl_i2c_devs **i2c_devs,
+ int n_clients, int remote)
+{
+ int i;
+
+ for (i = 0; i < n_clients; i++) {
+ if (remote)
+ intel_hddl_add_remote_clients(dev, c, i2c_devs[i]);
+ else
+ intel_hddl_add_localhost_clients(dev, c, i2c_devs[i]);
+ }
+}
+
+int intel_hddl_register_xlink_i2c_adap(struct device *dev,
+ struct intel_hddl_clients *c)
+{
+ int i;
+
+ for (i = 0; i < HDDL_XLINK_I2C_END; i++) {
+ struct platform_device_info xlink_i2c_info;
+ int soc_id = c->board_info.soc_id;
+
+ memset(&xlink_i2c_info, 0, sizeof(xlink_i2c_info));
+ xlink_i2c_info.name = "i2c_xlink";
+ xlink_i2c_info.id = c->board_info.board_id << 4 |
+ soc_id << 2 | i;
+ c->xlink_i2c_ch[i] =
+ c->i2c_chan_num + (soc_id * 2) + i;
+ xlink_i2c_info.data = c;
+ xlink_i2c_info.size_data =
+ sizeof(struct intel_hddl_clients);
+ c->xlink_i2c_plt_dev[i] =
+ platform_device_register_full(&xlink_i2c_info);
+ if (!c->xlink_i2c_plt_dev[i]) {
+ dev_err(dev, "platform device register failed\n");
+ return -EFAULT;
+ }
+ }
+ return 0;
+}
+
+static int intel_hddl_device_probe(struct intel_hddl_clients *d)
+{
+ char device_name[XLINK_MAX_DEVICE_NAME_SIZE];
+ int rc;
+
+ if (d->status == HDDL_DEV_STATUS_CONNECTED)
+ return 0;
+ rc = xlink_get_device_name(&d->xlink_dev,
+ device_name, XLINK_MAX_DEVICE_NAME_SIZE);
+ if (rc > 0) {
+ dev_err(&d->pdev->dev,
+ "HDDL:Failed to get device name of id [EC%d] %x\n",
+ rc, d->xlink_dev.sw_device_id);
+ return -ENODEV;
+ }
+
+ d->hddl_dev_connect_task =
+ kthread_run((intel_hddl_connect_task)d->task,
+ (void *)d,
+ device_name);
+ if (!d->hddl_dev_connect_task) {
+ dev_err(&d->pdev->dev, "failed to create thread\n");
+ return -EFAULT;
+ }
+ d->status = HDDL_DEV_STATUS_CONNECTED;
+
+ return 0;
+}
+
+void intel_hddl_device_remove(struct intel_hddl_clients *d)
+{
+ int i;
+
+ /** lock device removal.
+ * xlink core gives multiple device disconnected notifications,
+ * so add lock to diconnect the device and update the status as
+ * disconnected. subsequent notificatios will check the status
+ * and returs if the device is already disconnected.
+ */
+ mutex_lock(&d->lock);
+ if (d->status == HDDL_DEV_STATUS_DISCONNECTED) {
+ mutex_unlock(&d->lock);
+ return;
+ }
+
+ for (i = 0; i < d->n_clients; i++)
+ intel_hddl_free_i2c_client(d, d->i2c_devs[i]);
+ intel_hddl_unregister_pdev(d);
+ xlink_close_channel(&d->xlink_dev, d->chan_num);
+ xlink_disconnect(&d->xlink_dev);
+ kthread_stop(d->hddl_dev_connect_task);
+ d->status = HDDL_DEV_STATUS_DISCONNECTED;
+ mutex_unlock(&d->lock);
+}
+
+static int intel_hddl_device_event_notify(u32 sw_device_id,
+ uint32_t event_type)
+{
+ struct intel_hddl_clients **clients;
+ struct intel_hddl_clients *client;
+ int i, ret = 0;
+ int ndevs = 0;
+
+ clients = intel_hddl_get_clients(&ndevs);
+ if (!clients)
+ return 0;
+ for (i = 0; i < ndevs; i++) {
+ if (clients[i]->xlink_dev.sw_device_id == sw_device_id) {
+ client = clients[i];
+ break;
+ }
+ }
+ if (!client)
+ return -EINVAL;
+ switch (event_type) {
+ case HDDL_NOTIFY_DEVICE_DISCONNECTED:
+ intel_hddl_device_remove(client);
+ break;
+
+ case HDDL_NOTIFY_DEVICE_CONNECTED:
+ ret = intel_hddl_device_probe(client);
+ break;
+
+ default:
+ dev_err(&client->pdev->dev,
+ "HDDL:xlink pcie notify - Error[%x]: [%d]\n",
+ sw_device_id, event_type);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+void intel_hddl_close_xlink_device(struct device *dev,
+ struct intel_hddl_clients *d)
+{
+ xlink_close_channel(&d->xlink_dev, d->chan_num);
+ xlink_disconnect(&d->xlink_dev);
+ kthread_stop(d->hddl_dev_connect_task);
+ d->status = HDDL_DEV_STATUS_DISCONNECTED;
+}
+
+int intel_hddl_open_xlink_device(struct device *dev,
+ struct intel_hddl_clients *d)
+{
+ char device_name[XLINK_MAX_DEVICE_NAME_SIZE];
+ u32 device_status = 0xFF;
+ int rc;
+
+ rc = xlink_get_device_name(&d->xlink_dev,
+ device_name, XLINK_MAX_DEVICE_NAME_SIZE);
+ if (rc > 0) {
+ dev_err(dev,
+ "HDDL:Failed to get device name of id [EC%d] %x\n",
+ rc, d->xlink_dev.sw_device_id);
+ return -ENODEV;
+ }
+ if (xlink_boot_device(&d->xlink_dev, device_name) !=
+ X_LINK_SUCCESS) {
+ dev_err(dev, "xlink_boot_device failed\n");
+ return -ENODEV;
+ }
+ if (xlink_get_device_status(&d->xlink_dev, &device_status) !=
+ X_LINK_SUCCESS) {
+ dev_err(dev, "xlink_get_device_status failed\n");
+ return -ENODEV;
+ }
+ if (xlink_connect(&d->xlink_dev) != X_LINK_SUCCESS) {
+ dev_err(dev, "xlink_connect failed\n");
+ return -ENODEV;
+ }
+ mutex_init(&d->lock);
+ xlink_register_device_event(&d->xlink_dev,
+ hddl_device_events,
+ HDDL_NUM_EVENT_TYPE,
+ intel_hddl_device_event_notify);
+
+ d->status = HDDL_DEV_STATUS_CONNECTED;
+ /*
+ * Try opening xlink channel, open channel will fail till host/client
+ * initilaizes the channel. intel_hddl_open_xlink_device is invoked
+ * from kernel thread. so it is safe to try indefinitely.
+ */
+ while (xlink_open_channel(&d->xlink_dev,
+ d->chan_num, RXB_TXB,
+ 64 * 1024, 0 /* timeout */) !=
+ X_LINK_SUCCESS) {
+ if (kthread_should_stop()) {
+ xlink_disconnect(&d->xlink_dev);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/misc/hddl_device/hddl_device_lh.c b/drivers/misc/hddl_device/hddl_device_lh.c
new file mode 100644
index 000000000000..e44c480379bc
--- /dev/null
+++ b/drivers/misc/hddl_device/hddl_device_lh.c
@@ -0,0 +1,764 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * High Density Deep Learning Kernel module.
+ *
+ * Copyright (C) 2020 Intel Corporation
+ *
+ */
+
+#include <asm/page.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hddl_device.h>
+#include <linux/i2c.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/sched.h>
+#include <linux/sched/mm.h>
+#include <linux/sched/task.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/xlink.h>
+#include <uapi/linux/stat.h>
+#include "hddl_device_util.h"
+
+#define DRIVER_NAME "hddl_device_client"
+
+struct intel_tsens {
+ struct intel_tsens_data data;
+ struct intel_tsens_trip_info **trip_info;
+};
+
+struct intel_hddl_client_priv {
+ void __iomem *base_addr;
+ int board_id;
+ int soc_id;
+ int n_clients;
+ u32 nsens;
+ u32 xlink_chan;
+ u32 i2c_xlink_chan;
+ u32 n_hddl_devs;
+ struct platform_device *pdev;
+ struct intel_hddl_clients **hddl_client;
+ struct task_struct *hddl_dev_init_task;
+ struct intel_hddl_i2c_devs **i2c_devs;
+ struct intel_tsens **tsens;
+ struct intel_hddl_board_info board_info;
+};
+
+static struct intel_hddl_client_priv *g_priv;
+
+static inline int intel_hddl_get_xlink_data(struct device *dev,
+ struct xlink_handle *xlink,
+ int chan_num, u8 *msg,
+ int *size)
+{
+ int rc;
+
+ rc = xlink_read_data_to_buffer(xlink, chan_num,
+ msg, size);
+ if (rc) {
+ dev_err(dev,
+ "HDDL: xlink read data failed rc = %d\n",
+ rc);
+ return -EFAULT;
+ }
+ rc = xlink_release_data(xlink, chan_num, NULL);
+ if (rc) {
+ dev_err(dev,
+ "HDDL: xlink release failed rc = %d\n",
+ rc);
+ return -EFAULT;
+ }
+ return rc;
+}
+
+void intel_hddl_free_i2c_client(struct intel_hddl_clients *d,
+ struct intel_hddl_i2c_devs *i2c_dev)
+{
+ if (i2c_dev->xlk_client)
+ i2c_unregister_device(i2c_dev->xlk_client);
+ if (i2c_dev->i2c_client)
+ i2c_unregister_device(i2c_dev->i2c_client);
+ if (i2c_dev->smbus_client)
+ i2c_unregister_device(i2c_dev->smbus_client);
+ i2c_dev->xlk_client = NULL;
+ i2c_dev->i2c_client = NULL;
+ i2c_dev->smbus_client = NULL;
+}
+
+struct intel_hddl_clients **intel_hddl_get_clients(int *n_devs)
+{
+ if (!g_priv || !n_devs)
+ return NULL;
+ *n_devs = g_priv->n_hddl_devs;
+ return g_priv->hddl_client;
+}
+
+void intel_hddl_unregister_pdev(struct intel_hddl_clients *c)
+{
+ struct intel_hddl_client_priv *priv = c->pdata;
+
+ intel_hddl_xlink_remove_i2c_adap(&priv->pdev->dev, c);
+}
+
+static u8 *intel_tsens_thermal_msg(struct intel_hddl_clients *c,
+ struct intel_hddl_tsens_msg *msg,
+ u32 *size)
+{
+ struct intel_hddl_client_priv *priv = c->pdata;
+ struct intel_tsens **tsens = priv->tsens;
+
+ switch (msg->msg_type) {
+ case HDDL_GET_NSENS:
+ {
+ u32 *data;
+ *size = sizeof(int);
+ data = kzalloc(*size, GFP_KERNEL);
+ if (!data)
+ return NULL;
+ *data = priv->nsens;
+ return (u8 *)data;
+ }
+ case HDDL_GET_SENS_DETAILS:
+ {
+ struct intel_tsens_data *data;
+ u32 sensor_type = msg->sensor_type;
+ struct intel_tsens_data *tsens_data =
+ &tsens[sensor_type]->data;
+
+ *size = sizeof(struct intel_tsens_data);
+ data = kzalloc(*size, GFP_KERNEL);
+ if (!data)
+ return NULL;
+ strcpy(data->name, tsens_data->name);
+ data->n_trips = tsens_data->n_trips;
+ data->passive_delay = tsens_data->passive_delay;
+ data->polling_delay = tsens_data->polling_delay;
+ data->sensor_type = tsens_data->sensor_type;
+ return (u8 *)data;
+ }
+ case HDDL_GET_SENS_TRIP_INFO:
+ {
+ struct intel_tsens_trip_info *data;
+ u32 sensor_type = msg->sensor_type;
+ u32 trip_info_idx = msg->trip_info_idx;
+
+ *size = sizeof(struct intel_tsens_trip_info);
+ data = kzalloc(*size, GFP_KERNEL);
+ if (!data)
+ return NULL;
+ memcpy(data, tsens[sensor_type]->trip_info[trip_info_idx],
+ sizeof(struct intel_tsens_trip_info));
+ return (u8 *)data;
+ }
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static int intel_hddl_i2c_register_clients(struct device *dev,
+ struct intel_hddl_clients *c)
+{
+ struct intel_hddl_client_priv *priv = c->pdata;
+ struct xlink_handle *xlink = &c->xlink_dev;
+ struct intel_hddl_tsens_msg msg;
+ int rc, i;
+ int size;
+
+ /* Get msg type */
+ rc = intel_hddl_get_xlink_data(&priv->pdev->dev,
+ xlink, c->chan_num,
+ (u8 *)&msg, &size);
+ if (rc)
+ return rc;
+
+ while (msg.msg_type != HDDL_GET_SENS_COMPLETE) {
+ u32 *data;
+
+ switch (msg.msg_type) {
+ case HDDL_GET_N_I2C_DEVS:
+ {
+ size = sizeof(int);
+ data = kzalloc(size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ *data = priv->n_clients;
+ break;
+ }
+ case HDDL_GET_I2C_DEVS:
+ {
+ struct intel_hddl_i2c_devs_data *i2c_dev;
+ int sensor_type = msg.sensor_type;
+
+ size = sizeof(struct intel_hddl_i2c_devs_data);
+ i2c_dev = kzalloc(size, GFP_KERNEL);
+ if (!i2c_dev)
+ return -ENOMEM;
+ strcpy(i2c_dev->name,
+ priv->i2c_devs[sensor_type]->name);
+ i2c_dev->addr = priv->i2c_devs[sensor_type]->addr;
+ i2c_dev->bus = priv->i2c_devs[sensor_type]->bus;
+ i2c_dev->enabled =
+ priv->i2c_devs[sensor_type]->enabled;
+ i2c_dev->local_host =
+ priv->i2c_devs[sensor_type]->local_host;
+ i2c_dev->remote_host =
+ priv->i2c_devs[sensor_type]->remote_host;
+ data = (u32 *)i2c_dev;
+ break;
+ }
+ default:
+ dev_err(&priv->pdev->dev,
+ "HDDL: Invalid msg received\n");
+ return -EINVAL;
+ }
+ rc = xlink_write_volatile(xlink, c->chan_num,
+ (u8 *)data, size);
+ if (rc) {
+ dev_err(&priv->pdev->dev,
+ "xlink write data failed rc = %d\n",
+ rc);
+ return rc;
+ }
+ kfree(data);
+ rc = intel_hddl_get_xlink_data(&priv->pdev->dev,
+ xlink, c->chan_num,
+ (u8 *)&msg, &size);
+ if (rc)
+ return rc;
+ }
+
+ for (i = 0; i < priv->n_clients; i++) {
+ msg.msg_type = HDDL_GET_I2C_DEV_ADDR;
+ msg.sensor_type = i;
+ rc = xlink_write_volatile(xlink, c->chan_num,
+ (u8 *)&msg, sizeof(msg));
+ if (rc) {
+ dev_err(&priv->pdev->dev,
+ "xlink write data failed rc = %d\n",
+ rc);
+ return rc;
+ }
+ rc = intel_hddl_get_xlink_data(&priv->pdev->dev,
+ xlink, c->chan_num,
+ (u8 *)&priv->i2c_devs[i]->addr,
+ &size);
+ if (rc)
+ return rc;
+ priv->i2c_devs[i]->board_info.addr = priv->i2c_devs[i]->addr;
+ }
+ /* Send Complete */
+ msg.msg_type = HDDL_GET_SENS_COMPLETE;
+ rc = xlink_write_volatile(xlink, c->chan_num,
+ (u8 *)&msg, sizeof(msg));
+ if (rc) {
+ dev_err(&priv->pdev->dev,
+ "xlink write data failed rc = %d\n",
+ rc);
+ return rc;
+ }
+
+ intel_hddl_add_xlink_i2c_clients(&priv->pdev->dev, c, priv->i2c_devs,
+ priv->n_clients, 0);
+ return 0;
+}
+
+static int intel_hddl_send_tsens_data(struct intel_hddl_clients *c)
+{
+ struct intel_hddl_client_priv *priv = c->pdata;
+ struct xlink_handle *xlink = &c->xlink_dev;
+ struct intel_hddl_tsens_msg msg;
+ u32 size;
+ u8 *data;
+ int rc;
+
+ /* Get msg type */
+ rc = intel_hddl_get_xlink_data(&priv->pdev->dev,
+ xlink, c->chan_num,
+ (u8 *)&msg, &size);
+ if (rc)
+ return rc;
+
+ while (msg.msg_type != HDDL_GET_SENS_COMPLETE) {
+ data = intel_tsens_thermal_msg(c, &msg, &size);
+ if (!data) {
+ dev_err(&priv->pdev->dev, "HDDL: failed to get details\n");
+ return -EINVAL;
+ }
+ rc = xlink_write_volatile(xlink, c->chan_num,
+ (u8 *)data, size);
+ if (rc) {
+ dev_err(&priv->pdev->dev,
+ "xlink write data failed rc = %d\n",
+ rc);
+ return rc;
+ }
+ kfree(data);
+ rc = intel_hddl_get_xlink_data(&priv->pdev->dev,
+ xlink, c->chan_num,
+ (u8 *)&msg, &size);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int intel_hddl_device_connect_task(void *data)
+{
+ struct intel_hddl_clients *c = (struct intel_hddl_clients *)data;
+ struct intel_hddl_client_priv *priv = c->pdata;
+ struct intel_hddl_board_info board_info_rcvd;
+ struct xlink_handle *xlink = &c->xlink_dev;
+ struct timespec64 ts;
+ u32 size, rc;
+
+ memcpy(&c->board_info, &priv->board_info,
+ sizeof(struct intel_hddl_board_info));
+ c->chan_num = priv->xlink_chan;
+ c->i2c_chan_num = priv->i2c_xlink_chan;
+ c->i2c_devs = priv->i2c_devs;
+ c->n_clients = priv->n_clients;
+ if (intel_hddl_open_xlink_device(&priv->pdev->dev, c)) {
+ dev_err(&priv->pdev->dev, "HDDL open xlink dev failed\n");
+ return -EINVAL;
+ }
+ size = sizeof(ts);
+ rc = intel_hddl_get_xlink_data(&priv->pdev->dev,
+ xlink, c->chan_num,
+ (u8 *)&ts, &size);
+ if (rc)
+ goto close_xlink_dev;
+ do_settimeofday64(&ts);
+
+ rc = xlink_write_volatile(xlink, c->chan_num,
+ (u8 *)&c->board_info,
+ sizeof(struct intel_hddl_board_info));
+ if (rc) {
+ dev_err(&priv->pdev->dev,
+ "xlink write data failed rc = %d\n",
+ rc);
+ goto close_xlink_dev;
+ }
+
+ size = sizeof(board_info_rcvd);
+ rc = intel_hddl_get_xlink_data(&priv->pdev->dev,
+ xlink, c->chan_num,
+ (u8 *)&board_info_rcvd,
+ &size);
+ if (rc)
+ goto close_xlink_dev;
+ rc = intel_hddl_send_tsens_data(c);
+ if (rc) {
+ dev_err(&priv->pdev->dev, "HDDL: tsens data not sent\n");
+ goto close_xlink_dev;
+ }
+ rc = intel_hddl_register_xlink_i2c_adap(&priv->pdev->dev, c);
+ if (rc) {
+ dev_err(&priv->pdev->dev,
+ "HDDL: register xlink i2c adapter failed\n");
+ goto remove_xlink_i2c_adap;
+ }
+ rc = intel_hddl_i2c_register_clients(&priv->pdev->dev, c);
+ if (rc) {
+ dev_err(&priv->pdev->dev,
+ "HDDL: register i2c clients failed\n");
+ goto remove_xlink_i2c_adap;
+ }
+
+ return 0;
+remove_xlink_i2c_adap:
+ intel_hddl_xlink_remove_i2c_adap(&priv->pdev->dev, c);
+close_xlink_dev:
+ intel_hddl_close_xlink_device(&priv->pdev->dev, c);
+ return rc;
+}
+
+static int intel_hddl_check_for_new_device(struct intel_hddl_client_priv *priv)
+{
+ struct intel_hddl_clients **hddl_clients;
+
+ hddl_clients =
+ intel_hddl_setup_device(&priv->pdev->dev,
+ intel_hddl_device_connect_task,
+ &priv->n_hddl_devs, priv->hddl_client,
+ priv);
+ if (!hddl_clients) {
+ dev_err(&priv->pdev->dev,
+ "intel_hddl_setup_device returned NULL\n");
+ return 0;
+ }
+ priv->hddl_client = hddl_clients;
+ return 1;
+}
+
+static int intel_hddl_device_init_task(void *data)
+{
+ struct intel_hddl_client_priv *priv =
+ (struct intel_hddl_client_priv *)data;
+
+ while (!kthread_should_stop()) {
+ if (!intel_hddl_check_for_new_device(priv)) {
+ dev_err(&priv->pdev->dev,
+ "Error while checking for new device\n");
+ return -EFAULT;
+ }
+ msleep_interruptible(HDDL_NEW_DEV_POLL_TIME);
+ }
+
+ return 0;
+}
+
+static int intel_hddl_device_init(struct intel_hddl_client_priv *priv)
+{
+ priv->hddl_dev_init_task = kthread_run(intel_hddl_device_init_task,
+ (void *)priv,
+ "hddl_device_init");
+ if (!priv->hddl_dev_init_task) {
+ dev_err(&priv->pdev->dev, "failed to create thread\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hddl_tsens_config_sensors(struct device_node *s_node,
+ struct intel_hddl_client_priv *priv,
+ int sensor_type)
+{
+ struct intel_tsens *tsens = priv->tsens[sensor_type];
+ struct platform_device *pdev = priv->pdev;
+ s32 trip_temp_count, trip_temp_type_c, i;
+ int ret;
+
+ tsens->data.sensor_type = sensor_type;
+ if (of_property_read_u32(s_node, "passive_delay_rh",
+ &tsens->data.passive_delay)) {
+ dev_err(&pdev->dev,
+ "passive_delay missing in dt for %s\n",
+ tsens->data.name);
+ return -EINVAL;
+ }
+ if (of_property_read_u32(s_node, "polling_delay_rh",
+ &tsens->data.polling_delay)) {
+ dev_err(&pdev->dev,
+ "polling_delay missing in dt for %s\n",
+ tsens->data.name);
+ return -EINVAL;
+ }
+ trip_temp_count = of_property_count_u32_elems(s_node, "trip_temp_rh");
+ trip_temp_type_c = of_property_count_strings(s_node, "trip_type_rh");
+ if (trip_temp_count != trip_temp_type_c ||
+ trip_temp_count <= 0 || trip_temp_type_c <= 0) {
+ dev_err(&pdev->dev,
+ "trip temp config is missing in dt for %s\n",
+ tsens->data.name);
+ return -EINVAL;
+ }
+
+ tsens->trip_info =
+ devm_kcalloc(&pdev->dev, trip_temp_count,
+ sizeof(struct intel_tsens_trip_info *),
+ GFP_KERNEL);
+ if (!tsens->trip_info)
+ return -ENOMEM;
+ tsens->data.n_trips = trip_temp_count;
+ for (i = 0; i < trip_temp_count; i++) {
+ const char *trip_name;
+
+ tsens->trip_info[i] =
+ devm_kzalloc(&pdev->dev,
+ sizeof(struct intel_tsens_trip_info),
+ GFP_KERNEL);
+ if (!tsens->trip_info[i])
+ return -ENOMEM;
+ ret = of_property_read_u32_index(s_node, "trip_temp_rh", i,
+ &tsens->trip_info[i]->temp);
+ if (ret) {
+ dev_err(&pdev->dev, "Invalid trip temp");
+ return ret;
+ }
+ ret = of_property_read_string_index(s_node, "trip_type_rh", i,
+ &trip_name);
+ if (ret) {
+ dev_err(&pdev->dev, "Invalid trip type");
+ return ret;
+ }
+ if (!strcmp(trip_name, "passive"))
+ tsens->trip_info[i]->trip_type = THERMAL_TRIP_PASSIVE;
+ else if (!strcmp(trip_name, "critical"))
+ tsens->trip_info[i]->trip_type = THERMAL_TRIP_CRITICAL;
+ else if (!strcmp(trip_name, "hot"))
+ tsens->trip_info[i]->trip_type = THERMAL_TRIP_HOT;
+ else
+ tsens->trip_info[i]->trip_type = THERMAL_TRIP_ACTIVE;
+ }
+
+ return 0;
+}
+
+static int hddl_get_onchip_sensors(struct platform_device *pdev,
+ struct intel_hddl_client_priv *priv)
+{
+ struct device_node *s_node;
+ struct device_node *np = NULL;
+ int i = 0;
+
+ s_node = of_parse_phandle(pdev->dev.of_node, "soc-sensors", 0);
+ if (!s_node)
+ return -EINVAL;
+ priv->nsens = of_get_child_count(s_node);
+ if (priv->nsens == 0) {
+ dev_err(&pdev->dev, "No onchip sensors configured in dt\n");
+ return -EINVAL;
+ }
+ priv->tsens =
+ devm_kcalloc(&pdev->dev, priv->nsens,
+ sizeof(struct intel_tsens *),
+ GFP_KERNEL);
+ if (!priv->tsens)
+ return -ENOMEM;
+ for_each_child_of_node(s_node, np) {
+ struct intel_tsens *tsens;
+
+ tsens = devm_kzalloc(&pdev->dev,
+ sizeof(struct intel_tsens),
+ GFP_KERNEL);
+ if (!tsens)
+ return -ENOMEM;
+ priv->tsens[i] = tsens;
+ strcpy(tsens->data.name, np->name);
+ if (hddl_tsens_config_sensors(np, priv, i)) {
+ dev_err(&pdev->dev,
+ "Missing sensor info in dts for %s\n",
+ tsens->data.name);
+ return -EINVAL;
+ }
+ i++;
+ }
+
+ return 0;
+}
+
+static int intel_hddl_get_ids(struct platform_device *pdev,
+ struct intel_hddl_client_priv *priv)
+{
+ int ret;
+ struct gpio_descs *board_id_gpios;
+ struct gpio_descs *soc_id_gpios;
+ unsigned long values = 0;
+
+ board_id_gpios =
+ gpiod_get_array_optional(&pdev->dev, "board-id",
+ GPIOD_IN);
+ if (board_id_gpios) {
+ ret = gpiod_get_array_value(board_id_gpios->ndescs,
+ board_id_gpios->desc,
+ NULL, &values);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to get boardid values %d",
+ ret);
+ return ret;
+ }
+ priv->board_info.board_id = values;
+ priv->board_id = priv->board_info.board_id;
+ }
+ soc_id_gpios =
+ gpiod_get_array_optional(&pdev->dev, "soc-id",
+ GPIOD_IN);
+ if (soc_id_gpios) {
+ values = 0;
+ ret = gpiod_get_array_value(soc_id_gpios->ndescs,
+ soc_id_gpios->desc,
+ NULL, &values);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to get soc-id values %d",
+ ret);
+ return ret;
+ }
+ priv->board_info.soc_id = values;
+ priv->soc_id = priv->board_info.soc_id;
+ }
+
+ return 0;
+}
+
+static int intel_hddl_config_dt(struct intel_hddl_client_priv *priv)
+{
+ struct platform_device *pdev = priv->pdev;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *s_node = NULL;
+ struct resource *res;
+ int i, ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res) {
+ priv->base_addr = ioremap(res->start,
+ (res->end - res->start));
+ }
+ priv->board_info.soc_id = 0;
+ priv->board_info.board_id = 0;
+ ret = of_property_read_u32(np, "xlink_chan",
+ &priv->xlink_chan);
+ if (ret) {
+ dev_err(&pdev->dev, "xlink channel not available in dt");
+ return ret;
+ }
+ ret = of_property_read_u32(np, "i2c_xlink_chan",
+ &priv->i2c_xlink_chan);
+ if (ret) {
+ dev_err(&pdev->dev, "i2c xlink channel not available in dt");
+ return ret;
+ }
+ ret = intel_hddl_get_ids(pdev, priv);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to get board/soc id");
+ return ret;
+ }
+ ret = hddl_get_onchip_sensors(pdev, priv);
+ if (ret) {
+ dev_err(&pdev->dev, "Onchip sensor config failed");
+ return ret;
+ }
+ priv->n_clients = of_get_child_count(np);
+ priv->i2c_devs = devm_kcalloc(&pdev->dev, priv->n_clients,
+ sizeof(struct intel_hddl_i2c_devs *),
+ GFP_KERNEL);
+ if (!priv->i2c_devs)
+ return -ENOMEM;
+ i = 0;
+ for_each_child_of_node(np, s_node) {
+ const char *status;
+ struct intel_hddl_i2c_devs *i2c_dev;
+
+ i2c_dev = devm_kzalloc(&pdev->dev,
+ sizeof(struct intel_hddl_i2c_devs),
+ GFP_KERNEL);
+ if (!i2c_dev)
+ return -ENOMEM;
+ of_property_read_string_index(s_node, "status", 0,
+ &status);
+ if (!strcmp(status, "okay")) {
+ u32 addr;
+ const char *name = NULL;
+
+ i2c_dev->enabled = 1;
+ of_property_read_string_index(s_node, "compatible", 0,
+ &name);
+ if (name) {
+ strcpy(i2c_dev->name, name);
+ strcpy(i2c_dev->board_info.type,
+ i2c_dev->name);
+ }
+ /**
+ * below dt params are optional.
+ */
+ of_property_read_u32(s_node, "reg", &addr);
+ i2c_dev->board_info.addr = addr;
+ i2c_dev->addr = addr;
+ of_property_read_u32(s_node, "bus",
+ &i2c_dev->bus);
+ of_property_read_u32(s_node, "remote-host",
+ &i2c_dev->remote_host);
+ of_property_read_u32(s_node, "local-host",
+ &i2c_dev->local_host);
+ }
+ priv->i2c_devs[i] = i2c_dev;
+ i++;
+ }
+ return 0;
+}
+
+static int intel_hddl_client_probe(struct platform_device *pdev)
+{
+ struct intel_hddl_client_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct intel_hddl_client_priv),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->pdev = pdev;
+ if (pdev->dev.of_node) {
+ ret = intel_hddl_config_dt(priv);
+ if (ret) {
+ dev_err(&pdev->dev, "dt configuration failed\n");
+ devm_kfree(&pdev->dev, priv);
+ return ret;
+ }
+ } else {
+ dev_err(&pdev->dev,
+ "Non Device Tree build is not supported\n");
+ devm_kfree(&pdev->dev, priv);
+ return -EINVAL;
+ }
+ ret = intel_hddl_device_init(priv);
+ if (ret) {
+ dev_err(&pdev->dev, "HDDL device init failed\n");
+ devm_kfree(&pdev->dev, priv);
+ return -EINVAL;
+ }
+ g_priv = priv;
+ platform_set_drvdata(pdev, priv);
+ return 0;
+}
+
+/* Device Exit */
+static int intel_hddl_client_exit(struct platform_device *pdev)
+{
+ int k;
+ struct intel_hddl_client_priv *priv = platform_get_drvdata(pdev);
+
+ if (!priv)
+ return -EINVAL;
+ for (k = 0; k < priv->n_hddl_devs; k++) {
+ struct intel_hddl_clients *d = priv->hddl_client[k];
+
+ intel_hddl_device_remove(d);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id intel_hddl_client_id_table[] = {
+ { .compatible = "intel,hddl-client" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, intel_hddl_client_id_table);
+
+static struct platform_driver intel_hddl_client_driver = {
+ .probe = intel_hddl_client_probe,
+ .remove = intel_hddl_client_exit,
+ .driver = {
+ .name = "intel_hddl_client",
+ .of_match_table = intel_hddl_client_id_table,
+ },
+};
+
+module_platform_driver(intel_hddl_client_driver);
+
+MODULE_DESCRIPTION("Intel HDDL Device driver");
+MODULE_AUTHOR("Sandeep Singh <sandeep1.singh@...el.com>");
+MODULE_AUTHOR("Vaidya, Mahesh R <mahesh.r.vaidya@...el.com>");
+MODULE_AUTHOR("Udhayakumar C <udhayakumar.c@...el.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/hddl_device/hddl_device_util.h b/drivers/misc/hddl_device/hddl_device_util.h
new file mode 100644
index 000000000000..628619e0cdb9
--- /dev/null
+++ b/drivers/misc/hddl_device/hddl_device_util.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ *
+ * High Density Deep Learning utils.
+ *
+ * Copyright (C) 2020 Intel Corporation
+ *
+ */
+
+#ifndef _LINUX_HDDL_DEVICE_UTIL_H
+#define _LINUX_HDDL_DEVICE_UTIL_H
+
+#include <linux/xlink_drv_inf.h>
+#include "../xlink-core/xlink-defs.h"
+
+#define HDDL_NEW_DEV_POLL_TIME 2000
+
+typedef int (*intel_hddl_connect_task)(void *);
+
+struct intel_hddl_clients **
+ intel_hddl_setup_device(struct device *dev,
+ intel_hddl_connect_task task, u32 *n_devs,
+ struct intel_hddl_clients **hddl_clients,
+ void *pdata);
+
+int intel_hddl_xlink_remove_i2c_adap(struct device *dev,
+ struct intel_hddl_clients *c);
+
+void intel_hddl_add_xlink_i2c_clients(struct device *dev,
+ struct intel_hddl_clients *c,
+ struct intel_hddl_i2c_devs **i2c_devs,
+ int n_clients, int remote);
+
+int intel_hddl_register_xlink_i2c_adap(struct device *dev,
+ struct intel_hddl_clients *c);
+
+struct intel_hddl_clients **intel_hddl_get_clients(int *n_devs);
+
+void intel_hddl_device_remove(struct intel_hddl_clients *d);
+
+void intel_hddl_unregister_pdev(struct intel_hddl_clients *c);
+
+void intel_hddl_close_xlink_device(struct device *dev,
+ struct intel_hddl_clients *d);
+
+int intel_hddl_open_xlink_device(struct device *dev,
+ struct intel_hddl_clients *d);
+
+void intel_hddl_free_i2c_client(struct intel_hddl_clients *d,
+ struct intel_hddl_i2c_devs *i2c_dev);
+
+#endif /* _LINUX_HDDL_DEVICE_UTIL_H */
--
2.17.1
Powered by blists - more mailing lists