lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <CAHQ1cqH7k4-V-4V63k+=6DQ70syY_B02KYbcBt89d3YqAvqAOA@mail.gmail.com>
Date:   Mon, 4 Dec 2017 12:57:30 -0800
From:   Andrey Smirnov <andrew.smirnov@...il.com>
To:     Lee Jones <lee.jones@...aro.org>
Cc:     Andrey Smirnov <andrew.smirnov@...il.com>,
        linux-kernel <linux-kernel@...r.kernel.org>,
        Chris Healy <cphealy@...il.com>,
        Lucas Stach <l.stach@...gutronix.de>,
        Nikita Yushchenko <nikita.yoush@...entembedded.com>,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        Pavel Machek <pavel@....cz>,
        Andy Shevchenko <andy.shevchenko@...il.com>,
        Guenter Roeck <linux@...ck-us.net>,
        Rob Herring <robh@...nel.org>, Johan Hovold <johan@...nel.org>,
        Sebastian Reichel <sebastian.reichel@...labora.co.uk>
Subject: Re: [PATCH v13 3/5] mfd: Add driver for RAVE Supervisory Processor

On Mon, Dec 4, 2017 at 8:11 AM, Andrey Smirnov <andrew.smirnov@...il.com> wrote:
> Add a driver for RAVE Supervisory Processor, an MCU implementing
> various 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: linux-kernel@...r.kernel.org
> Cc: cphealy@...il.com
> Cc: Lucas Stach <l.stach@...gutronix.de>
> Cc: Nikita Yushchenko <nikita.yoush@...entembedded.com>
> Cc: Lee Jones <lee.jones@...aro.org>
> Cc: Greg Kroah-Hartman <gregkh@...uxfoundation.org>
> Cc: Pavel Machek <pavel@....cz>
> Cc: Andy Shevchenko <andy.shevchenko@...il.com>
> Cc: Guenter Roeck <linux@...ck-us.net>
> Cc: Rob Herring <robh@...nel.org>
> Cc: Johan Hovold <johan@...nel.org>
> Cc: Sebastian Reichel <sebastian.reichel@...labora.co.uk>
> Tested-by: Chris Healy <cphealy@...il.com>
> Reviewed-by: Guenter Roeck <linux@...ck-us.net>
> Reviewed-by: Andy Shevchenko <andy.shevchenko@...il.com>
> Signed-off-by: Andrey Smirnov <andrew.smirnov@...il.com>
> ---
>  drivers/mfd/Kconfig         |   9 +
>  drivers/mfd/Makefile        |   2 +
>  drivers/mfd/rave-sp.c       | 660 ++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/rave-sp.h |  56 ++++
>  4 files changed, 727 insertions(+)
>  create mode 100644 drivers/mfd/rave-sp.c
>  create mode 100644 include/linux/mfd/rave-sp.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 1d20a800e967..8faeebbfcc7f 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1860,4 +1860,13 @@ config MFD_VEXPRESS_SYSREG
>           on the ARM Ltd. Versatile Express board.
>
>  endmenu
> +
> +config RAVE_SP_CORE
> +       tristate "RAVE SP MCU core driver"
> +       depends on SERIAL_DEV_BUS
> +       select CRC_CCITT
> +       help
> +         Select this to get support for the Supervisory Processor
> +         device found on several devices in RAVE line of hardware.
> +

Just noticed that I accidentally placed this block after "endmenu".
Will fix in v14.

>  endif
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index d9474ade32e6..61abc297b97c 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -230,3 +230,5 @@ obj-$(CONFIG_MFD_STM32_LPTIMER)     += stm32-lptimer.o
>  obj-$(CONFIG_MFD_STM32_TIMERS)         += stm32-timers.o
>  obj-$(CONFIG_MFD_MXS_LRADC)     += mxs-lradc.o
>  obj-$(CONFIG_MFD_SC27XX_PMIC)  += sprd-sc27xx-spi.o
> +obj-$(CONFIG_RAVE_SP_CORE)     += rave-sp.o
> +
> diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c
> new file mode 100644
> index 000000000000..c51ba2a864d5
> --- /dev/null
> +++ b/drivers/mfd/rave-sp.c
> @@ -0,0 +1,660 @@
> +/*
> + * 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/>.
> + */
> +
> +#include <linux/atomic.h>
> +#include <linux/crc-ccitt.h>
> +#include <linux/delay.h>
> +#include <linux/export.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/rave-sp.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/sched.h>
> +#include <linux/serdev.h>
> +#include <asm/unaligned.h>
> +
> +/*
> + * 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 RAVE_SP_STX                    0x02
> +#define RAVE_SP_ETX                    0x03
> +#define RAVE_SP_DLE                    0x10
> +
> +#define RAVE_SP_MAX_DATA_SIZE          64
> +#define RAVE_SP_CHECKSUM_SIZE          2  /* Worst case scenario on RDU2 */
> +/*
> + * We don't store STX, ETX and unescaped bytes, so Rx is only
> + * DATA + CSUM
> + */
> +#define RAVE_SP_RX_BUFFER_SIZE                         \
> +       (RAVE_SP_MAX_DATA_SIZE + RAVE_SP_CHECKSUM_SIZE)
> +
> +#define RAVE_SP_STX_ETX_SIZE           2
> +/*
> + * For Tx we have to have space for everything, STX, EXT and
> + * potentially stuffed DATA + CSUM data + csum
> + */
> +#define RAVE_SP_TX_BUFFER_SIZE                         \
> +       (RAVE_SP_STX_ETX_SIZE + 2 * RAVE_SP_RX_BUFFER_SIZE)
> +
> +#define RAVE_SP_BOOT_SOURCE_GET                0
> +#define RAVE_SP_BOOT_SOURCE_SET                1
> +
> +#define RAVE_SP_RDU2_BOARD_TYPE_RMB    0
> +#define RAVE_SP_RDU2_BOARD_TYPE_DEB    1
> +
> +#define RAVE_SP_BOOT_SOURCE_SD         0
> +#define RAVE_SP_BOOT_SOURCE_EMMC       1
> +#define RAVE_SP_BOOT_SOURCE_NOR                2
> +
> +/**
> + * enum rave_sp_deframer_state - Possible state for de-framer
> + *
> + * @RAVE_SP_EXPECT_SOF:                 Scanning input for start-of-frame marker
> + * @RAVE_SP_EXPECT_DATA:        Got start of frame marker, collecting frame
> + * @RAVE_SP_EXPECT_ESCAPED_DATA: Got escape character, collecting escaped byte
> + */
> +enum rave_sp_deframer_state {
> +       RAVE_SP_EXPECT_SOF,
> +       RAVE_SP_EXPECT_DATA,
> +       RAVE_SP_EXPECT_ESCAPED_DATA,
> +};
> +
> +/**
> + * struct rave_sp_deframer - Device protocol deframer
> + *
> + * @state:  Current state of the deframer
> + * @data:   Buffer used to collect deframed data
> + * @length: Number of bytes de-framed so far
> + */
> +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 - Reply as per RAVE device protocol
> + *
> + * @length:    Expected reply length
> + * @data:      Buffer to store reply payload in
> + * @code:      Expected reply code
> + * @ackid:     Expected reply ACK ID
> + * @completion: Successful reply reception completion
> + */
> +struct rave_sp_reply {
> +       size_t length;
> +       void  *data;
> +       u8     code;
> +       u8     ackid;
> +       struct completion received;
> +};
> +
> +/**
> + * struct rave_sp_checksum - Variant specific checksum implementation details
> + *
> + * @length:    Caculated checksum length
> + * @subroutine:        Utilized checksum algorithm implementation
> + */
> +struct rave_sp_checksum {
> +       size_t length;
> +       void (*subroutine)(const u8 *, size_t, u8 *);
> +};
> +
> +/**
> + * struct rave_sp_variant_cmds - Variant specific command routines
> + *
> + * @translate: Generic to variant specific command mapping routine
> + *
> + */
> +struct rave_sp_variant_cmds {
> +       int (*translate)(enum rave_sp_command);
> +};
> +
> +/**
> + * struct rave_sp_variant - RAVE supervisory processor core variant
> + *
> + * @checksum:  Variant specific checksum implementation
> + * @cmd:       Variant specific command pointer table
> + *
> + */
> +struct rave_sp_variant {
> +       const struct rave_sp_checksum *checksum;
> +       struct rave_sp_variant_cmds cmd;
> +};
> +
> +/**
> + * struct rave_sp - RAVE supervisory processor core
> + *
> + * @serdev:                    Pointer to underlying serdev
> + * @deframer:                  Stored state of the protocol deframer
> + * @ackid:                     ACK ID used in last reply sent to the device
> + * @bus_lock:                  Lock to serialize access to the device
> + * @reply_lock:                        Lock protecting @reply
> + * @reply:                     Pointer to memory to store reply payload
> + *
> + * @variant:                   Device variant specific information
> + * @event_notifier_list:       Input event notification chain
> + *
> + */
> +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 struct rave_sp_variant *variant;
> +       struct blocking_notifier_head event_notifier_list;
> +};
> +
> +static bool rave_sp_id_is_event(u8 code)
> +{
> +       return (code & 0xF0) == RAVE_SP_EVNT_BASE;
> +}
> +
> +static void 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(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 void csum_8b2c(const u8 *buf, size_t size, u8 *crc)
> +{
> +       *crc = *buf++;
> +       size--;
> +
> +       while (size--)
> +               *crc += *buf++;
> +
> +       *crc = 1 + ~(*crc);
> +}
> +
> +static void csum_ccitt(const u8 *buf, size_t size, u8 *crc)
> +{
> +       const u16 calculated = crc_ccitt_false(0xffff, buf, size);
> +
> +       /*
> +        * While the rest of the wire protocol is little-endian,
> +        * CCITT-16 CRC in RDU2 device is sent out in big-endian order.
> +        */
> +       put_unaligned_be16(calculated, crc);
> +}
> +
> +static void *stuff(unsigned char *dest, const unsigned char *src, size_t n)
> +{
> +       while (n--) {
> +               const unsigned char byte = *src++;
> +
> +               switch (byte) {
> +               case RAVE_SP_STX:
> +               case RAVE_SP_ETX:
> +               case RAVE_SP_DLE:
> +                       *dest++ = RAVE_SP_DLE;
> +                       /* FALLTHROUGH */
> +               default:
> +                       *dest++ = byte;
> +               }
> +       }
> +
> +       return dest;
> +}
> +
> +static int rave_sp_write(struct rave_sp *sp, const u8 *data, u8 data_size)
> +{
> +       const size_t checksum_length = sp->variant->checksum->length;
> +       unsigned char frame[RAVE_SP_TX_BUFFER_SIZE];
> +       unsigned char crc[RAVE_SP_CHECKSUM_SIZE];
> +       unsigned char *dest = frame;
> +       size_t length;
> +
> +       if (WARN_ON(checksum_length > sizeof(crc)))
> +               return -ENOMEM;
> +
> +       if (WARN_ON(data_size > sizeof(frame)))
> +               return -ENOMEM;
> +
> +       sp->variant->checksum->subroutine(data, data_size, crc);
> +
> +       *dest++ = RAVE_SP_STX;
> +       dest = stuff(dest, data, data_size);
> +       dest = stuff(dest, crc, checksum_length);
> +       *dest++ = RAVE_SP_ETX;
> +
> +       length = dest - frame;
> +
> +       print_hex_dump(KERN_DEBUG, "rave-sp tx: ", DUMP_PREFIX_NONE,
> +                      16, 1, frame, length, false);
> +
> +       return serdev_device_write(sp->serdev, frame, length, HZ);
> +}
> +
> +static u8 rave_sp_reply_code(u8 command)
> +{
> +       /*
> +        * There isn't a single rule that describes command code ->
> +        * ACK code transformation, but, going through various
> +        * versions of ICDs, there appear to be three distinct groups
> +        * that can be described by simple transformation.
> +        */
> +       switch (command) {
> +       case 0xA0 ... 0xBE:
> +               /*
> +                * Commands implemented by firmware found in RDU1 and
> +                * older devices all seem to obey the following rule
> +                */
> +               return command + 0x20;
> +       case 0xE0 ... 0xEF:
> +               /*
> +                * Events emitted by all versions of the firmare use
> +                * least significant bit to get an ACK code
> +                */
> +               return command | 0x01;
> +       default:
> +               /*
> +                * Commands implemented by firmware found in RDU2 are
> +                * similar to "old" commands, but they use slightly
> +                * different offset
> +                */
> +               return command + 0x40;
> +       }
> +}
> +
> +int rave_sp_exec(struct rave_sp *sp,
> +                void *__data,  size_t data_size,
> +                void *reply_data, size_t reply_data_size)
> +{
> +       struct rave_sp_reply reply = {
> +               .data     = reply_data,
> +               .length   = reply_data_size,
> +               .received = COMPLETION_INITIALIZER_ONSTACK(reply.received),
> +       };
> +       unsigned char *data = __data;
> +       int command, ret = 0;
> +       u8 ackid;
> +
> +       command = sp->variant->cmd.translate(data[0]);
> +       if (command < 0)
> +               return command;
> +
> +       ackid       = atomic_inc_return(&sp->ackid);
> +       reply.ackid = ackid;
> +       reply.code  = rave_sp_reply_code((u8)command),
> +
> +       mutex_lock(&sp->bus_lock);
> +
> +       mutex_lock(&sp->reply_lock);
> +       sp->reply = &reply;
> +       mutex_unlock(&sp->reply_lock);
> +
> +       data[0] = command;
> +       data[1] = ackid;
> +
> +       rave_sp_write(sp, data, data_size);
> +
> +       if (!wait_for_completion_timeout(&reply.received, HZ)) {
> +               dev_err(&sp->serdev->dev, "Command timeout\n");
> +               ret = -ETIMEDOUT;
> +
> +               mutex_lock(&sp->reply_lock);
> +               sp->reply = NULL;
> +               mutex_unlock(&sp->reply_lock);
> +       }
> +
> +       mutex_unlock(&sp->bus_lock);
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(rave_sp_exec);
> +
> +static void rave_sp_receive_event(struct rave_sp *sp,
> +                                 const unsigned char *data, size_t length)
> +{
> +       u8 cmd[] = {
> +               [0] = rave_sp_reply_code(data[0]),
> +               [1] = data[1],
> +       };
> +
> +       rave_sp_write(sp, cmd, sizeof(cmd));
> +
> +       blocking_notifier_call_chain(&sp->event_notifier_list,
> +                                    rave_sp_action_pack(data[0], data[2]),
> +                                    NULL);
> +}
> +
> +static void rave_sp_receive_reply(struct rave_sp *sp,
> +                                 const unsigned char *data, size_t length)
> +{
> +       struct device *dev = &sp->serdev->dev;
> +       struct rave_sp_reply *reply;
> +       const  size_t payload_length = length - 2;
> +
> +       mutex_lock(&sp->reply_lock);
> +       reply = sp->reply;
> +
> +       if (reply) {
> +               if (reply->code == data[0] && reply->ackid == data[1] &&
> +                   payload_length >= reply->length) {
> +                       /*
> +                        * We are relying on memcpy(dst, src, 0) to be a no-op
> +                        * when handling commands that have a no-payload reply
> +                        */
> +                       memcpy(reply->data, &data[2], reply->length);
> +                       complete(&reply->received);
> +                       sp->reply = NULL;
> +               } else {
> +                       dev_err(dev, "Ignoring incorrect reply\n");
> +                       dev_dbg(dev, "Code:   expected = 0x%08x received = 0x%08x\n",
> +                               reply->code, data[0]);
> +                       dev_dbg(dev, "ACK ID: expected = 0x%08x received = 0x%08x\n",
> +                               reply->ackid, data[1]);
> +                       dev_dbg(dev, "Length: expected = %zu received = %zu\n",
> +                               reply->length, payload_length);
> +               }
> +       }
> +
> +       mutex_unlock(&sp->reply_lock);
> +}
> +
> +static void rave_sp_receive_frame(struct rave_sp *sp,
> +                                 const unsigned char *data,
> +                                 size_t length)
> +{
> +       const size_t checksum_length = sp->variant->checksum->length;
> +       const size_t payload_length  = length - checksum_length;
> +       const u8 *crc_reported       = &data[payload_length];
> +       struct device *dev           = &sp->serdev->dev;
> +       u8 crc_calculated[checksum_length];
> +
> +       print_hex_dump(KERN_DEBUG, "rave-sp rx: ", DUMP_PREFIX_NONE,
> +                      16, 1, data, length, false);
> +
> +       if (unlikely(length <= checksum_length)) {
> +               dev_warn(dev, "Dropping short frame\n");
> +               return;
> +       }
> +
> +       sp->variant->checksum->subroutine(data, payload_length,
> +                                         crc_calculated);
> +
> +       if (memcmp(crc_calculated, crc_reported, checksum_length)) {
> +               dev_warn(dev, "Dropping bad frame\n");
> +               return;
> +       }
> +
> +       if (rave_sp_id_is_event(data[0]))
> +               rave_sp_receive_event(sp, data, length);
> +       else
> +               rave_sp_receive_reply(sp, data, length);
> +}
> +
> +static int rave_sp_receive_buf(struct serdev_device *serdev,
> +                              const unsigned char *buf, size_t size)
> +{
> +       struct device *dev  = &serdev->dev;
> +       struct rave_sp *sp = dev_get_drvdata(dev);
> +       struct rave_sp_deframer *deframer = &sp->deframer;
> +       const unsigned char *src = buf;
> +       const unsigned char *end = buf + size;
> +       bool reset_framer = false;
> +
> +       while (src < end) {
> +               const unsigned char byte = *src++;
> +
> +               switch (deframer->state) {
> +               case RAVE_SP_EXPECT_SOF:
> +                       if (byte == RAVE_SP_STX)
> +                               deframer->state = RAVE_SP_EXPECT_DATA;
> +                       continue;
> +
> +               case RAVE_SP_EXPECT_DATA:
> +                       switch (byte) {
> +                       case RAVE_SP_ETX:
> +                               rave_sp_receive_frame(sp,
> +                                                     deframer->data,
> +                                                     deframer->length);
> +                               reset_framer = true;
> +                               break;
> +                       case RAVE_SP_STX:
> +                               dev_warn(dev, "Bad frame: STX before ETX\n");
> +                               reset_framer = true;
> +                               break;
> +                       case RAVE_SP_DLE:
> +                               deframer->state = RAVE_SP_EXPECT_ESCAPED_DATA;
> +                               continue;
> +                       }
> +                       /* FALLTHROUGH */
> +
> +               case RAVE_SP_EXPECT_ESCAPED_DATA:
> +                       deframer->data[deframer->length++] = byte;
> +
> +                       if (deframer->length == sizeof(deframer->data)) {
> +                               dev_warn(dev, "Bad frame: Too long\n");
> +                               reset_framer = true;
> +                               break;
> +                       }
> +
> +                       deframer->state = RAVE_SP_EXPECT_DATA;
> +                       break;
> +               }
> +       }
> +
> +       if (reset_framer) {
> +               deframer->state  = RAVE_SP_EXPECT_SOF;
> +               deframer->length = 0;
> +       }
> +
> +       return src - buf;
> +}
> +
> +static int rave_sp_rdu1_cmd_translate(enum rave_sp_command command)
> +{
> +       if (command >= RAVE_SP_CMD_STATUS &&
> +           command <= RAVE_SP_CMD_CONTROL_EVENTS)
> +               return command;
> +
> +       return -EINVAL;
> +}
> +
> +static int rave_sp_rdu2_cmd_translate(enum rave_sp_command command)
> +{
> +       if (command >= RAVE_SP_CMD_GET_FIRMWARE_VERSION &&
> +           command <= RAVE_SP_CMD_GET_GPIO_STATE)
> +               return command;
> +
> +       if (command == RAVE_SP_CMD_REQ_COPPER_REV) {
> +               /*
> +                * As per RDU2 ICD 3.4.47 CMD_GET_COPPER_REV code is
> +                * different from that for RDU1 and it is set to 0x28.
> +                */
> +               return 0x28;
> +       }
> +
> +       return rave_sp_rdu1_cmd_translate(command);
> +}
> +
> +static int rave_sp_default_cmd_translate(enum rave_sp_command command)
> +{
> +       /*
> +        * All of the following command codes were taken from "Table :
> +        * Communications Protocol Message Types" in section 3.3
> +        * "MESSAGE TYPES" of Rave PIC24 ICD.
> +        */
> +       switch (command) {
> +       case RAVE_SP_CMD_GET_FIRMWARE_VERSION:
> +               return 0x11;
> +       case RAVE_SP_CMD_GET_BOOTLOADER_VERSION:
> +               return 0x12;
> +       case RAVE_SP_CMD_BOOT_SOURCE:
> +               return 0x14;
> +       case RAVE_SP_CMD_SW_WDT:
> +               return 0x1C;
> +       case RAVE_SP_CMD_RESET:
> +               return 0x1E;
> +       case RAVE_SP_CMD_RESET_REASON:
> +               return 0x1F;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static const struct rave_sp_checksum rave_sp_checksum_8b2c = {
> +       .length     = 1,
> +       .subroutine = csum_8b2c,
> +};
> +
> +static const struct rave_sp_checksum rave_sp_checksum_ccitt = {
> +       .length     = 2,
> +       .subroutine = csum_ccitt,
> +};
> +
> +static const struct rave_sp_variant rave_sp_legacy = {
> +       .checksum = &rave_sp_checksum_8b2c,
> +       .cmd = {
> +               .translate = rave_sp_default_cmd_translate,
> +       },
> +};
> +
> +static const struct rave_sp_variant rave_sp_rdu1 = {
> +       .checksum = &rave_sp_checksum_8b2c,
> +       .cmd = {
> +               .translate = rave_sp_rdu1_cmd_translate,
> +       },
> +};
> +
> +static const struct rave_sp_variant rave_sp_rdu2 = {
> +       .checksum = &rave_sp_checksum_ccitt,
> +       .cmd = {
> +               .translate = rave_sp_rdu2_cmd_translate,
> +       },
> +};
> +
> +static const struct of_device_id rave_sp_dt_ids[] = {
> +       { .compatible = COMPATIBLE_RAVE_SP_NIU,  .data = &rave_sp_legacy },
> +       { .compatible = COMPATIBLE_RAVE_SP_MEZZ, .data = &rave_sp_legacy },
> +       { .compatible = COMPATIBLE_RAVE_SP_ESB,  .data = &rave_sp_legacy },
> +       { .compatible = COMPATIBLE_RAVE_SP_RDU1, .data = &rave_sp_rdu1   },
> +       { .compatible = COMPATIBLE_RAVE_SP_RDU2, .data = &rave_sp_rdu2   },
> +       { /* sentinel */ }
> +};
> +
> +static const struct serdev_device_ops rave_sp_serdev_device_ops = {
> +       .receive_buf  = rave_sp_receive_buf,
> +       .write_wakeup = serdev_device_write_wakeup,
> +};
> +
> +static int rave_sp_probe(struct serdev_device *serdev)
> +{
> +       struct device *dev = &serdev->dev;
> +       struct rave_sp *sp;
> +       u32 baud;
> +       int ret;
> +
> +       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;
> +       }
> +
> +       sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);
> +       if (!sp)
> +               return -ENOMEM;
> +
> +       sp->serdev = serdev;
> +       dev_set_drvdata(dev, sp);
> +
> +       sp->variant = of_device_get_match_data(dev);
> +       if (!sp->variant)
> +               return -ENODEV;
> +
> +       mutex_init(&sp->bus_lock);
> +       mutex_init(&sp->reply_lock);
> +       BLOCKING_INIT_NOTIFIER_HEAD(&sp->event_notifier_list);
> +
> +       serdev_device_set_client_ops(serdev, &rave_sp_serdev_device_ops);
> +       ret = devm_serdev_device_open(dev, serdev);
> +       if (ret)
> +               return ret;
> +
> +       serdev_device_set_baudrate(serdev, baud);
> +
> +       return devm_of_platform_populate(dev);
> +}
> +
> +MODULE_DEVICE_TABLE(of, rave_sp_dt_ids);
> +
> +static struct serdev_device_driver rave_sp_drv = {
> +       .probe                  = rave_sp_probe,
> +       .driver = {
> +               .name           = "rave-sp",
> +               .of_match_table = rave_sp_dt_ids,
> +       },
> +};
> +module_serdev_device_driver(rave_sp_drv);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@...entembedded.com>");
> +MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@...entembedded.com>");
> +MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@...il.com>");
> +MODULE_DESCRIPTION("RAVE SP core driver");
> diff --git a/include/linux/mfd/rave-sp.h b/include/linux/mfd/rave-sp.h
> new file mode 100644
> index 000000000000..62cb9b18ad91
> --- /dev/null
> +++ b/include/linux/mfd/rave-sp.h
> @@ -0,0 +1,56 @@
> +#ifndef _LINUX_RAVE_SP_H_
> +#define _LINUX_RAVE_SP_H_
> +
> +#include <linux/notifier.h>
> +
> +enum rave_sp_command {
> +       RAVE_SP_CMD_GET_FIRMWARE_VERSION        = 0x20,
> +       RAVE_SP_CMD_GET_BOOTLOADER_VERSION      = 0x21,
> +       RAVE_SP_CMD_BOOT_SOURCE                 = 0x26,
> +       RAVE_SP_CMD_GET_BOARD_COPPER_REV        = 0x2B,
> +       RAVE_SP_CMD_GET_GPIO_STATE              = 0x2F,
> +
> +       RAVE_SP_CMD_STATUS                      = 0xA0,
> +       RAVE_SP_CMD_SW_WDT                      = 0xA1,
> +       RAVE_SP_CMD_PET_WDT                     = 0xA2,
> +       RAVE_SP_CMD_RESET                       = 0xA7,
> +       RAVE_SP_CMD_RESET_REASON                = 0xA8,
> +
> +       RAVE_SP_CMD_REQ_COPPER_REV              = 0xB6,
> +       RAVE_SP_CMD_GET_I2C_DEVICE_STATUS       = 0xBA,
> +       RAVE_SP_CMD_GET_SP_SILICON_REV          = 0xB9,
> +       RAVE_SP_CMD_CONTROL_EVENTS              = 0xBB,
> +
> +       RAVE_SP_EVNT_BASE                       = 0xE0,
> +};
> +
> +struct rave_sp;
> +
> +static inline unsigned long rave_sp_action_pack(u8 event, u8 value)
> +{
> +       return ((unsigned long)value << 8) | event;
> +}
> +
> +static inline u8 rave_sp_action_unpack_event(unsigned long action)
> +{
> +       return action;
> +}
> +
> +static inline u8 rave_sp_action_unpack_value(unsigned long action)
> +{
> +       return action >> 8;
> +}
> +
> +int rave_sp_exec(struct rave_sp *sp,
> +                void *__data,  size_t data_size,
> +                void *reply_data, size_t reply_data_size);
> +int devm_rave_sp_register_event_notifier(struct device *dev,
> +                                        struct notifier_block *nb);
> +
> +#define COMPATIBLE_RAVE_SP_NIU         "zii,rave-sp-niu"
> +#define COMPATIBLE_RAVE_SP_MEZZ                "zii,rave-sp-mezz"
> +#define COMPATIBLE_RAVE_SP_ESB         "zii,rave-sp-esb"
> +#define COMPATIBLE_RAVE_SP_RDU1                "zii,rave-sp-rdu1"
> +#define COMPATIBLE_RAVE_SP_RDU2                "zii,rave-sp-rdu2"
> +
> +#endif /* _LINUX_RAVE_SP_H_ */
> --
> 2.14.3
>

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ