From patchwork Mon Nov 30 00:15:58 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 7719761 X-Patchwork-Delegate: geert@linux-m68k.org Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 22D869F1C0 for ; Mon, 30 Nov 2015 00:22:57 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 153112053B for ; Mon, 30 Nov 2015 00:22:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D76A82053D for ; Mon, 30 Nov 2015 00:22:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751501AbbK3AWk (ORCPT ); Sun, 29 Nov 2015 19:22:40 -0500 Received: from vsp-unauthed02.binero.net ([195.74.38.227]:44211 "EHLO bin-vsp-out-02.atm.binero.net" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751842AbbK3AWi (ORCPT ); Sun, 29 Nov 2015 19:22:38 -0500 X-Halon-ID: 9a5e41e6-96f7-11e5-bdaa-005056917f90 Authorized-sender: niklas.soderlund@ragnatech.se Received: from hydra.berto.se (unknown [89.233.230.99]) by bin-vsp-out-02.atm.binero.net (Halon Mail Gateway) with ESMTPSA; Mon, 30 Nov 2015 01:16:29 +0100 (CET) From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= To: wsa@the-dreams.de, linux-i2c@vger.kernel.org Cc: ian.molton@codethink.co.uk, laurent.pinchart@ideasonboard.com, magnus.damm@gmail.com, linux-sh@vger.kernel.org, =?UTF-8?q?Niklas=20S=C3=B6derlund?= Subject: [PATCH] i2c: emev2: add slave support Date: Mon, 30 Nov 2015 01:15:58 +0100 Message-Id: <1448842558-18654-2-git-send-email-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.6.2 In-Reply-To: <1448842558-18654-1-git-send-email-niklas.soderlund@ragnatech.se> References: <1448842558-18654-1-git-send-email-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add I2C slave provider using the generic slave interface. Signed-off-by: Niklas Söderlund --- drivers/i2c/busses/i2c-emev2.c | 110 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c index 192ef6b..ad87b5a 100644 --- a/drivers/i2c/busses/i2c-emev2.c +++ b/drivers/i2c/busses/i2c-emev2.c @@ -71,6 +71,7 @@ struct em_i2c_device { struct i2c_adapter adap; struct completion msg_done; struct clk *sclk; + struct i2c_client *slave; }; static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg) @@ -226,22 +227,129 @@ static int em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, return num; } +static bool em_i2c_slave_irq(struct em_i2c_device *priv) +{ + u8 status, value; + + if (!priv->slave) + return false; + + status = readb(priv->base + I2C_OFS_IICSE0); + + /* Extension code, do not participate */ + if (status & I2C_BIT_EXC0) { + em_clear_set_bit(priv, 0, I2C_BIT_LREL0, I2C_OFS_IICC0); + return true; + } + + /* Stop detected, we don't know if it's for slave or master */ + if (status & I2C_BIT_SPD0) { + /* Notify slave device */ + i2c_slave_event(priv->slave, I2C_SLAVE_STOP, &value); + /* Pretend we did not handle the interrupt */ + return false; + } + + /* Only handle interrupts addressed to us */ + if (!(status & I2C_BIT_COI0)) + return false; + + /* Enable stop interrupts */ + em_clear_set_bit(priv, 0, I2C_BIT_SPIE0, I2C_OFS_IICC0); + + /* Transmission or Reception */ + if (status & I2C_BIT_TRC0) { + if (status & I2C_BIT_ACKD0) { + /* 9 bit interrupt mode */ + em_clear_set_bit(priv, 0, I2C_BIT_WTIM0, I2C_OFS_IICC0); + + /* Send data */ + enum i2c_slave_event event = status & I2C_BIT_STD0 ? + I2C_SLAVE_READ_REQUESTED : + I2C_SLAVE_READ_PROCESSED; + i2c_slave_event(priv->slave, event, &value); + writeb(value, priv->base + I2C_OFS_IIC0); + } else { + /* NACK, stop transmitting */ + em_clear_set_bit(priv, 0, I2C_BIT_LREL0, I2C_OFS_IICC0); + } + } else { + /* 8 bit interrupt mode */ + em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_ACKE0, + I2C_OFS_IICC0); + em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_WREL0, + I2C_OFS_IICC0); + + if (status & I2C_BIT_STD0) { + i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED, + &value); + } else { + /* Recv data */ + value = readb(priv->base + I2C_OFS_IIC0); + int ret = i2c_slave_event(priv->slave, + I2C_SLAVE_WRITE_RECEIVED, &value); + if (ret < 0) + em_clear_set_bit(priv, I2C_BIT_ACKE0, 0, + I2C_OFS_IICC0); + } + } + + return true; +} + static irqreturn_t em_i2c_irq_handler(int this_irq, void *dev_id) { struct em_i2c_device *priv = dev_id; + if (em_i2c_slave_irq(priv)) + return IRQ_HANDLED; + complete(&priv->msg_done); + return IRQ_HANDLED; } static u32 em_i2c_func(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SLAVE; +} + +static int em_i2c_reg_slave(struct i2c_client *slave) +{ + struct em_i2c_device *priv = i2c_get_adapdata(slave->adapter); + + if (priv->slave) + return -EBUSY; + + if (slave->flags & I2C_CLIENT_TEN) + return -EAFNOSUPPORT; + + priv->slave = slave; + + /* Set slave address */ + writeb(slave->addr << 1, priv->base + I2C_OFS_SVA0); + + return 0; +} + +static int em_i2c_unreg_slave(struct i2c_client *slave) +{ + struct em_i2c_device *priv = i2c_get_adapdata(slave->adapter); + + WARN_ON(!priv->slave); + + writeb(0, priv->base + I2C_OFS_SVA0); + + priv->slave = NULL; + + return 0; } static struct i2c_algorithm em_i2c_algo = { .master_xfer = em_i2c_xfer, .functionality = em_i2c_func, + .reg_slave = em_i2c_reg_slave, + .unreg_slave = em_i2c_unreg_slave, }; static int em_i2c_probe(struct platform_device *pdev)