From patchwork Sun Apr 27 01:14:26 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrey Danin X-Patchwork-Id: 343143 X-Patchwork-Delegate: twarren@nvidia.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id BF1BE1400D8 for ; Sun, 27 Apr 2014 20:41:31 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 37D404BBD1; Sun, 27 Apr 2014 12:41:30 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 40Cx3otgtuIJ; Sun, 27 Apr 2014 12:41:29 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 46CDD4BBD8; Sun, 27 Apr 2014 12:41:27 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id EE9504BBF1 for ; Sun, 27 Apr 2014 12:41:24 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id cg4jBazaDOq9 for ; Sun, 27 Apr 2014 12:41:24 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from fallback4.mail.ru (fallback4.mail.ru [94.100.176.42]) by theia.denx.de (Postfix) with ESMTP id 13FAD4BBD8 for ; Sun, 27 Apr 2014 12:41:22 +0200 (CEST) Received: from smtp33.i.mail.ru (smtp33.i.mail.ru [94.100.177.93]) by fallback4.mail.ru (mPOP.Fallback_MX) with ESMTP id 23EDD1EAD435 for ; Sun, 27 Apr 2014 14:32:59 +0400 (MSK) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.ru; s=mail2; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=9jeXEZ5URQAT710JlvgHTAtcGOom+OA7MCburMGQcR8=; b=aHdqz8JFiFzt+GnTj5NfAouKE/37d9M8R3XYaX/QDeLR55Yhk3YC1aDa5unggtgmlUomW5YgVrt4PkS3RABC+0TC4R4hq3o/+G9UB/HW0OibptwL0EXAQIiNe2DsOwSc331VB89RQMCKY8wFc79DPcTjiVkdZIHQbuRLqx5W5KE=; Received: from [87.255.14.112] (port=50586 helo=localhost.localdomain) by smtp33.i.mail.ru with esmtpa (envelope-from ) id 1WeMOE-00061u-Q1; Sun, 27 Apr 2014 14:32:55 +0400 From: Andrey Danin To: Tom Warren , u-boot@lists.denx.de Date: Sun, 27 Apr 2014 05:14:26 +0400 Message-Id: <1398561270-25091-3-git-send-email-danindrey@mail.ru> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1398561270-25091-1-git-send-email-danindrey@mail.ru> References: <[PATCH 0/3] ARM: tegra: add nvec keyboard support for paz00> <1398561270-25091-1-git-send-email-danindrey@mail.ru> X-Spam: Not detected X-Mras: Ok Cc: Julian Andres Klode , Stephen Warren , ac100@lists.launchpad.net Subject: [U-Boot] [PATCH v2 2/6] ARM: tegra: i2c: add slave mode support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de Signed-off-by: Andrey Danin CC: Stephen Warren CC: Marc Dietrich CC: Julian Andres Klode CC: ac100@lists.launchpad.net --- arch/arm/include/asm/arch-tegra/tegra_i2c.h | 6 + drivers/i2c/tegra_i2c.c | 199 ++++++++++++++++++++++++++- 2 files changed, 202 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/arch-tegra/tegra_i2c.h b/arch/arm/include/asm/arch-tegra/tegra_i2c.h index 853e59b..f818731 100644 --- a/arch/arm/include/asm/arch-tegra/tegra_i2c.h +++ b/arch/arm/include/asm/arch-tegra/tegra_i2c.h @@ -132,13 +132,19 @@ struct i2c_ctlr { (1 << DVC_CTRL_REG3_I2C_HW_SW_PROG_SHIFT) /* I2C_CNFG */ +#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12 +#define I2C_CNFG_DEBOUNCE_CNT_MASK (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT) #define I2C_CNFG_NEW_MASTER_FSM_SHIFT 11 #define I2C_CNFG_NEW_MASTER_FSM_MASK (1 << I2C_CNFG_NEW_MASTER_FSM_SHIFT) #define I2C_CNFG_PACKET_MODE_SHIFT 10 #define I2C_CNFG_PACKET_MODE_MASK (1 << I2C_CNFG_PACKET_MODE_SHIFT) /* I2C_SL_CNFG */ +#define I2C_SL_CNFG_RESP_SHIFT 0 +#define I2C_SL_CNFG_NACK_SHIFT 1 #define I2C_SL_CNFG_NEWSL_SHIFT 2 +#define I2C_SL_CNFG_IRQ_SHIFT 3 +#define I2C_SL_CNFG_END_TRANS_SHIFT 4 #define I2C_SL_CNFG_NEWSL_MASK (1 << I2C_SL_CNFG_NEWSL_SHIFT) /* I2C_FIFO_STATUS */ diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index 594e5dd..287a5df 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -22,6 +22,7 @@ DECLARE_GLOBAL_DATA_PTR; /* Information about i2c controller */ struct i2c_bus { int id; + int node; enum periph_id periph_id; int speed; int pinmux_config; @@ -30,10 +31,31 @@ struct i2c_bus { int is_dvc; /* DVC type, rather than I2C */ int is_scs; /* single clock source (T114+) */ int inited; /* bus is inited */ + int slave_addr; }; static struct i2c_bus i2c_controllers[TEGRA_I2C_NUM_CONTROLLERS]; +/** + * Init i2c controller to operate in slave mode. + * + * @param bus i2c bus/controller state struct + */ +static void set_slave_mode(struct i2c_bus *bus) +{ + unsigned long val; + + val = I2C_CNFG_NEW_MASTER_FSM_MASK | I2C_CNFG_PACKET_MODE_MASK | + I2C_CNFG_DEBOUNCE_CNT_MASK; + writel(val, &bus->regs->cnfg); + + writel(I2C_SL_CNFG_NEWSL_MASK, &bus->regs->sl_cnfg); + writel(0x1E, &bus->regs->sl_delay_count); + + writel(bus->slave_addr >> 1, &bus->regs->sl_addr1); + writel(0, &bus->regs->sl_addr2); +} + static void set_packet_mode(struct i2c_bus *i2c_bus) { u32 config; @@ -59,8 +81,12 @@ static void i2c_reset_controller(struct i2c_bus *i2c_bus) /* Reset I2C controller. */ reset_periph(i2c_bus->periph_id, 1); - /* re-program config register to packet mode */ - set_packet_mode(i2c_bus); + if (i2c_bus->slave_addr == 0) { + /* re-program config register to packet mode */ + set_packet_mode(i2c_bus); + } else { + set_slave_mode(i2c_bus); + } } static void i2c_init_controller(struct i2c_bus *i2c_bus) @@ -193,6 +219,121 @@ static int wait_for_transfer_complete(struct i2c_control *control) return -1; } + +#define I2C_SL_IRQ (1<<3) +#define END_TRANS (1<<4) +#define RCVD (1<<2) +#define RNW (1<<1) + + +static inline int is_ready(unsigned long status) +{ + return status & I2C_SL_IRQ; +} + +static inline int is_read(unsigned long status) +{ + return (status & RNW) == 0; +} + +static inline int is_trans_start(unsigned long status) +{ + return status & RCVD; +} + +static inline int is_trans_end(unsigned long status) +{ + return status & END_TRANS; +} + + +/** + * Send or receive packet in slave mode. + * + * @param i2c_bus pointer to bus structure + * @param trans I2C transaction object + * + * @return 0 if succeeded, + * 1 if not ready, + * 2 if operation timed out, + * 3 if not our packet, + * other - unknown error. + */ +static int slave_send_recv_packets(struct i2c_bus *i2c_bus, + struct i2c_transaction *trans) +{ + unsigned int poll_start_ms = 0; + unsigned long status; + + unsigned int received = 0; + unsigned int to_send = 0; + unsigned int timer_ms = 0; + int addr = -1; + + poll_start_ms = get_timer(0); + + while (1) { + status = readl(&i2c_bus->regs->sl_status); + if (!is_ready(status)) { + timer_ms = get_timer(poll_start_ms); + if (addr != i2c_bus->slave_addr && + trans->start_timeout && + timer_ms > trans->start_timeout) { + trans->res = 1; + return 1; /*not ready*/ + } + + if (timer_ms > trans->timeout) { + trans->res = 2; + return 2; /*timeout*/ + } + + udelay(100); + continue; + } + + if (!is_trans_start(status) && addr != i2c_bus->slave_addr) { + trans->res = 3; + return 3; /* not our packet, retry */ + } + + if (is_trans_start(status)) { + if (!is_read(status) && addr != i2c_bus->slave_addr) { + trans->res = 3; + return 3; /* not our packet, retry */ + } + if (is_read(status)) { + addr = readl(&i2c_bus->regs->sl_rcvd); + trans->rx_buf[trans->rx_pos++] = addr; + continue; + } + } + + if (is_trans_end(status)) { + /* Check for repeated start */ + if (!is_trans_start(status)) { + trans->res = 0; + return 0; + } + } + + if (is_read(status)) { + /* TODO Check sizes */ + received = readl(&i2c_bus->regs->sl_rcvd); + trans->rx_buf[trans->rx_pos++] = received; + } else { + /* TODO Check sizes */ + to_send = trans->tx_buf[trans->tx_pos++]; + writel(to_send, &i2c_bus->regs->sl_rcvd); + } + } + + /* not reachable */ + trans->res = 4; + return 4; +} + + static int send_recv_packets(struct i2c_bus *i2c_bus, struct i2c_trans_info *trans) { @@ -343,6 +484,7 @@ static unsigned int tegra_i2c_set_bus_speed(struct i2c_adapter *adap, static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus) { + i2c_bus->node = node; i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg"); /* @@ -365,6 +507,8 @@ static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus) if (i2c_bus->periph_id == -1) return -FDT_ERR_NOTFOUND; + i2c_bus->slave_addr = fdtdec_get_int(blob, node, "slave-addr", -1); + return 0; } @@ -422,6 +566,28 @@ static int process_nodes(const void *blob, int node_list[], int count, return 0; } +static int tegra_i2c_slave_io(struct i2c_adapter *adap, + struct i2c_transaction *trans) +{ + struct i2c_bus *bus; + debug("tegra_i2c_slave_io: hwadapnr=%d\n", adap->hwadapnr); + + bus = tegra_i2c_get_bus(adap); + if (!bus) { + error("tegra_i2c_slave_io: no bus for adapter %d\n", + adap->hwadapnr); + return -1; + } + + if (!bus->slave_addr) { + error("tegra_i2c_slave_io: adapter %d isn't in slave mode\n", + adap->hwadapnr); + return -2; + } + + return slave_send_recv_packets(bus, trans); +} + /* Sadly there is no error return from this function */ void i2c_init_board(void) { @@ -453,11 +619,20 @@ void i2c_init_board(void) static void tegra_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) { + struct i2c_bus *bus; + /* No i2c support prior to relocation */ if (!(gd->flags & GD_FLG_RELOC)) return; - /* This will override the speed selected in the fdt for that port */ + bus = tegra_i2c_get_bus(adap); + if (bus) { + adap->slave_io = tegra_i2c_slave_io; + debug("i2c_init: ignore static init for adapter %d\n", + adap->hwadapnr); + return; + } + debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); i2c_set_bus_speed(speed); } @@ -614,6 +789,24 @@ int tegra_i2c_get_dvc_bus_num(void) return -1; } +/** + * Find the I2C bus number by given a FDT I2C node. + * + * @param blob Device tree blbo + * @param node FDT I2C node to find + * @return the number of I2C bus (zero based), or -1 on error + */ +int i2c_get_bus_num_fdt(int node) +{ + int i; + + for (i = 0; i < TEGRA_I2C_NUM_CONTROLLERS; ++i) + if (i2c_controllers[i].node == node) + return i2c_controllers[i].id; + + return -1; +} + /* * Register soft i2c adapters */