From patchwork Wed Aug 16 13:22:26 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ulrich Hecht X-Patchwork-Id: 9903793 X-Patchwork-Delegate: geert@linux-m68k.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id B6A3C60231 for ; Wed, 16 Aug 2017 13:22:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A83F3289E7 for ; Wed, 16 Aug 2017 13:22:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9CEB7289EE; Wed, 16 Aug 2017 13:22:50 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D9AD9289E7 for ; Wed, 16 Aug 2017 13:22:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751782AbdHPNWt (ORCPT ); Wed, 16 Aug 2017 09:22:49 -0400 Received: from mail-wr0-f193.google.com ([209.85.128.193]:38092 "EHLO mail-wr0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751679AbdHPNWq (ORCPT ); Wed, 16 Aug 2017 09:22:46 -0400 Received: by mail-wr0-f193.google.com with SMTP id g32so3046363wrd.5; Wed, 16 Aug 2017 06:22:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=u6YzR7qzyg0Nu7e8Nxd0MBak4HPTZFnxjecwaNN3GQo=; b=VLeuSONz3rr+GZZV26GNDc1NZlXkWiDhiKmqLm62mgIeToeY9nDM/GSaInuqgZoBVw 12SJ2ZjAoQ01WZoSjUdDHB4bXc95W5jK5x7MDfSEq/uzF+dpruq206OK8fBoUN3GApy4 eiayZNKPns1Wc9n+MgEvXHBUIw+y+3f/HSZmwrCK+fZLY1z9qrOuynjh53C1fSKvBQ3j NimR0tki88nAikq04qcB7IOxhgRKwSqtDwy1GKNBsO0SVsCgGPUX87VH8zRpbjtGrshr F4crnzmnrG10WFQzAf8F+B2CcTO8QpzBxYvfJLZC6mXDR9+a/O8DIPxalbxWv6NiG4Vg aG3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=u6YzR7qzyg0Nu7e8Nxd0MBak4HPTZFnxjecwaNN3GQo=; b=RswDtoxP+0jj9VekkwZCtAhSqCKVNR+2C3aEVRWsQVsg12UkY0uJQ4kRt/cxLHYol9 f3xW8ajDznb14BJCZ/BVxO3vYcCff/rjMk+vJlFMBc6OCU9R6qlGKDUJObmYCVmrNzmw ANFbVddtFEPGCB6OwuMmxXMXRxnao2GAKBlHow0NXuXaxAngB8x+z00J03pUsMFzDbB6 erSGI5iEZQKPZHZCHZ1liq0p4aucxNBGmoOBygYnQ2LiiWVtw4NWRGJnkhZCsK+tNz+k r11bbaMTb4GE77DZv+2dPJO+q1TtuYXAa1jEX9Tvz/1sslBum5N7kHhXt7eZ5M1W8pCm 25Yw== X-Gm-Message-State: AHYfb5hE7/gRrCeM9bqwsuocGY3LTd23m3LWP3Kk5uiGsy0LDzZopYrR 0AyeTtiwdjOvEeWOAYI= X-Received: by 10.28.105.87 with SMTP id e84mr131153wmc.90.1502889764798; Wed, 16 Aug 2017 06:22:44 -0700 (PDT) Received: from groucho.site (ipbcc2817c.dynamic.kabel-deutschland.de. [188.194.129.124]) by smtp.gmail.com with ESMTPSA id m130sm1054894wma.27.2017.08.16.06.22.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 16 Aug 2017 06:22:44 -0700 (PDT) From: Ulrich Hecht To: linux-serial@vger.kernel.org Cc: linux-renesas-soc@vger.kernel.org, magnus.damm@gmail.com, laurent.pinchart@ideasonboard.com, wsa@the-dreams.de, robh@kernel.org, peda@axentia.se, geert@linux-m68k.org, linux-i2c@vger.kernel.org, Ulrich Hecht Subject: [PATCH 4/6] max9260: add driver for i2c over GMSL passthrough Date: Wed, 16 Aug 2017 15:22:26 +0200 Message-Id: <1502889748-31499-5-git-send-email-ulrich.hecht+renesas@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1502889748-31499-1-git-send-email-ulrich.hecht+renesas@gmail.com> References: <1502889748-31499-1-git-send-email-ulrich.hecht+renesas@gmail.com> Sender: linux-renesas-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-renesas-soc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This driver implements tunnelling of i2c requests over GMSL via a MAX9260 deserializer. It provides an i2c adapter that can be used to reach devices on the far side of the link. Signed-off-by: Ulrich Hecht --- drivers/media/i2c/Kconfig | 6 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/max9260.c | 300 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+) create mode 100644 drivers/media/i2c/max9260.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index fa2e7d8..b85ae78 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -424,6 +424,12 @@ config VIDEO_VPX3220 To compile this driver as a module, choose M here: the module will be called vpx3220. +config VIDEO_MAX9260 + tristate "Maxim MAX9260 GMSL deserializer support" + depends on I2C + ---help--- + This driver supports the Maxim MAX9260 GMSL deserializer. + comment "Video and audio decoders" config VIDEO_SAA717X diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 30e856c..3b6f6f2 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o +obj-$(CONFIG_VIDEO_MAX9260) += max9260.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o diff --git a/drivers/media/i2c/max9260.c b/drivers/media/i2c/max9260.c new file mode 100644 index 0000000..9c890ab --- /dev/null +++ b/drivers/media/i2c/max9260.c @@ -0,0 +1,300 @@ +/* + * Maxim MAX9260 GMSL Deserializer Driver + * + * Copyright (C) 2017 Ulrich Hecht + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SYNC 0x79 +#define ACK 0xc3 + +#define REG_ID 0x1e + +#define ID_MAX9260 0x02 + +enum max9260_rx_state { + RX_FINISHED, + RX_FRAME_ERROR, + RX_EXPECT_ACK, + RX_EXPECT_ACK_DATA, + RX_EXPECT_DATA, +}; + +struct max9260_device { + struct serdev_device *serdev; + u8 *rx_buf; + size_t rx_len; + enum max9260_rx_state rx_state; + wait_queue_head_t rx_wq; + struct i2c_adapter adap; +}; + +static void max9260_wait_for_transaction(struct max9260_device *dev) +{ + wait_event_timeout(dev->rx_wq, dev->rx_state <= RX_FRAME_ERROR, + HZ/2); +} + +static void max9260_transact(struct max9260_device *dev, + int expect, + const u8 *request, int len, + u8 *rx_buf, int rx_len) +{ + dev->rx_buf = rx_buf; + dev->rx_len = rx_len; + + serdev_device_mux_select(dev->serdev); + + serdev_device_set_baudrate(dev->serdev, 115200); + serdev_device_set_parity(dev->serdev, SERDEV_PARITY_EVEN); + + dev->rx_state = expect; + serdev_device_write_buf(dev->serdev, request, len); + + max9260_wait_for_transaction(dev); + + serdev_device_mux_deselect(dev->serdev); +} + +static int max9260_read_reg(struct max9260_device *dev, int reg) +{ + u8 request[] = { SYNC, 0x91, reg, 1 }; + u8 rx; + + dev->rx_len = 1; + dev->rx_buf = ℞ + + max9260_transact(dev, RX_EXPECT_ACK_DATA, request, + ARRAY_SIZE(request), + &rx, 1); + + if (dev->rx_state == RX_FINISHED) + return rx; + + return -EIO; +} + +static int max9260_setup(struct max9260_device *dev) +{ + int ret; + + ret = max9260_read_reg(dev, REG_ID); + + if (ret != ID_MAX9260) { + dev_err(&dev->serdev->dev, + "device does not identify as MAX9260\n"); + return -ENODEV; + } + + return 0; +} + +static void max9260_uart_write_wakeup(struct serdev_device *serdev) +{ +} + +static int max9260_uart_receive_buf(struct serdev_device *serdev, + const u8 *data, size_t count) +{ + struct max9260_device *dev = serdev_device_get_drvdata(serdev); + size_t accepted; + + switch (dev->rx_state) { + case RX_FINISHED: + dev_dbg(&dev->serdev->dev, "excess data ignored\n"); + return count; + + case RX_EXPECT_ACK: + case RX_EXPECT_ACK_DATA: + if (data[0] != ACK) { + dev_dbg(&dev->serdev->dev, "frame error"); + dev->rx_state = RX_FRAME_ERROR; + wake_up_interruptible(&dev->rx_wq); + return 1; + } + + if (dev->rx_state == RX_EXPECT_ACK_DATA) { + dev->rx_state = RX_EXPECT_DATA; + } else { + dev->rx_state = RX_FINISHED; + wake_up_interruptible(&dev->rx_wq); + } + return 1; + + case RX_EXPECT_DATA: + accepted = min(dev->rx_len, count); + + memcpy(dev->rx_buf, data, accepted); + + dev->rx_len -= accepted; + dev->rx_buf += accepted; + + if (!dev->rx_len) { + dev->rx_state = RX_FINISHED; + wake_up_interruptible(&dev->rx_wq); + } + + return accepted; + + case RX_FRAME_ERROR: + dev_dbg(&dev->serdev->dev, "%d bytes ignored\n", count); + return count; + + } + return 0; +} + +static const struct serdev_device_ops max9260_serdev_client_ops = { + .receive_buf = max9260_uart_receive_buf, + .write_wakeup = max9260_uart_write_wakeup, +}; + +static u32 max9260_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA; +} + +static s32 max9260_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + u8 request[] = { SYNC, + (addr << 1) + (read_write == I2C_SMBUS_READ), + command, 0, 0 }; + struct max9260_device *dev = i2c_get_adapdata(adap); + + switch (size) { + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + max9260_transact(dev, RX_EXPECT_ACK, request, 4, + NULL, 0); + dev_dbg(&adap->dev, + "smbus byte - addr 0x%02x, wrote 0x%02x.\n", + addr, command); + } else { + /* TBD */ + return -EOPNOTSUPP; + } + break; + + case I2C_SMBUS_BYTE_DATA: + request[3] = 1; + if (read_write == I2C_SMBUS_WRITE) { + request[4] = data->byte; + max9260_transact(dev, RX_EXPECT_ACK, request, 5, + NULL, 0); + dev_dbg(&adap->dev, + "smbus byte data - addr 0x%02x, wrote 0x%02x at 0x%02x.\n", + addr, data->byte, command); + } else { + max9260_transact(dev, RX_EXPECT_ACK_DATA, request, 4, + &data->byte, 1); + dev_dbg(&adap->dev, + "smbus byte data - addr 0x%02x, read 0x%02x at 0x%02x.\n", + addr, data->byte, command); + } + break; + default: + dev_dbg(&adap->dev, + "Unsupported I2C/SMBus command %d\n", size); + return -EOPNOTSUPP; + } + + if (dev->rx_state != RX_FINISHED) { + dev_dbg(&adap->dev, "xfer timed out\n"); + return -EIO; + } + + return 0; +} + +static const struct i2c_algorithm max9260_i2c_algorithm = { + .functionality = max9260_i2c_func, + .smbus_xfer = max9260_smbus_xfer, +}; + +static int max9260_probe(struct serdev_device *serdev) +{ + struct max9260_device *dev; + struct i2c_adapter *adap; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + init_waitqueue_head(&dev->rx_wq); + + dev->serdev = serdev; + serdev_device_open(serdev); + serdev_device_set_drvdata(serdev, dev); + + serdev_device_set_client_ops(serdev, &max9260_serdev_client_ops); + + ret = max9260_setup(dev); + if (ret < 0) + goto err_free; + + adap = &dev->adap; + i2c_set_adapdata(adap, dev); + + adap->owner = THIS_MODULE; + adap->algo = &max9260_i2c_algorithm; + adap->dev.parent = &serdev->dev; + adap->retries = 5; + strlcpy(adap->name, dev_name(&serdev->dev), sizeof(adap->name)); + + ret = i2c_add_adapter(adap); + if (ret < 0) + return ret; + + return 0; + +err_free: + kfree(dev); + return ret; +} + +static void max9260_remove(struct serdev_device *serdev) +{ + struct max9260_device *dev = serdev_device_get_drvdata(serdev); + + serdev_device_close(dev->serdev); + + kfree(dev); +} + +static const struct of_device_id max9260_dt_ids[] = { + { .compatible = "maxim,max9260" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, max9260_dt_ids); + +static struct serdev_device_driver max9260_driver = { + .probe = max9260_probe, + .remove = max9260_remove, + .driver = { + .name = "max9260", + .of_match_table = of_match_ptr(max9260_dt_ids), + }, +}; + +module_serdev_device_driver(max9260_driver); + +MODULE_DESCRIPTION("Maxim MAX9260 GMSL Deserializer Driver"); +MODULE_AUTHOR("Ulrich Hecht"); +MODULE_LICENSE("GPL");