Received: by 2002:ac0:a5b6:0:0:0:0:0 with SMTP id m51-v6csp4324589imm; Wed, 30 May 2018 03:35:32 -0700 (PDT) X-Google-Smtp-Source: ADUXVKIy5rNTwTBa2pbS3bTZDVSUuSkrXEoQvE/0p8/Pyzw5+kP+452ShM3ST2ujO+Hxg8SpVkO/ X-Received: by 2002:a65:648a:: with SMTP id e10-v6mr1818926pgv.34.1527676532752; Wed, 30 May 2018 03:35:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527676532; cv=none; d=google.com; s=arc-20160816; b=EpbV1vkkZlwQqFEQp6ru/o75uGra5a5/s/OyAttMHTkjOGGo0b1B19EBROgoc1NmFH dA1vS0qCeygW84zxf2APCwZzq2iByQNmsV4FB3eAvG1mT57QANuesRfbQooUiEN/2yIu WHvxVzSLGSF2v/OZPyNXdhqJ1XFXV7bmyVtYfU6s9pS12GrMc5ByzCBxsEVSpbHLTu0S oBXVFbK+Tg2DU80K2PBXaBP9NZqLhHE1DS7pxPwcfTKYIyF+BcxCBHn7NmjASQFiCUGf ARC5QmcLwtJqQNGavc2dy09XeU/Qxo2nqjf7rml801q6t47fGFLdqy5uePoDWy5B4fo/ Tbiw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=bTcSKEFqAaSCiNtGLsQRGLDYjwNDVglPtfJYxrNxIy4=; b=cF7zv5Q1TMcWAj69PdIu30MuZfl3tJa14pGHOBww3hUsQ8zK5i9TGTU5Se14YEaItP P6ZfaRBaxwMc/LZKA1e2NxbvQjmh4HkA+5cg2/nXy9I7KCFOWB933vPbhsIBm3zlX08f 2xbF+8wtxuo3iE5m+jA3IA12jJHfgwUbQVTADGHNLpXQpsH74DmRehJFPmU+Ruartyoy gIpg1DOo6BH8hxkctGV6vYTk7xMsgcA0BlvB/+GWp7JU33TnWsFKwNJz1JKVJ2n/sU/s ClqwQPBJ2qfHQTKDPho2qx2P5yOKbJnzzBNxmdaa4/IIlNbf+jrFJtu0afVSu/CTCoGp cG7A== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b=k5/Nf+NQ; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id a65-v6si979893pge.133.2018.05.30.03.35.18; Wed, 30 May 2018 03:35:32 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b=k5/Nf+NQ; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751984AbeE3KeY (ORCPT + 99 others); Wed, 30 May 2018 06:34:24 -0400 Received: from mail-lf0-f66.google.com ([209.85.215.66]:46346 "EHLO mail-lf0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751361AbeE3KeL (ORCPT ); Wed, 30 May 2018 06:34:11 -0400 Received: by mail-lf0-f66.google.com with SMTP id h9-v6so3487651lfc.13; Wed, 30 May 2018 03:34:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=bTcSKEFqAaSCiNtGLsQRGLDYjwNDVglPtfJYxrNxIy4=; b=k5/Nf+NQnOjRg1bkN09NhHSKmZV8gnEpjgyfQ+PWbObiXg98EnrjrdydloQt5nNSOV J60sQChxyWFyG0QXwjG/2TTkXkgjSJFnvwWIU+hb8H8zeDchjgFTL2IE1jvMUsrT7Hvi D5wy3wPsE44J4xYlZVcVmE38HQJtEi7orqwcfnzEDVj6VbA6qCwUK5JhD4lsHUQdeG9i Tf5L4teC0Ccid8hV9V+O0rdcM3Ol5eGYzSZHsI/x0FkauK5xHhC3rstoi+U4iPOOa1s3 9caoFj96J2awG9u6YKWBRFbzLcj2GpAo1+KcTsFyzgCW6O3g/0ljKP2cDmWgquEsi0Zf jovw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=bTcSKEFqAaSCiNtGLsQRGLDYjwNDVglPtfJYxrNxIy4=; b=sCEFEw7fV/DRqKIotBXCoTtx1F3oyp1+oy9a4pyZfKcJKlgTlB7sfArPq0pDWScb5g BZlbgBWeiZ/9a7jA8kSTfmDRVXIyy50pA6GnrtzrydfjJGoVr8n5pe0rwZp/ARYDTDcN wMfShbpaBuNnV26bCJ+5hS5XFw2Jgz5ZxMmArTKq7X+bDKlGdXdiFAe7gEaXZqJip/Vc 80Swx1qAtMZ3neTrQCIb2iLwoLe0HQOQhFWYE3qeGR77RBvdwhDlJRyBTQWfQjkayw0K X+Q6GllSoeIzSBmp5PrKwobnsgtPso54ebdWYJmZ/uiUqQMma5CK6/uvd4GYkoOPhnbi tg1Q== X-Gm-Message-State: ALKqPwfYOqZZNzZ4Ja4pn7qKz7w7Ek994jQagakF9vXm+m+DviSYmvJi 6VUjTJgDoKxb6kucLwZfIqk= X-Received: by 2002:a2e:934f:: with SMTP id m15-v6mr1637022ljh.39.1527676449268; Wed, 30 May 2018 03:34:09 -0700 (PDT) Received: from xi.terra (c-8bb2e655.07-184-6d6c6d4.bbcust.telenor.se. [85.230.178.139]) by smtp.gmail.com with ESMTPSA id p17-v6sm6948104ljc.72.2018.05.30.03.34.07 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 30 May 2018 03:34:07 -0700 (PDT) Received: from johan by xi.terra with local (Exim 4.90_1) (envelope-from ) id 1fNyQg-0005QD-LQ; Wed, 30 May 2018 12:34:06 +0200 From: Johan Hovold To: Greg Kroah-Hartman , Rob Herring , Mark Rutland Cc: Andreas Kemnade , Arnd Bergmann , "H . Nikolaus Schaller" , Pavel Machek , Marcel Holtmann , Sebastian Reichel , Tony Lindgren , linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, Johan Hovold Subject: [PATCH v2 3/8] gnss: add generic serial driver Date: Wed, 30 May 2018 12:32:37 +0200 Message-Id: <20180530103242.20773-4-johan@kernel.org> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180530103242.20773-1-johan@kernel.org> References: <20180530103242.20773-1-johan@kernel.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a generic serial GNSS driver (library) which provides a common implementation for the gnss interface and power management (runtime and system suspend). This allows GNSS drivers for specific chip to be implemented by simply providing a set_power() callback to handle three states: ACTIVE, STANDBY and OFF. Signed-off-by: Johan Hovold --- drivers/gnss/Kconfig | 7 ++ drivers/gnss/Makefile | 3 + drivers/gnss/serial.c | 275 ++++++++++++++++++++++++++++++++++++++++++ drivers/gnss/serial.h | 47 ++++++++ 4 files changed, 332 insertions(+) create mode 100644 drivers/gnss/serial.c create mode 100644 drivers/gnss/serial.h diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig index 103fcc70992e..f8ee54f99a8d 100644 --- a/drivers/gnss/Kconfig +++ b/drivers/gnss/Kconfig @@ -9,3 +9,10 @@ menuconfig GNSS To compile this driver as a module, choose M here: the module will be called gnss. + +if GNSS + +config GNSS_SERIAL + tristate + +endif # GNSS diff --git a/drivers/gnss/Makefile b/drivers/gnss/Makefile index 1f7a7baab1d9..171aba71684d 100644 --- a/drivers/gnss/Makefile +++ b/drivers/gnss/Makefile @@ -5,3 +5,6 @@ obj-$(CONFIG_GNSS) += gnss.o gnss-y := core.o + +obj-$(CONFIG_GNSS_SERIAL) += gnss-serial.o +gnss-serial-y := serial.o diff --git a/drivers/gnss/serial.c b/drivers/gnss/serial.c new file mode 100644 index 000000000000..b01ba4438501 --- /dev/null +++ b/drivers/gnss/serial.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic serial GNSS receiver driver + * + * Copyright (C) 2018 Johan Hovold + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" + +static int gnss_serial_open(struct gnss_device *gdev) +{ + struct gnss_serial *gserial = gnss_get_drvdata(gdev); + struct serdev_device *serdev = gserial->serdev; + int ret; + + ret = serdev_device_open(serdev); + if (ret) + return ret; + + serdev_device_set_baudrate(serdev, gserial->speed); + serdev_device_set_flow_control(serdev, false); + + ret = pm_runtime_get_sync(&serdev->dev); + if (ret < 0) { + pm_runtime_put_noidle(&serdev->dev); + goto err_close; + } + + return 0; + +err_close: + serdev_device_close(serdev); + + return ret; +} + +static void gnss_serial_close(struct gnss_device *gdev) +{ + struct gnss_serial *gserial = gnss_get_drvdata(gdev); + struct serdev_device *serdev = gserial->serdev; + + serdev_device_close(serdev); + + pm_runtime_put(&serdev->dev); +} + +static int gnss_serial_write_raw(struct gnss_device *gdev, + const unsigned char *buf, size_t count) +{ + struct gnss_serial *gserial = gnss_get_drvdata(gdev); + struct serdev_device *serdev = gserial->serdev; + int ret; + + /* write is only buffered synchronously */ + ret = serdev_device_write(serdev, buf, count, 0); + if (ret < 0) + return ret; + + /* FIXME: determine if interrupted? */ + serdev_device_wait_until_sent(serdev, 0); + + return count; +} + +static const struct gnss_operations gnss_serial_gnss_ops = { + .open = gnss_serial_open, + .close = gnss_serial_close, + .write_raw = gnss_serial_write_raw, +}; + +static int gnss_serial_receive_buf(struct serdev_device *serdev, + const unsigned char *buf, size_t count) +{ + struct gnss_serial *gserial = serdev_device_get_drvdata(serdev); + struct gnss_device *gdev = gserial->gdev; + + return gnss_insert_raw(gdev, buf, count); +} + +static const struct serdev_device_ops gnss_serial_serdev_ops = { + .receive_buf = gnss_serial_receive_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +static int gnss_serial_set_power(struct gnss_serial *gserial, + enum gnss_serial_pm_state state) +{ + if (!gserial->ops || !gserial->ops->set_power) + return 0; + + return gserial->ops->set_power(gserial, state); +} + +/* + * FIXME: need to provide subdriver defaults or separate dt parsing from + * allocation. + */ +static int gnss_serial_parse_dt(struct serdev_device *serdev) +{ + struct gnss_serial *gserial = serdev_device_get_drvdata(serdev); + struct device_node *node = serdev->dev.of_node; + u32 speed = 4800; + + of_property_read_u32(node, "current-speed", &speed); + + gserial->speed = speed; + + return 0; +} + +struct gnss_serial *gnss_serial_allocate(struct serdev_device *serdev, + size_t data_size) +{ + struct gnss_serial *gserial; + struct gnss_device *gdev; + int ret; + + gserial = kzalloc(sizeof(*gserial) + data_size, GFP_KERNEL); + if (!gserial) + return ERR_PTR(-ENOMEM); + + gdev = gnss_allocate_device(&serdev->dev); + if (!gdev) { + ret = -ENOMEM; + goto err_free_gserial; + } + + gdev->ops = &gnss_serial_gnss_ops; + gnss_set_drvdata(gdev, gserial); + + gserial->serdev = serdev; + gserial->gdev = gdev; + + serdev_device_set_drvdata(serdev, gserial); + serdev_device_set_client_ops(serdev, &gnss_serial_serdev_ops); + + ret = gnss_serial_parse_dt(serdev); + if (ret) + goto err_put_device; + + return gserial; + +err_put_device: + gnss_put_device(gserial->gdev); +err_free_gserial: + kfree(gserial); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(gnss_serial_allocate); + +void gnss_serial_free(struct gnss_serial *gserial) +{ + gnss_put_device(gserial->gdev); + kfree(gserial); +}; +EXPORT_SYMBOL_GPL(gnss_serial_free); + +int gnss_serial_register(struct gnss_serial *gserial) +{ + struct serdev_device *serdev = gserial->serdev; + int ret; + + if (IS_ENABLED(CONFIG_PM)) { + pm_runtime_enable(&serdev->dev); + } else { + ret = gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE); + if (ret < 0) + return ret; + } + + ret = gnss_register_device(gserial->gdev); + if (ret) + goto err_disable_rpm; + + return 0; + +err_disable_rpm: + if (IS_ENABLED(CONFIG_PM)) + pm_runtime_disable(&serdev->dev); + else + gnss_serial_set_power(gserial, GNSS_SERIAL_OFF); + + return ret; +} +EXPORT_SYMBOL_GPL(gnss_serial_register); + +void gnss_serial_deregister(struct gnss_serial *gserial) +{ + struct serdev_device *serdev = gserial->serdev; + + gnss_deregister_device(gserial->gdev); + + if (IS_ENABLED(CONFIG_PM)) + pm_runtime_disable(&serdev->dev); + else + gnss_serial_set_power(gserial, GNSS_SERIAL_OFF); +} +EXPORT_SYMBOL_GPL(gnss_serial_deregister); + +#ifdef CONFIG_PM +static int gnss_serial_runtime_suspend(struct device *dev) +{ + struct gnss_serial *gserial = dev_get_drvdata(dev); + + return gnss_serial_set_power(gserial, GNSS_SERIAL_STANDBY); +} + +static int gnss_serial_runtime_resume(struct device *dev) +{ + struct gnss_serial *gserial = dev_get_drvdata(dev); + + return gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE); +} +#endif /* CONFIG_PM */ + +static int gnss_serial_prepare(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + return 1; + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int gnss_serial_suspend(struct device *dev) +{ + struct gnss_serial *gserial = dev_get_drvdata(dev); + int ret = 0; + + /* + * FIXME: serdev currently lacks support for managing the underlying + * device's wakeup settings. A workaround would be to close the serdev + * device here if it is open. + */ + + if (!pm_runtime_suspended(dev)) + ret = gnss_serial_set_power(gserial, GNSS_SERIAL_STANDBY); + + return ret; +} + +static int gnss_serial_resume(struct device *dev) +{ + struct gnss_serial *gserial = dev_get_drvdata(dev); + int ret = 0; + + if (!pm_runtime_suspended(dev)) + ret = gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +const struct dev_pm_ops gnss_serial_pm_ops = { + .prepare = gnss_serial_prepare, + SET_SYSTEM_SLEEP_PM_OPS(gnss_serial_suspend, gnss_serial_resume) + SET_RUNTIME_PM_OPS(gnss_serial_runtime_suspend, gnss_serial_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(gnss_serial_pm_ops); + +MODULE_AUTHOR("Johan Hovold "); +MODULE_DESCRIPTION("Generic serial GNSS receiver driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gnss/serial.h b/drivers/gnss/serial.h new file mode 100644 index 000000000000..980ffdc86c2a --- /dev/null +++ b/drivers/gnss/serial.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Generic serial GNSS receiver driver + * + * Copyright (C) 2018 Johan Hovold + */ + +#ifndef _LINUX_GNSS_SERIAL_H +#define _LINUX_GNSS_SERIAL_H + +#include +#include + +struct gnss_serial { + struct serdev_device *serdev; + struct gnss_device *gdev; + speed_t speed; + const struct gnss_serial_ops *ops; + unsigned long drvdata[0]; +}; + +enum gnss_serial_pm_state { + GNSS_SERIAL_OFF, + GNSS_SERIAL_ACTIVE, + GNSS_SERIAL_STANDBY, +}; + +struct gnss_serial_ops { + int (*set_power)(struct gnss_serial *gserial, + enum gnss_serial_pm_state state); +}; + +extern const struct dev_pm_ops gnss_serial_pm_ops; + +struct gnss_serial *gnss_serial_allocate(struct serdev_device *gserial, + size_t data_size); +void gnss_serial_free(struct gnss_serial *gserial); + +int gnss_serial_register(struct gnss_serial *gserial); +void gnss_serial_deregister(struct gnss_serial *gserial); + +static inline void *gnss_serial_get_drvdata(struct gnss_serial *gserial) +{ + return gserial->drvdata; +} + +#endif /* _LINUX_GNSS_SERIAL_H */ -- 2.17.0