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] [thread-next>] [day] [month] [year] [list]
Date:   Sat, 21 Oct 2017 10:17:31 -0700
From:   Guenter Roeck <linux@...ck-us.net>
To:     Andrey Smirnov <andrew.smirnov@...il.com>,
        linux-kernel@...r.kernel.org
Cc:     cphealy@...il.com, Lucas Stach <l.stach@...gutronix.de>,
        Nikita Yushchenko <nikita.yoush@...entembedded.com>,
        Lee Jones <lee.jones@...aro.org>,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        Pavel Machek <pavel@....cz>,
        Andy Shevchenko <andy.shevchenko@...il.com>,
        Rob Herring <robh@...nel.org>, Johan Hovold <johan@...nel.org>
Subject: Re: [PATCH v8 3/5] platform: Add driver for RAVE Supervisory
 Processor

On 10/18/2017 10:01 AM, Andrey Smirnov wrote:
> Add a driver for RAVE Supervisory Processor, an MCU implementing
> varoius bits of housekeeping functionality (watchdoging, backlight

various

> 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>
> Tested-by: Chris Healy <cphealy@...il.com>
> Reviewed-by: Andy Shevchenko <andy.shevchenko@...il.com>
> Signed-off-by: Andrey Smirnov <andrew.smirnov@...il.com>

Reviewed-by: Guenter Roeck <linux@...ck-us.net>

[question below]

> ---
>   drivers/platform/Kconfig        |   2 +
>   drivers/platform/Makefile       |   1 +
>   drivers/platform/rave/Kconfig   |  25 ++
>   drivers/platform/rave/Makefile  |   1 +
>   drivers/platform/rave/rave-sp.c | 670 ++++++++++++++++++++++++++++++++++++++++
>   include/linux/rave-sp.h         |  54 ++++
>   6 files changed, 753 insertions(+)
>   create mode 100644 drivers/platform/rave/Kconfig
>   create mode 100644 drivers/platform/rave/Makefile
>   create mode 100644 drivers/platform/rave/rave-sp.c
>   create mode 100644 include/linux/rave-sp.h
> 
> diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
> index c11db8bceea1..e6db685bb895 100644
> --- a/drivers/platform/Kconfig
> +++ b/drivers/platform/Kconfig
> @@ -8,3 +8,5 @@ endif
>   source "drivers/platform/goldfish/Kconfig"
>   
>   source "drivers/platform/chrome/Kconfig"
> +
> +source "drivers/platform/rave/Kconfig"
> diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
> index ca2692510733..17bdec5ece0c 100644
> --- a/drivers/platform/Makefile
> +++ b/drivers/platform/Makefile
> @@ -7,3 +7,4 @@ obj-$(CONFIG_MIPS)		+= mips/
>   obj-$(CONFIG_OLPC)		+= olpc/
>   obj-$(CONFIG_GOLDFISH)		+= goldfish/
>   obj-$(CONFIG_CHROME_PLATFORMS)	+= chrome/
> +obj-y += rave/
> diff --git a/drivers/platform/rave/Kconfig b/drivers/platform/rave/Kconfig
> new file mode 100644
> index 000000000000..c0964a531991
> --- /dev/null
> +++ b/drivers/platform/rave/Kconfig
> @@ -0,0 +1,25 @@
> +#
> +# Platform support for Zodiac RAVE hardware
> +#
> +
> +menuconfig RAVE_PLATFORMS
> +	bool "Platform support for Zodiac RAVE hardware"
> +	help
> +	  Say Y here to get to see options for platform support for
> +	  various devices present in RAVE hardware. This option alone
> +	  does not add any kernel code.
> +
> +	  If you say N, all options in this submenu will be skipped
> +	  and disabled.
> +
> +if RAVE_PLATFORMS
> +
> +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.
> +

Are there going to be more entries in this menu ?

> +endif
> diff --git a/drivers/platform/rave/Makefile b/drivers/platform/rave/Makefile
> new file mode 100644
> index 000000000000..e4c21ab2d2f5
> --- /dev/null
> +++ b/drivers/platform/rave/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o
> diff --git a/drivers/platform/rave/rave-sp.c b/drivers/platform/rave/rave-sp.c
> new file mode 100644
> index 000000000000..2ca7c8cba01f
> --- /dev/null
> +++ b/drivers/platform/rave/rave-sp.c
> @@ -0,0 +1,670 @@
> +/*
> + * 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/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/rave-sp.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 - 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;
> +};
> +
> +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 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(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;
> +			}
> +
> +		case RAVE_SP_EXPECT_ESCAPED_DATA: /* FALLTHROUGH */
> +			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 v2");
> +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/rave-sp.h b/include/linux/rave-sp.h
> new file mode 100644
> index 000000000000..14f9b3177b03
> --- /dev/null
> +++ b/include/linux/rave-sp.h
> @@ -0,0 +1,54 @@
> +#ifndef _LINUX_RAVE_SP_H_
> +#define _LINUX_RAVE_SP_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_ */
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ