Re: [PATCH] mfd: Add driver for RAVE Supervisory Processor
From: Andrey Smirnov
Date: Tue Jun 06 2017 - 21:26:52 EST
On Tue, Jun 6, 2017 at 11:06 AM, Andrey Smirnov
<andrew.smirnov@xxxxxxxxx> wrote:
> Add a driver for RAVE Supervisory Processor, an MCU implementing
> varoius bits of housekeeping functionality (watchdoging, backlight
> control, LED control, etc) on RAVE family of products by Zodiac
> Inflight Innovations.
>
> This driver implementes core MFD/serdev device as well as
> communication subroutines necessary for commanding the device.
>
> Cc: cphealy@xxxxxxxxx
> Cc: Lucas Stach <l.stach@xxxxxxxxxxxxxx>
> Cc: Nikita Yushchenko <nikita.yoush@xxxxxxxxxxxxxxxxxx>
> Cc: Rob Herring <robh+dt@xxxxxxxxxx>
> Cc: Mark Rutland <mark.rutland@xxxxxxx>
> Cc: devicetree@xxxxxxxxxxxxxxx
> Cc: linux-kernel@xxxxxxxxxxxxxxx
> Signed-off-by: Andrey Smirnov <andrew.smirnov@xxxxxxxxx>
> ---
>
> Note that the driver for "zii,rave-sp-watchdog" exists, but I haven't
> submitted it yet, becuase I wanted to make sure that API exposed by
> this MFD is acceptable and doesn't need drastic changes
>
> .../devicetree/bindings/mfd/zii,rave-sp.txt | 33 +
> drivers/mfd/Kconfig | 9 +
> drivers/mfd/Makefile | 1 +
> drivers/mfd/rave-sp.c | 1009 ++++++++++++++++++++
> include/linux/rave-sp.h | 54 ++
> 5 files changed, 1106 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/zii,rave-sp.txt
> create mode 100644 drivers/mfd/rave-sp.c
> create mode 100644 include/linux/rave-sp.h
>
> diff --git a/Documentation/devicetree/bindings/mfd/zii,rave-sp.txt b/Documentation/devicetree/bindings/mfd/zii,rave-sp.txt
> new file mode 100644
> index 0000000..46a904c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/zii,rave-sp.txt
> @@ -0,0 +1,33 @@
> +Zodiac Inflight Innovations RAVE Supervisory Processor
> +
> +Required parent device properties:
> +
> + - compatible: Should be one of:
> + - "zii,rave-sp-niu"
> + - "zii,rave-sp-mezz"
> + - "zii,rave-sp-esb"
> + - "zii,rave-sp-rdu1"
> + - "zii,rave-sp-rdu2"
> +
> + - current-speed: Should be set to baud rate SP device is using
> +
> +RAVE SP consists of the following sub-devices:
> +
> +Device Description
> +------ -----------
> +rave-sp-wdt : Watchdog
> +
> +
> +Example of usage:
> +
> + rdu {
> + compatible = "zii,rave-sp-rdu2";
> + current-speed = <1000000>;
> + status = "okay";
> +
> + watchdog {
> + compatible = "zii,rave-sp-watchdog";
> + status = "okay";
> + };
> + };
> +
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 3eb5c93..6cab311 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -867,6 +867,15 @@ config MFD_SPMI_PMIC
> Say M here if you want to include support for the SPMI PMIC
> series as a module. The module will be called "qcom-spmi-pmic".
>
> +config MFD_RAVE_SP
> + tristate "RAVE SP MCU core driver"
> + select MFD_CORE
> + select SERIAL_DEV_BUS
> + select CRC_CCITT
> + help
> + Select this to get support for the RAVE Supervisory
> + Processor driver
> +
> config MFD_RDC321X
> tristate "RDC R-321x southbridge"
> select MFD_CORE
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index c16bf1e..bc3df0a 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -221,3 +221,4 @@ obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
>
> obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
> obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
> +obj-$(CONFIG_MFD_RAVE_SP) += rave-sp.o
> diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c
> new file mode 100644
> index 0000000..1260f94
> --- /dev/null
> +++ b/drivers/mfd/rave-sp.c
> @@ -0,0 +1,1009 @@
> +/*
> + * rave-sp.c - Multifunction core driver for Zodiac Inflight Innovations
> + * SP MCU that is connected via dedicated UART port
> + *
> + * Copyright (C) 2017 Zodiac Inflight Innovations
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +/*
> + * UART protocol using following entities:
> + * - message to MCU => ACK response
> + * - event from MCU => event ACK
> + *
> + * Frame structure:
> + * <STX> <DATA> <CHECKSUM> <ETX>
> + * Where:
> + * - STX - is start of transmission character
> + * - ETX - end of transmission
> + * - DATA - payload
> + * - CHECKSUM - checksum calculated on <DATA>
> + *
> + * If <DATA> or <CHECKSUM> contain one of control characters, then it is
> + * escaped using <DLE> control code. Added <DLE> does not participate in
> + * checksum calculation.
> + */
> +
> +/* #define DEBUG */
> +
> +#include <linux/atomic.h>
> +#include <linux/crc-ccitt.h>
> +#include <linux/delay.h>
> +#include <linux/export.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/sched.h>
> +#include <linux/serdev.h>
> +#include <linux/rave-sp.h>
> +
> +enum {
> + STX = 0x02,
> + ETX = 0x03,
> + DLE = 0x10,
> +};
> +
> +enum {
> + RAVE_SP_BOOT_SOURCE_GET = 0,
> + RAVE_SP_BOOT_SOURCE_SET = 1,
> +
> + RAVE_SP_MAX_DATA_SIZE = 64,
> + RAVE_SP_CHECKSUM_SIZE = 2, /* Worst case scenariou on RDU2 */
> + /*
> + * We don't store STX, ETX and unescaped bytes, so Rx is only
> + * DATA + CSUM
> + */
> + RAVE_SP_RX_BUFFER_SIZE = RAVE_SP_MAX_DATA_SIZE +
> + RAVE_SP_CHECKSUM_SIZE,
> + RAVE_SP_STX_ETX_SIZE = 2,
> + /*
> + * For Tx we have to have space for everything, STX, EXT and
> + * potentially stuffed DATA + CSUM data + csum
> + */
> + RAVE_SP_TX_BUFFER_SIZE = RAVE_SP_STX_ETX_SIZE +
> + 2 * RAVE_SP_RX_BUFFER_SIZE,
> +};
> +
> +enum rave_sp_deframer_state {
> + RAVE_SP_EXPECT_SOF,
> + RAVE_SP_EXPECT_DATA,
> + RAVE_SP_EXPECT_ESCAPED_DATA,
> +};
> +
> +struct rave_sp_deframer {
> + enum rave_sp_deframer_state state;
> + unsigned char data[RAVE_SP_RX_BUFFER_SIZE];
> + size_t length;
> +};
> +
> +struct rave_sp_reply {
> + size_t length;
> + void *data;
> + u8 code;
> + u8 ackid;
> + struct completion received;
> +};
> +
> +struct rave_sp_checksum {
> + size_t length;
> + void (*subroutine)(const u8 *, size_t, u8 *);
> +};
> +
> +enum rave_sp_boot_source {
> + RAVE_SP_BOOT_SOURCE_SD = 0,
> + RAVE_SP_BOOT_SOURCE_EMMC = 1,
> + RAVE_SP_BOOT_SOURCE_NOR = 2,
> +};
> +
> +struct rave_sp_variant {
> + const struct rave_sp_checksum *checksum;
> +
> + struct {
> + int (*translate)(enum rave_sp_command);
> + int (*get_boot_source)(struct rave_sp *);
> + int (*set_boot_source)(struct rave_sp *,
> + enum rave_sp_boot_source);
> + } cmd;
> +
> + void (*init)(struct rave_sp *);
> +
> + struct attribute_group group;
> +};
> +
> +struct rave_sp {
> + struct serdev_device *serdev;
> +
> + struct rave_sp_deframer deframer;
> + atomic_t ackid;
> +
> + struct mutex bus_lock;
> + struct mutex reply_lock;
> + struct rave_sp_reply *reply;
> +
> + const char *part_number_firmware;
> + const char *part_number_bootloader;
> +
> + const char *reset_reason;
> + const char *copper_rev_rmb;
> + const char *copper_rev_deb;
> + const char *silicon_devid;
> + const char *silicon_devrev;
> +
> + const char *copper_mod_rmb;
> + const char *copper_mod_deb;
> +
> + const struct rave_sp_variant *variant;
> +
> + struct blocking_notifier_head event_notifier_list;
> +
> + struct attribute_group *group;
> +};
> +
> +struct rave_sp_rsp_status {
> + u8 bl_bytes[6];
> + u8 fw_bytes[6];
> + u8 gs_format;
> +} __packed;
> +
> +static bool rave_sp_id_is_event(u8 code)
> +{
> + return (code & 0xF0) == RAVE_SP_EVNT_BASE;
> +}
> +
> +static void
> +devm_rave_sp_unregister_event_notifier(struct device *dev, void *res)
> +{
> + struct rave_sp *sp = dev_get_drvdata(dev->parent);
> + struct notifier_block *nb = *(struct notifier_block **)res;
> + struct blocking_notifier_head *bnh = &sp->event_notifier_list;
> +
> + WARN_ON(blocking_notifier_chain_unregister(bnh, nb));
> +}
> +
> +int devm_rave_sp_register_event_notifier(struct device *dev,
> + struct notifier_block *nb)
> +{
> + struct rave_sp *sp = dev_get_drvdata(dev->parent);
> + struct notifier_block **rcnb;
> + int ret;
> +
> + rcnb = devres_alloc(devm_rave_sp_unregister_event_notifier,
> + sizeof(*rcnb), GFP_KERNEL);
> + if (!rcnb)
> + return -ENOMEM;
> +
> + ret = blocking_notifier_chain_register(&sp->event_notifier_list, nb);
> + if (!ret) {
> + *rcnb = nb;
> + devres_add(dev, rcnb);
> + } else {
> + devres_free(rcnb);
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(devm_rave_sp_register_event_notifier);
> +
> +static const char *devm_rave_sp_version(struct device *dev, const char *buf)
> +{
> + return devm_kasprintf(dev, GFP_KERNEL, "%02d%02d%02d.%c%c\n",
> + buf[0], le16_to_cpup((const __le16 *)&buf[1]),
> + buf[3], buf[4], buf[5]);
> +}
> +
> +static int rave_sp_get_status(struct rave_sp *sp,
> + struct rave_sp_rsp_status *status)
> +{
> + u8 cmd[] = {
> + [0] = RAVE_SP_CMD_STATUS,
> + [1] = 0
> + };
> + return rave_sp_exec(sp, cmd, sizeof(cmd), &status, sizeof(status));
Ugh, found a bug in my code, should be:
return rave_sp_exec(sp, cmd, sizeof(cmd), status, sizeof(*status));
will be fixed in v2.
Sorry for missing that!
Andrey