From patchwork Sat Sep 5 13:03:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luka Kovacic X-Patchwork-Id: 255432 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.0 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 706BBC2D0E0 for ; Sat, 5 Sep 2020 13:07:24 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 16DE920760 for ; Sat, 5 Sep 2020 13:07:24 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=sartura-hr.20150623.gappssmtp.com header.i=@sartura-hr.20150623.gappssmtp.com header.b="m2+NEPeM" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728573AbgIENHX (ORCPT ); Sat, 5 Sep 2020 09:07:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41978 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728486AbgIENFp (ORCPT ); Sat, 5 Sep 2020 09:05:45 -0400 Received: from mail-ed1-x542.google.com (mail-ed1-x542.google.com [IPv6:2a00:1450:4864:20::542]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AB2C3C061247 for ; Sat, 5 Sep 2020 06:05:44 -0700 (PDT) Received: by mail-ed1-x542.google.com with SMTP id i1so8411396edv.2 for ; Sat, 05 Sep 2020 06:05:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sartura-hr.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=NmxtKjTst7Kjy3hD3ics0v0CsKKPDK+NS2czAKePEHA=; b=m2+NEPeM0Nk8EIXUVdk20Fi9XvW1T+u2ldRu6mCBcy9Dtkvq6ds+Fsy3dcHJ0894ey j2pTAEeBi3mDCLajX9+ZbgiJOi2QizFlRMFW0bUlewn2fIwyf15Oft4QgVGJFOvLyzHf r9fU9EXHL5KyaRVFGtGzJjfhZ6mZvBuRNYOxoNFwu8cncMg6GST8Hh/zzqiUKe8r1h1R pSTGvMiucKyBDT6vCx3KokkRRMHY7k7Ldjh7KcrWVX57HF17nwomDoSD6kgMgFndUNL6 tlDj9ymIGeGyP35GmT+Mtff6y8sUeJJssnt4bMNWEVTjeT76w5pPzjskoIeXT0LvUiNd NyRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=NmxtKjTst7Kjy3hD3ics0v0CsKKPDK+NS2czAKePEHA=; b=c3aUPz482R5/eitNRSlx3sdY2B+fWb3scdE1bKEgI2tDa7TmjXXep+yI0A9nKfG9VL k9TQfmNY4ccEZxYNXgc9hqvLuy+i3MQBF16nfzFndaS8ZLRDTsVjN1ZZkxUPza4UBPma NEVGqCq25RE8vb+lXE5EfircV00TmW8EhGK6wRuu3VaeU5RjgM2B3LYRDXEB0qItY0Z+ uQSMjvF1IkiOFAKPV1TpFZJ1fi2NLVGynhessG4ZkAioXE25klUHTAxV07U9K8XDBA+a CX9dba2hyo6mO0MgVf5qv3rLhj0azLgJMWZiH6WBzMB7VPfSxmEAdv6iGtjqkJFwUaWO N9RA== X-Gm-Message-State: AOAM5330nMXg1sCuq92++sDGiNB1FP1nY7IdV12jy3Tp0roe8bU/soj0 I2zphRFVauVhok3rs2NHAg2+7Q== X-Google-Smtp-Source: ABdhPJz+jDJz8jF5m4yaygyUSEwgXRccENAzfuD+GhZyIOEh2ljZnVdGwO9k91KY/l0pV2A/OoM3vQ== X-Received: by 2002:a50:e685:: with SMTP id z5mr12199366edm.305.1599311142940; Sat, 05 Sep 2020 06:05:42 -0700 (PDT) Received: from localhost.localdomain ([2a00:ee2:4b0d:3002:290:faff:fe54:449c]) by smtp.gmail.com with ESMTPSA id s18sm9372655ejd.54.2020.09.05.06.05.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 05 Sep 2020 06:05:42 -0700 (PDT) From: Luka Kovacic To: linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-leds@vger.kernel.org Cc: lee.jones@linaro.org, pavel@ucw.cz, dmurphy@ti.com, robh+dt@kernel.org, jdelvare@suse.com, linux@roeck-us.net, andrew@lunn.ch, jason@lakedaemon.net, gregory.clement@bootlin.com, luka.perkov@sartura.hr, Luka Kovacic Subject: [PATCH 2/7] drivers: mfd: Add a driver for iEi WT61P803 PUZZLE MCU Date: Sat, 5 Sep 2020 15:03:31 +0200 Message-Id: <20200905130336.967622-3-luka.kovacic@sartura.hr> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200905130336.967622-1-luka.kovacic@sartura.hr> References: <20200905130336.967622-1-luka.kovacic@sartura.hr> MIME-Version: 1.0 Sender: linux-leds-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add a driver for the iEi WT61P803 PUZZLE microcontroller, used in some iEi Puzzle series devices. The microcontroller controls system power, temperature sensors, fans and LEDs. This driver implements the core functionality for device communication over the system serial (serdev bus). It handles MCU messages and the internal MCU properties. Some properties can be managed over sysfs. Signed-off-by: Luka Kovacic Cc: Luka Perkov --- drivers/mfd/Kconfig | 8 + drivers/mfd/Makefile | 1 + drivers/mfd/iei-wt61p803-puzzle.c | 1242 +++++++++++++++++++++++ include/linux/mfd/iei-wt61p803-puzzle.h | 27 + 4 files changed, 1278 insertions(+) create mode 100644 drivers/mfd/iei-wt61p803-puzzle.c create mode 100644 include/linux/mfd/iei-wt61p803-puzzle.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 33df0837ab41..3fcda95f07a3 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2118,5 +2118,13 @@ config SGI_MFD_IOC3 If you have an SGI Origin, Octane, or a PCI IOC3 card, then say Y. Otherwise say N. +config MFD_IEI_WT61P803_PUZZLE + tristate "iEi WT61P803 PUZZLE MCU driver" + depends on SERIAL_DEV_BUS + help + iEi WT61P803 PUZZLE is a system power management microcontroller + used for fan control, temperature sensor reading, led control + and system identification. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index a60e5f835283..33b88023a68d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -236,6 +236,7 @@ obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o obj-$(CONFIG_MFD_DLN2) += dln2.o obj-$(CONFIG_MFD_RT5033) += rt5033.o obj-$(CONFIG_MFD_SKY81452) += sky81452.o +obj-$(CONFIG_MFD_IEI_WT61P803_PUZZLE) += iei-wt61p803-puzzle.o intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o diff --git a/drivers/mfd/iei-wt61p803-puzzle.c b/drivers/mfd/iei-wt61p803-puzzle.c new file mode 100644 index 000000000000..110ea1b84c53 --- /dev/null +++ b/drivers/mfd/iei-wt61p803-puzzle.c @@ -0,0 +1,1242 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* iEi WT61P803 PUZZLE MCU Driver + * + * Copyright (C) 2020 Sartura Ltd. + * Author: Luka Kovacic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH (20 + 2) +#define IEI_WT61P803_PUZZLE_RESP_BUF_SIZE 512 +#define IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT HZ + +/** + * struct iei_wt61p803_puzzle_mcu_status - MCU flags state + * + * @ac_recovery_status_flag: AC Recovery Status Flag + * @power_loss_recovery: System recovery after power loss + * @power_status: System Power-on Method + */ +struct iei_wt61p803_puzzle_mcu_status { + u8 ac_recovery_status_flag; + u8 power_loss_recovery; + u8 power_status; +}; + +/** + * enum iei_wt61p803_puzzle_reply_state - State of the reply + * @FRAME_OK: The frame was completely processed/received + * @FRAME_PROCESSING: First bytes were received, but the frame isn't complete + * @FRAME_STRUCT_EMPTY: The frame struct is empty, no data was received + * @FRAME_TIMEOUT: The frame processing timed out, communication failed + * + * The enum iei_wt61p803_puzzle_reply_state is used to describe the general state + * of the frame that is currently being received. + */ +enum iei_wt61p803_puzzle_reply_state { + FRAME_OK = 0x00, + FRAME_PROCESSING = 0x01, + FRAME_STRUCT_EMPTY = 0xFF, + FRAME_TIMEOUT = 0xFE +}; + +/** + * struct iei_wt61p803_puzzle_reply - MCU reply + * + * @size: Size of the MCU reply + * @data: Full MCU reply buffer + * @state: Current state of the packet + * @received: Was the response fullfilled + */ +struct iei_wt61p803_puzzle_reply { + size_t size; + unsigned char *data; + u8 state; + struct completion received; +}; + +/** + * struct iei_wt61p803_puzzle_mcu_version - MCU version status + * + * @version: Primary firmware version + * @build_info: Build date and time + * @bootloader_mode: Status of the MCU operation + * @protocol_version: MCU communication protocol version + * @serial_number: Device factory serial number + * @mac_address: Device factory MAC addresses + */ +struct iei_wt61p803_puzzle_mcu_version { + const char *version; + const char *build_info; + bool bootloader_mode; + const char *protocol_version; + const char *serial_number; + const char *mac_address[8]; +}; + +/** + * struct iei_wt61p803_puzzle - iEi WT61P803 PUZZLE MCU Driver + * + * @serdev: Pointer to underlying serdev device + * @kobj: Pointer to kobject (sysfs) + * @reply_lock: Reply mutex lock + * @bus_lock: Bus mutex lock + * @reply: Pointer to the iei_wt61p803_puzzle_reply struct + * @version_lock: Version R/W mutex lock + * @version: Pointer to the iei_wt61p803_puzzle_mcu_version struct + * @status_lock: Status R/W mutex lock + * @status: Pointer to the iei_wt61p803_puzzle_mcu_status struct + */ +struct iei_wt61p803_puzzle { + struct serdev_device *serdev; + struct kobject *kobj; + + struct mutex reply_lock; + struct mutex bus_lock; + + struct iei_wt61p803_puzzle_reply *reply; + + struct mutex version_lock; + struct iei_wt61p803_puzzle_mcu_version *version; + + struct mutex status_lock; + struct iei_wt61p803_puzzle_mcu_status *status; +}; + +static const struct of_device_id iei_wt61p803_puzzle_dt_ids[] = { + { .compatible = "iei,wt61p803-puzzle" }, + { /* sentinel */ } +}; + +unsigned char iei_wt61p803_puzzle_add_xor_checksum(unsigned char *buf, + unsigned char xor_len) +{ + unsigned char xor_sum = 0; + unsigned int i; + + for (i = 0; i < xor_len; i++) + xor_sum ^= buf[i]; + + return xor_sum; +} + +static int iei_wt61p803_puzzle_process_resp(struct iei_wt61p803_puzzle *mcu, + unsigned char *raw_resp_data, size_t size) +{ + struct device *dev = &mcu->serdev->dev; + + unsigned char xor_checksum; + + /* + * Start receiving the frame/Continue receiving the frame + * + * The code below determines whether: + * * we are receiving a new frame + * * appending data to an existing frame + * + */ + + /* Lock the reply struct mutex */ + mutex_lock(&mcu->reply_lock); + + /* Check the incoming frame header */ + if (!(raw_resp_data[0] == '@' || raw_resp_data[0] == '%' || + (raw_resp_data[0] == 0xF7 && raw_resp_data[1] == 0xA1))) { + + /* Frame header is not correct, check whether to append */ + if (mcu->reply->state != FRAME_PROCESSING) { + /* The incoming frame should be discarded */ + dev_err(dev, "Invalid frame header and state (0x%x)", + mcu->reply->state); + + mutex_unlock(&mcu->reply_lock); + return -1; + } + + /* The frame should be appended to the existing data */ + memcpy(mcu->reply->data+mcu->reply->size, raw_resp_data, size); + mcu->reply->size += size; + } else { + /* Start processing a new frame */ + memcpy(mcu->reply->data, raw_resp_data, size); + mcu->reply->size = size; + mcu->reply->state = FRAME_PROCESSING; + } + + /* Create an xor checksum of the data */ + xor_checksum = iei_wt61p803_puzzle_add_xor_checksum(mcu->reply->data, + ((int)mcu->reply->size)-1); + + /* Check whether the caluclated checksum matches the received one */ + if (xor_checksum != mcu->reply->data[((int)mcu->reply->size)-1]) { + /* The checksum doesn't match, waiting for data */ + mutex_unlock(&mcu->reply_lock); + return (int)size; + } + + /* + * Update internal driver state with the latest response code + */ + + /* Received all the data */ + mcu->reply->state = FRAME_OK; + complete(&mcu->reply->received); + + mutex_unlock(&mcu->reply_lock); + + return (int)size; +} + +static int iei_wt61p803_puzzle_recv_buf(struct serdev_device *serdev, + const unsigned char *data, size_t size) +{ + struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev); + int ret; + + ret = iei_wt61p803_puzzle_process_resp(mcu, (unsigned char *)data, size); + + /* Return the number of processed bytes if function returns error */ + if (ret < 0) + return (int)size; + + return ret; +} + +static const struct serdev_device_ops iei_wt61p803_puzzle_serdev_device_ops = { + .receive_buf = iei_wt61p803_puzzle_recv_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +/** + * iei_wt61p803_puzzle_write_command_watchdog() - Watchdog of the normal cmd + * @mcu: Pointer to the iei_wt61p803_puzzle core MFD struct + * @cmd: Pointer to the char array to send (size should be content + 1 (xor)) + * @size: Size of the cmd char array + * @reply_data: Pointer to the reply/response data array (should be allocated) + * @reply_size: Pointer to size_t (size of reply_data) + * @retry_count: Number of times to retry sending the command to the MCU + */ +int iei_wt61p803_puzzle_write_command_watchdog(struct iei_wt61p803_puzzle *mcu, + unsigned char *cmd, size_t size, unsigned char *reply_data, + size_t *reply_size, int retry_count) +{ + struct device *dev = &mcu->serdev->dev; + int ret, i; + + for (i = 0; i < retry_count; i++) { + ret = iei_wt61p803_puzzle_write_command(mcu, cmd, size, + reply_data, reply_size); + + if (ret != -ETIMEDOUT) + return ret; + } + + dev_err(dev, "%s: Command response timed out. Retries: %d", __func__, + retry_count); + + return -ETIMEDOUT; +} +EXPORT_SYMBOL_GPL(iei_wt61p803_puzzle_write_command_watchdog); + +/** + * iei_wt61p803_puzzle_write_command() - Send a structured command to the MCU + * @mcu: Pointer to the iei_wt61p803_puzzle core MFD struct + * @cmd: Pointer to the char array to send (size should be content + 1 (xor)) + * @size: Size of the cmd char array + * @reply_data: Pointer to the reply/response data array (should be allocated) + * + * Function iei_wt61p803_puzzle_write_command() sends a structured command to + * the MCU. + */ +int iei_wt61p803_puzzle_write_command(struct iei_wt61p803_puzzle *mcu, + unsigned char *cmd, size_t size, unsigned char *reply_data, + size_t *reply_size) +{ + struct device *dev = &mcu->serdev->dev; + + int ret; + int len = (int)size; + + if (size > IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH) + return -EINVAL; + + /* Create an XOR checksum for the provided command */ + cmd[len - 1] = iei_wt61p803_puzzle_add_xor_checksum(cmd, len); + + mutex_lock(&mcu->bus_lock); + + /* Initialize reply struct */ + mutex_lock(&mcu->reply_lock); + if (mcu->reply) { + reinit_completion(&mcu->reply->received); + + mcu->reply->state = FRAME_STRUCT_EMPTY; + + mcu->reply->size = 0; + } else { + dev_err(dev, "Reply struct is NULL.\n"); + + mutex_unlock(&mcu->reply_lock); + mutex_unlock(&mcu->bus_lock); + + return -1; + } + mutex_unlock(&mcu->reply_lock); + + /* Write to MCU UART */ + ret = serdev_device_write(mcu->serdev, cmd, len, + IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT); + + if (ret < 0) + return ret; + + /* Wait for the MCU response */ + if (!wait_for_completion_timeout(&mcu->reply->received, + IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT)) { + dev_err(dev, "Command reply receive timeout\n"); + + ret = -ETIMEDOUT; + + mutex_lock(&mcu->reply_lock); + reinit_completion(&mcu->reply->received); + mcu->reply->state = FRAME_TIMEOUT; + mutex_unlock(&mcu->reply_lock); + } + + if (ret < 0) { + mutex_unlock(&mcu->bus_lock); + return ret; + } + + /* Verify MCU response status */ + mutex_lock(&mcu->reply_lock); + + if (mcu->reply) { + /* Copy response */ + *reply_size = mcu->reply->size; + memcpy(reply_data, mcu->reply->data, mcu->reply->size); + } else { + dev_err(dev, "Reply struct is NULL.\n"); + + mutex_unlock(&mcu->reply_lock); + mutex_unlock(&mcu->bus_lock); + + return -1; + } + + mutex_unlock(&mcu->reply_lock); + mutex_unlock(&mcu->bus_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(iei_wt61p803_puzzle_write_command); + +int iei_wt61p803_puzzle_buzzer(struct iei_wt61p803_puzzle *mcu, bool long_beep) +{ + /* Buzzer 0.5 sec */ + unsigned char buzzer_short_cmd[4] = { '@', 'C', '2' }; + /* Buzzer 1.5 sec */ + unsigned char buzzer_long_cmd[4] = { '@', 'C', '3' }; + + int ret; + + size_t reply_size = 0; + unsigned char *resp_buf; + + resp_buf = kmalloc(IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + if (!resp_buf) + return -ENOMEM; + + ret = iei_wt61p803_puzzle_write_command(mcu, + long_beep ? buzzer_long_cmd : buzzer_short_cmd, 4, + resp_buf, &reply_size); + + if (ret) { + kfree(resp_buf); + return ret; + } + + if (reply_size != 3) { + kfree(resp_buf); + return -EIO; + } + + if (!(resp_buf[0] == '@' && resp_buf[1] == '0' && + resp_buf[2] == 0x70)) { + kfree(resp_buf); + return -EPROTO; + } + + kfree(resp_buf); + return 0; +} + +int iei_wt61p803_puzzle_get_version(struct iei_wt61p803_puzzle *mcu) +{ + struct device *dev = &mcu->serdev->dev; + + /* Commands */ + unsigned char version_cmd[3] = { '%', 'V' }; + unsigned char build_info_cmd[3] = { '%', 'B' }; + unsigned char bootloader_mode_cmd[3] = { '%', 'M' }; + unsigned char protocol_version_cmd[3] = { '%', 'P' }; + + unsigned char *rd; + size_t reply_size = 0; + + int ret; + + /* Allocate memory for the buffer */ + rd = kmalloc(IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + if (!rd) + return -ENOMEM; + + ret = iei_wt61p803_puzzle_write_command(mcu, version_cmd, + sizeof(version_cmd), rd, &reply_size); + if (ret) + goto err; + + if (reply_size < 7) { + ret = -EIO; + goto err; + } + + mcu->version->version = devm_kasprintf(dev, GFP_KERNEL, "v%c.%c%c%c", + rd[2], rd[3], rd[4], rd[5]); + + ret = iei_wt61p803_puzzle_write_command(mcu, build_info_cmd, + sizeof(build_info_cmd), rd, &reply_size); + if (ret) + goto err; + + if (reply_size < 15) { + ret = -EIO; + goto err; + } + + mcu->version->build_info = devm_kasprintf(dev, GFP_KERNEL, + "%c%c/%c%c/%c%c%c%c %c%c:%c%c", + rd[8], rd[9], rd[6], rd[7], rd[2], + rd[3], rd[4], rd[5], rd[10], rd[11], + rd[12], rd[13]); + + ret = iei_wt61p803_puzzle_write_command(mcu, bootloader_mode_cmd, + sizeof(bootloader_mode_cmd), rd, &reply_size); + if (ret) + goto err; + + if (reply_size < 4) { + ret = -EIO; + goto err; + } + + if (rd[2] == 0x31) { + /* The MCU is in normal mode */ + mcu->version->bootloader_mode = false; + } else if (rd[2] == 0x30) { + /* The MCU is in bootloader mode */ + mcu->version->bootloader_mode = true; + } + + ret = iei_wt61p803_puzzle_write_command(mcu, protocol_version_cmd, + sizeof(protocol_version_cmd), rd, &reply_size); + if (ret) + goto err; + + if (reply_size < 9) { + ret = -EIO; + goto err; + } + + mcu->version->protocol_version = devm_kasprintf(dev, GFP_KERNEL, + "v%c.%c%c%c%c%c", + rd[7], rd[6], rd[5], rd[4], rd[3], rd[2]); + + /* Free the allocated memory resources */ + kfree(rd); + + return 0; +err: + kfree(rd); + return ret; +} + +int iei_wt61p803_puzzle_get_mcu_status(struct iei_wt61p803_puzzle *mcu) +{ + /* MCU Status Command */ + unsigned char mcu_status_cmd[5] = { '@', 'O', 'S', 'S' }; + + int ret; + + unsigned char *rd; + size_t reply_size = 0; + + /* Allocate memory for the buffer */ + rd = kmalloc(IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + if (!rd) + return -ENOMEM; + + ret = iei_wt61p803_puzzle_write_command(mcu, mcu_status_cmd, + sizeof(mcu_status_cmd), rd, &reply_size); + if (ret) { + kfree(rd); + return ret; + } + + if (reply_size < 20) { + kfree(rd); + return -EIO; + } + + /* Response format: + * (IDX RESPONSE) + * 0 @ + * 1 O + * 2 S + * 3 S + * ... + * 5 AC Recovery Status Flag + * ... + * 10 Power Loss Recovery + * ... + * 19 Power Status (system power on method) + * 20 XOR checksum + */ + + if (rd[0] == '@' && rd[1] == 'O' && rd[2] == 'S' && rd[3] == 'S') { + mutex_lock(&mcu->status_lock); + mcu->status->ac_recovery_status_flag = rd[5]; + mcu->status->power_loss_recovery = rd[10]; + mcu->status->power_status = rd[19]; + mutex_unlock(&mcu->status_lock); + } + + /* Free the allocated memory resources */ + kfree(rd); + + return 0; +} + +int iei_wt61p803_puzzle_get_serial_number(struct iei_wt61p803_puzzle *mcu) +{ + struct device *dev = &mcu->serdev->dev; + unsigned char serial_number_cmd[5] = { 0xF7, 0xA1, 0x00, 0x24 }; + + int ret; + + unsigned char *rd; + size_t reply_size = 0; + + /* Allocate memory for the buffer */ + rd = kmalloc(IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + if (!rd) + return -ENOMEM; + + ret = iei_wt61p803_puzzle_write_command(mcu, serial_number_cmd, + sizeof(serial_number_cmd), rd, &reply_size); + if (ret) + goto err; + + mutex_lock(&mcu->version_lock); + mcu->version->serial_number = devm_kasprintf(dev, GFP_KERNEL, "%.*s", + (int)reply_size - 5, rd + 4); + mutex_unlock(&mcu->version_lock); + + /* Free the allocated memory resources */ + kfree(rd); + + return 0; + +err: + kfree(rd); + return ret; + +} + +int iei_wt61p803_puzzle_write_serial_number(struct iei_wt61p803_puzzle *mcu, + unsigned char serial_number[36]) +{ + struct device *dev = &mcu->serdev->dev; + unsigned char serial_number_header[4] = { 0xF7, 0xA0, 0x00, 0xC }; + + /* 4 byte header, 12 byte serial number chunk, 1 byte XOR checksum */ + unsigned char serial_number_cmd[4+12+1]; + + int ret, sn_counter; + + unsigned char *rd; + size_t reply_size = 0; + + /* Allocate memory for the buffer */ + rd = kmalloc(IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + if (!rd) + return -ENOMEM; + + /* The MCU can only handle 22 byte messages, send the S/N in chunks */ + for (sn_counter = 0; sn_counter < 3; sn_counter++) { + serial_number_header[2] = 0x0 + (0xC) * sn_counter; + + memcpy(serial_number_cmd, serial_number_header, 4); + memcpy(serial_number_cmd + 4, serial_number + (0xC) * sn_counter, 0xC); + + /* Initialize the last byte */ + serial_number_cmd[sizeof(serial_number_cmd) - 1] = 0; + + ret = iei_wt61p803_puzzle_write_command(mcu, serial_number_cmd, + sizeof(serial_number_cmd), rd, &reply_size); + if (ret) + goto err; + + if (reply_size != 3) { + ret = -EIO; + goto err; + } + + if (!(rd[0] == '@' && rd[1] == '0' && rd[2] == 0x70)) { + ret = -EPROTO; + goto err; + } + } + + mutex_lock(&mcu->version_lock); + mcu->version->serial_number = devm_kasprintf(dev, GFP_KERNEL, "%.*s", + 36, serial_number); + mutex_unlock(&mcu->version_lock); + + /* Free the allocated memory resources */ + kfree(rd); + + return 0; + +err: + kfree(rd); + return ret; +} + +int iei_wt61p803_puzzle_get_mac_addresses(struct iei_wt61p803_puzzle *mcu) +{ + struct device *dev = &mcu->serdev->dev; + unsigned char mac_address_cmd[5] = { 0xF7, 0xA1, 0x00, 0x11 }; + + int ret, mac_counter; + + unsigned char *rd; + size_t reply_size = 0; + + /* Allocate memory for the buffer */ + rd = kmalloc(IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + if (!rd) + return -ENOMEM; + + for (mac_counter = 0; mac_counter < 8; mac_counter++) { + mac_address_cmd[2] = 0x24 + (0x11) * mac_counter; + mac_address_cmd[4] = 0x00; + + ret = iei_wt61p803_puzzle_write_command(mcu, mac_address_cmd, + sizeof(mac_address_cmd), rd, &reply_size); + if (ret) + continue; + + if (reply_size < 22) { + ret = -EIO; + goto err; + } + + mutex_lock(&mcu->version_lock); + mcu->version->mac_address[mac_counter] = devm_kasprintf(dev, + GFP_KERNEL, "%.*s", (int)reply_size - 5, + rd + 4); + mutex_unlock(&mcu->version_lock); + } + + /* Free the allocated memory resources */ + kfree(rd); + + return 0; + +err: + kfree(rd); + return ret; +} + +int iei_wt61p803_puzzle_write_mac_address(struct iei_wt61p803_puzzle *mcu, + unsigned char mac_address[17], int mac_address_idx) +{ + struct device *dev = &mcu->serdev->dev; + unsigned char mac_address_header[4] = { 0xF7, 0xA0, 0x00, 0x11 }; + + /* 4 byte header, 17 byte MAC address, 1 byte XOR checksum */ + unsigned char mac_address_cmd[4+17+1]; + + int ret; + + unsigned char *rd; + size_t reply_size = 0; + + if (!(mac_address_idx < 8)) + return -EINVAL; + + /* Allocate memory for the buffer */ + rd = kmalloc(IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + if (!rd) + return -ENOMEM; + + mac_address_header[2] = 0x24 + (0x11) * mac_address_idx; + + /* Concat mac_address_header, mac_address to mac_address_cmd */ + memcpy(mac_address_cmd, mac_address_header, 4); + memcpy(mac_address_cmd + 4, mac_address, 17); + + /* Initialize the last byte */ + mac_address_cmd[sizeof(mac_address_cmd) - 1] = 0; + + ret = iei_wt61p803_puzzle_write_command(mcu, mac_address_cmd, + sizeof(mac_address_cmd), rd, &reply_size); + if (ret) + goto err; + + if (reply_size != 3) { + ret = -EIO; + goto err; + } + + if (!(rd[0] == '@' && rd[1] == '0' && rd[2] == 0x70)) { + ret = -EPROTO; + goto err; + } + + mutex_lock(&mcu->version_lock); + mcu->version->mac_address[mac_address_idx] = devm_kasprintf(dev, + GFP_KERNEL, "%.*s", 17, mac_address); + mutex_unlock(&mcu->version_lock); + + /* Free the allocated memory resources */ + kfree(rd); + + return 0; + +err: + kfree(rd); + return ret; +} + +int iei_wt61p803_puzzle_write_power_loss_recovery(struct iei_wt61p803_puzzle *mcu, + int power_loss_recovery_action) +{ + unsigned char power_loss_recovery_cmd[5] = { '@', 'O', 'A', '0' }; + + unsigned char *resp_buf; + size_t reply_size = 0; + + /* Buffer for the power_loss_recovery_action to character */ + unsigned char cmd_buf[2]; + + int ret; + + /* Allocate memory for the buffer */ + resp_buf = kmalloc(IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + if (!resp_buf) + return -ENOMEM; + + /* Check the acceptable range */ + if (power_loss_recovery_action < 0 || power_loss_recovery_action > 4) { + kfree(resp_buf); + return -EINVAL; + } + + /* Convert int to char */ + ret = snprintf(cmd_buf, sizeof(cmd_buf), "%d", + power_loss_recovery_action); + if (ret < 0) { + kfree(resp_buf); + return ret; + } + + /* Modify the command with the action index */ + power_loss_recovery_cmd[3] = cmd_buf[0]; + + ret = iei_wt61p803_puzzle_write_command(mcu, power_loss_recovery_cmd, + sizeof(power_loss_recovery_cmd), resp_buf, &reply_size); + if (ret) { + kfree(resp_buf); + return ret; + } + + /* Update the internal status (struct) */ + mutex_lock(&mcu->status_lock); + mcu->status->power_loss_recovery = power_loss_recovery_action; + mutex_unlock(&mcu->status_lock); + + /* Free the allocated memory resources */ + kfree(resp_buf); + + return 0; +} + + +#define sysfs_container(dev) \ + (container_of((dev)->kobj.parent, struct device, kobj)) + +static ssize_t version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + return sprintf(buf, "%s\n", mcu->version->version); +} + +static DEVICE_ATTR_RO(version); + +static ssize_t build_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + return sprintf(buf, "%s\n", mcu->version->build_info); +} + +static DEVICE_ATTR_RO(build_info); + +static ssize_t bootloader_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + return sprintf(buf, "%d\n", mcu->version->bootloader_mode); +} + +static DEVICE_ATTR_RO(bootloader_mode); + +static ssize_t protocol_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + return sprintf(buf, "%s\n", mcu->version->protocol_version); +} + +static DEVICE_ATTR_RO(protocol_version); + +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + int ret; + + mutex_lock(&mcu->version_lock); + ret = sprintf(buf, "%s\n", mcu->version->serial_number); + mutex_unlock(&mcu->version_lock); + + return ret; +} + +static ssize_t serial_number_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + unsigned char serial_number[36]; + int ret; + + /* Check whether the serial number is 36 characters long + null */ + if ((int)count != 36 + 1) + return -EINVAL; + + /* Copy 36 characters from the buffer to serial_number */ + memcpy(serial_number, (unsigned char *)buf, 36); + + ret = iei_wt61p803_puzzle_write_serial_number(mcu, serial_number); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(serial_number); + +static ssize_t mac_address_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + int ret; + + mutex_lock(&mcu->version_lock); + + if (!strcmp(attr->attr.name, "mac_address_0")) + ret = sprintf(buf, "%s\n", mcu->version->mac_address[0]); + else if (!strcmp(attr->attr.name, "mac_address_1")) + ret = sprintf(buf, "%s\n", mcu->version->mac_address[1]); + else if (!strcmp(attr->attr.name, "mac_address_2")) + ret = sprintf(buf, "%s\n", mcu->version->mac_address[2]); + else if (!strcmp(attr->attr.name, "mac_address_3")) + ret = sprintf(buf, "%s\n", mcu->version->mac_address[3]); + else if (!strcmp(attr->attr.name, "mac_address_4")) + ret = sprintf(buf, "%s\n", mcu->version->mac_address[4]); + else if (!strcmp(attr->attr.name, "mac_address_5")) + ret = sprintf(buf, "%s\n", mcu->version->mac_address[5]); + else if (!strcmp(attr->attr.name, "mac_address_6")) + ret = sprintf(buf, "%s\n", mcu->version->mac_address[6]); + else if (!strcmp(attr->attr.name, "mac_address_7")) + ret = sprintf(buf, "%s\n", mcu->version->mac_address[7]); + else + ret = sprintf(buf, "\n"); + + mutex_unlock(&mcu->version_lock); + + return ret; +} + +static ssize_t mac_address_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + unsigned char mac_address[17]; + int ret; + + /* Check whether the MAC address is 17 characters long + null */ + if ((int)count != 17 + 1) + return -EINVAL; + + /* Copy 17 characters from the buffer to mac_address */ + memcpy(mac_address, (unsigned char *)buf, 17); + + if (!strcmp(attr->attr.name, "mac_address_0")) + ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, 0); + else if (!strcmp(attr->attr.name, "mac_address_1")) + ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, 1); + else if (!strcmp(attr->attr.name, "mac_address_2")) + ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, 2); + else if (!strcmp(attr->attr.name, "mac_address_3")) + ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, 3); + else if (!strcmp(attr->attr.name, "mac_address_4")) + ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, 4); + else if (!strcmp(attr->attr.name, "mac_address_5")) + ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, 5); + else if (!strcmp(attr->attr.name, "mac_address_6")) + ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, 6); + else if (!strcmp(attr->attr.name, "mac_address_7")) + ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, 7); + + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR(mac_address_0, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_1, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_2, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_3, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_4, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_5, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_6, 0644, mac_address_show, mac_address_store); +static DEVICE_ATTR(mac_address_7, 0644, mac_address_show, mac_address_store); + +static ssize_t ac_recovery_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + int ret; + + ret = iei_wt61p803_puzzle_get_mcu_status(mcu); + if (ret) + return ret; + + mutex_lock(&mcu->status_lock); + ret = sprintf(buf, "%x\n", mcu->status->ac_recovery_status_flag); + mutex_unlock(&mcu->status_lock); + + return ret; +} + +static DEVICE_ATTR_RO(ac_recovery_status); + +static ssize_t power_loss_recovery_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + int ret; + + ret = iei_wt61p803_puzzle_get_mcu_status(mcu); + if (ret) + return ret; + + mutex_lock(&mcu->status_lock); + ret = sprintf(buf, "%x\n", mcu->status->power_loss_recovery); + mutex_unlock(&mcu->status_lock); + + return ret; +} + +static ssize_t power_loss_recovery_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + int ret; + long power_loss_recovery_action = 0; + + ret = kstrtol(buf, 10, &power_loss_recovery_action); + if (ret) + return ret; + + ret = iei_wt61p803_puzzle_write_power_loss_recovery(mcu, + (int)power_loss_recovery_action); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(power_loss_recovery); + +static ssize_t power_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *dev_container = sysfs_container(dev); + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev_container); + + int ret; + + ret = iei_wt61p803_puzzle_get_mcu_status(mcu); + if (ret) + return ret; + + mutex_lock(&mcu->status_lock); + ret = sprintf(buf, "%x\n", mcu->status->power_status); + mutex_unlock(&mcu->status_lock); + + return ret; +} + +static DEVICE_ATTR_RO(power_status); + +static struct attribute *iei_wt61p803_puzzle_attrs[] = { + &dev_attr_version.attr, + &dev_attr_build_info.attr, + &dev_attr_bootloader_mode.attr, + &dev_attr_protocol_version.attr, + &dev_attr_serial_number.attr, + &dev_attr_mac_address_0.attr, + &dev_attr_mac_address_1.attr, + &dev_attr_mac_address_2.attr, + &dev_attr_mac_address_3.attr, + &dev_attr_mac_address_4.attr, + &dev_attr_mac_address_5.attr, + &dev_attr_mac_address_6.attr, + &dev_attr_mac_address_7.attr, + &dev_attr_ac_recovery_status.attr, + &dev_attr_power_loss_recovery.attr, + &dev_attr_power_status.attr, + NULL +}; + +ATTRIBUTE_GROUPS(iei_wt61p803_puzzle); + +static int iei_wt61p803_puzzle_sysfs_create(struct device *dev, + struct iei_wt61p803_puzzle *mcu) +{ + int ret; + + mcu->kobj = + kobject_create_and_add("iei_wt61p803_puzzle_core", &dev->kobj); + if (!mcu->kobj) + return -ENOMEM; + + ret = sysfs_create_groups(mcu->kobj, iei_wt61p803_puzzle_groups); + if (ret) { + dev_err(dev, "sysfs creation failed"); + + /* Clean up */ + kobject_del(mcu->kobj); + kobject_put(mcu->kobj); + mcu->kobj = NULL; + + return ret; + } + + return 0; +} + +static int iei_wt61p803_puzzle_sysfs_remove(struct device *dev, + struct iei_wt61p803_puzzle *mcu) +{ + /* Remove sysfs groups */ + sysfs_remove_groups(mcu->kobj, iei_wt61p803_puzzle_groups); + + /* Remove the kobject */ + kobject_del(mcu->kobj); + kobject_put(mcu->kobj); + mcu->kobj = NULL; + + return 0; +} + +static int iei_wt61p803_puzzle_probe(struct serdev_device *serdev) +{ + struct device *dev = &serdev->dev; + struct iei_wt61p803_puzzle *mcu; + u32 baud; + int ret; + + /* Read the current-speed property from the device tree */ + if (of_property_read_u32(dev->of_node, "current-speed", &baud)) { + dev_err(dev, + "'current-speed' is not specified in device node\n"); + return -EINVAL; + } + + /* Log the specified BAUD RATE */ + dev_info(dev, "Driver baud rate: %d", baud); + + /* Allocate the memory */ + mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL); + if (!mcu) + return -ENOMEM; + + mcu->version = devm_kzalloc(dev, sizeof(*mcu->version), GFP_KERNEL); + if (!mcu->version) + return -ENOMEM; + + mcu->status = devm_kzalloc(dev, sizeof(*mcu->version), GFP_KERNEL); + if (!mcu->status) + return -ENOMEM; + + mcu->reply = devm_kzalloc(dev, sizeof(*mcu->reply), GFP_KERNEL); + if (!mcu->reply) + return -ENOMEM; + + mcu->reply->data = devm_kzalloc(dev, IEI_WT61P803_PUZZLE_RESP_BUF_SIZE, + GFP_KERNEL); + if (!mcu->reply->data) + return -ENOMEM; + + /* Initialize device struct data */ + mcu->serdev = serdev; + + mcu->status->ac_recovery_status_flag = 0x00; + mcu->status->power_loss_recovery = 0x00; + mcu->status->power_status = 0x00; + + init_completion(&mcu->reply->received); + + /* Mutexes */ + mutex_init(&mcu->reply_lock); + mutex_init(&mcu->bus_lock); + mutex_init(&mcu->status_lock); + mutex_init(&mcu->version_lock); + + /* Setup UART interface */ + serdev_device_set_drvdata(serdev, mcu); + serdev_device_set_client_ops(serdev, + &iei_wt61p803_puzzle_serdev_device_ops); + + ret = devm_serdev_device_open(dev, serdev); + if (ret) + return ret; + + serdev_device_set_baudrate(serdev, baud); + + serdev_device_set_flow_control(serdev, false); + + ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); + if (ret) { + dev_err(dev, "Failed to set parity\n"); + return ret; + } + + ret = iei_wt61p803_puzzle_get_version(mcu); + if (ret) + return ret; + + ret = iei_wt61p803_puzzle_get_mac_addresses(mcu); + if (ret) + return ret; + + ret = iei_wt61p803_puzzle_get_serial_number(mcu); + if (ret) + return ret; + + dev_info(dev, "MCU version: %s", mcu->version->version); + + dev_info(dev, "MCU firmware build info: %s", mcu->version->build_info); + dev_info(dev, "MCU in bootloader mode: %s", + mcu->version->bootloader_mode ? "true" : "false"); + dev_info(dev, "MCU protocol version: %s", mcu->version->protocol_version); + + if (of_property_read_bool(dev->of_node, "enable-probe-beep")) { + ret = iei_wt61p803_puzzle_buzzer(mcu, false); + if (ret) + return ret; + } + + ret = iei_wt61p803_puzzle_sysfs_create(dev, mcu); + if (ret) + return ret; + + return devm_of_platform_populate(dev); +} + +static void iei_wt61p803_puzzle_remove(struct serdev_device *serdev) +{ + struct device *dev = &serdev->dev; + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev); + + /* Remove sysfs kobjects and attributes */ + iei_wt61p803_puzzle_sysfs_remove(dev, mcu); + + dev_info(dev, "Device unregistered"); +} + + +MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_dt_ids); + +static struct serdev_device_driver iei_wt61p803_puzzle_drv = { + .probe = iei_wt61p803_puzzle_probe, + .remove = iei_wt61p803_puzzle_remove, + .driver = { + .name = "iei-wt61p803-puzzle", + .of_match_table = iei_wt61p803_puzzle_dt_ids, + }, +}; + +module_serdev_device_driver(iei_wt61p803_puzzle_drv); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Luka Kovacic "); +MODULE_DESCRIPTION("iEi WT61P803 PUZZLE MCU Driver"); diff --git a/include/linux/mfd/iei-wt61p803-puzzle.h b/include/linux/mfd/iei-wt61p803-puzzle.h new file mode 100644 index 000000000000..2bb6256574bc --- /dev/null +++ b/include/linux/mfd/iei-wt61p803-puzzle.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* iEi WT61P803 PUZZLE MCU MFD Driver + * + * Copyright (C) 2020 Sartura Ltd. + * Author: Luka Kovacic + */ + +#ifndef _LINUX_IEI_WT61P803_PUZZLE_H_ +#define _LINUX_IEI_WT61P803_PUZZLE_H_ + +#define IEI_WT61P803_PUZZLE_BUF_SIZE 512 + +struct iei_wt61p803_puzzle_mcu_version; +struct iei_wt61p803_puzzle_reply; +struct iei_wt61p803_puzzle; + +int iei_wt61p803_puzzle_write_command_watchdog(struct iei_wt61p803_puzzle *mcu, + unsigned char *cmd, size_t size, + unsigned char *reply_data, size_t *reply_size, + int retry_count); + +int iei_wt61p803_puzzle_write_command(struct iei_wt61p803_puzzle *mcu, + unsigned char *cmd, size_t size, + unsigned char *reply_data, size_t *reply_size); + +#endif /* _LINUX_IEI_WT61P803_PUZZLE_H_ */ From patchwork Sat Sep 5 13:03:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luka Kovacic X-Patchwork-Id: 255433 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.0 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A8B7CC43461 for ; Sat, 5 Sep 2020 13:07:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 700902072D for ; Sat, 5 Sep 2020 13:07:11 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=sartura-hr.20150623.gappssmtp.com header.i=@sartura-hr.20150623.gappssmtp.com header.b="DQ/jMFJv" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728512AbgIENHJ (ORCPT ); Sat, 5 Sep 2020 09:07:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42004 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728573AbgIENFt (ORCPT ); Sat, 5 Sep 2020 09:05:49 -0400 Received: from mail-ej1-x642.google.com (mail-ej1-x642.google.com [IPv6:2a00:1450:4864:20::642]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 879B8C061246 for ; Sat, 5 Sep 2020 06:05:48 -0700 (PDT) Received: by mail-ej1-x642.google.com with SMTP id gr14so10965025ejb.1 for ; Sat, 05 Sep 2020 06:05:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sartura-hr.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=SF1qQ5y6zS+uuXI0X6117nR7jpHsJ8QwMXzF/nJxgiY=; b=DQ/jMFJvt+FCQjnz+50PJXFDwxKjYTQPxSIhBTijj220+HGAJfCJz6kCUgDRJltpLE TYREY+v3rW7rUGaSwrVhMLc706WQ52m4EgeqTWjCS147oCbtiJCLuNbVfkZCLk5zunKb MzHyf4v4YLQG/dw1Ej6nfzxFUlR/KS6POdin+WQ5EX+0vaZW/beF/sI5AQQfF2skrs2l yZ4Qd68H5HHYo1gP7dc27LyfUgMnPcw08tIyJZQWv2/wLsaIUkIY+vrf66/5oZC9Dch+ cLPScIto6s5WfdK0AlB7OJCVnOhPBsHnYEOElLpdLQJMQQjRQnsDYpBSFvMk9XDppzj5 gJ4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=SF1qQ5y6zS+uuXI0X6117nR7jpHsJ8QwMXzF/nJxgiY=; b=UYygM4NWpTcddo7Tn3wSDNs6BSSaAM9KgnEkII+qZKbPAzoOq2t2oIvz/jMDdsMX+k Js7uaX+6KlSvEuhXFSyA7TSdbU9dJJ5oGGN+jj7QmKfe0GCK7R1hbowbnLKG+irRXX5c gcN7+b7WKx8KWBo3jFNCjRiwe3+Bmy6Qw5XhV5yZukoChTu2jSJjNo7j9ShSNwqyS1Ss btUHVcQlFMH+kHbu4kO83WkW2/KUzAGDdiyDSL3x1xJaXstXQCWjnxgk/waBkXH4CzbH Y/LtbSkTLEjf/SHKlFE106ZC35L8VjDvGIGkJgu0vnVPO6wDygfxKaoXqURPO9ZooaXm NqtQ== X-Gm-Message-State: AOAM532xzvxWQiTc+u1fB37lJP2mN9HWZEy4Rkz+PliLwD/Q7s7/bdRh 5tQ0aOq4aIdgTW+SxAgqYjvokYmstFrwcNBz X-Google-Smtp-Source: ABdhPJxoi1XmHpGnVJEd12p7iaAwu3SnVjIJokGuTJEIZy9ha0sakApo0hCSJzO95UQP1fWnVKfr2w== X-Received: by 2002:a17:906:19c7:: with SMTP id h7mr12246865ejd.517.1599311147152; Sat, 05 Sep 2020 06:05:47 -0700 (PDT) Received: from localhost.localdomain ([2a00:ee2:4b0d:3002:290:faff:fe54:449c]) by smtp.gmail.com with ESMTPSA id s18sm9372655ejd.54.2020.09.05.06.05.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 05 Sep 2020 06:05:46 -0700 (PDT) From: Luka Kovacic To: linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-leds@vger.kernel.org Cc: lee.jones@linaro.org, pavel@ucw.cz, dmurphy@ti.com, robh+dt@kernel.org, jdelvare@suse.com, linux@roeck-us.net, andrew@lunn.ch, jason@lakedaemon.net, gregory.clement@bootlin.com, luka.perkov@sartura.hr, Luka Kovacic Subject: [PATCH 4/7] drivers: leds: Add the iEi WT61P803 PUZZLE LED driver Date: Sat, 5 Sep 2020 15:03:33 +0200 Message-Id: <20200905130336.967622-5-luka.kovacic@sartura.hr> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200905130336.967622-1-luka.kovacic@sartura.hr> References: <20200905130336.967622-1-luka.kovacic@sartura.hr> MIME-Version: 1.0 Sender: linux-leds-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add support for the iEi WT61P803 PUZZLE LED driver. Currently only the front panel power LED is supported. This driver depends on the iEi WT61P803 PUZZLE MFD driver. Signed-off-by: Luka Kovacic Cc: Luka Perkov --- drivers/leds/Kconfig | 8 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-iei-wt61p803-puzzle.c | 184 ++++++++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 drivers/leds/leds-iei-wt61p803-puzzle.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1c181df24eae..8a25fb753dec 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -332,6 +332,14 @@ config LEDS_IPAQ_MICRO Choose this option if you want to use the notification LED on Compaq/HP iPAQ h3100 and h3600. +config LEDS_IEI_WT61P803_PUZZLE + tristate "LED Support for the iEi WT61P803 PUZZLE MCU" + depends on LEDS_CLASS + depends on MFD_IEI_WT61P803_PUZZLE + help + This option enables support for LEDs controlled by the iEi WT61P803 + M801 MCU. + config LEDS_HP6XX tristate "LED Support for the HP Jornada 6xx" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index c2c7d7ade0d0..cd362437fefd 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o obj-$(CONFIG_LEDS_IP30) += leds-ip30.o obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o +obj-$(CONFIG_LEDS_IEI_WT61P803_PUZZLE) += leds-iei-wt61p803-puzzle.o obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o diff --git a/drivers/leds/leds-iei-wt61p803-puzzle.c b/drivers/leds/leds-iei-wt61p803-puzzle.c new file mode 100644 index 000000000000..50d1e4e81571 --- /dev/null +++ b/drivers/leds/leds-iei-wt61p803-puzzle.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* iEi WT61P803 PUZZLE MCU LED Driver + * + * Copyright (C) 2020 Sartura Ltd. + * Author: Luka Kovacic + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define CMD_CHAR(x) (char)(x) + +/** + * enum iei_wt61p803_puzzle_led_state - LED state values + * + * @IEI_LED_OFF: The LED is turned off + * @IEI_LED_ON: The LED is turned on + * @IEI_LED_BLINK_5HZ: The LED will blink with a freq of 5 Hz + * @IEI_LED_BLINK_1HZ: The LED will blink with a freq of 1 Hz + */ +enum iei_wt61p803_puzzle_led_state { + IEI_LED_OFF = 0x30, + IEI_LED_ON = 0x31, + IEI_LED_BLINK_5HZ = 0x32, + IEI_LED_BLINK_1HZ = 0x33 +}; + +/** + * struct iei_wt61p803_puzzle_led - MCU LED Driver + * + * @mcu: MCU struct pointer + * @lock: General mutex lock for LED operations + * @led_power_state: State of the front panel power LED + */ +struct iei_wt61p803_puzzle_led { + struct iei_wt61p803_puzzle *mcu; + struct mutex lock; + + int led_power_state; +}; + +static inline struct iei_wt61p803_puzzle_led * + cdev_to_iei_wt61p803_puzzle_led(struct led_classdev *led_cdev) +{ + return dev_get_drvdata(led_cdev->dev->parent); +} + +static int iei_wt61p803_puzzle_led_brightness_set_blocking + (struct led_classdev *cdev, enum led_brightness brightness) +{ + struct iei_wt61p803_puzzle_led *mcu_led = + cdev_to_iei_wt61p803_puzzle_led(cdev); + unsigned char led_power_cmd[5] = { '@', 'R', '1', + CMD_CHAR(IEI_LED_OFF) }; + + int ret; + + size_t reply_size = 0; + unsigned char *resp_buf = kmalloc(IEI_WT61P803_PUZZLE_BUF_SIZE, GFP_KERNEL); + + mutex_lock(&mcu_led->lock); + + if (brightness == LED_OFF) { + led_power_cmd[3] = CMD_CHAR(IEI_LED_OFF); + mcu_led->led_power_state = LED_OFF; + } else { + led_power_cmd[3] = CMD_CHAR(IEI_LED_ON); + mcu_led->led_power_state = LED_ON; + } + + mutex_unlock(&mcu_led->lock); + + ret = iei_wt61p803_puzzle_write_command(mcu_led->mcu, led_power_cmd, + sizeof(led_power_cmd), resp_buf, &reply_size); + + kfree(resp_buf); + + return ret; +} + +static enum led_brightness +iei_wt61p803_puzzle_led_brightness_get(struct led_classdev *cdev) +{ + struct iei_wt61p803_puzzle_led *mcu_led = + cdev_to_iei_wt61p803_puzzle_led(cdev); + + int led_state; + + mutex_lock(&mcu_led->lock); + led_state = mcu_led->led_power_state; + mutex_unlock(&mcu_led->lock); + + return led_state; +} + +static int iei_wt61p803_puzzle_led_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent); + struct device_node *np = dev->of_node; + + struct iei_wt61p803_puzzle_led *mcu_led; + struct device_node *child; + + int ret; + + mcu_led = devm_kzalloc(dev, sizeof(*mcu_led), GFP_KERNEL); + if (!mcu_led) + return -ENOMEM; + + mcu_led->mcu = mcu; + + /* The default LED power state is 1 */ + mcu_led->led_power_state = 1; + + /* Init the mutex lock */ + mutex_init(&mcu_led->lock); + + dev_set_drvdata(dev, mcu_led); + + for_each_child_of_node(np, child) { + struct led_classdev *led; + u32 reg; + + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + ret = of_property_read_u32(child, "reg", ®); + if (ret || reg > 1) { + dev_err(dev, "Could not register 'reg' of %s\n", + child->name); + continue; + } + + if (of_property_read_string(child, "label", &led->name)) + led->name = child->name; + + of_property_read_string(child, "linux,default-trigger", + &led->default_trigger); + + led->brightness_set_blocking = + iei_wt61p803_puzzle_led_brightness_set_blocking; + led->brightness_get = iei_wt61p803_puzzle_led_brightness_get; + + led->max_brightness = 1; + + ret = devm_led_classdev_register(dev, led); + if (ret) { + dev_err(dev, "Could not register %s\n", led->name); + return ret; + } + } + + return 0; + +} + +static const struct of_device_id iei_wt61p803_puzzle_led_of_match[] = { + { .compatible = "iei,wt61p803-puzzle-leds" }, + { } +}; +MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_led_of_match); + +static struct platform_driver iei_wt61p803_puzzle_led_driver = { + .driver = { + .name = "iei-wt61p803-puzzle-led", + .of_match_table = iei_wt61p803_puzzle_led_of_match, + }, + .probe = iei_wt61p803_puzzle_led_probe, +}; +module_platform_driver(iei_wt61p803_puzzle_led_driver); + +MODULE_DESCRIPTION("iEi WT61P803 PUZZLE front panel LED driver"); +MODULE_AUTHOR("Luka Kovacic "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:leds-iei-wt61p803-puzzle"); From patchwork Sat Sep 5 13:03:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luka Kovacic X-Patchwork-Id: 255434 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.0 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7B319C2BBD0 for ; Sat, 5 Sep 2020 13:06:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 46BCF2072D for ; Sat, 5 Sep 2020 13:06:44 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=sartura-hr.20150623.gappssmtp.com header.i=@sartura-hr.20150623.gappssmtp.com header.b="yNmUINmz" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728628AbgIENGm (ORCPT ); Sat, 5 Sep 2020 09:06:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42018 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728580AbgIENFw (ORCPT ); Sat, 5 Sep 2020 09:05:52 -0400 Received: from mail-ed1-x52f.google.com (mail-ed1-x52f.google.com [IPv6:2a00:1450:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 26624C06125C for ; Sat, 5 Sep 2020 06:05:52 -0700 (PDT) Received: by mail-ed1-x52f.google.com with SMTP id q21so8579065edv.1 for ; Sat, 05 Sep 2020 06:05:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sartura-hr.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=jbNBYJsjrynxmZsMzET80D74Cg5aetkjCqBMZs1glRY=; b=yNmUINmza9k+RTgHaXMy9Nuzc7/Hknt1zLTOYz8J0YBb/k1wMa/DF9mtRS8KmG5DsN aH6Mw2N0dAXjyO4lcED2y0TJLSY9W/hpSoo2jpOnhHDar7nIuTraTNSJy4tC/s58vmtd P2ysE6piC+hgCLm4v6M2Ds7sj8sLNeAIZIrnoyJAxlRI933pSVhLsXnNbf56NJJkJEyN KJMhB0+Pgk6Yw/QYdM8Up/luP1ZG9e4g8b8d8hn4Ocp9dL7WUAtZMA1bBX/AmPMP5sMu dhxV+bKeXkGVzH+3LBni/9wJBMPsu3SSYJ2xgXIQxczvS5t53Do4TbWRaCAVkrbC3Guc l35A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=jbNBYJsjrynxmZsMzET80D74Cg5aetkjCqBMZs1glRY=; b=cr8pQgDgSEZcBWR8XrsNWvnIuGjFSkmGbe1JF5hMgSuN6rzLBljsI0tRseVayIjSbN lhG61xG3cgTU7QvZ6o0rP8etxUPh+mql4+5Zq3/EGf/J9n+AJ+9XRE4NH9sl2ZlYKqeZ v7J3o7Wj3Ke1KlH/P/TYzd0sFB05WjRqUgbCiOrIQ1neEjjkxTLBxR7Hwd+I4RE6t5t9 rEy7PVKM0YMBBtWAeX+lYHxnYdJ1tSDW6bdyS6XzmWfguele7uVslWTk1jJZgBpxobDt ZQytEtLfbpZCJyShHTr1H7t9/7qNfgLxE3Svy4HI8BEuG0+OQd/xLsrnd0nVFL3VW9I6 0HUg== X-Gm-Message-State: AOAM532DQ4hRszjfhpNZdqhf8ov9QtVpDO7MEJjGldJo2XebWcILu7jJ 6Hal+1xzcjxXK7CmPQUjMZXsX+byj0bHVAx0 X-Google-Smtp-Source: ABdhPJx/V0WmQvcU6/X0stOthigOnu+n8tOJtymLLR8nAcOmKK+z71JQtoB5PH2Cw0lJ8uSxHMD4+Q== X-Received: by 2002:a50:d304:: with SMTP id g4mr13254256edh.248.1599311149280; Sat, 05 Sep 2020 06:05:49 -0700 (PDT) Received: from localhost.localdomain ([2a00:ee2:4b0d:3002:290:faff:fe54:449c]) by smtp.gmail.com with ESMTPSA id s18sm9372655ejd.54.2020.09.05.06.05.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 05 Sep 2020 06:05:48 -0700 (PDT) From: Luka Kovacic To: linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-leds@vger.kernel.org Cc: lee.jones@linaro.org, pavel@ucw.cz, dmurphy@ti.com, robh+dt@kernel.org, jdelvare@suse.com, linux@roeck-us.net, andrew@lunn.ch, jason@lakedaemon.net, gregory.clement@bootlin.com, luka.perkov@sartura.hr, Luka Kovacic Subject: [PATCH 5/7] Documentation/ABI: Add iei-wt61p803-puzzle driver sysfs interface documentation Date: Sat, 5 Sep 2020 15:03:34 +0200 Message-Id: <20200905130336.967622-6-luka.kovacic@sartura.hr> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200905130336.967622-1-luka.kovacic@sartura.hr> References: <20200905130336.967622-1-luka.kovacic@sartura.hr> MIME-Version: 1.0 Sender: linux-leds-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add the iei-wt61p803-puzzle driver sysfs interface documentation to allow monitoring and control of the microcontroller from user space. Signed-off-by: Luka Kovacic Cc: Luka Perkov --- .../stable/sysfs-driver-iei-wt61p803-puzzle | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Documentation/ABI/stable/sysfs-driver-iei-wt61p803-puzzle diff --git a/Documentation/ABI/stable/sysfs-driver-iei-wt61p803-puzzle b/Documentation/ABI/stable/sysfs-driver-iei-wt61p803-puzzle new file mode 100644 index 000000000000..36fca70d66ef --- /dev/null +++ b/Documentation/ABI/stable/sysfs-driver-iei-wt61p803-puzzle @@ -0,0 +1,65 @@ +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/mac_address_* +Date: September 2020 +Contact: Luka Kovacic +Description: Read the internal iEi WT61P803 PUZZLE MCU MAC address values. + These are factory assigned and can be changed. + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/serial_number +Date: September 2020 +Contact: Luka Kovacic +Description: Read the internal iEi WT61P803 PUZZLE MCU serial number. + This value is factory assigned and can be changed. + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/version +Date: September 2020 +Contact: Luka Kovacic +Description: Read the internal iEi WT61P803 PUZZLE MCU version. + This value is read only. + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/protocol_version +Date: September 2020 +Contact: Luka Kovacic +Description: Read the internal iEi WT61P803 PUZZLE MCU protocol version. + This value is read only. + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/power_loss_recovery +Date: September 2020 +Contact: Luka Kovacic +Description: Read the iEi WT61P803 PUZZLE MCU power loss recovery value. + This value is read write. + Value mapping: 0 - Always-On, 1 - Always-Off, 2 - Always-AC, 3 - Always-WA + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/bootloader_mode +Date: September 2020 +Contact: Luka Kovacic +Description: Read whether the MCU is in bootloader mode. + This value is read only. + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/power_status +Date: September 2020 +Contact: Luka Kovacic +Description: Read the iEi WT61P803 PUZZLE MCU power status. Power status indicates + the power on method. + This value is read only. + Value mapping (bitwise list): + 0x80 - Null + 0x40 - Firmware flag + 0x20 - Power loss detection flag (powered off) + 0x10 - Power loss detection flag (AC mode) + 0x08 - Button power on + 0x04 - WOL power on + 0x02 - RTC alarm power on + 0x01 - AC recover power on + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/build_info +Date: September 2020 +Contact: Luka Kovacic +Description: Read the iEi WT61P803 PUZZLE MCU firmware build date. + This value is read only. + Format: yyyy/mm/dd hh:mm + +What: /sys/bus/serial/devices/.../iei_wt61p803_puzzle_core/ac_recovery_status +Date: September 2020 +Contact: Luka Kovacic +Description: Read the iEi WT61P803 PUZZLE MCU AC recovery status. + This value is read only. From patchwork Sat Sep 5 13:03:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luka Kovacic X-Patchwork-Id: 255435 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.0 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AFDB9C433E2 for ; Sat, 5 Sep 2020 13:06:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6D784208DB for ; Sat, 5 Sep 2020 13:06:23 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=sartura-hr.20150623.gappssmtp.com header.i=@sartura-hr.20150623.gappssmtp.com header.b="kQNjQbg2" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728616AbgIENGR (ORCPT ); Sat, 5 Sep 2020 09:06:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42034 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728596AbgIENFz (ORCPT ); Sat, 5 Sep 2020 09:05:55 -0400 Received: from mail-ej1-x642.google.com (mail-ej1-x642.google.com [IPv6:2a00:1450:4864:20::642]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1E0C8C061251 for ; Sat, 5 Sep 2020 06:05:55 -0700 (PDT) Received: by mail-ej1-x642.google.com with SMTP id i22so12083696eja.5 for ; Sat, 05 Sep 2020 06:05:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sartura-hr.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=322eLsEkmbv53gakapl+TlGOtJ3WQUkBPPAQWpyScfE=; b=kQNjQbg24wE2noUhH/XL3Yv48U/r42eVaF/Vcl6GP9z139gppRyLfkLNMKrcUheWD8 St2+aHKvpcrSBPge0mIzvBo3Hfg/iLDojHnF8cwbUuSvyDyuZm+fuhMTKxlPdI8NaxCh JIWUsqGem8KmLGBftrsb1MIXf3av9mgDc55ZsqIOtDjJZ9V4SNelbJqI8DfFrk9HYJRD EemIaP+fOJOQOueqoIaTqnGnAanjJzzUl71Z33NzkOwDuwO+ZVCeCPGkZZVp1ZEdWLlP lFBI03nMva2ZDWuYc77pPrq7LeyevivgPlX8wetBM8Qqut/kbl5IjlsgBPRrUMKBPlVT CSGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=322eLsEkmbv53gakapl+TlGOtJ3WQUkBPPAQWpyScfE=; b=hxlfTWkOLjV9o8lS6C7BSUN1EtvW+QMacyus4Cya+01y4qLCzF+G08h/F/OJ1/e8Kj AYjsc8UOsu6In3LBxvHTmKyMHyXrJ/ucL5bUh/a/e7rOc3nfd417o9NVlt6K/iZwQQCL 9779dRSEO7REPpmwsFJVxukBc8raciegJ8g9C6U8+fThtVw4AyJ30ubyLMR1VQdcNtq8 OJspp1ocYiI3hYZVSvpbHJp6DYADwhlsQs5XmfiHJEaybE1YQR6Y2uyiPmAvUMyWAUAQ 5e9idPpi3pekRJVxqSQSta++RcT61b3LQ6EXot6PW6F8mPLy9CS3iR9FcICWB5TMma5c LHAg== X-Gm-Message-State: AOAM533VhyZ482a3kLX7ElY1S3ajXYTQPprWpjWbrRztrIVs78Kf7I+p kWZqo3o6k7bcTk1LlyuLyzjtWA== X-Google-Smtp-Source: ABdhPJwTFofQxT5+Xqmduh9lEX7PsTno/supz5boDjSPMZy4MCfbWku9BL2CzrpPh1wWjP/gUjz9/w== X-Received: by 2002:a17:906:4cc7:: with SMTP id q7mr12780600ejt.437.1599311153536; Sat, 05 Sep 2020 06:05:53 -0700 (PDT) Received: from localhost.localdomain ([2a00:ee2:4b0d:3002:290:faff:fe54:449c]) by smtp.gmail.com with ESMTPSA id s18sm9372655ejd.54.2020.09.05.06.05.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 05 Sep 2020 06:05:52 -0700 (PDT) From: Luka Kovacic To: linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-leds@vger.kernel.org Cc: lee.jones@linaro.org, pavel@ucw.cz, dmurphy@ti.com, robh+dt@kernel.org, jdelvare@suse.com, linux@roeck-us.net, andrew@lunn.ch, jason@lakedaemon.net, gregory.clement@bootlin.com, luka.perkov@sartura.hr, Luka Kovacic Subject: [PATCH 7/7] arm64: dts: marvell: Add a device tree for the iEi Puzzle-M801 board Date: Sat, 5 Sep 2020 15:03:36 +0200 Message-Id: <20200905130336.967622-8-luka.kovacic@sartura.hr> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200905130336.967622-1-luka.kovacic@sartura.hr> References: <20200905130336.967622-1-luka.kovacic@sartura.hr> MIME-Version: 1.0 Sender: linux-leds-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add initial support for the iEi Puzzle-M801 1U Rackmount Network Appliance board. The board is based on the quad-core Marvell Armada 8040 SoC and supports up to 16 GB of DDR4 2400 MHz ECC RAM. It has a PCIe x16 slot (x2 lanes only) and an M.2 type B slot. Main system hardware: 2x USB 3.0 4x Gigabit Ethernet 2x SFP+ 1x SATA 3.0 1x M.2 type B 1x RJ45 UART 1x SPI flash 1x iEi WT61P803 PUZZLE Microcontroller 1x EPSON RX8010 RTC (used instead of the integrated Marvell RTC controller) 6x SFP+ LED 1x HDD LED All of the hardware listed above is supported and tested in this port. Signed-off-by: Luka Kovacic Cc: Luka Perkov --- arch/arm64/boot/dts/marvell/Makefile | 1 + .../dts/marvell/armada-8040-puzzle-m801.dts | 519 ++++++++++++++++++ 2 files changed, 520 insertions(+) create mode 100644 arch/arm64/boot/dts/marvell/armada-8040-puzzle-m801.dts diff --git a/arch/arm64/boot/dts/marvell/Makefile b/arch/arm64/boot/dts/marvell/Makefile index 3e5f2e7a040c..e413c3261792 100644 --- a/arch/arm64/boot/dts/marvell/Makefile +++ b/arch/arm64/boot/dts/marvell/Makefile @@ -12,6 +12,7 @@ dtb-$(CONFIG_ARCH_MVEBU) += armada-8040-clearfog-gt-8k.dtb dtb-$(CONFIG_ARCH_MVEBU) += armada-8040-db.dtb dtb-$(CONFIG_ARCH_MVEBU) += armada-8040-mcbin.dtb dtb-$(CONFIG_ARCH_MVEBU) += armada-8040-mcbin-singleshot.dtb +dtb-$(CONFIG_ARCH_MVEBU) += armada-8040-puzzle-m801.dtb dtb-$(CONFIG_ARCH_MVEBU) += armada-8080-db.dtb dtb-$(CONFIG_ARCH_MVEBU) += cn9130-db.dtb dtb-$(CONFIG_ARCH_MVEBU) += cn9131-db.dtb diff --git a/arch/arm64/boot/dts/marvell/armada-8040-puzzle-m801.dts b/arch/arm64/boot/dts/marvell/armada-8040-puzzle-m801.dts new file mode 100644 index 000000000000..2921202b4f7b --- /dev/null +++ b/arch/arm64/boot/dts/marvell/armada-8040-puzzle-m801.dts @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2016 Marvell Technology Group Ltd. + * Copyright (C) 2020 Sartura Ltd. + * + * Device Tree file for iEi Puzzle-M801 + */ + +#include "armada-8040.dtsi" + +#include +#include + +/ { + model = "iEi-Puzzle-M801"; + compatible = "marvell,armada8040", "marvell,armada-ap806-quad", "marvell,armada-ap806"; + + aliases { + ethernet0 = &cp0_eth0; + ethernet1 = &cp1_eth0; + ethernet2 = &cp0_eth1; + ethernet3 = &cp0_eth2; + ethernet4 = &cp1_eth1; + ethernet5 = &cp1_eth2; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x0 0x0 0x80000000>; + }; + + /* Regulator labels correspond with schematics */ + v_3_3: regulator-3-3v { + compatible = "regulator-fixed"; + regulator-name = "v_3_3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + status = "okay"; + }; + + v_5v0_usb3_hst_vbus: regulator-usb3-vbus0 { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&cp0_gpio2 15 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&cp0_xhci_vbus_pins>; + regulator-name = "v_5v0_usb3_hst_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + status = "okay"; + }; + + v_vddo_h: regulator-1-8v { + compatible = "regulator-fixed"; + regulator-name = "v_vddo_h"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + status = "okay"; + }; + + sfp_cp0_eth0: sfp-cp0-eth0 { + compatible = "sff,sfp"; + i2c-bus = <&sfpplus0_i2c>; + los-gpio = <&sfpplus_gpio 11 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&sfpplus_gpio 10 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&sfpplus_gpio 9 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&sfpplus_gpio 8 GPIO_ACTIVE_HIGH>; + maximum-power-milliwatt = <3000>; + }; + + sfp_cp1_eth0: sfp-cp1-eth0 { + compatible = "sff,sfp"; + i2c-bus = <&sfpplus1_i2c>; + los-gpio = <&sfpplus_gpio 3 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&sfpplus_gpio 2 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&sfpplus_gpio 1 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&sfpplus_gpio 0 GPIO_ACTIVE_HIGH>; + maximum-power-milliwatt = <3000>; + }; + + leds { + compatible = "gpio-leds"; + status = "okay"; + pinctrl-0 = <&cp0_sfpplus_led_pins &cp1_sfpplus_led_pins>; + pinctrl-names = "default"; + + led0 { + function = LED_FUNCTION_STATUS; + label = "p2_act"; + gpios = <&cp1_gpio1 6 GPIO_ACTIVE_LOW>; + }; + + led1 { + function = LED_FUNCTION_STATUS; + label = "p1_act"; + gpios = <&cp1_gpio1 14 GPIO_ACTIVE_LOW>; + }; + + led2 { + function = LED_FUNCTION_STATUS; + label = "p2_10g"; + gpios = <&cp1_gpio1 7 GPIO_ACTIVE_LOW>; + }; + + led3 { + function = LED_FUNCTION_STATUS; + label = "p2_1g"; + gpios = <&cp1_gpio1 8 GPIO_ACTIVE_LOW>; + }; + + led4 { + function = LED_FUNCTION_STATUS; + label = "p1_10g"; + gpios = <&cp1_gpio1 10 GPIO_ACTIVE_LOW>; + }; + + led5 { + function = LED_FUNCTION_STATUS; + label = "p1_1g"; + gpios = <&cp1_gpio1 31 GPIO_ACTIVE_LOW>; + }; + + led6 { + function = LED_FUNCTION_STATUS; + linux,default-trigger = "disk-activity"; + label = "front-hdd-led"; + gpios = <&cp0_gpio2 22 GPIO_ACTIVE_HIGH>; + }; + + }; +}; + +&ap_sdhci0 { + bus-width = <8>; + /* + * Not stable in HS modes - phy needs "more calibration", so add + * the "slow-mode" and disable SDR104, SDR50 and DDR50 modes. + */ + marvell,xenon-phy-slow-mode; + no-1-8-v; + no-sd; + no-sdio; + non-removable; + status = "okay"; + vqmmc-supply = <&v_vddo_h>; +}; + +&ap_thermal_cpu1 { + trips { + cpu_active: cpu-active { + temperature = <44000>; + hysteresis = <2000>; + type = "active"; + }; + }; + cooling-maps { + fan-map { + trip = <&cpu_active>; + cooling-device = <&chassis_fan_group0 64 THERMAL_NO_LIMIT>, + <&chassis_fan_group1 64 THERMAL_NO_LIMIT>; + }; + }; +}; + +&i2c0 { + clock-frequency = <100000>; + status = "okay"; + + rtc@32 { + compatible = "epson,rx8010"; + reg = <0x32>; + }; +}; + +&spi0 { + status = "okay"; + spi-flash@0 { + #address-cells = <0x1>; + #size-cells = <0x1>; + compatible = "jedec,spi-nor"; + reg = <0x0>; + spi-max-frequency = <20000000>; + partition@u-boot { + label = "u-boot"; + reg = <0x00000000 0x001f0000>; + }; + partition@u-boot-env { + label = "u-boot-env"; + reg = <0x001f0000 0x00010000>; + }; + partition@ubi1 { + label = "ubi1"; + reg = <0x00200000 0x03f00000>; + }; + partition@ubi2 { + label = "ubi2"; + reg = <0x04100000 0x03f00000>; + }; + }; +}; + +&uart0 { + status = "okay"; + pinctrl-0 = <&uart0_pins>; + pinctrl-names = "default"; +}; + +&uart1 { + status = "okay"; + /* iEi WT61P803 PUZZLE MCU Controller */ + mcu { + compatible = "iei,wt61p803-puzzle"; + current-speed = <115200>; + enable-probe-beep; + + leds { + compatible = "iei,wt61p803-puzzle-leds"; + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + label = "front-power-led"; + }; + }; + + iei-wt61p803-puzzle-hwmon { + compatible = "iei,wt61p803-puzzle-hwmon"; + + #address-cells = <1>; + #size-cells = <0>; + + chassis_fan_group0:fan-group@0 { + #cooling-cells = <2>; + reg = <0x00>; + cooling-levels = <64 102 170 230 250>; + }; + + chassis_fan_group1:fan-group@1 { + #cooling-cells = <2>; + reg = <0x01>; + cooling-levels = <64 102 170 230 250>; + }; + }; + }; +}; + +&cp0_rtc { + status = "disabled"; +}; + +&cp0_i2c0 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&cp0_i2c0_pins>; + status = "okay"; + + sfpplus_gpio: gpio@21 { + compatible = "nxp,pca9555"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + }; + + eeprom@54 { + compatible = "atmel,24c04"; + reg = <0x54>; + }; +}; + +&cp0_i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&cp0_i2c1_pins>; + status = "okay"; + + i2c-switch@70 { + compatible = "nxp,pca9544"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + + sfpplus0_i2c: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + sfpplus1_i2c: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + }; +}; + +&cp0_uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&cp0_uart1_pins>; + status = "okay"; +}; + +&cp0_mdio { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + + ge_phy2: ethernet-phy@2 { + reg = <0>; + }; + + ge_phy3: ethernet-phy@3 { + reg = <1>; + }; +}; + +&cp0_pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&cp0_pcie_pins>; + num-lanes = <1>; + num-viewport = <8>; + reset-gpios = <&cp0_gpio2 20 GPIO_ACTIVE_LOW>; + ranges = <0x82000000 0x0 0xc0000000 0x0 0xc0000000 0x0 0x20000000>; + phys = <&cp0_comphy0 0>; + phy-names = "cp0-pcie0-x1-phy"; + status = "okay"; +}; + +&cp0_pinctrl { + cp0_ge_mdio_pins: ge-mdio-pins { + marvell,pins = "mpp32", "mpp34"; + marvell,function = "ge"; + }; + cp0_i2c1_pins: i2c1-pins { + marvell,pins = "mpp35", "mpp36"; + marvell,function = "i2c1"; + }; + cp0_i2c0_pins: i2c0-pins { + marvell,pins = "mpp37", "mpp38"; + marvell,function = "i2c0"; + }; + cp0_uart1_pins: uart1-pins { + marvell,pins = "mpp40", "mpp41"; + marvell,function = "uart1"; + }; + cp0_xhci_vbus_pins: xhci0-vbus-pins { + marvell,pins = "mpp47"; + marvell,function = "gpio"; + }; + cp0_pcie_pins: pcie-pins { + marvell,pins = "mpp52"; + marvell,function = "gpio"; + }; + cp0_sdhci_pins: sdhci-pins { + marvell,pins = "mpp55", "mpp56", "mpp57", "mpp58", "mpp59", + "mpp60", "mpp61"; + marvell,function = "sdio"; + }; + cp0_sfpplus_led_pins: sfpplus-led-pins { + marvell,pins = "mpp54"; + marvell,function = "gpio"; + }; +}; + +&cp0_ethernet { + status = "okay"; +}; + +&cp0_eth0 { + status = "okay"; + phy-mode = "10gbase-r"; + phys = <&cp0_comphy4 0>; + local-mac-address = [00 50 43 de ff 00]; + sfp = <&sfp_cp0_eth0>; + managed = "in-band-status"; +}; + +&cp0_eth1 { + status = "okay"; + phy = <&ge_phy2>; + phy-mode = "sgmii"; + local-mac-address = [00 50 43 de ff 01]; + phys = <&cp0_comphy3 1>; +}; + +&cp0_eth2 { + status = "okay"; + phy-mode = "sgmii"; + phys = <&cp0_comphy1 2>; + local-mac-address = [00 50 43 de ff 02]; + phy = <&ge_phy3>; +}; + +&cp0_sata0 { + status = "okay"; + + sata-port@0 { + phys = <&cp0_comphy2 0>; + phy-names = "cp0-sata0-0-phy"; + }; + + sata-port@1 { + phys = <&cp0_comphy5 1>; + phy-names = "cp0-sata0-1-phy"; + }; +}; + +&cp0_sdhci0 { + broken-cd; + bus-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&cp0_sdhci_pins>; + status = "okay"; + vqmmc-supply = <&v_3_3>; +}; + +&cp0_usb3_0 { + status = "okay"; +}; + +&cp0_usb3_1 { + status = "okay"; +}; + +&cp1_i2c0 { + clock-frequency = <100000>; + status = "disabled"; +}; + +&cp1_i2c1 { + clock-frequency = <100000>; + status = "disabled"; +}; + +&cp1_rtc { + status = "disabled"; +}; + +&cp1_ethernet { + status = "okay"; +}; + +&cp1_eth0 { + status = "okay"; + phy-mode = "10gbase-r"; + phys = <&cp1_comphy4 0>; + local-mac-address = [00 50 43 de ff 03]; + sfp = <&sfp_cp1_eth0>; + managed = "in-band-status"; +}; + +&cp1_eth1 { + status = "okay"; + phy = <&ge_phy4>; + phy-mode = "sgmii"; + local-mac-address = [00 50 43 de ff 04]; + phys = <&cp1_comphy3 1>; +}; + +&cp1_eth2 { + status = "okay"; + phy-mode = "sgmii"; + local-mac-address = [00 50 43 de ff 05]; + phys = <&cp1_comphy5 2>; + phy = <&ge_phy5>; +}; + +&cp1_pinctrl { + cp1_sfpplus_led_pins: sfpplus-led-pins { + marvell,pins = "mpp6", "mpp7", "mpp8", "mpp10", "mpp14", "mpp31"; + marvell,function = "gpio"; + }; +}; + +&cp1_uart0 { + status = "disabled"; +}; + +&cp1_comphy2 { + cp1_usbh0_con: connector { + compatible = "usb-a-connector"; + phy-supply = <&v_5v0_usb3_hst_vbus>; + }; +}; + +&cp1_usb3_0 { + phys = <&cp1_comphy2 0>; + phy-names = "cp1-usb3h0-comphy"; + status = "okay"; +}; + +&cp1_mdio { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + + ge_phy4: ethernet-phy@4 { + reg = <1>; + }; + ge_phy5: ethernet-phy@5 { + reg = <0>; + }; +}; + +&cp1_pcie0 { + num-lanes = <2>; + phys = <&cp1_comphy0 0>, <&cp1_comphy1 0>; + phy-names = "cp1-pcie0-x2-lane0-phy", "cp1-pcie0-x2-lane1-phy"; + status = "okay"; +};