From patchwork Fri Oct 26 18:14:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Carlos Iglesias X-Patchwork-Id: 10657713 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id AD2495A4 for ; Fri, 26 Oct 2018 18:14:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A1E0C2C1A5 for ; Fri, 26 Oct 2018 18:14:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 959E82C7B4; Fri, 26 Oct 2018 18:14:50 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0B1F42C1A5 for ; Fri, 26 Oct 2018 18:14:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727977AbeJ0Cwr (ORCPT ); Fri, 26 Oct 2018 22:52:47 -0400 Received: from mail-eopbgr30093.outbound.protection.outlook.com ([40.107.3.93]:50723 "EHLO EUR03-AM5-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726815AbeJ0Cwq (ORCPT ); Fri, 26 Oct 2018 22:52:46 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=emutex.onmicrosoft.com; s=selector1-emutex-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=G9lEOxZPKXJrpoVlohnWzrYu8iEh+MhmhexTqvZL7TU=; b=OPil9t6IrUrv48oeySkixitIXfj2BajTUumV8QMlVGdwFrOOm/8Jq7aLLcIPHmFBJ3S/S4mBVvu1u/bH9EUOUOco8cQG5E7SQX091VIT6dGGZKPwd+0yWFur4rECBZ8kQuphUL8XsAca0N362OYGb6e+WrPUpm48/H7hec0vTY8= Received: from HE1P190MB0011.EURP190.PROD.OUTLOOK.COM (10.172.129.136) by HE1P190MB0156.EURP190.PROD.OUTLOOK.COM (10.172.131.15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.1273.19; Fri, 26 Oct 2018 18:14:37 +0000 Received: from HE1P190MB0011.EURP190.PROD.OUTLOOK.COM ([fe80::8544:bd36:102c:871f]) by HE1P190MB0011.EURP190.PROD.OUTLOOK.COM ([fe80::8544:bd36:102c:871f%11]) with mapi id 15.20.1273.022; Fri, 26 Oct 2018 18:14:37 +0000 From: Carlos Iglesias To: "linux-kernel@vger.kernel.org" , "jic23@kernel.org" , "robh+dt@kernel.org" , "mark.rutland@arm.com" CC: "knaack.h@gmx.de" , "lars@metafoo.de" , "pmeerw@pmeerw.net" , "linux-iio@vger.kernel.org" , "devicetree@vger.kernel.org" , Dan O'Donovan , Carlos Iglesias Subject: [PATCH 2/2] iio: pressure: Add support for Honeywell HSC SPI sensors Thread-Topic: [PATCH 2/2] iio: pressure: Add support for Honeywell HSC SPI sensors Thread-Index: AQHUbVfBw4UELp7oLUyTBRWwtUvMFg== Date: Fri, 26 Oct 2018 18:14:37 +0000 Message-ID: <20181026181406.14545-3-carlos.iglesias@emutex.com> References: <20181026181406.14545-1-carlos.iglesias@emutex.com> In-Reply-To: <20181026181406.14545-1-carlos.iglesias@emutex.com> Accept-Language: en-IE, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-clientproxiedby: LO2P265CA0477.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:a2::33) To HE1P190MB0011.EURP190.PROD.OUTLOOK.COM (2603:10a6:3:c9::8) authentication-results: spf=none (sender IP is ) smtp.mailfrom=carlos.iglesias@emutex.com; x-ms-exchange-messagesentrepresentingtype: 1 x-originating-ip: [92.51.199.138] x-ms-publictraffictype: Email x-microsoft-exchange-diagnostics: 1;HE1P190MB0156;6:WUujHzJV8ecm65hBMGaM/I/xxl6bQNtWsEeTiWb638+N7M442F5m5fio71G/wczQsFrhh/UFHSlcOIZg6nvGOlrfyBscGKL9acgE2Z6yo9QLzQEMDEFP/XsycuGwK4crf3a+63FTCmJE64nkX2ESUOmEb7DtoO+SrkJpNfhvCeSNXdOa2Z3yrOWm+zHOMnH4RTilbqe8rjlebUEMw0PqQwrxMN8gAap5eN7s8UH+KCUZ5Ysa/Q2lfOibqVTH8lasKjRbLpwb1oT9PRqmeozUR+yuJMsW9ii0CMEMUPl0qGqxCyN8jahRSIhsPZ5klSlYcl9YQjayr9z5A/eeihLSKZfse75d1PIreDwKPwb1VyyWp1ZkZhbBz4+rC3SVHoT7TDdDWVs00IZf92yqiqh1TXUI4SQC/phoALTS9Yefh8fFcTqzgiNNVRQv9Ow4mXKm1PY9Bi2g0iehW1Eo9yvJVA==;5:mfP/vKIdJOXYWt3hiw+U8Wms4l9pC1tVFkBsZFI/ma+e62vkyyKZ6hA+ElvWg/lpYN4nCtMDDRFh9yv8DD0u9fyJQhbqU6kj/1+SQ9vUbea00aYiGEoSi5dy82uzd49pwNhqe7tTQgICOPHGf20wFSUyHKNexriYqWF5HfrlBhI=;7:vhlsIHAXiuB8n+NWQ0ClmpL2VjJlzGW9Ab/PdgIeqjETMToUzSyjPJXhNqMb//AMDDd4AfENqw+L/2CtO3jMXE3CeLhXNZDrkMO+hfFRzujTnfqQJzn+yCO8XsnQyJg7E7V8E6p1Btza+fEnWR7ryA== x-ms-office365-filtering-correlation-id: b2b1ac5f-2cc7-47f8-c23d-08d63b6ee385 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(7020095)(4652040)(8989299)(5600074)(711020)(4534185)(7168020)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7153060)(7193020);SRVR:HE1P190MB0156; x-ms-traffictypediagnostic: HE1P190MB0156: x-microsoft-antispam-prvs: x-exchange-antispam-report-test: UriScan:(84791874153150); x-ms-exchange-senderadcheck: 1 x-exchange-antispam-report-cfa-test: BCL:0;PCL:0;RULEID:(6040522)(2401047)(5005006)(8121501046)(10201501046)(3231355)(944501410)(4982022)(52105095)(3002001)(93006095)(93001095)(148016)(149066)(150057)(6041310)(201703131423095)(20161123555045)(201703061421075)(20161123558120)(20161123562045)(20161123564045)(20161123560045)(201708071742011)(7699051)(76991095);SRVR:HE1P190MB0156;BCL:0;PCL:0;RULEID:;SRVR:HE1P190MB0156; x-forefront-prvs: 083751FCA6 x-forefront-antispam-report: SFV:NSPM;SFS:(10019020)(346002)(136003)(376002)(366004)(199004)(189003)(14454004)(53936002)(106356001)(55236004)(105586002)(71190400001)(99286004)(2900100001)(386003)(71200400001)(4744004)(6506007)(8676002)(81156014)(110136005)(81166006)(8936002)(54906003)(25786009)(76176011)(52116002)(4326008)(68736007)(97736004)(5660300001)(508600001)(186003)(2501003)(44832011)(66066001)(6486002)(6436002)(26005)(217873002)(2616005)(11346002)(256004)(14444005)(2906002)(5250100002)(102836004)(486006)(476003)(446003)(6116002)(3846002)(86362001)(575784001)(305945005)(7736002)(36756003)(107886003)(6306002)(1076002)(6512007)(53946003)(579004);DIR:OUT;SFP:1102;SCL:1;SRVR:HE1P190MB0156;H:HE1P190MB0011.EURP190.PROD.OUTLOOK.COM;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: emutex.com does not designate permitted sender hosts) x-microsoft-antispam-message-info: NDIxiWRuSWvYiHrnux+dS1jt29SbY4K6WvgsbzzeNwxxPuzNkeaTMlxXqoonM16zQhi2g0gRfzjE7H1eUlZyAlQr2UD66yCo2tzt7CskxtCgNZW06/cbHX30cXZuF9hZIwbxE5Gvgd6TaeIP2jOZPRF4l1KT1SN2AzGhvUgPPKFrkXgWNiJ3RI+ZnJ9Pid2DbHVvv+Faj57nkLQxohj2v9qBybEWNjn3uZyEW7z9q0z7GCT0aYF7xk6qTFJIpAV9KnN66LWtPDxmNk1sAe9cB7K6IG06GBfL3vZsVPPlRhtQfRdg4fkw3Gx5y/nTLtXCIxE7msWOVn7a1LZL3Jf/yIR40IpSyKhgfwgIP8d1EeM= spamdiagnosticoutput: 1:99 spamdiagnosticmetadata: NSPM MIME-Version: 1.0 X-OriginatorOrg: emutex.com X-MS-Exchange-CrossTenant-Network-Message-Id: b2b1ac5f-2cc7-47f8-c23d-08d63b6ee385 X-MS-Exchange-CrossTenant-originalarrivaltime: 26 Oct 2018 18:14:37.3993 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 84cc844b-5c53-4da1-99da-34ef4089ea8e X-MS-Exchange-Transport-CrossTenantHeadersStamped: HE1P190MB0156 Sender: linux-iio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add device driver for the HSC pressure sensors with SPI interface. In addition to the main measurement -pressure- these sensors also provide temperature measurement. Signed-off-by: Carlos Iglesias --- MAINTAINERS | 6 + drivers/iio/pressure/Kconfig | 10 + drivers/iio/pressure/Makefile | 2 + drivers/iio/pressure/hsc_spi.c | 543 +++++++++++++++++++++++++++++++++ 4 files changed, 561 insertions(+) create mode 100644 drivers/iio/pressure/hsc_spi.c diff --git a/MAINTAINERS b/MAINTAINERS index 556f902b3766..a4af4cbd4b0f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6696,6 +6696,12 @@ W: http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi S: Maintained F: fs/hpfs/ +HSC SERIES PRESSURE SENSORS +M; Carlos Iglesias +S: Maintained +F: Documentation/devicetree/bindings/iio/pressure/hsc_spi.txt +F: drivers/iio/pressure/hsc_spi.c + HSI SUBSYSTEM M: Sebastian Reichel T: git git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-hsi.git diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index eaa7cfcb4c2a..593663d4ffa8 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -227,4 +227,14 @@ config ZPA2326_SPI tristate select REGMAP_SPI +config HSC_SPI + tristate "Honeywell HSC pressure sensors (SPI)" + depends on SPI_MASTER + help + Say yes here to build support for the Honeywell HSC range of + pressure sensors (SPI interface only). + + To compile this driver as a module, choose M here: the module will + be called hsc_spi. + endmenu diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index c2058d7b2f93..b1db2116e9f6 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -31,3 +31,5 @@ obj-$(CONFIG_ZPA2326_SPI) += zpa2326_spi.o obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o + +obj-$(CONFIG_HSC_SPI) += hsc_spi.o diff --git a/drivers/iio/pressure/hsc_spi.c b/drivers/iio/pressure/hsc_spi.c new file mode 100644 index 000000000000..efe45a09da8f --- /dev/null +++ b/drivers/iio/pressure/hsc_spi.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * hsc_spi.c - Driver for Honeywell HSC pressure sensors with + * SPI interface + * + * Copyright (c) 2018 Carlos Iglesias + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define HSC_MAX_SPI_FREQ_HZ 400000 + +#define HSC_TEMP_BITS 11 +#define HSC_PRESS_BITS 14 +#define HSC_TEMP_MASK (0x7FF) +#define HSC_TEMP_SHIFT (5) + +#define HSC_STATUS_S0 BIT(14) +#define HSC_STATUS_S1 BIT(15) +#define HSC_STATUS_MSK ((HSC_STATUS_S0) | (HSC_STATUS_S1)) +#define HSC_STATUS_CMD HSC_STATUS_S0 +#define HSC_STATUS_STALE HSC_STATUS_S1 +#define HSC_STATUS_DIAG ((HSC_STATUS_S0) | (HSC_STATUS_S1)) + +static inline int hsc_status_error(struct device dev, int val) +{ + int st_check = val & HSC_STATUS_MSK; + + if (st_check) { + switch (st_check) { + case HSC_STATUS_CMD: + dev_warn(&dev, "%s:Device in COMMAND MODE\n", + __func__); + return -EIO; + case HSC_STATUS_STALE: + dev_warn(&dev, "%s:Stale data - sampling too fast?\n", + __func__); + return -EAGAIN; + case HSC_STATUS_DIAG: + dev_warn(&dev, "%s:Calibration signature changed\n", + __func__); + return -EIO; + default: + dev_err(&dev, "%s:Invalid status code (%d)\n", + __func__, st_check); + return -EIO; + } + } + + return 0; +} + +enum hsc_variant { + /* Note: Only the absolute range sensors are supported */ + HSC001BAA, HSC001BAB, HSC001BAC, HSC001BAF, + HSC1_6BAA, HSC1_6BAB, HSC1_6BAC, HSC1_6BAF, + HSC2_5BAA, HSC2_5BAB, HSC2_5BAC, HSC2_5BAF, + HSC004BAA, HSC004BAB, HSC004BAC, HSC004BAF, + HSC006BAA, HSC006BAB, HSC006BAC, HSC006BAF, + HSC010BAA, HSC010BAB, HSC010BAC, HSC010BAF, + + HSC100KAA, HSC100KAB, HSC100KAC, HSC100KAF, + HSC160KAA, HSC160KAB, HSC160KAC, HSC160KAF, + HSC250KAA, HSC250KAB, HSC250KAC, HSC250KAF, + HSC400KAA, HSC400KAB, HSC400KAC, HSC400KAF, + HSC600KAA, HSC600KAB, HSC600KAC, HSC600KAF, + HSC001GAA, HSC001GAB, HSC001GAC, HSC001GAF, + + HSC015PAA, HSC015PAB, HSC015PAC, HSC015PAF, + HSC030PAA, HSC030PAB, HSC030PAC, HSC030PAF, + HSC060PAA, HSC060PAB, HSC060PAC, HSC060PAF, + HSC100PAA, HSC100PAB, HSC100PAC, HSC100PAF, + HSC150PAA, HSC150PAB, HSC150PAC, HSC150PAF, +}; + +enum hsc_meas_channel { + HSC_CH_PRESSURE, + HSC_CH_TEMPERATURE +}; + +struct hsc_config { + int pmin; /* Lower pressure limit */ + int pmax; /* Upper pressure limit */ + int rmin; /* Lower transfer function limit (%) */ + int rmax; /* Upper transfer function limit (%) */ + int knum; /* Pressure kPa conversion factor (numerator) */ + int kden; /* Pressure kPa conversion factor /denominator) */ +}; + +struct hsc_fract_val { + int num; /* numerator */ + int den; /* denominator */ +}; + +struct hsc_state { + struct device *dev; + struct spi_device *spi_dev; + struct spi_transfer spi_xfer; + struct spi_message spi_msg; + __be16 rx_buf[2]; + + /* Model-dependent values */ + struct hsc_fract_val scale; + struct hsc_fract_val offset; +}; + +#define HSC_CONFIG(_pmin, _pmax, _rmin, _rmax, _knum, _kden) { \ + .pmin = (_pmin), \ + .pmax = (_pmax), \ + .rmin = (_rmin), \ + .rmax = (_rmax), \ + .knum = (_knum), \ + .kden = (_kden), \ + } + +static struct hsc_config hsc_cfg[] = { + /* Absolute range, mbar */ + [HSC001BAA] = HSC_CONFIG(0, 1000, 10, 90, 1, 10), + [HSC001BAB] = HSC_CONFIG(0, 1000, 5, 95, 1, 10), + [HSC001BAC] = HSC_CONFIG(0, 1000, 5, 85, 1, 10), + [HSC001BAF] = HSC_CONFIG(0, 1000, 4, 94, 1, 10), + [HSC1_6BAA] = HSC_CONFIG(0, 1600, 10, 90, 1, 10), + [HSC1_6BAB] = HSC_CONFIG(0, 1600, 5, 95, 1, 10), + [HSC1_6BAC] = HSC_CONFIG(0, 1600, 5, 85, 1, 10), + [HSC1_6BAF] = HSC_CONFIG(0, 1600, 4, 94, 1, 10), + [HSC2_5BAA] = HSC_CONFIG(0, 2500, 10, 90, 1, 10), + [HSC2_5BAB] = HSC_CONFIG(0, 2500, 5, 95, 1, 10), + [HSC2_5BAC] = HSC_CONFIG(0, 2500, 5, 85, 1, 10), + [HSC2_5BAF] = HSC_CONFIG(0, 2500, 4, 94, 1, 10), + [HSC004BAA] = HSC_CONFIG(0, 4000, 10, 90, 1, 10), + [HSC004BAB] = HSC_CONFIG(0, 4000, 5, 95, 1, 10), + [HSC004BAC] = HSC_CONFIG(0, 4000, 5, 85, 1, 10), + [HSC004BAF] = HSC_CONFIG(0, 4000, 4, 94, 1, 10), + [HSC006BAA] = HSC_CONFIG(0, 6000, 10, 90, 1, 10), + [HSC006BAB] = HSC_CONFIG(0, 6000, 5, 95, 1, 10), + [HSC006BAC] = HSC_CONFIG(0, 6000, 5, 85, 1, 10), + [HSC006BAF] = HSC_CONFIG(0, 6000, 4, 94, 1, 10), + [HSC010BAA] = HSC_CONFIG(0, 10000, 10, 90, 1, 10), + [HSC010BAB] = HSC_CONFIG(0, 10000, 5, 95, 1, 10), + [HSC010BAC] = HSC_CONFIG(0, 10000, 5, 85, 1, 10), + [HSC010BAF] = HSC_CONFIG(0, 10000, 4, 94, 1, 10), + /* Absolute range, kPa */ + [HSC100KAA] = HSC_CONFIG(0, 100, 10, 90, 1, 1), + [HSC100KAB] = HSC_CONFIG(0, 100, 5, 95, 1, 1), + [HSC100KAC] = HSC_CONFIG(0, 100, 5, 85, 1, 1), + [HSC100KAF] = HSC_CONFIG(0, 100, 4, 94, 1, 1), + [HSC160KAA] = HSC_CONFIG(0, 160, 10, 90, 1, 1), + [HSC160KAB] = HSC_CONFIG(0, 160, 5, 95, 1, 1), + [HSC160KAC] = HSC_CONFIG(0, 160, 5, 85, 1, 1), + [HSC160KAF] = HSC_CONFIG(0, 160, 4, 94, 1, 1), + [HSC250KAA] = HSC_CONFIG(0, 250, 10, 90, 1, 1), + [HSC250KAB] = HSC_CONFIG(0, 250, 5, 95, 1, 1), + [HSC250KAC] = HSC_CONFIG(0, 250, 5, 85, 1, 1), + [HSC250KAF] = HSC_CONFIG(0, 250, 4, 94, 1, 1), + [HSC400KAA] = HSC_CONFIG(0, 400, 10, 90, 1, 1), + [HSC400KAB] = HSC_CONFIG(0, 400, 5, 95, 1, 1), + [HSC400KAC] = HSC_CONFIG(0, 400, 5, 85, 1, 1), + [HSC400KAF] = HSC_CONFIG(0, 400, 4, 94, 1, 1), + [HSC600KAA] = HSC_CONFIG(0, 600, 10, 90, 1, 1), + [HSC600KAB] = HSC_CONFIG(0, 600, 5, 95, 1, 1), + [HSC600KAC] = HSC_CONFIG(0, 600, 5, 85, 1, 1), + [HSC600KAF] = HSC_CONFIG(0, 600, 4, 94, 1, 1), + [HSC001GAA] = HSC_CONFIG(0, 1000, 10, 90, 1, 1), + [HSC001GAB] = HSC_CONFIG(0, 1000, 5, 95, 1, 1), + [HSC001GAC] = HSC_CONFIG(0, 1000, 5, 85, 1, 1), + [HSC001GAF] = HSC_CONFIG(0, 1000, 4, 94, 1, 1), + /* Absolute range, psi */ + [HSC015PAA] = HSC_CONFIG(0, 15, 10, 90, 6895, 1000), + [HSC015PAB] = HSC_CONFIG(0, 15, 5, 95, 6895, 1000), + [HSC015PAC] = HSC_CONFIG(0, 15, 5, 85, 6895, 1000), + [HSC015PAF] = HSC_CONFIG(0, 15, 4, 94, 6895, 1000), + [HSC030PAA] = HSC_CONFIG(0, 30, 10, 90, 6895, 1000), + [HSC030PAB] = HSC_CONFIG(0, 30, 5, 95, 6895, 1000), + [HSC030PAC] = HSC_CONFIG(0, 30, 5, 85, 6895, 1000), + [HSC030PAF] = HSC_CONFIG(0, 30, 4, 94, 6895, 1000), + [HSC060PAA] = HSC_CONFIG(0, 60, 10, 90, 6895, 1000), + [HSC060PAB] = HSC_CONFIG(0, 60, 5, 95, 6895, 1000), + [HSC060PAC] = HSC_CONFIG(0, 60, 5, 85, 6895, 1000), + [HSC060PAF] = HSC_CONFIG(0, 60, 4, 94, 6895, 1000), + [HSC100PAA] = HSC_CONFIG(0, 100, 10, 90, 6895, 1000), + [HSC100PAB] = HSC_CONFIG(0, 100, 5, 95, 6895, 1000), + [HSC100PAC] = HSC_CONFIG(0, 100, 5, 85, 6895, 1000), + [HSC100PAF] = HSC_CONFIG(0, 100, 4, 94, 6895, 1000), + [HSC150PAA] = HSC_CONFIG(0, 150, 10, 90, 6895, 1000), + [HSC150PAB] = HSC_CONFIG(0, 150, 5, 95, 6895, 1000), + [HSC150PAC] = HSC_CONFIG(0, 150, 5, 85, 6895, 1000), + [HSC150PAF] = HSC_CONFIG(0, 150, 4, 94, 6895, 1000), +}; + +static const struct iio_chan_spec hsc_channels[] = { + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .channel = HSC_CH_PRESSURE, + .scan_type = { + .sign = 'u', + .realbits = HSC_PRESS_BITS, + .storagebits = 16, + .shift = 0, + .endianness = IIO_BE, + } + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .channel = HSC_CH_TEMPERATURE, + .scan_type = { + .sign = 'u', + .realbits = HSC_TEMP_BITS, + .storagebits = 16, + .shift = HSC_TEMP_SHIFT, + .endianness = IIO_BE, + } + } +}; + +static int hsc_get_pressure(struct hsc_state *state) +{ + int ret = 0; + int error; + + state->spi_xfer.len = 2; + ret = spi_sync(state->spi_dev, &state->spi_msg); + if (ret) + return ret; + + ret = be16_to_cpu(state->rx_buf[0]); + + error = hsc_status_error(state->spi_dev->dev, ret); + if (error) + return error; + + return ret; +} + +static int hsc_get_temperature(struct hsc_state *state) +{ + int ret; + int error; + + state->spi_xfer.len = 4; + ret = spi_sync(state->spi_dev, &state->spi_msg); + if (ret) + return ret; + + ret = be16_to_cpu(state->rx_buf[0]); + error = hsc_status_error(state->spi_dev->dev, ret); + if (error) + return error; + + ret = be16_to_cpu(state->rx_buf[1]); + ret = (ret >> HSC_TEMP_SHIFT) & HSC_TEMP_MASK; + + return ret; +} + +static int hsc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct hsc_state *state = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->channel) { + case HSC_CH_PRESSURE: + ret = hsc_get_pressure(state); + if (ret < 0) + break; + *val = ret; + ret = IIO_VAL_INT; + break; + case HSC_CH_TEMPERATURE: + ret = hsc_get_temperature(state); + if (ret < 0) + break; + *val = ret; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + dev_err(state->dev, + "%s - IIO_CHAN_INFO_RAW-bad channel (%d)\n", + __func__, chan->channel); + break; + } + break; + case IIO_CHAN_INFO_OFFSET: + switch (chan->channel) { + case HSC_CH_PRESSURE: + *val = state->offset.num; + *val2 = state->offset.den; + ret = IIO_VAL_FRACTIONAL; + break; + case HSC_CH_TEMPERATURE: + *val = BIT(HSC_TEMP_BITS) - 1; + *val2 = -200 / 50; + ret = IIO_VAL_FRACTIONAL; + break; + default: + dev_err(state->dev, + "%s - IIO_CHAN_INFO_OFFSET-bad channel (%d)\n", + __func__, chan->channel); + ret = -EINVAL; + break; + } + break; + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case HSC_CH_PRESSURE: /* output unit is kPa */ + *val = state->scale.num; + *val2 = state->scale.den; + ret = IIO_VAL_FRACTIONAL; + break; + case HSC_CH_TEMPERATURE: /* output unit is milli Celsius */ + *val = 200 * 1000; + *val2 = BIT(HSC_TEMP_BITS) - 1; + ret = IIO_VAL_FRACTIONAL; + break; + default: + dev_err(state->dev, + "%s - IIO_CHAN_INFO_SCALE-bad channel (%d)\n", + __func__, chan->channel); + ret = -EINVAL; + break; + } + break; + default: + dev_err(state->dev, "%s - mask = %ld (INVALID)\n", + __func__, mask); + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct iio_info hsc_info = { + .read_raw = hsc_read_raw, +}; + +static void hsc_init_device(struct iio_dev *indio_dev) +{ + struct hsc_state *state = iio_priv(indio_dev); + const struct hsc_config *cfg = of_device_get_match_data(state->dev); + + /* Pressure offset value */ + state->offset.num = BIT(HSC_PRESS_BITS) * + (cfg->pmin * cfg->rmax - cfg->pmax * cfg->rmin); + state->offset.den = 100 * (cfg->pmax - cfg->pmin); + + /* Pressure scale value */ + state->scale.num = 100 * cfg->knum * (cfg->pmax - cfg->pmin); + state->scale.den = BIT(HSC_PRESS_BITS) * + cfg->kden * (cfg->rmax - cfg->rmin); +} + +static int hsc_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct hsc_state *state; + struct iio_dev *indio_dev; + int ret; + + if (spi->max_speed_hz > HSC_MAX_SPI_FREQ_HZ) { + dev_warn(dev, "SPI CLK, %d Hz exceeds %d Hz - changed to max\n", + spi->max_speed_hz, + HSC_MAX_SPI_FREQ_HZ); + spi->max_speed_hz = HSC_MAX_SPI_FREQ_HZ; + } + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + + ret = spi_setup(spi); + if (ret < 0) { + dev_err(dev, "%s - Error in spi_setup()\n", __func__); + return ret; + } + + indio_dev = devm_iio_device_alloc(dev, sizeof(*state)); + if (!indio_dev) { + dev_err(dev, "%s - Error allocating iio_device\n", __func__); + return -ENOMEM; + } + + state = iio_priv(indio_dev); + spi_set_drvdata(spi, state); + state->spi_dev = spi; + state->dev = dev; + + indio_dev->dev.parent = dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &hsc_info; + indio_dev->channels = hsc_channels; + indio_dev->num_channels = ARRAY_SIZE(hsc_channels); + + state->spi_xfer.rx_buf = &state->rx_buf[0]; + state->spi_xfer.tx_buf = NULL; + state->spi_xfer.cs_change = 0; + spi_message_init(&state->spi_msg); + spi_message_add_tail(&state->spi_xfer, &state->spi_msg); + + hsc_init_device(indio_dev); + + ret = iio_device_register(indio_dev); + if (ret < 0) + dev_err(dev, "iio_device_register failed: %d\n", ret); + + dev_dbg(dev, "%s - scale = %d/%d, offset = %d/%d\n", __func__, + state->scale.num, state->scale.den, + state->offset.num, state->offset.den); + + return ret; +} + +static int hsc_spi_remove(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + iio_device_unregister(indio_dev); + + return 0; +} + +static const struct of_device_id hsc_of_match[] = { + { .compatible = "honeywell,hsc001baa", .data = &hsc_cfg[HSC001BAA] }, + { .compatible = "honeywell,hsc001bab", .data = &hsc_cfg[HSC001BAB] }, + { .compatible = "honeywell,hsc001bac", .data = &hsc_cfg[HSC001BAC] }, + { .compatible = "honeywell,hsc001baf", .data = &hsc_cfg[HSC001BAF] }, + + { .compatible = "honeywell,hsc1_6baa", .data = &hsc_cfg[HSC1_6BAA] }, + { .compatible = "honeywell,hsc1_6bab", .data = &hsc_cfg[HSC1_6BAB] }, + { .compatible = "honeywell,hsc1_6bac", .data = &hsc_cfg[HSC1_6BAC] }, + { .compatible = "honeywell,hsc1_6baf", .data = &hsc_cfg[HSC1_6BAF] }, + + { .compatible = "honeywell,hsc2_5baa", .data = &hsc_cfg[HSC2_5BAA] }, + { .compatible = "honeywell,hsc2_5bab", .data = &hsc_cfg[HSC2_5BAB] }, + { .compatible = "honeywell,hsc2_5bac", .data = &hsc_cfg[HSC2_5BAC] }, + { .compatible = "honeywell,hsc2_5baf", .data = &hsc_cfg[HSC2_5BAF] }, + + { .compatible = "honeywell,hsc004baa", .data = &hsc_cfg[HSC004BAA] }, + { .compatible = "honeywell,hsc004bab", .data = &hsc_cfg[HSC004BAB] }, + { .compatible = "honeywell,hsc004bac", .data = &hsc_cfg[HSC004BAC] }, + { .compatible = "honeywell,hsc004baf", .data = &hsc_cfg[HSC004BAF] }, + + { .compatible = "honeywell,hsc006baa", .data = &hsc_cfg[HSC006BAA] }, + { .compatible = "honeywell,hsc006bab", .data = &hsc_cfg[HSC006BAB] }, + { .compatible = "honeywell,hsc006bac", .data = &hsc_cfg[HSC006BAC] }, + { .compatible = "honeywell,hsc006baf", .data = &hsc_cfg[HSC006BAF] }, + + { .compatible = "honeywell,hsc010baa", .data = &hsc_cfg[HSC010BAA] }, + { .compatible = "honeywell,hsc010bab", .data = &hsc_cfg[HSC010BAB] }, + { .compatible = "honeywell,hsc010bac", .data = &hsc_cfg[HSC010BAC] }, + { .compatible = "honeywell,hsc010baf", .data = &hsc_cfg[HSC010BAF] }, + + { .compatible = "honeywell,hsc100kaa", .data = &hsc_cfg[HSC100KAA] }, + { .compatible = "honeywell,hsc100kab", .data = &hsc_cfg[HSC100KAB] }, + { .compatible = "honeywell,hsc100kac", .data = &hsc_cfg[HSC100KAC] }, + { .compatible = "honeywell,hsc100kaf", .data = &hsc_cfg[HSC100KAF] }, + + { .compatible = "honeywell,hsc160kaa", .data = &hsc_cfg[HSC160KAA] }, + { .compatible = "honeywell,hsc160kab", .data = &hsc_cfg[HSC160KAB] }, + { .compatible = "honeywell,hsc160kac", .data = &hsc_cfg[HSC160KAC] }, + { .compatible = "honeywell,hsc160kaf", .data = &hsc_cfg[HSC160KAF] }, + + { .compatible = "honeywell,hsc250kaa", .data = &hsc_cfg[HSC250KAA] }, + { .compatible = "honeywell,hsc250kab", .data = &hsc_cfg[HSC250KAB] }, + { .compatible = "honeywell,hsc250kac", .data = &hsc_cfg[HSC250KAC] }, + { .compatible = "honeywell,hsc250kaf", .data = &hsc_cfg[HSC250KAF] }, + + { .compatible = "honeywell,hsc400kaa", .data = &hsc_cfg[HSC400KAA] }, + { .compatible = "honeywell,hsc400kab", .data = &hsc_cfg[HSC400KAB] }, + { .compatible = "honeywell,hsc400kac", .data = &hsc_cfg[HSC400KAC] }, + { .compatible = "honeywell,hsc400kaf", .data = &hsc_cfg[HSC400KAF] }, + + { .compatible = "honeywell,hsc600kaa", .data = &hsc_cfg[HSC600KAA] }, + { .compatible = "honeywell,hsc600kab", .data = &hsc_cfg[HSC600KAB] }, + { .compatible = "honeywell,hsc600kac", .data = &hsc_cfg[HSC600KAC] }, + { .compatible = "honeywell,hsc600kaf", .data = &hsc_cfg[HSC600KAF] }, + + { .compatible = "honeywell,hsc001gaa", .data = &hsc_cfg[HSC001GAA] }, + { .compatible = "honeywell,hsc001gab", .data = &hsc_cfg[HSC001GAB] }, + { .compatible = "honeywell,hsc001gac", .data = &hsc_cfg[HSC001GAC] }, + { .compatible = "honeywell,hsc001gaf", .data = &hsc_cfg[HSC001GAF] }, + + { .compatible = "honeywell,hsc015paa", .data = &hsc_cfg[HSC015PAA] }, + { .compatible = "honeywell,hsc015pab", .data = &hsc_cfg[HSC015PAB] }, + { .compatible = "honeywell,hsc015pac", .data = &hsc_cfg[HSC015PAC] }, + { .compatible = "honeywell,hsc015paf", .data = &hsc_cfg[HSC015PAF] }, + + { .compatible = "honeywell,hsc030paa", .data = &hsc_cfg[HSC030PAA] }, + { .compatible = "honeywell,hsc030pab", .data = &hsc_cfg[HSC030PAB] }, + { .compatible = "honeywell,hsc030pac", .data = &hsc_cfg[HSC030PAC] }, + { .compatible = "honeywell,hsc030paf", .data = &hsc_cfg[HSC030PAF] }, + + { .compatible = "honeywell,hsc060paa", .data = &hsc_cfg[HSC060PAA] }, + { .compatible = "honeywell,hsc060pab", .data = &hsc_cfg[HSC060PAB] }, + { .compatible = "honeywell,hsc060pac", .data = &hsc_cfg[HSC060PAC] }, + { .compatible = "honeywell,hsc060paf", .data = &hsc_cfg[HSC060PAF] }, + + { .compatible = "honeywell,hsc100paa", .data = &hsc_cfg[HSC100PAA] }, + { .compatible = "honeywell,hsc100pab", .data = &hsc_cfg[HSC100PAB] }, + { .compatible = "honeywell,hsc100pac", .data = &hsc_cfg[HSC100PAC] }, + { .compatible = "honeywell,hsc100paf", .data = &hsc_cfg[HSC100PAF] }, + + { .compatible = "honeywell,hsc150paa", .data = &hsc_cfg[HSC150PAA] }, + { .compatible = "honeywell,hsc150pab", .data = &hsc_cfg[HSC150PAB] }, + { .compatible = "honeywell,hsc150pac", .data = &hsc_cfg[HSC150PAC] }, + { .compatible = "honeywell,hsc150paf", .data = &hsc_cfg[HSC150PAF] }, + + { }, +}; +MODULE_DEVICE_TABLE(of, hsc_of_match); + +static struct spi_driver hsc_spi_driver = { + .probe = hsc_spi_probe, + .remove = hsc_spi_remove, + .driver = { + .name = "hsc_spi_pressure_sensor", + .of_match_table = hsc_of_match, + }, +}; + +module_spi_driver(hsc_spi_driver); + +MODULE_AUTHOR("Carlos Iglesias "); +MODULE_DESCRIPTION("Honeywell HSC SPI pressure sensor driver"); +MODULE_LICENSE("GPL v2");