From patchwork Tue Mar 20 06:19:09 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?Q2hlbktlbllZIOmZs+awuOeHnyBUQU8=?= X-Patchwork-Id: 888088 X-Patchwork-Delegate: patchwork@peda.user.lysator.liu.se Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-i2c-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=inventec.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 4052r35kqhz9sYd for ; Tue, 20 Mar 2018 17:21:07 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751929AbeCTGVF (ORCPT ); Tue, 20 Mar 2018 02:21:05 -0400 Received: from hub02.inventec.com ([218.32.67.167]:55980 "EHLO mail.inventec.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751697AbeCTGVA (ORCPT ); Tue, 20 Mar 2018 02:21:00 -0400 Received: from IEC1-MSE-FE1.inventec.com (10.1.254.203) by IEC1-EX2010-04.iec.inventec (10.1.254.157) with Microsoft SMTP Server id 14.3.361.1; Tue, 20 Mar 2018 14:16:36 +0800 Received: from IEC1-SPAM-2.inventec.com (smg2.inventec.com [10.1.254.202]) by IEC1-MSE-FE1.inventec.com with ESMTP id w2K6GRTM002082 for ; Tue, 20 Mar 2018 14:16:27 +0800 (GMT-8) (envelope-from chen.kenyy@inventec.com) Received: from mail-pl0-f72.google.com (mail-pl0-f72.google.com [209.85.160.72]) by IEC1-SPAM-2.inventec.com with ESMTP id w2K6GLJd076375 (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Tue, 20 Mar 2018 14:16:22 +0800 (GMT-8) (envelope-from chen.kenyy@inventec.com) Received: by mail-pl0-f72.google.com with SMTP id f3-v6so464065plf.1 for ; Mon, 19 Mar 2018 23:16:22 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=bqSYFSxY5G75DEBKUMShxPOm/vXsI0p2c/WUeKTI14M=; b=EoGlYMRT8IEzs79Y9Arbjp2Lgejapv3OLQv2Ri4JUpWPeAezSJLxGq+UvtXDHchEwU 3DDt28QqX8F9Y0jXQH7d/z1wM1bGIaAzmwp2cX9MD2Z2EB1vod5Ol8Kpglk+TJbSi7k7 d1GzpMi29hJsK6VxVsXILQEQlvG3nvOiokxahHpBbrdWTUvLtSnf4sxfdeIeXes97LId mxAuL029Q3TJo08HAee/Ok254QJnWBvLLb5M89NYjipXMiQmIN6UKDbYoNYtU7LfXRvK Ag66XVHQaxX6GI8jZXr2CzYo+Ar+QFvBH4MOhPsCN7x7RqBo6r6FIQFWW+sH4rWhSAic vWFg== X-Gm-Message-State: AElRT7HKKt08ZyeEHRZ+/1uDAEp8UGVJTU1+vLWL9FFwJK+nSzNpounZ xrLt72hyUAc4RjziQRfFjh0bH6oY2iXBSldU1NWXxFznlYqtb04OYFICMvW9Q8aga3+mHP/NNHT D0oSwQug9wM2GNL5W2VQ+ejg= X-Received: by 10.98.72.10 with SMTP id v10mr12598336pfa.148.1521526580539; Mon, 19 Mar 2018 23:16:20 -0700 (PDT) X-Google-Smtp-Source: AG47ELuP5x5YBjKjk2Z8FYEF9PvsJPN0Wj5PBDtnMEfF+CkfZN/ZxEfQQMqSZQq884x3qisYJCVOFQ== X-Received: by 10.98.72.10 with SMTP id v10mr12598316pfa.148.1521526580092; Mon, 19 Mar 2018 23:16:20 -0700 (PDT) Received: from localhost (59-120-179-172.HINET-IP.hinet.net. [59.120.179.172]) by smtp.gmail.com with ESMTPSA id n25sm1586712pfk.174.2018.03.19.23.16.18 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 19 Mar 2018 23:16:18 -0700 (PDT) From: Ken Chen To: , , , , CC: Ken Chen , Subject: [PATCH linux dev-4.16 v2] i2c: muxes: pca9641: new driver Date: Tue, 20 Mar 2018 14:19:09 +0800 Message-ID: <20180320061909.5775-1-chen.kenyy@inventec.com> X-Mailer: git-send-email 2.9.3 X-DNSRBL: X-MAIL: IEC1-MSE-FE1.inventec.com w2K6GRTM002082 MIME-Version: 1.0 Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org Signed-off-by: Ken Chen --- v1->v2 - Merged PCA9641 code into i2c-mux-pca9541.c - Modified title - Add PCA9641 detect function --- drivers/i2c/muxes/i2c-mux-pca9541.c | 184 ++++++++++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 10 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index 6a39ada..493f947 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -1,5 +1,5 @@ /* - * I2C multiplexer driver for PCA9541 bus master selector + * I2C multiplexer driver for PCA9541/PCA9641 bus master selector * * Copyright (c) 2010 Ericsson AB. * @@ -26,8 +26,8 @@ #include /* - * The PCA9541 is a bus master selector. It supports two I2C masters connected - * to a single slave bus. + * The PCA9541/PCA9641 is a bus master selector. It supports two I2C masters + * connected to a single slave bus. * * Before each bus transaction, a master has to acquire bus ownership. After the * transaction is complete, bus ownership has to be released. This fits well @@ -58,11 +58,43 @@ #define PCA9541_ISTAT_MYTEST (1 << 6) #define PCA9541_ISTAT_NMYTEST (1 << 7) +#define PCA9641_ID 0x00 +#define PCA9641_ID_MAGIC 0x38 + +#define PCA9641_CONTROL 0x01 +#define PCA9641_STATUS 0x02 +#define PCA9641_TIME 0x03 + +#define PCA9641_CTL_LOCK_REQ BIT(0) +#define PCA9641_CTL_LOCK_GRANT BIT(1) +#define PCA9641_CTL_BUS_CONNECT BIT(2) +#define PCA9641_CTL_BUS_INIT BIT(3) +#define PCA9641_CTL_SMBUS_SWRST BIT(4) +#define PCA9641_CTL_IDLE_TIMER_DIS BIT(5) +#define PCA9641_CTL_SMBUS_DIS BIT(6) +#define PCA9641_CTL_PRIORITY BIT(7) + +#define PCA9641_STS_OTHER_LOCK BIT(0) +#define PCA9641_STS_BUS_INIT_FAIL BIT(1) +#define PCA9641_STS_BUS_HUNG BIT(2) +#define PCA9641_STS_MBOX_EMPTY BIT(3) +#define PCA9641_STS_MBOX_FULL BIT(4) +#define PCA9641_STS_TEST_INT BIT(5) +#define PCA9641_STS_SCL_IO BIT(6) +#define PCA9641_STS_SDA_IO BIT(7) + +#define PCA9641_RES_TIME 0x03 + #define BUSON (PCA9541_CTL_BUSON | PCA9541_CTL_NBUSON) #define MYBUS (PCA9541_CTL_MYBUS | PCA9541_CTL_NMYBUS) #define mybus(x) (!((x) & MYBUS) || ((x) & MYBUS) == MYBUS) #define busoff(x) (!((x) & BUSON) || ((x) & BUSON) == BUSON) +#define BUSOFF(x, y) (!((x) & PCA9641_CTL_LOCK_GRANT) && \ + !((y) & PCA9641_STS_OTHER_LOCK)) +#define other_lock(x) ((x) & PCA9641_STS_OTHER_LOCK) +#define lock_grant(x) ((x) & PCA9641_CTL_LOCK_GRANT) + /* arbitration timeouts, in jiffies */ #define ARB_TIMEOUT (HZ / 8) /* 125 ms until forcing bus ownership */ #define ARB2_TIMEOUT (HZ / 4) /* 250 ms until acquisition failure */ @@ -79,6 +111,7 @@ struct pca9541 { static const struct i2c_device_id pca9541_id[] = { {"pca9541", 0}, + {"pca9641", 1}, {} }; @@ -87,6 +120,7 @@ MODULE_DEVICE_TABLE(i2c, pca9541_id); #ifdef CONFIG_OF static const struct of_device_id pca9541_of_match[] = { { .compatible = "nxp,pca9541" }, + { .compatible = "nxp,pca9641" }, {} }; MODULE_DEVICE_TABLE(of, pca9541_of_match); @@ -328,6 +362,125 @@ static int pca9541_release_chan(struct i2c_mux_core *muxc, u32 chan) } /* + * Arbitration management functions + */ +static void pca9641_release_bus(struct i2c_client *client) +{ + pca9541_reg_write(client, PCA9641_CONTROL, 0); +} + +/* + * Channel arbitration + * + * Return values: + * <0: error + * 0 : bus not acquired + * 1 : bus acquired + */ +static int pca9641_arbitrate(struct i2c_client *client) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca9541 *data = i2c_mux_priv(muxc); + int reg_ctl, reg_sts; + + reg_ctl = pca9541_reg_read(client, PCA9641_CONTROL); + if (reg_ctl < 0) + return reg_ctl; + reg_sts = pca9541_reg_read(client, PCA9641_STATUS); + + if (BUSOFF(reg_ctl, reg_sts)) { + /* + * Bus is off. Request ownership or turn it on unless + * other master requested ownership. + */ + reg_ctl |= PCA9641_CTL_LOCK_REQ; + pca9541_reg_write(client, PCA9641_CONTROL, reg_ctl); + reg_ctl = pca9541_reg_read(client, PCA9641_CONTROL); + + if (lock_grant(reg_ctl)) { + /* + * Other master did not request ownership, + * or arbitration timeout expired. Take the bus. + */ + reg_ctl |= PCA9641_CTL_BUS_CONNECT + | PCA9641_CTL_LOCK_REQ; + pca9541_reg_write(client, PCA9641_CONTROL, reg_ctl); + data->select_timeout = SELECT_DELAY_SHORT; + + return 1; + } else { + /* + * Other master requested ownership. + * Set extra long timeout to give it time to acquire it. + */ + data->select_timeout = SELECT_DELAY_LONG * 2; + } + } else if (lock_grant(reg_ctl)) { + /* + * Bus is on, and we own it. We are done with acquisition. + */ + reg_ctl |= PCA9641_CTL_BUS_CONNECT | PCA9641_CTL_LOCK_REQ; + pca9541_reg_write(client, PCA9641_CONTROL, reg_ctl); + + return 1; + } else if (other_lock(reg_sts)) { + /* + * Other master owns the bus. + * If arbitration timeout has expired, force ownership. + * Otherwise request it. + */ + data->select_timeout = SELECT_DELAY_LONG; + reg_ctl |= PCA9641_CTL_LOCK_REQ; + pca9541_reg_write(client, PCA9641_CONTROL, reg_ctl); + } + return 0; +} + +static int pca9641_select_chan(struct i2c_mux_core *muxc, u32 chan) +{ + struct pca9541 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + int ret; + unsigned long timeout = jiffies + ARB2_TIMEOUT; + /* give up after this time */ + + data->arb_timeout = jiffies + ARB_TIMEOUT; + /* force bus ownership after this time */ + + do { + ret = pca9641_arbitrate(client); + if (ret) + return ret < 0 ? ret : 0; + + if (data->select_timeout == SELECT_DELAY_SHORT) + udelay(data->select_timeout); + else + msleep(data->select_timeout / 1000); + } while (time_is_after_eq_jiffies(timeout)); + + return -ETIMEDOUT; +} + +static int pca9641_release_chan(struct i2c_mux_core *muxc, u32 chan) +{ + struct pca9541 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + + pca9641_release_bus(client); + return 0; +} + +static int pca9641_detect_id(struct i2c_client *client) +{ + int reg; + + reg = pca9541_reg_read(client, PCA9641_ID); + if (reg == PCA9641_ID_MAGIC) + return 1; + else + return 0; +} +/* * I2C init/probing/exit functions */ static int pca9541_probe(struct i2c_client *client, @@ -339,34 +492,45 @@ static int pca9541_probe(struct i2c_client *client, struct pca9541 *data; int force; int ret; + int detect_id; if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; + detect_id = pca9641_detect_id(client); /* * I2C accesses are unprotected here. * We have to lock the adapter before releasing the bus. */ - i2c_lock_adapter(adap); - pca9541_release_bus(client); - i2c_unlock_adapter(adap); - + if (detect_id == 0) { + i2c_lock_adapter(adap); + pca9541_release_bus(client); + i2c_unlock_adapter(adap); + } else { + i2c_lock_adapter(adap); + pca9641_release_bus(client); + i2c_unlock_adapter(adap); + } /* Create mux adapter */ force = 0; if (pdata) force = pdata->modes[0].adap_id; - muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data), + if (detect_id == 0) { + muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data), I2C_MUX_ARBITRATOR, pca9541_select_chan, pca9541_release_chan); + } else { + muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data), + I2C_MUX_ARBITRATOR, + pca9641_select_chan, pca9641_release_chan); + } if (!muxc) return -ENOMEM; data = i2c_mux_priv(muxc); data->client = client; - i2c_set_clientdata(client, muxc); - ret = i2c_mux_add_adapter(muxc, force, 0, 0); if (ret) return ret;