From patchwork Mon Jun 1 19:56:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alex Nemirovsky X-Patchwork-Id: 1301916 X-Patchwork-Delegate: hs@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=cortina-access.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=CortinaAccess.onmicrosoft.com header.i=@CortinaAccess.onmicrosoft.com header.a=rsa-sha256 header.s=selector2-CortinaAccess-onmicrosoft-com header.b=dSY2N7/x; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49bQtX6Sv4z9sWN for ; Tue, 2 Jun 2020 05:57:08 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id D8F9A813BB; Mon, 1 Jun 2020 21:56:57 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=cortina-access.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=CortinaAccess.onmicrosoft.com header.i=@CortinaAccess.onmicrosoft.com header.b="dSY2N7/x"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 22BF281260; Mon, 1 Jun 2020 21:56:54 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,FORGED_SPF_HELO,MSGID_FROM_MTA_HEADER,SPF_HELO_PASS, URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.2 Received: from APC01-SG2-obe.outbound.protection.outlook.com (mail-sg2apc01on060b.outbound.protection.outlook.com [IPv6:2a01:111:f400:febd::60b]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 4FE8080677 for ; Mon, 1 Jun 2020 21:56:49 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=cortina-access.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=Alex.Nemirovsky@cortina-access.com ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=McSb6ZX94Lz0N73mGm9s7VY0psZGS+hyTIkE7MaZuSg/tOm+vlNvTL2jfprmgQWkOHMn+gZhS2/WbXdxaCVDT9e2U4mnQT84tjm5friQCr3XKnls1oFAlqY+gO+9fPixhWSMSl0Uh2ANixifI5VCD7R62saivdTQNIFE6zjkuFlJEWF807OTjRBgRJZ75SEelYuAk5Xn1B8IKjl55g5dofZuatygCnZbFFwmgez5e9Na7sWPdwEMPXkMk3o1z4j347ew/rXU8tTwSr50vmuej2fmdoQRLKWcS7H7Ew4iPT0XUMOdw+UoW7atimCiU6uV3K3zdGJe1Dia61BV6+AfOw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=XQpt5doctsqJJe6xdwaGcaF9i2/WnZRtPgINkN4Mdeo=; b=eCfVgwv5CS5+/F6XaT7h74itGXanYdRKz6q8deQ3O8+rDiw5U5PXDKtuTozmn2k9tpHOEmHnPxGjeeGB4tHj3Ebve3cIXklo8v80JwJddzB8YazlwvP5PKFEXBUcJITGzcmhDL7pfJhRj9xBwvwT/flcTbap2OUtbJ+Uy9ocdAKA0qKJwShdPeWCLsfQ3JyyEQt/URCM6cYroC+fqj/Ds8f6aT38Blh8wff9PxAumKp7kXuCKQbQrkmpF++6CtjVS+DP9hZ91n7YoTKUuoDjKkHjTbVvV6Lk4oz3vBk/6jE6rhqvMb8A7Cgj9pE+YJCU5Sg+YCY9qaMu2wOXZay8wA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=cortina-access.com; dmarc=pass action=none header.from=cortina-access.com; dkim=pass header.d=cortina-access.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=CortinaAccess.onmicrosoft.com; s=selector2-CortinaAccess-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=XQpt5doctsqJJe6xdwaGcaF9i2/WnZRtPgINkN4Mdeo=; b=dSY2N7/x13X6PNcAvbF6Vt+1c+B1/qfRM0Uwusnaj0mgb6NC7xevAGq9OzGamTj/hSY57iP+x2bTlwST/pyadNyCo+BF4StLNLp7zo53JZ2aPKT6PlAUFq6VDt4JrLsBJUmalybCCHF6wofbYtlhtthe11TgqhqwXJJlDmtOU9A= Authentication-Results: lists.denx.de; dkim=none (message not signed) header.d=none;lists.denx.de; dmarc=none action=none header.from=cortina-access.com; Received: from PU1PR01MB1964.apcprd01.prod.exchangelabs.com (2603:1096:803:1e::16) by PU1PR01MB2090.apcprd01.prod.exchangelabs.com (2603:1096:803:1b::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3045.17; Mon, 1 Jun 2020 19:56:46 +0000 Received: from PU1PR01MB1964.apcprd01.prod.exchangelabs.com ([fe80::c19b:2121:2956:35a5]) by PU1PR01MB1964.apcprd01.prod.exchangelabs.com ([fe80::c19b:2121:2956:35a5%4]) with mapi id 15.20.3045.024; Mon, 1 Jun 2020 19:56:46 +0000 From: Alex Nemirovsky To: u-boot@lists.denx.de Cc: Arthur Li , Alex Nemirovsky , Heiko Schocher Subject: [PATCH v9 1/2] i2c: i2c-cortina: added CAxxxx I2C support Date: Mon, 1 Jun 2020 12:56:31 -0700 Message-Id: <1591041392-3563-1-git-send-email-alex.nemirovsky@cortina-access.com> X-Mailer: git-send-email 2.7.4 X-ClientProxiedBy: MWHPR10CA0052.namprd10.prod.outlook.com (2603:10b6:300:2c::14) To PU1PR01MB1964.apcprd01.prod.exchangelabs.com (2603:1096:803:1e::16) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from smok.hrh.localdomain (70.58.207.205) by MWHPR10CA0052.namprd10.prod.outlook.com (2603:10b6:300:2c::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.3045.17 via Frontend Transport; Mon, 1 Jun 2020 19:56:43 +0000 X-Mailer: git-send-email 2.7.4 X-Originating-IP: [70.58.207.205] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 6ce52606-3ad3-437b-9c84-08d80665e94c X-MS-TrafficTypeDiagnostic: PU1PR01MB2090: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-LD-Processed: 0694623c-6669-497c-89c3-3a32a9934313,ExtAddr X-MS-Oob-TLC-OOBClassifiers: OLM:1265; X-Forefront-PRVS: 0421BF7135 X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 5WeMdqXCdjomDMhsS+OkiAeNb6qR4d1U69uFwTl5Gv0GZrTt8v0CsFKH+KBefv/WVXFHXrRRA6+hoDTmIAc32wgNlr0ISbGKyidbVH07fSjlaegTf1qZloCe0WltLg9ntMYJKRCEKojxO86b1aI5Y9EAoVmNIPyhGJ1uWafEW1DqXHPGVxLxXTd+qLPBABXEdo4Y5tw00E8Mb7anqItjpk1i4M2l8Gde5XHGaIere6fQU/d0j/ehkkmo90dVzAQvOtK/kIXEZcfpXjuDFhsYqkuvdTJJWRJWPqOie6R/D8pYuhx8g9iRvxr1mSj62KqJJkYVKQTEFSUszv2UM56ufuQp5UxQyOvKM7kuTtRCQHqeq4hVIOwzjWXLkfhrou3xX9tj5uBK5H9vNO5YwXo0kg== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:PU1PR01MB1964.apcprd01.prod.exchangelabs.com; PTR:; CAT:NONE; SFTY:; SFS:(346002)(376002)(39850400004)(136003)(366004)(396003)(6666004)(316002)(52116002)(2906002)(66556008)(66476007)(6486002)(66946007)(956004)(2616005)(6506007)(44832011)(54906003)(26005)(186003)(30864003)(16526019)(8936002)(4326008)(508600001)(36756003)(6916009)(86362001)(8676002)(5660300002)(83380400001)(6512007); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData: sXcUeZKwWZ7jYjXQCk5iqBG5mE2cpdmog5C/esXp15KcKz3VOQrEtAohghg+5rRJ9fQJDO1/kvaq1o1/0VtIMmrJDN/Xiar13Qicgx8Mqgm5Nw2uSeaoiWihGLEst+FpVWW2SlAt2s9QhCg3X7WlkCOn18OUvpHQUruMuhomfRIfbuf4z9RTX306piHpt1PPlgyLdLlUHR0prGzMBJn4LUYbqLNpVl3rvo0RYsBpX80QCXUB5kHj10HXhz6LJbrvBbXiBmlYKLs5XfcyBZW69wL+BSq8tc8ROH3kNq+2CFpaOPYvTaxuYyKdWiyvxfL2/3gdLAOU7/3eRrPmc3TyqhroAEE5udmGgsvxdKNR1y2EvCuEpexZR2Afbkulg7BiO+Bv5siBsWaODzItffOFHs9k+nXzahkRrgNBU/tcg29yMYiCXd7vpPdZkHiaX2TUKPMykLBncTpl02jDlLXkf0HRAFhVcUMSeakK8nJfJSQ= X-OriginatorOrg: CORTINA-ACCESS.COM X-MS-Exchange-CrossTenant-Network-Message-Id: 6ce52606-3ad3-437b-9c84-08d80665e94c X-MS-Exchange-CrossTenant-OriginalArrivalTime: 01 Jun 2020 19:56:45.9544 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 0694623c-6669-497c-89c3-3a32a9934313 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: fHoJOZ2BcI1JJB+NhnlLZqT9q6MHQybmYKejd2TShqC35J9Xj3Dr6NmHY6e6tsIXjCt+tLQy4rpOfVK4ax+Ms9DGxLCkNFpqEiZAKWncnwRQLWWmIaMhkKK38XfPZ6PL X-MS-Exchange-Transport-CrossTenantHeadersStamped: PU1PR01MB2090 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.30rc1 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.2 at phobos.denx.de X-Virus-Status: Clean From: Arthur Li Add I2C controller support for Cortina Access CAxxxx SoCs Signed-off-by: Arthur Li Signed-off-by: Alex Nemirovsky CC: Heiko Schocher Reviewed-by: Heiko Schocher --- Changes in v9: - specially include bitops.h and delay.h which were removed from common.h Changes in v8: - No code change - Split out individual driver from Cortina Package 2 patch series to help streamline acceptence into master Changes in v7: - Added additional description info in I2C KConfig Changes in v6: - Add I2C DT binding document Changes in v4: - Utilize standard I2C macros from - Return ETIMEDOUT in funcs that can timeout - Return i2c_xfer_init() result to caller of i2c_read() if it fails within i2c_read() execution - Fix misc. style guide conformance issues - Use printf() to report i2c_xfer() runtime errors instead of debug() MAINTAINERS | 4 + doc/device-tree-bindings/i2c/i2c-cortina.txt | 18 ++ drivers/i2c/Kconfig | 8 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c-cortina.c | 346 +++++++++++++++++++++++++++ drivers/i2c/i2c-cortina.h | 87 +++++++ 6 files changed, 464 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/i2c-cortina.txt create mode 100644 drivers/i2c/i2c-cortina.c create mode 100644 drivers/i2c/i2c-cortina.h diff --git a/MAINTAINERS b/MAINTAINERS index 8add9d4..ce70ca9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -181,6 +181,8 @@ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c +F: drivers/i2c/i2c-cortina.c +F: drivers/i2c/i2c-cortina.h ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun @@ -732,6 +734,8 @@ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c +F: drivers/i2c/i2c-cortina.c +F: drivers/i2c/i2c-cortina.h MIPS MSCC M: Gregory CLEMENT diff --git a/doc/device-tree-bindings/i2c/i2c-cortina.txt b/doc/device-tree-bindings/i2c/i2c-cortina.txt new file mode 100644 index 0000000..59d5235 --- /dev/null +++ b/doc/device-tree-bindings/i2c/i2c-cortina.txt @@ -0,0 +1,18 @@ +* I2C for Cortina platforms + +Required properties : +- compatible : Must be "cortina,ca-i2c" +- reg : Offset and length of the register set for the device + +Recommended properties : +- clock-frequency : desired I2C bus clock frequency in Hz. If not specified, + default value is 100000. Possible values are 100000, + 400000 and 1000000. + +Examples : + + i2c: i2c@f4329120 { + compatible = "cortina,ca-i2c"; + reg = <0x0 0xf4329120 0x28>; + clock-frequency = <400000>; + }; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index f8b18de..b56e0d9 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -93,6 +93,14 @@ config SYS_I2C_CADENCE Say yes here to select Cadence I2C Host Controller. This controller is e.g. used by Xilinx Zynq. +config SYS_I2C_CA + tristate "Cortina-Access I2C Controller" + depends on DM_I2C && CORTINA_PLATFORM + default n + help + Add support for the Cortina Access I2C host controller. + Say yes here to select Cortina-Access I2C Host Controller. + config SYS_I2C_DAVINCI bool "Davinci I2C Controller" depends on (ARCH_KEYSTONE || ARCH_DAVINCI) diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 62935b7..d2b07ce 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SYS_I2C) += i2c_core.o obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o +obj-$(CONFIG_SYS_I2C_CA) += i2c-cortina.o obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o obj-$(CONFIG_SYS_I2C_DW) += designware_i2c.o ifdef CONFIG_DM_PCI diff --git a/drivers/i2c/i2c-cortina.c b/drivers/i2c/i2c-cortina.c new file mode 100644 index 0000000..08b812a --- /dev/null +++ b/drivers/i2c/i2c-cortina.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2020 + * Arthur Li, Cortina Access, arthur.li@cortina-access.com. + */ + +#include +#include +#include +#include +#include +#include "i2c-cortina.h" + +static void set_speed(struct i2c_regs *regs, int i2c_spd) +{ + union ca_biw_cfg i2c_cfg; + + i2c_cfg.wrd = readl(®s->i2c_cfg); + i2c_cfg.bf.core_en = 0; + writel(i2c_cfg.wrd, ®s->i2c_cfg); + + switch (i2c_spd) { + case IC_SPEED_MODE_FAST_PLUS: + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / + (5 * I2C_SPEED_FAST_PLUS_RATE) - 1; + break; + + case IC_SPEED_MODE_STANDARD: + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / + (5 * I2C_SPEED_STANDARD_RATE) - 1; + break; + + case IC_SPEED_MODE_FAST: + default: + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / + (5 * I2C_SPEED_FAST_RATE) - 1; + break; + } + + i2c_cfg.bf.core_en = 1; + writel(i2c_cfg.wrd, ®s->i2c_cfg); +} + +static int ca_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + struct ca_i2c *priv = dev_get_priv(bus); + int i2c_spd; + + if (speed >= I2C_SPEED_FAST_PLUS_RATE) { + i2c_spd = IC_SPEED_MODE_FAST_PLUS; + priv->speed = I2C_SPEED_FAST_PLUS_RATE; + } else if (speed >= I2C_SPEED_FAST_RATE) { + i2c_spd = IC_SPEED_MODE_FAST; + priv->speed = I2C_SPEED_FAST_RATE; + } else { + i2c_spd = IC_SPEED_MODE_STANDARD; + priv->speed = I2C_SPEED_STANDARD_RATE; + } + + set_speed(priv->regs, i2c_spd); + + return 0; +} + +static int ca_i2c_get_bus_speed(struct udevice *bus) +{ + struct ca_i2c *priv = dev_get_priv(bus); + + return priv->speed; +} + +static void ca_i2c_init(struct i2c_regs *regs) +{ + union ca_biw_cfg i2c_cfg; + + i2c_cfg.wrd = readl(®s->i2c_cfg); + i2c_cfg.bf.core_en = 0; + i2c_cfg.bf.biw_soft_reset = 1; + writel(i2c_cfg.wrd, ®s->i2c_cfg); + mdelay(10); + i2c_cfg.bf.biw_soft_reset = 0; + writel(i2c_cfg.wrd, ®s->i2c_cfg); + + set_speed(regs, IC_SPEED_MODE_STANDARD); + + i2c_cfg.wrd = readl(®s->i2c_cfg); + i2c_cfg.bf.core_en = 1; + writel(i2c_cfg.wrd, ®s->i2c_cfg); +} + +static int i2c_wait_complete(struct i2c_regs *regs) +{ + union ca_biw_ctrl i2c_ctrl; + unsigned long start_time_bb = get_timer(0); + + i2c_ctrl.wrd = readl(®s->i2c_ctrl); + + while (i2c_ctrl.bf.biwdone == 0) { + i2c_ctrl.wrd = readl(®s->i2c_ctrl); + + if (get_timer(start_time_bb) > + (unsigned long)(I2C_BYTE_TO_BB)) { + printf("%s not done!!!\n", __func__); + return -ETIMEDOUT; + } + } + + /* Clear done bit */ + writel(i2c_ctrl.wrd, ®s->i2c_ctrl); + + return 0; +} + +static void i2c_setaddress(struct i2c_regs *regs, unsigned int i2c_addr, + int write_read) +{ + writel(i2c_addr | write_read, ®s->i2c_txr); + + writel(BIW_CTRL_START | BIW_CTRL_WRITE, + ®s->i2c_ctrl); + + i2c_wait_complete(regs); +} + +static int i2c_wait_for_bus_busy(struct i2c_regs *regs) +{ + union ca_biw_ack i2c_ack; + unsigned long start_time_bb = get_timer(0); + + i2c_ack.wrd = readl(®s->i2c_ack); + + while (i2c_ack.bf.biw_busy) { + i2c_ack.wrd = readl(®s->i2c_ack); + + if (get_timer(start_time_bb) > + (unsigned long)(I2C_BYTE_TO_BB)) { + printf("%s: timeout!\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int i2c_xfer_init(struct i2c_regs *regs, uint8_t chip, uint addr, + int alen, int write_read) +{ + int addr_len = alen; + + if (i2c_wait_for_bus_busy(regs)) + return 1; + + /* First cycle must write addr + offset */ + chip = ((chip & 0x7F) << 1); + if (alen == 0 && write_read == I2C_CMD_RD) + i2c_setaddress(regs, chip, I2C_CMD_RD); + else + i2c_setaddress(regs, chip, I2C_CMD_WT); + + while (alen) { + alen--; + writel(addr, ®s->i2c_txr); + if (write_read == I2C_CMD_RD) + writel(BIW_CTRL_WRITE | BIW_CTRL_STOP, + ®s->i2c_ctrl); + else + writel(BIW_CTRL_WRITE, ®s->i2c_ctrl); + i2c_wait_complete(regs); + } + + /* Send address again with Read flag if it's read command */ + if (write_read == I2C_CMD_RD && addr_len > 0) + i2c_setaddress(regs, chip, I2C_CMD_RD); + + return 0; +} + +static int i2c_xfer_finish(struct i2c_regs *regs) +{ + /* Dummy read makes bus free */ + writel(BIW_CTRL_READ | BIW_CTRL_STOP, ®s->i2c_ctrl); + i2c_wait_complete(regs); + + if (i2c_wait_for_bus_busy(regs)) { + printf("Timed out waiting for bus\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int ca_i2c_read(struct i2c_regs *regs, uint8_t chip, uint addr, + int alen, uint8_t *buffer, int len) +{ + unsigned long start_time_rx; + int rc = 0; + + rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_RD); + if (rc) + return rc; + + start_time_rx = get_timer(0); + while (len) { + /* ACK_IN is ack value to send during read. + * ack high only on the very last byte! + */ + if (len == 1) + writel(BIW_CTRL_READ | BIW_CTRL_ACK_IN | BIW_CTRL_STOP, + ®s->i2c_ctrl); + else + writel(BIW_CTRL_READ, ®s->i2c_ctrl); + + rc = i2c_wait_complete(regs); + udelay(1); + + if (rc == 0) { + *buffer++ = + (uchar) readl(®s->i2c_rxr); + len--; + start_time_rx = get_timer(0); + + } else if (get_timer(start_time_rx) > I2C_BYTE_TO) { + return -ETIMEDOUT; + } + } + i2c_xfer_finish(regs); + return rc; +} + +static int ca_i2c_write(struct i2c_regs *regs, uint8_t chip, uint addr, + int alen, uint8_t *buffer, int len) +{ + int rc, nb = len; + unsigned long start_time_tx; + + rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_WT); + if (rc) + return rc; + + start_time_tx = get_timer(0); + while (len) { + writel(*buffer, ®s->i2c_txr); + if (len == 1) + writel(BIW_CTRL_WRITE | BIW_CTRL_STOP, + ®s->i2c_ctrl); + else + writel(BIW_CTRL_WRITE, ®s->i2c_ctrl); + + rc = i2c_wait_complete(regs); + + if (rc == 0) { + len--; + buffer++; + start_time_tx = get_timer(0); + } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) { + return -ETIMEDOUT; + } + } + + return 0; +} + +static int ca_i2c_probe_chip(struct udevice *bus, uint chip_addr, + uint chip_flags) +{ + struct ca_i2c *priv = dev_get_priv(bus); + int ret; + u32 tmp; + + /* Try to read the first location of the chip */ + ret = ca_i2c_read(priv->regs, chip_addr, 0, 1, (uchar *)&tmp, 1); + if (ret) + ca_i2c_init(priv->regs); + + return ret; +} + +static int ca_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) +{ + struct ca_i2c *priv = dev_get_priv(bus); + int ret; + + debug("i2c_xfer: %d messages\n", nmsgs); + for (; nmsgs > 0; nmsgs--, msg++) { + debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); + if (msg->flags & I2C_M_RD) + ret = ca_i2c_read(priv->regs, msg->addr, 0, 0, + msg->buf, msg->len); + else + ret = ca_i2c_write(priv->regs, msg->addr, 0, 0, + msg->buf, msg->len); + + if (ret) { + printf("i2c_xfer: %s error\n", + msg->flags & I2C_M_RD ? "read" : "write"); + return ret; + } + } + + return 0; +} + +static const struct dm_i2c_ops ca_i2c_ops = { + .xfer = ca_i2c_xfer, + .probe_chip = ca_i2c_probe_chip, + .set_bus_speed = ca_i2c_set_bus_speed, + .get_bus_speed = ca_i2c_get_bus_speed, +}; + +static const struct udevice_id ca_i2c_ids[] = { + { .compatible = "cortina,ca-i2c", }, + { } +}; + +static int ca_i2c_probe(struct udevice *bus) +{ + struct ca_i2c *priv = dev_get_priv(bus); + + ca_i2c_init(priv->regs); + + return 0; +} + +static int ca_i2c_ofdata_to_platdata(struct udevice *bus) +{ + struct ca_i2c *priv = dev_get_priv(bus); + + priv->regs = map_sysmem(dev_read_addr(bus), sizeof(struct i2c_regs)); + if (!priv->regs) { + printf("I2C: base address is invalid\n"); + return -EINVAL; + } + + return 0; +} + +U_BOOT_DRIVER(i2c_cortina) = { + .name = "i2c_cortina", + .id = UCLASS_I2C, + .of_match = ca_i2c_ids, + .ofdata_to_platdata = ca_i2c_ofdata_to_platdata, + .probe = ca_i2c_probe, + .priv_auto_alloc_size = sizeof(struct ca_i2c), + .ops = &ca_i2c_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/i2c/i2c-cortina.h b/drivers/i2c/i2c-cortina.h new file mode 100644 index 0000000..7e406b5 --- /dev/null +++ b/drivers/i2c/i2c-cortina.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2019 + * Cortina Access, + */ + +#ifndef __CA_I2C_H_ +#define __CA_I2C_H_ + +#include +#include + +#if !defined(__ASSEMBLER__) && !defined(__ASSEMBLY__) +struct i2c_regs { + u32 i2c_cfg; + u32 i2c_ctrl; + u32 i2c_txr; + u32 i2c_rxr; + u32 i2c_ack; + u32 i2c_ie0; + u32 i2c_int0; + u32 i2c_ie1; + u32 i2c_int1; + u32 i2c_stat; +}; + +union ca_biw_cfg { + struct biw_cfg { + u32 core_en : 1; + u32 biw_soft_reset : 1; + u32 busywait_en : 1; + u32 stretch_en : 1; + u32 arb_en : 1; + u32 clksync_en : 1; + u32 rsrvd1 : 2; + u32 spike_cnt : 4; + u32 rsrvd2 : 4; + u32 prer : 16; + } bf; + unsigned int wrd; +}; + +union ca_biw_ctrl { + struct biw_ctrl { + u32 biwdone : 1; + u32 rsrvd1 : 2; + u32 ack_in : 1; + u32 write : 1; + u32 read : 1; + u32 stop : 1; + u32 start : 1; + u32 rsrvd2 : 24; + } bf; + unsigned int wrd; +}; + +union ca_biw_ack { + struct biw_ack { + u32 al :1; + u32 biw_busy :1; + u32 ack_out :1; + u32 rsrvd1 :29; + } bf; + unsigned int wrd; +}; +#endif /* !__ASSEMBLER__*/ + +struct ca_i2c { + struct i2c_regs *regs; + unsigned int speed; +}; + +#define I2C_CMD_WT 0 +#define I2C_CMD_RD 1 + +#define BIW_CTRL_DONE BIT(0) +#define BIW_CTRL_ACK_IN BIT(3) +#define BIW_CTRL_WRITE BIT(4) +#define BIW_CTRL_READ BIT(5) +#define BIW_CTRL_STOP BIT(6) +#define BIW_CTRL_START BIT(7) + +#define I2C_BYTE_TO (CONFIG_SYS_HZ / 500) +#define I2C_STOPDET_TO (CONFIG_SYS_HZ / 500) +#define I2C_BYTE_TO_BB (10) + +#endif /* __CA_I2C_H_ */