From patchwork Mon Jun 25 15:12:24 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 10486647 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id C901B6023A for ; Mon, 25 Jun 2018 15:17:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C26B92835B for ; Mon, 25 Jun 2018 15:17:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B4E0D2850D; Mon, 25 Jun 2018 15:17:40 +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=-2.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 01F222835B for ; Mon, 25 Jun 2018 15:17:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=SUHFgE/xuTSbQqUtMrdtCZMGe0R8PyjQt90wovMI8g4=; b=Wmj+/HTiLM4bt5/9I9klemMDI3 YiDiu5bMdWLipLaFveaBrMPMFYVhlBg/A7Lccu3kidMJ61yhJzPEnINgeXeasH+cHAQC11W5njc2P cbLfSPw1Jd5r7tDt2cpDs/dGPR3YpDLlZXWjXMh+/ixDsuQVVpogOgUyokD+UR/tsmNdr2WIOla+Z pZ7qJxRhQUueg/k0UEVw2uMkmQaE1arKM2Mv3P7Obtd3MFQrRt5wiECb1BgIKLOfgwsySHEmYQqTW kgBLRg9hYxakj7mqwimv19r7tlMy3wXkXtdlAWrtRMmEIVp/7V1Z6zhoQrF+/ZoFMPXxLXnycFcMU 4DAHP80Q==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1fXTFA-000395-Ef; Mon, 25 Jun 2018 15:17:28 +0000 Received: from merlin.infradead.org ([2001:8b0:10b:1231::1]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1fXTB3-0007xS-8y for linux-arm-kernel@bombadil.infradead.org; Mon, 25 Jun 2018 15:13:13 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=merlin.20170209; h=References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Sender:Reply-To:MIME-Version:Content-Type: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id: List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=w+y1hhRBB4WXPSfl5IlHcJohIZ2nkBVvxfrBpWUHwkI=; b=afrLuAZh8aVx2BpPZRzISNWNq hef603Xw7z1dndf2nrHyHukgoof0wkYv/IrIJVCxuITJB+0F2DDiqst5hwZ2rSKX3CnTN//4/eWcW w92LDoETE/w/MO68xRemfeci1PWyvxSsPNh2C+MR3DIDhpE59TK+IRsJTpFdH8MSurADs2cUxGXVk ggbZbAsoBORmRz385zyrs6ISqkD2XxR65Fg6m2meaBuHsQ0PpOhoEm97Ntfr5QuXJA5lzbNW1uGHM Lk0a53JBhEPBuNC25brZ89st58/4Bp13w8y0gWcQ+C9KtQTtLUp86W9ZvefHRaRKextEF92SoeCtw hE8myDtdg==; Received: from mail.bootlin.com ([62.4.15.54]) by merlin.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1fXTAw-0003dV-Bt for linux-arm-kernel@lists.infradead.org; Mon, 25 Jun 2018 15:13:10 +0000 Received: by mail.bootlin.com (Postfix, from userid 110) id 6627820CF8; Mon, 25 Jun 2018 17:12:44 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-87-188.w90-88.abo.wanadoo.fr [90.88.29.188]) by mail.bootlin.com (Postfix) with ESMTPSA id E6E93203D9; Mon, 25 Jun 2018 17:12:43 +0200 (CEST) From: Miquel Raynal To: Gregory Clement , Jason Cooper , Andrew Lunn , Sebastian Hesselbarth , Zhang Rui , Eduardo Valentin Subject: [PATCH v2 08/23] thermal: armada: add multi-channel sensors support Date: Mon, 25 Jun 2018 17:12:24 +0200 Message-Id: <20180625151239.20976-9-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20180625151239.20976-1-miquel.raynal@bootlin.com> References: <20180625151239.20976-1-miquel.raynal@bootlin.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180625_111306_815969_DC456F00 X-CRM114-Status: GOOD ( 25.95 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , devicetree@vger.kernel.org, linux-pm@vger.kernel.org, Antoine Tenart , Catalin Marinas , Will Deacon , Maxime Chevallier , Nadav Haklai , David Sniatkiwicz , Rob Herring , Thomas Petazzoni , Miquel Raynal , linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP MVEBU thermal IP supports multiple channels. Each channel may have several sensors but for now each channel is wired to only one thermal sensor. The first channel always points to the so called internal sensor, within the thermal IP. There is usually one more channel (with one sensor each time) per CPU. The code has been written to support possible evolutions of the ap806 IP that would embed more CPUs and thus more channels to select. Each channel should be referenced in the device tree as an independent thermal zone. Add the possibility to read each of these sensors through sysfs by registering all the sensors (translated in "thermal_zone"). Also add a mutex on these accesses to avoid read conflicts (only one channel/sensor may be selected and read at a time). Signed-off-by: Miquel Raynal --- drivers/thermal/armada_thermal.c | 130 +++++++++++++++++++++++++++++++++------ 1 file changed, 111 insertions(+), 19 deletions(-) diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 8a734264ecdd..2b11bf929b64 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -49,8 +49,13 @@ #define CONTROL0_TSEN_RESET BIT(1) #define CONTROL0_TSEN_ENABLE BIT(2) #define CONTROL0_TSEN_AVG_BYPASS BIT(6) +#define CONTROL0_TSEN_CHAN_SHIFT 13 +#define CONTROL0_TSEN_CHAN_MASK 0xF #define CONTROL0_TSEN_OSR_SHIFT 24 #define CONTROL0_TSEN_OSR_MAX 0x3 +#define CONTROL0_TSEN_MODE_SHIFT 30 +#define CONTROL0_TSEN_MODE_EXTERNAL 0x2 +#define CONTROL0_TSEN_MODE_MASK 0x3 #define CONTROL1_TSEN_AVG_SHIFT 0 #define CONTROL1_TSEN_AVG_MASK 0x7 @@ -67,7 +72,9 @@ struct armada_thermal_priv { struct device *dev; struct regmap *syscon; char zone_name[THERMAL_NAME_LENGTH]; + struct mutex update_lock; struct armada_thermal_data *data; + int current_channel; }; struct armada_thermal_data { @@ -94,6 +101,9 @@ struct armada_thermal_data { unsigned int syscon_control0_off; unsigned int syscon_control1_off; unsigned int syscon_status_off; + + /* One sensor is in the thermal IC, the others are in the CPUs if any */ + unsigned int cpu_nr; }; struct armada_drvdata { @@ -111,9 +121,11 @@ struct armada_drvdata { * struct armada_thermal_sensor - hold the information of one thermal sensor * @thermal: pointer to the local private structure * @tzd: pointer to the thermal zone device + * @id: identifier of the thermal sensor */ struct armada_thermal_sensor { struct armada_thermal_priv *priv; + int id; }; static void armadaxp_init(struct platform_device *pdev, @@ -181,14 +193,15 @@ static void armada375_init(struct platform_device *pdev, msleep(50); } -static void armada_wait_sensor_validity(struct armada_thermal_priv *priv) +static int armada_wait_sensor_validity(struct armada_thermal_priv *priv) { u32 reg; - regmap_read_poll_timeout(priv->syscon, priv->data->syscon_status_off, - reg, reg & priv->data->is_valid_bit, - STATUS_POLL_PERIOD_US, - STATUS_POLL_TIMEOUT_US); + return regmap_read_poll_timeout(priv->syscon, + priv->data->syscon_status_off, reg, + reg & priv->data->is_valid_bit, + STATUS_POLL_PERIOD_US, + STATUS_POLL_TIMEOUT_US); } static void armada380_init(struct platform_device *pdev, @@ -264,6 +277,58 @@ static bool armada_is_valid(struct armada_thermal_priv *priv) return reg & priv->data->is_valid_bit; } +/* There is currently no board with more than one sensor per channel */ +static int armada_select_channel(struct armada_thermal_priv *priv, int channel) +{ + struct armada_thermal_data *data = priv->data; + u32 ctrl0; + + if (channel < 0 || channel > priv->data->cpu_nr) + return -EINVAL; + + if (priv->current_channel == channel) + return 0; + + /* Stop the measurements */ + regmap_read(priv->syscon, data->syscon_control0_off, &ctrl0); + ctrl0 &= ~CONTROL0_TSEN_START; + regmap_write(priv->syscon, data->syscon_control0_off, ctrl0); + + /* Reset the mode, internal sensor will be automatically selected */ + ctrl0 &= ~(CONTROL0_TSEN_MODE_MASK << CONTROL0_TSEN_MODE_SHIFT); + + /* Other channels are external and should be selected accordingly */ + if (channel) { + /* Change the mode to external */ + ctrl0 |= CONTROL0_TSEN_MODE_EXTERNAL << + CONTROL0_TSEN_MODE_SHIFT; + /* Select the sensor */ + ctrl0 &= ~(CONTROL0_TSEN_CHAN_MASK << CONTROL0_TSEN_CHAN_SHIFT); + ctrl0 |= (channel - 1) << CONTROL0_TSEN_CHAN_SHIFT; + } + + /* Actually set the mode/channel */ + regmap_write(priv->syscon, data->syscon_control0_off, ctrl0); + priv->current_channel = channel; + + /* Re-start the measurements */ + ctrl0 |= CONTROL0_TSEN_START; + regmap_write(priv->syscon, data->syscon_control0_off, ctrl0); + + /* + * The IP has a latency of ~15ms, so after updating the selected source, + * we must absolutely wait for the sensor validity bit to ensure we read + * actual data. + */ + if (armada_wait_sensor_validity(priv)) { + dev_err(priv->dev, + "Temperature sensor reading not valid\n"); + return -EIO; + } + + return 0; +} + static int armada_read_sensor(struct armada_thermal_priv *priv, long *temp) { u32 reg, div; @@ -322,10 +387,23 @@ static int armada_get_temp(void *_sensor, int *temp) long temperature; int ret; + mutex_lock(&priv->update_lock); + + /* Select the desired channel */ + ret = armada_select_channel(priv, sensor->id); + if (ret) + goto unlock_mutex; + /* Do the actual reading */ ret = armada_read_sensor(priv, &temperature); + if (ret) + goto unlock_mutex; + *temp = temperature; +unlock_mutex: + mutex_unlock(&priv->update_lock); + return ret; } @@ -400,6 +478,7 @@ static const struct armada_thermal_data armada_ap806_data = { .syscon_control0_off = 0x84, .syscon_control1_off = 0x88, .syscon_status_off = 0x8C, + .cpu_nr = 4, }; static const struct armada_thermal_data armada_cp110_data = { @@ -532,10 +611,11 @@ static void armada_set_sane_name(struct platform_device *pdev, static int armada_thermal_probe(struct platform_device *pdev) { struct thermal_zone_device *tz; - struct armada_thermal_sensor *sensors; + struct armada_thermal_sensor *sensor; struct armada_drvdata *drvdata; const struct of_device_id *match; struct armada_thermal_priv *priv; + int sensor_id; int ret; match = of_match_device(armada_thermal_id_table, &pdev->dev); @@ -553,6 +633,8 @@ static int armada_thermal_probe(struct platform_device *pdev) priv->dev = &pdev->dev; priv->data = (struct armada_thermal_data *)match->data; + mutex_init(&priv->update_lock); + /* * Legacy DT bindings only described "control1" register (also referred * as "control MSB" on old documentation). Then, bindings moved to cover @@ -594,22 +676,32 @@ static int armada_thermal_probe(struct platform_device *pdev) if (ret) return ret; + priv->current_channel = -1; priv->data->init(pdev, priv); - sensors = devm_kzalloc(&pdev->dev, sizeof(struct armada_thermal_sensor), - GFP_KERNEL); - if (!sensors) - return -ENOMEM; + /* + * There is one channel for the IC and one per CPU (if any), each + * channel has one sensor. + */ + for (sensor_id = 0; sensor_id <= priv->data->cpu_nr; sensor_id++) { + sensor = devm_kzalloc(&pdev->dev, + sizeof(struct armada_thermal_sensor), + GFP_KERNEL); + if (!sensor) + return -ENOMEM; - sensors->priv = priv; - - tz = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, sensors, - &of_ops); - if (IS_ERR(tz)) { - dev_err(&pdev->dev, - "Failed to register thermal sensor (err: %ld)\n", - PTR_ERR(tz)); - return PTR_ERR(tz); + /* Register the sensor */ + sensor->priv = priv; + sensor->id = sensor_id; + tz = devm_thermal_zone_of_sensor_register(&pdev->dev, + sensor->id, sensor, + &of_ops); + if (IS_ERR(tz)) { + dev_info(&pdev->dev, "Thermal sensor %d unavailable\n", + sensor_id); + devm_kfree(&pdev->dev, sensor); + continue; + } } drvdata->type = SYSCON;