From patchwork Tue Sep 9 14:54:29 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wolfram Sang X-Patchwork-Id: 4871031 Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 8DFE0C0338 for ; Tue, 9 Sep 2014 14:59:16 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4593A20176 for ; Tue, 9 Sep 2014 14:59:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D400E200F4 for ; Tue, 9 Sep 2014 14:59:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932202AbaIIO64 (ORCPT ); Tue, 9 Sep 2014 10:58:56 -0400 Received: from sauhun.de ([89.238.76.85]:34782 "EHLO pokefinder.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932497AbaIIOy2 (ORCPT ); Tue, 9 Sep 2014 10:54:28 -0400 Received: from p4fe24daa.dip0.t-ipconnect.de ([79.226.77.170]:40152 helo=localhost) by pokefinder.org with esmtpsa (TLS1.2:RSA_AES_128_CBC_SHA1:128) (Exim 4.80) (envelope-from ) id 1XRMoK-0000pF-JZ; Tue, 09 Sep 2014 16:54:24 +0200 From: Wolfram Sang To: linux-i2c@vger.kernel.org Cc: linux-sh@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Jean Delvare , Magnus Damm Subject: [RFC 3/4] i2c: rcar: add slave support Date: Tue, 9 Sep 2014 16:54:29 +0200 Message-Id: <1410274470-12712-4-git-send-email-wsa@the-dreams.de> X-Mailer: git-send-email 2.0.0 In-Reply-To: <1410274470-12712-1-git-send-email-wsa@the-dreams.de> References: <1410274470-12712-1-git-send-email-wsa@the-dreams.de> Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org X-Spam-Status: No, score=-9.4 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable 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 From: Wolfram Sang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 122 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 113 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 1cc146cfc1f3..fe875d994441 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -48,6 +48,12 @@ #define ICMAR 0x20 /* master address */ #define ICRXTX 0x24 /* data port */ +/* ICSCR */ +#define SDBS (1 << 3) /* slave data buffer select */ +#define SIE (1 << 2) /* slave interface enable */ +#define GCAE (1 << 1) /* general call address enable */ +#define FNA (1 << 0) /* forced non acknowledgement */ + /* ICMCR */ #define MDBS (1 << 7) /* non-fifo mode switch */ #define FSCL (1 << 6) /* override SCL pin */ @@ -58,6 +64,15 @@ #define FSB (1 << 1) /* force stop bit */ #define ESG (1 << 0) /* en startbit gen */ +/* ICSSR (also for ICSIER) */ +#define GCAR (1 << 6) /* general call received */ +#define STM (1 << 5) /* slave transmit mode */ +#define SSR (1 << 4) /* stop received */ +#define SDE (1 << 3) /* slave data empty */ +#define SDT (1 << 2) /* slave data transmitted */ +#define SDR (1 << 1) /* slave data received */ +#define SAR (1 << 0) /* slave addr received */ + /* ICMSR (also for ICMIE) */ #define MNR (1 << 6) /* nack received */ #define MAL (1 << 5) /* arbitration lost */ @@ -103,6 +118,7 @@ struct rcar_i2c_priv { u32 icccr; u32 flags; enum rcar_i2c_type devtype; + struct i2c_client *slave; }; #define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent) @@ -126,15 +142,6 @@ static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg) static void rcar_i2c_init(struct rcar_i2c_priv *priv) { - /* - * reset slave mode. - * slave mode is not used on this driver - */ - rcar_i2c_write(priv, ICSIER, 0); - rcar_i2c_write(priv, ICSAR, 0); - rcar_i2c_write(priv, ICSCR, 0); - rcar_i2c_write(priv, ICSSR, 0); - /* reset master mode */ rcar_i2c_write(priv, ICMIER, 0); rcar_i2c_write(priv, ICMCR, 0); @@ -362,6 +369,63 @@ static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr) return 0; } +static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv) +{ + u32 ssr_raw, ssr_filtered; + u8 value; + + ssr_raw = rcar_i2c_read(priv, ICSSR) & 0xff; + ssr_filtered = ssr_raw & rcar_i2c_read(priv, ICSIER); + + if (!ssr_filtered) + return false; + + /* address detected */ + if (ssr_filtered & SAR) { + /* read or write request */ + if (ssr_raw & STM) { + i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value); + rcar_i2c_write(priv, ICRXTX, value); + rcar_i2c_write(priv, ICSIER, SDE | SSR | SAR); + } else { + i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, NULL); + rcar_i2c_read(priv, ICRXTX); /* dummy read */ + rcar_i2c_write(priv, ICSIER, SDR | SSR | SAR); + } + + rcar_i2c_write(priv, ICSSR, ~SAR & 0xff); + } + + /* master sent stop */ + if (ssr_filtered & SSR) { + i2c_slave_event(priv->slave, I2C_SLAVE_STOP, NULL); + rcar_i2c_write(priv, ICSIER, SAR | SSR); + rcar_i2c_write(priv, ICSSR, ~SSR & 0xff); + } + + /* master wants to write to us */ + if (ssr_filtered & SDR) { + int ret; + + value = rcar_i2c_read(priv, ICRXTX); + ret = i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_END, &value); + /* Send NACK in case of error */ + rcar_i2c_write(priv, ICSCR, SIE | SDBS | (ret < 0 ? FNA : 0)); + i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, NULL); + rcar_i2c_write(priv, ICSSR, ~SDR & 0xff); + } + + /* master wants to read from us */ + if (ssr_filtered & SDE) { + i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_END, NULL); + i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value); + rcar_i2c_write(priv, ICRXTX, value); + rcar_i2c_write(priv, ICSSR, ~SDE & 0xff); + } + + return true; +} + static irqreturn_t rcar_i2c_irq(int irq, void *ptr) { struct rcar_i2c_priv *priv = ptr; @@ -370,6 +434,9 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr) /*-------------- spin lock -----------------*/ spin_lock(&priv->lock); + if (rcar_i2c_slave_irq(priv)) + goto unlock_out; + msr = rcar_i2c_read(priv, ICMSR); /* Only handle interrupts that are currently enabled */ @@ -408,6 +475,7 @@ out: wake_up(&priv->wait); } +unlock_out: spin_unlock(&priv->lock); /*-------------- spin unlock -----------------*/ @@ -498,6 +566,40 @@ out: return ret; } + +static int rcar_reg_slave(struct i2c_client *slave) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter); + + if (priv->slave) + return -EBUSY; + + if (slave->flags & I2C_CLIENT_TEN) + return -EAFNOSUPPORT; + + priv->slave = slave; + rcar_i2c_write(priv, ICSAR, slave->addr); + rcar_i2c_write(priv, ICSSR, 0); + rcar_i2c_write(priv, ICSIER, SAR | SSR); + rcar_i2c_write(priv, ICSCR, SIE | SDBS); + + return 0; +} + +static int rcar_unreg_slave(struct i2c_client *slave) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter); + + WARN_ON(!priv->slave); + + rcar_i2c_write(priv, ICSIER, 0); + rcar_i2c_write(priv, ICSCR, 0); + + priv->slave = NULL; + + return 0; +} + static u32 rcar_i2c_func(struct i2c_adapter *adap) { /* This HW can't do SMBUS_QUICK and NOSTART */ @@ -507,6 +609,8 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap) static const struct i2c_algorithm rcar_i2c_algo = { .master_xfer = rcar_i2c_master_xfer, .functionality = rcar_i2c_func, + .reg_slave = rcar_reg_slave, + .unreg_slave = rcar_unreg_slave, }; static const struct of_device_id rcar_i2c_dt_ids[] = {