* Re: [PATCH v2 3/4] remoteproc/k3-dsp: Add a remoteproc driver of K3 C66x DSPs
From: Bjorn Andersson @ 2020-05-21 18:34 UTC (permalink / raw)
To: Suman Anna
Cc: Rob Herring, Mathieu Poirier, Lokesh Vutla, linux-remoteproc,
devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20200521001006.2725-4-s-anna@ti.com>
On Wed 20 May 17:10 PDT 2020, Suman Anna wrote:
> The Texas Instrument's K3 J721E SoCs have two C66x DSP Subsystems in MAIN
> voltage domain that are based on the TI's standard TMS320C66x DSP CorePac
> module. Each subsystem has a Fixed/Floating-Point DSP CPU, with 32 KB each
> of L1P & L1D SRAMs that can be configured and partitioned as either RAM
> and/or Cache, and 288 KB of L2 SRAM with 256 KB of memory configurable as
> either RAM and/or Cache. The CorePac also includes an Internal DMA (IDMA),
> External Memory Controller (EMC), Extended Memory Controller (XMC) with a
> Region Address Translator (RAT) unit for 32-bit to 48-bit address
> extension/translations, an Interrupt Controller (INTC) and a Powerdown
> Controller (PDC).
>
> A new remoteproc module is added to perform the device management of
> these DSP devices. The support is limited to images using only external
> DDR memory at the moment, the loading support to internal memories and
> any on-chip RAM memories will be added in a subsequent patch. RAT support
> is also left for a future patch, and as such the reserved memory carveout
> regions are all expected to be using memory regions within the first 2 GB.
> Error Recovery and Power Management features are not currently supported.
>
> The C66x remote processors do not have an MMU, and so require fixed memory
> carveout regions matching the firmware image addresses. Support for this
> is provided by mandating multiple memory regions to be attached to the
> remoteproc device. The first memory region will be used to serve as the
> DMA pool for all dynamic allocations like the vrings and vring buffers.
> The remaining memory regions are mapped into the kernel at device probe
> time, and are used to provide address translations for firmware image
> segments without the need for any RSC_CARVEOUT entries. Any firmware
> image using memory outside of the supplied reserved memory carveout
> regions will be errored out.
>
> The driver uses various TI-SCI interfaces to talk to the System Controller
> (DMSC) for managing configuration, power and reset management of these
> cores. IPC between the A72 cores and the DSP cores is supported through
> the virtio rpmsg stack using shared memory and OMAP Mailboxes.
>
There's room for more devm_ usage, but this looks good to me.
Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Regards,
Bjorn
> Signed-off-by: Suman Anna <s-anna@ti.com>
> ---
> v2:
> - Dropped the pm_runtime usage
> - Replaced the private k3_dsp_rproc_get_firmware() with the newly introduced
> rproc_of_parse_firmware()
> - Addressed other minor comments from Mathieu - Revised help on Kconfig, reordered
> header files, whitespace indentation fixes, remove the stale memset comment on
> internal memories, renamed struct k3_dsp_rproc_mem to struct k3_dsp_mem.
> v1: https://patchwork.kernel.org/patch/11458577/
>
> drivers/remoteproc/Kconfig | 13 +
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/ti_k3_dsp_remoteproc.c | 701 ++++++++++++++++++++++
> 3 files changed, 715 insertions(+)
> create mode 100644 drivers/remoteproc/ti_k3_dsp_remoteproc.c
>
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 2c9fb870a276..ef787774b52b 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -265,6 +265,19 @@ config TI_K3_R5_REMOTEPROC
> It's safe to say N here if you're not interested in utilizing
> a slave processor
>
> +config TI_K3_DSP_REMOTEPROC
> + tristate "TI K3 DSP remoteproc support"
> + depends on ARCH_K3
> + select MAILBOX
> + select OMAP2PLUS_MBOX
> + help
> + Say m here to support TI's C66x and C71x DSP remote processor
> + subsystems on various TI K3 family of SoCs through the remote
> + processor framework.
> +
> + It's safe to say N here if you're not interested in utilizing
> + the DSP slave processors.
> +
> endif # REMOTEPROC
>
> endmenu
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index ea0c6812e4fc..9f87d222744c 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -31,3 +31,4 @@ obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o
> obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o
> obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o
> obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o
> +obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o
> diff --git a/drivers/remoteproc/ti_k3_dsp_remoteproc.c b/drivers/remoteproc/ti_k3_dsp_remoteproc.c
> new file mode 100644
> index 000000000000..e4036f5992fe
> --- /dev/null
> +++ b/drivers/remoteproc/ti_k3_dsp_remoteproc.c
> @@ -0,0 +1,701 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * TI K3 DSP Remote Processor(s) driver
> + *
> + * Copyright (C) 2018-2020 Texas Instruments Incorporated - http://www.ti.com/
> + * Suman Anna <s-anna@ti.com>
> + */
> +
> +#include <linux/io.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/omap-mailbox.h>
> +#include <linux/platform_device.h>
> +#include <linux/remoteproc.h>
> +#include <linux/reset.h>
> +#include <linux/soc/ti/ti_sci_protocol.h>
> +
> +#include "omap_remoteproc.h"
> +#include "remoteproc_internal.h"
> +#include "ti_sci_proc.h"
> +
> +#define KEYSTONE_RPROC_LOCAL_ADDRESS_MASK (SZ_16M - 1)
> +
> +/**
> + * struct k3_dsp_mem - internal memory structure
> + * @cpu_addr: MPU virtual address of the memory region
> + * @bus_addr: Bus address used to access the memory region
> + * @dev_addr: Device address of the memory region from DSP view
> + * @size: Size of the memory region
> + */
> +struct k3_dsp_mem {
> + void __iomem *cpu_addr;
> + phys_addr_t bus_addr;
> + u32 dev_addr;
> + size_t size;
> +};
> +
> +/**
> + * struct k3_dsp_mem_data - memory definitions for a DSP
> + * @name: name for this memory entry
> + * @dev_addr: device address for the memory entry
> + */
> +struct k3_dsp_mem_data {
> + const char *name;
> + const u32 dev_addr;
> +};
> +
> +/**
> + * struct k3_dsp_dev_data - device data structure for a DSP
> + * @mems: pointer to memory definitions for a DSP
> + * @num_mems: number of memory regions in @mems
> + * @boot_align_addr: boot vector address alignment granularity
> + * @uses_lreset: flag to denote the need for local reset management
> + */
> +struct k3_dsp_dev_data {
> + const struct k3_dsp_mem_data *mems;
> + u32 num_mems;
> + u32 boot_align_addr;
> + bool uses_lreset;
> +};
> +
> +/**
> + * struct k3_dsp_rproc - k3 DSP remote processor driver structure
> + * @dev: cached device pointer
> + * @rproc: remoteproc device handle
> + * @mem: internal memory regions data
> + * @num_mems: number of internal memory regions
> + * @rmem: reserved memory regions data
> + * @num_rmems: number of reserved memory regions
> + * @reset: reset control handle
> + * @data: pointer to DSP-specific device data
> + * @tsp: TI-SCI processor control handle
> + * @ti_sci: TI-SCI handle
> + * @ti_sci_id: TI-SCI device identifier
> + * @mbox: mailbox channel handle
> + * @client: mailbox client to request the mailbox channel
> + */
> +struct k3_dsp_rproc {
> + struct device *dev;
> + struct rproc *rproc;
> + struct k3_dsp_mem *mem;
> + int num_mems;
> + struct k3_dsp_mem *rmem;
> + int num_rmems;
> + struct reset_control *reset;
> + const struct k3_dsp_dev_data *data;
> + struct ti_sci_proc *tsp;
> + const struct ti_sci_handle *ti_sci;
> + u32 ti_sci_id;
> + struct mbox_chan *mbox;
> + struct mbox_client client;
> +};
> +
> +/**
> + * k3_dsp_rproc_mbox_callback() - inbound mailbox message handler
> + * @client: mailbox client pointer used for requesting the mailbox channel
> + * @data: mailbox payload
> + *
> + * This handler is invoked by the OMAP mailbox driver whenever a mailbox
> + * message is received. Usually, the mailbox payload simply contains
> + * the index of the virtqueue that is kicked by the remote processor,
> + * and we let remoteproc core handle it.
> + *
> + * In addition to virtqueue indices, we also have some out-of-band values
> + * that indicate different events. Those values are deliberately very
> + * large so they don't coincide with virtqueue indices.
> + */
> +static void k3_dsp_rproc_mbox_callback(struct mbox_client *client, void *data)
> +{
> + struct k3_dsp_rproc *kproc = container_of(client, struct k3_dsp_rproc,
> + client);
> + struct device *dev = kproc->rproc->dev.parent;
> + const char *name = kproc->rproc->name;
> + u32 msg = omap_mbox_message(data);
> +
> + dev_dbg(dev, "mbox msg: 0x%x\n", msg);
> +
> + switch (msg) {
> + case RP_MBOX_CRASH:
> + /*
> + * remoteproc detected an exception, but error recovery is not
> + * supported. So, just log this for now
> + */
> + dev_err(dev, "K3 DSP rproc %s crashed\n", name);
> + break;
> + case RP_MBOX_ECHO_REPLY:
> + dev_info(dev, "received echo reply from %s\n", name);
> + break;
> + default:
> + /* silently handle all other valid messages */
> + if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG)
> + return;
> + if (msg > kproc->rproc->max_notifyid) {
> + dev_dbg(dev, "dropping unknown message 0x%x", msg);
> + return;
> + }
> + /* msg contains the index of the triggered vring */
> + if (rproc_vq_interrupt(kproc->rproc, msg) == IRQ_NONE)
> + dev_dbg(dev, "no message was found in vqid %d\n", msg);
> + }
> +}
> +
> +/*
> + * Kick the remote processor to notify about pending unprocessed messages.
> + * The vqid usage is not used and is inconsequential, as the kick is performed
> + * through a simulated GPIO (a bit in an IPC interrupt-triggering register),
> + * the remote processor is expected to process both its Tx and Rx virtqueues.
> + */
> +static void k3_dsp_rproc_kick(struct rproc *rproc, int vqid)
> +{
> + struct k3_dsp_rproc *kproc = rproc->priv;
> + struct device *dev = rproc->dev.parent;
> + mbox_msg_t msg = (mbox_msg_t)vqid;
> + int ret;
> +
> + /* send the index of the triggered virtqueue in the mailbox payload */
> + ret = mbox_send_message(kproc->mbox, (void *)msg);
> + if (ret < 0)
> + dev_err(dev, "failed to send mailbox message, status = %d\n",
> + ret);
> +}
> +
> +/* Put the DSP processor into reset */
> +static int k3_dsp_rproc_reset(struct k3_dsp_rproc *kproc)
> +{
> + struct device *dev = kproc->dev;
> + int ret;
> +
> + ret = reset_control_assert(kproc->reset);
> + if (ret) {
> + dev_err(dev, "local-reset assert failed, ret = %d\n", ret);
> + return ret;
> + }
> +
> + ret = kproc->ti_sci->ops.dev_ops.put_device(kproc->ti_sci,
> + kproc->ti_sci_id);
> + if (ret) {
> + dev_err(dev, "module-reset assert failed, ret = %d\n", ret);
> + if (reset_control_deassert(kproc->reset))
> + dev_warn(dev, "local-reset deassert back failed\n");
> + }
> +
> + return ret;
> +}
> +
> +/* Release the DSP processor from reset */
> +static int k3_dsp_rproc_release(struct k3_dsp_rproc *kproc)
> +{
> + struct device *dev = kproc->dev;
> + int ret;
> +
> + ret = kproc->ti_sci->ops.dev_ops.get_device(kproc->ti_sci,
> + kproc->ti_sci_id);
> + if (ret) {
> + dev_err(dev, "module-reset deassert failed, ret = %d\n", ret);
> + return ret;
> + }
> +
> + ret = reset_control_deassert(kproc->reset);
> + if (ret) {
> + dev_err(dev, "local-reset deassert failed, ret = %d\n", ret);
> + if (kproc->ti_sci->ops.dev_ops.put_device(kproc->ti_sci,
> + kproc->ti_sci_id))
> + dev_warn(dev, "module-reset assert back failed\n");
> + }
> +
> + return ret;
> +}
> +
> +/*
> + * Power up the DSP remote processor.
> + *
> + * This function will be invoked only after the firmware for this rproc
> + * was loaded, parsed successfully, and all of its resource requirements
> + * were met.
> + */
> +static int k3_dsp_rproc_start(struct rproc *rproc)
> +{
> + struct k3_dsp_rproc *kproc = rproc->priv;
> + struct mbox_client *client = &kproc->client;
> + struct device *dev = kproc->dev;
> + u32 boot_addr;
> + int ret;
> +
> + client->dev = dev;
> + client->tx_done = NULL;
> + client->rx_callback = k3_dsp_rproc_mbox_callback;
> + client->tx_block = false;
> + client->knows_txdone = false;
> +
> + kproc->mbox = mbox_request_channel(client, 0);
> + if (IS_ERR(kproc->mbox)) {
> + ret = -EBUSY;
> + dev_err(dev, "mbox_request_channel failed: %ld\n",
> + PTR_ERR(kproc->mbox));
> + return ret;
> + }
> +
> + /*
> + * Ping the remote processor, this is only for sanity-sake for now;
> + * there is no functional effect whatsoever.
> + *
> + * Note that the reply will _not_ arrive immediately: this message
> + * will wait in the mailbox fifo until the remote processor is booted.
> + */
> + ret = mbox_send_message(kproc->mbox, (void *)RP_MBOX_ECHO_REQUEST);
> + if (ret < 0) {
> + dev_err(dev, "mbox_send_message failed: %d\n", ret);
> + goto put_mbox;
> + }
> +
> + boot_addr = rproc->bootaddr;
> + if (boot_addr & (kproc->data->boot_align_addr - 1)) {
> + dev_err(dev, "invalid boot address 0x%x, must be aligned on a 0x%x boundary\n",
> + boot_addr, kproc->data->boot_align_addr);
> + ret = -EINVAL;
> + goto put_mbox;
> + }
> +
> + dev_err(dev, "booting DSP core using boot addr = 0x%x\n", boot_addr);
> + ret = ti_sci_proc_set_config(kproc->tsp, boot_addr, 0, 0);
> + if (ret)
> + goto put_mbox;
> +
> + ret = k3_dsp_rproc_release(kproc);
> + if (ret)
> + goto put_mbox;
> +
> + return 0;
> +
> +put_mbox:
> + mbox_free_channel(kproc->mbox);
> + return ret;
> +}
> +
> +/*
> + * Stop the DSP remote processor.
> + *
> + * This function puts the DSP processor into reset, and finishes processing
> + * of any pending messages.
> + */
> +static int k3_dsp_rproc_stop(struct rproc *rproc)
> +{
> + struct k3_dsp_rproc *kproc = rproc->priv;
> +
> + mbox_free_channel(kproc->mbox);
> +
> + k3_dsp_rproc_reset(kproc);
> +
> + return 0;
> +}
> +
> +/*
> + * Custom function to translate a DSP device address (internal RAMs only) to a
> + * kernel virtual address. The DSPs can access their RAMs at either an internal
> + * address visible only from a DSP, or at the SoC-level bus address. Both these
> + * addresses need to be looked through for translation. The translated addresses
> + * can be used either by the remoteproc core for loading (when using kernel
> + * remoteproc loader), or by any rpmsg bus drivers.
> + */
> +static void *k3_dsp_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
> +{
> + struct k3_dsp_rproc *kproc = rproc->priv;
> + void __iomem *va = NULL;
> + phys_addr_t bus_addr;
> + u32 dev_addr, offset;
> + size_t size;
> + int i;
> +
> + if (len == 0)
> + return NULL;
> +
> + for (i = 0; i < kproc->num_mems; i++) {
> + bus_addr = kproc->mem[i].bus_addr;
> + dev_addr = kproc->mem[i].dev_addr;
> + size = kproc->mem[i].size;
> +
> + if (da < KEYSTONE_RPROC_LOCAL_ADDRESS_MASK) {
> + /* handle DSP-view addresses */
> + if (da >= dev_addr &&
> + ((da + len) <= (dev_addr + size))) {
> + offset = da - dev_addr;
> + va = kproc->mem[i].cpu_addr + offset;
> + return (__force void *)va;
> + }
> + } else {
> + /* handle SoC-view addresses */
> + if (da >= bus_addr &&
> + (da + len) <= (bus_addr + size)) {
> + offset = da - bus_addr;
> + va = kproc->mem[i].cpu_addr + offset;
> + return (__force void *)va;
> + }
> + }
> + }
> +
> + /* handle static DDR reserved memory regions */
> + for (i = 0; i < kproc->num_rmems; i++) {
> + dev_addr = kproc->rmem[i].dev_addr;
> + size = kproc->rmem[i].size;
> +
> + if (da >= dev_addr && ((da + len) <= (dev_addr + size))) {
> + offset = da - dev_addr;
> + va = kproc->rmem[i].cpu_addr + offset;
> + return (__force void *)va;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +static const struct rproc_ops k3_dsp_rproc_ops = {
> + .start = k3_dsp_rproc_start,
> + .stop = k3_dsp_rproc_stop,
> + .kick = k3_dsp_rproc_kick,
> + .da_to_va = k3_dsp_rproc_da_to_va,
> +};
> +
> +static int k3_dsp_rproc_of_get_memories(struct platform_device *pdev,
> + struct k3_dsp_rproc *kproc)
> +{
> + const struct k3_dsp_dev_data *data = kproc->data;
> + struct device *dev = &pdev->dev;
> + struct resource *res;
> + int num_mems = 0;
> + int i;
> +
> + num_mems = kproc->data->num_mems;
> + kproc->mem = devm_kcalloc(kproc->dev, num_mems,
> + sizeof(*kproc->mem), GFP_KERNEL);
> + if (!kproc->mem)
> + return -ENOMEM;
> +
> + for (i = 0; i < num_mems; i++) {
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> + data->mems[i].name);
> + if (!res) {
> + dev_err(dev, "found no memory resource for %s\n",
> + data->mems[i].name);
> + return -EINVAL;
> + }
> + if (!devm_request_mem_region(dev, res->start,
> + resource_size(res),
> + dev_name(dev))) {
> + dev_err(dev, "could not request %s region for resource\n",
> + data->mems[i].name);
> + return -EBUSY;
> + }
> +
> + kproc->mem[i].cpu_addr = devm_ioremap_wc(dev, res->start,
> + resource_size(res));
> + if (IS_ERR(kproc->mem[i].cpu_addr)) {
> + dev_err(dev, "failed to map %s memory\n",
> + data->mems[i].name);
> + return PTR_ERR(kproc->mem[i].cpu_addr);
> + }
> + kproc->mem[i].bus_addr = res->start;
> + kproc->mem[i].dev_addr = data->mems[i].dev_addr;
> + kproc->mem[i].size = resource_size(res);
> +
> + dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %pK da 0x%x\n",
> + data->mems[i].name, &kproc->mem[i].bus_addr,
> + kproc->mem[i].size, kproc->mem[i].cpu_addr,
> + kproc->mem[i].dev_addr);
> + }
> + kproc->num_mems = num_mems;
> +
> + return 0;
> +}
> +
> +static int k3_dsp_reserved_mem_init(struct k3_dsp_rproc *kproc)
> +{
> + struct device *dev = kproc->dev;
> + struct device_node *np = dev->of_node;
> + struct device_node *rmem_np;
> + struct reserved_mem *rmem;
> + int num_rmems;
> + int ret, i;
> +
> + num_rmems = of_property_count_elems_of_size(np, "memory-region",
> + sizeof(phandle));
> + if (num_rmems <= 0) {
> + dev_err(dev, "device does not reserved memory regions, ret = %d\n",
> + num_rmems);
> + return -EINVAL;
> + }
> + if (num_rmems < 2) {
> + dev_err(dev, "device needs atleast two memory regions to be defined, num = %d\n",
> + num_rmems);
> + return -EINVAL;
> + }
> +
> + /* use reserved memory region 0 for vring DMA allocations */
> + ret = of_reserved_mem_device_init_by_idx(dev, np, 0);
> + if (ret) {
> + dev_err(dev, "device cannot initialize DMA pool, ret = %d\n",
> + ret);
> + return ret;
> + }
> +
> + num_rmems--;
> + kproc->rmem = kcalloc(num_rmems, sizeof(*kproc->rmem), GFP_KERNEL);
> + if (!kproc->rmem) {
> + ret = -ENOMEM;
> + goto release_rmem;
> + }
> +
> + /* use remaining reserved memory regions for static carveouts */
> + for (i = 0; i < num_rmems; i++) {
> + rmem_np = of_parse_phandle(np, "memory-region", i + 1);
> + if (!rmem_np) {
> + ret = -EINVAL;
> + goto unmap_rmem;
> + }
> +
> + rmem = of_reserved_mem_lookup(rmem_np);
> + if (!rmem) {
> + of_node_put(rmem_np);
> + ret = -EINVAL;
> + goto unmap_rmem;
> + }
> + of_node_put(rmem_np);
> +
> + kproc->rmem[i].bus_addr = rmem->base;
> + /* 64-bit address regions currently not supported */
> + kproc->rmem[i].dev_addr = (u32)rmem->base;
> + kproc->rmem[i].size = rmem->size;
> + kproc->rmem[i].cpu_addr = ioremap_wc(rmem->base, rmem->size);
> + if (!kproc->rmem[i].cpu_addr) {
> + dev_err(dev, "failed to map reserved memory#%d at %pa of size %pa\n",
> + i + 1, &rmem->base, &rmem->size);
> + ret = -ENOMEM;
> + goto unmap_rmem;
> + }
> +
> + dev_dbg(dev, "reserved memory%d: bus addr %pa size 0x%zx va %pK da 0x%x\n",
> + i + 1, &kproc->rmem[i].bus_addr,
> + kproc->rmem[i].size, kproc->rmem[i].cpu_addr,
> + kproc->rmem[i].dev_addr);
> + }
> + kproc->num_rmems = num_rmems;
> +
> + return 0;
> +
> +unmap_rmem:
> + for (i--; i >= 0; i--) {
> + if (kproc->rmem[i].cpu_addr)
> + iounmap(kproc->rmem[i].cpu_addr);
> + }
> + kfree(kproc->rmem);
> +release_rmem:
> + of_reserved_mem_device_release(kproc->dev);
> + return ret;
> +}
> +
> +static void k3_dsp_reserved_mem_exit(struct k3_dsp_rproc *kproc)
> +{
> + int i;
> +
> + for (i = 0; i < kproc->num_rmems; i++)
> + iounmap(kproc->rmem[i].cpu_addr);
> + kfree(kproc->rmem);
> +
> + of_reserved_mem_device_release(kproc->dev);
> +}
> +
> +static
> +struct ti_sci_proc *k3_dsp_rproc_of_get_tsp(struct device *dev,
> + const struct ti_sci_handle *sci)
> +{
> + struct ti_sci_proc *tsp;
> + u32 temp[2];
> + int ret;
> +
> + ret = of_property_read_u32_array(dev->of_node, "ti,sci-proc-ids",
> + temp, 2);
> + if (ret < 0)
> + return ERR_PTR(ret);
> +
> + tsp = kzalloc(sizeof(*tsp), GFP_KERNEL);
> + if (!tsp)
> + return ERR_PTR(-ENOMEM);
> +
> + tsp->dev = dev;
> + tsp->sci = sci;
> + tsp->ops = &sci->ops.proc_ops;
> + tsp->proc_id = temp[0];
> + tsp->host_id = temp[1];
> +
> + return tsp;
> +}
> +
> +static int k3_dsp_rproc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + const struct k3_dsp_dev_data *data;
> + struct k3_dsp_rproc *kproc;
> + struct rproc *rproc;
> + const char *fw_name;
> + int ret = 0;
> + int ret1;
> +
> + data = of_device_get_match_data(dev);
> + if (!data)
> + return -ENODEV;
> +
> + ret = rproc_of_parse_firmware(dev, 0, &fw_name);
> + if (ret) {
> + dev_err(dev, "failed to parse firmware-name property, ret = %d\n",
> + ret);
> + return ret;
> + }
> +
> + rproc = rproc_alloc(dev, dev_name(dev), &k3_dsp_rproc_ops, fw_name,
> + sizeof(*kproc));
> + if (!rproc)
> + return -ENOMEM;
> +
> + rproc->has_iommu = false;
> + rproc->recovery_disabled = true;
> + kproc = rproc->priv;
> + kproc->rproc = rproc;
> + kproc->dev = dev;
> + kproc->data = data;
> +
> + kproc->ti_sci = ti_sci_get_by_phandle(np, "ti,sci");
> + if (IS_ERR(kproc->ti_sci)) {
> + ret = PTR_ERR(kproc->ti_sci);
> + if (ret != -EPROBE_DEFER) {
> + dev_err(dev, "failed to get ti-sci handle, ret = %d\n",
> + ret);
> + }
> + kproc->ti_sci = NULL;
> + goto free_rproc;
> + }
> +
> + ret = of_property_read_u32(np, "ti,sci-dev-id", &kproc->ti_sci_id);
> + if (ret) {
> + dev_err(dev, "missing 'ti,sci-dev-id' property\n");
> + goto put_sci;
> + }
> +
> + kproc->reset = devm_reset_control_get_exclusive(dev, NULL);
> + if (IS_ERR(kproc->reset)) {
> + ret = PTR_ERR(kproc->reset);
> + dev_err(dev, "failed to get reset, status = %d\n", ret);
> + goto put_sci;
> + }
> +
> + kproc->tsp = k3_dsp_rproc_of_get_tsp(dev, kproc->ti_sci);
> + if (IS_ERR(kproc->tsp)) {
> + dev_err(dev, "failed to construct ti-sci proc control, ret = %d\n",
> + ret);
> + ret = PTR_ERR(kproc->tsp);
> + goto put_sci;
> + }
> +
> + ret = ti_sci_proc_request(kproc->tsp);
> + if (ret < 0) {
> + dev_err(dev, "ti_sci_proc_request failed, ret = %d\n", ret);
> + goto free_tsp;
> + }
> +
> + ret = k3_dsp_rproc_of_get_memories(pdev, kproc);
> + if (ret)
> + goto release_tsp;
> +
> + ret = k3_dsp_reserved_mem_init(kproc);
> + if (ret) {
> + dev_err(dev, "reserved memory init failed, ret = %d\n", ret);
> + goto release_tsp;
> + }
> +
> + ret = rproc_add(rproc);
> + if (ret) {
> + dev_err(dev, "failed to add register device with remoteproc core, status = %d\n",
> + ret);
> + goto release_mem;
> + }
> +
> + platform_set_drvdata(pdev, kproc);
> +
> + return 0;
> +
> +release_mem:
> + k3_dsp_reserved_mem_exit(kproc);
> +release_tsp:
> + ret1 = ti_sci_proc_release(kproc->tsp);
> + if (ret1)
> + dev_err(dev, "failed to release proc, ret = %d\n", ret1);
> +free_tsp:
> + kfree(kproc->tsp);
> +put_sci:
> + ret1 = ti_sci_put_handle(kproc->ti_sci);
> + if (ret1)
> + dev_err(dev, "failed to put ti_sci handle, ret = %d\n", ret1);
> +free_rproc:
> + rproc_free(rproc);
> + return ret;
> +}
> +
> +static int k3_dsp_rproc_remove(struct platform_device *pdev)
> +{
> + struct k3_dsp_rproc *kproc = platform_get_drvdata(pdev);
> + struct device *dev = &pdev->dev;
> + int ret;
> +
> + rproc_del(kproc->rproc);
> +
> + ret = ti_sci_proc_release(kproc->tsp);
> + if (ret)
> + dev_err(dev, "failed to release proc, ret = %d\n", ret);
> +
> + kfree(kproc->tsp);
> +
> + ret = ti_sci_put_handle(kproc->ti_sci);
> + if (ret)
> + dev_err(dev, "failed to put ti_sci handle, ret = %d\n", ret);
> +
> + k3_dsp_reserved_mem_exit(kproc);
> + rproc_free(kproc->rproc);
> +
> + return 0;
> +}
> +
> +static const struct k3_dsp_mem_data c66_mems[] = {
> + { .name = "l2sram", .dev_addr = 0x800000 },
> + { .name = "l1pram", .dev_addr = 0xe00000 },
> + { .name = "l1dram", .dev_addr = 0xf00000 },
> +};
> +
> +static const struct k3_dsp_dev_data c66_data = {
> + .mems = c66_mems,
> + .num_mems = ARRAY_SIZE(c66_mems),
> + .boot_align_addr = SZ_1K,
> + .uses_lreset = true,
> +};
> +
> +static const struct of_device_id k3_dsp_of_match[] = {
> + { .compatible = "ti,j721e-c66-dsp", .data = &c66_data, },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, k3_dsp_of_match);
> +
> +static struct platform_driver k3_dsp_rproc_driver = {
> + .probe = k3_dsp_rproc_probe,
> + .remove = k3_dsp_rproc_remove,
> + .driver = {
> + .name = "k3-dsp-rproc",
> + .of_match_table = k3_dsp_of_match,
> + },
> +};
> +
> +module_platform_driver(k3_dsp_rproc_driver);
> +
> +MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("TI K3 DSP Remoteproc driver");
> --
> 2.26.0
>
^ permalink raw reply
* Re: [PATCH v2 3/4] iio: imu: bmi160: added regulator support
From: Jonathan Cameron @ 2020-05-21 18:30 UTC (permalink / raw)
To: Jonathan Albrieux
Cc: Jonathan Cameron, linux-kernel,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Hartmut Knaack, Lars-Peter Clausen,
open list:IIO SUBSYSTEM AND DRIVERS, Peter Meerwald-Stadler
In-Reply-To: <20200520071751.GD3361@ict14-OptiPlex-980>
On Wed, 20 May 2020 09:17:51 +0200
Jonathan Albrieux <jonathan.albrieux@gmail.com> wrote:
> On Tue, May 19, 2020 at 06:55:35PM +0100, Jonathan Cameron wrote:
> > On Tue, 19 May 2020 09:50:59 +0200
> > Jonathan Albrieux <jonathan.albrieux@gmail.com> wrote:
> >
> > > v2: fixed missing description
> >
> > Don't put change log here....
>
> Yep I will put it in the cover letter
>
> > >
> > > Add vdd-supply and vddio-supply support. Without this support vdd and vddio
> > > should be set to always-on in device tree
> >
> > Kind of the opposite. If they are always on we don't have to provide them
> > in the device tree.
> >
>
> I wrote that because, testing on msm8916, without setting the regulators to
> always on they were controlled by other components and it happened that
> the line wasn't ready during probe causing failure to load the module.
>
> I will try to reword based on your comment, thank you.
Ah. Understood. I'd give that explicit example in the patch description.
I'd assumed this was the normal case of they weren't being described
at all in DT, whereas you case is more complex.
Jonathan
>
> > A few trivial things inline.
> >
> > >
> > > Signed-off-by: Jonathan Albrieux <jonathan.albrieux@gmail.com>
> > > ---
> >
> > Change log goes here so we don't end up keeping it in the git log.
> >
> > > drivers/iio/imu/bmi160/bmi160.h | 2 ++
> > > drivers/iio/imu/bmi160/bmi160_core.c | 27 ++++++++++++++++++++++++++-
> > > 2 files changed, 28 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/iio/imu/bmi160/bmi160.h b/drivers/iio/imu/bmi160/bmi160.h
> > > index 621f5309d735..923c3b274fde 100644
> > > --- a/drivers/iio/imu/bmi160/bmi160.h
> > > +++ b/drivers/iio/imu/bmi160/bmi160.h
> > > @@ -3,10 +3,12 @@
> > > #define BMI160_H_
> > >
> > > #include <linux/iio/iio.h>
> > > +#include <linux/regulator/consumer.h>
> > >
> > > struct bmi160_data {
> > > struct regmap *regmap;
> > > struct iio_trigger *trig;
> > > + struct regulator_bulk_data supplies[2];
> > > };
> > >
> > > extern const struct regmap_config bmi160_regmap_config;
> > > diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
> > > index 6af65d6f1d28..9bbe0d8e6720 100644
> > > --- a/drivers/iio/imu/bmi160/bmi160_core.c
> > > +++ b/drivers/iio/imu/bmi160/bmi160_core.c
> > > @@ -15,6 +15,7 @@
> > > #include <linux/delay.h>
> > > #include <linux/irq.h>
> > > #include <linux/of_irq.h>
> > > +#include <linux/regulator/consumer.h>
> > >
> > > #include <linux/iio/iio.h>
> > > #include <linux/iio/triggered_buffer.h>
> > > @@ -709,6 +710,12 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
> > > unsigned int val;
> > > struct device *dev = regmap_get_device(data->regmap);
> > >
> > > + ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies), data->supplies);
> > > + if (ret) {
> > > + dev_err(dev, "Failed to enable regulators: %d\n", ret);
> > > + return ret;
> > > + }
> > > +
> > > ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
> > > if (ret)
> > > return ret;
> > > @@ -793,9 +800,17 @@ int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type)
> > > static void bmi160_chip_uninit(void *data)
> > > {
> > > struct bmi160_data *bmi_data = data;
> > > + struct device *dev = regmap_get_device(bmi_data->regmap);
> > > + int ret;
> > >
> > > bmi160_set_mode(bmi_data, BMI160_GYRO, false);
> > > bmi160_set_mode(bmi_data, BMI160_ACCEL, false);
> > > +
> > > + ret = regulator_bulk_disable(ARRAY_SIZE(bmi_data->supplies),
> > > + bmi_data->supplies);
> > > + if (ret) {
> > > + dev_err(dev, "Failed to disable regulators: %d\n", ret);
> > > + }
> > No need for brackets around a 1 line if block
> >
>
> Thank you, I didn't noticed that :-)
>
> > if (ret)
> > dev_err(dev, "failed to disable regulators: %d\n", ret);
> >
> > > }
> > >
> > > int bmi160_core_probe(struct device *dev, struct regmap *regmap,
> > > @@ -815,6 +830,16 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
> > > dev_set_drvdata(dev, indio_dev);
> > > data->regmap = regmap;
> > >
> > > + data->supplies[0].supply = "vdd";
> > > + data->supplies[1].supply = "vddio";
> > > + ret = devm_regulator_bulk_get(dev,
> > > + ARRAY_SIZE(data->supplies),
> > > + data->supplies);
> > > + if (ret) {
> > > + dev_err(dev, "Failed to get regulators: %d\n", ret);
> > > + return ret;
> > > + }
> > > +
> > > ret = bmi160_chip_init(data, use_spi);
> > > if (ret)
> > > return ret;
> > > @@ -853,6 +878,6 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
> > > }
> > > EXPORT_SYMBOL_GPL(bmi160_core_probe);
> > >
> > > -MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
> > > +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
> >
> > Good fix but shouldn't be in this patch. Put it a separate patch on it's own.
> >
>
> Ok will separate this fix into another patch, thank you!
>
> > > MODULE_DESCRIPTION("Bosch BMI160 driver");
> > > MODULE_LICENSE("GPL v2");
> >
> >
>
> Best regards,
> Jonathan Albrieux
^ permalink raw reply
* Re: [RFC PATCH V4 4/4] platform: mtk-isp: Add Mediatek FD driver
From: Tomasz Figa @ 2020-05-21 18:28 UTC (permalink / raw)
To: Jerry-ch Chen
Cc: hans.verkuil, laurent.pinchart+renesas, matthias.bgg, mchehab,
pihsun, yuzhao, zwisler, linux-mediatek, linux-arm-kernel,
Sean.Cheng, sj.huang, christie.yu, frederic.chen, jungo.lin,
Rynn.Wu, linux-media, srv_heupstream, devicetree
In-Reply-To: <20191204124732.10932-5-Jerry-Ch.chen@mediatek.com>
Hi Jerry,
On Wed, Dec 04, 2019 at 08:47:32PM +0800, Jerry-ch Chen wrote:
> From: Jerry-ch Chen <jerry-ch.chen@mediatek.com>
>
> This patch adds the driver of Face Detection (FD) unit in
> Mediatek camera system, providing face detection function.
>
> The mtk-isp directory will contain drivers for multiple IP
> blocks found in Mediatek ISP system. It will include ISP Pass 1
> driver (CAM), sensor interface driver, DIP driver and face
> detection driver.
>
> Signed-off-by: Jerry-ch Chen <jerry-ch.chen@mediatek.com>
> ---
> drivers/media/platform/Kconfig | 2 +
> drivers/media/platform/Makefile | 2 +
> drivers/media/platform/mtk-isp/fd/Kconfig | 19 +
> drivers/media/platform/mtk-isp/fd/Makefile | 5 +
> drivers/media/platform/mtk-isp/fd/mtk_fd.h | 149 ++
> drivers/media/platform/mtk-isp/fd/mtk_fd_40.c | 1279 +++++++++++++++++
> include/uapi/linux/v4l2-controls.h | 4 +
> include/uapi/linux/videodev2.h | 3 +
> 8 files changed, 1463 insertions(+)
> create mode 100644 drivers/media/platform/mtk-isp/fd/Kconfig
> create mode 100644 drivers/media/platform/mtk-isp/fd/Makefile
> create mode 100644 drivers/media/platform/mtk-isp/fd/mtk_fd.h
> create mode 100644 drivers/media/platform/mtk-isp/fd/mtk_fd_40.c
>
Thank you for the patch. Please see my comments inline. Really sorry for
the much delayed review.
[snip]
> +config VIDEO_MEDIATEK_FD
> + tristate "Mediatek face detection processing function"
> + depends on VIDEO_V4L2
> + depends on ARCH_MEDIATEK
> + select VIDEOBUF2_DMA_CONTIG
> + select VIDEOBUF2_CORE
> + select VIDEOBUF2_V4L2
> + select VIDEOBUF2_MEMOPS
> + select MEDIA_CONTROLLER
> + select MTK_SCP
> +
> + default n
> + help
> + Support the Face Detection (FD) feature in the Mediatek
> + mt8183 Soc.
MT8183?
> +
> + FD driver is a V4L2 memory-to-memory device driver which
> + provides hardware accelerated face detection function,
> + it can detect different sizes of faces in a raw image.
A YUV image I guess?
[snip]
> diff --git a/drivers/media/platform/mtk-isp/fd/mtk_fd.h b/drivers/media/platform/mtk-isp/fd/mtk_fd.h
> new file mode 100644
> index 000000000000..d85bdcb70d6d
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/fd/mtk_fd.h
> @@ -0,0 +1,149 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +//
> +// Copyright (c) 2018 MediaTek Inc.
> +
> +#ifndef __MTK_FD_HW_H__
> +#define __MTK_FD_HW_H__
> +
> +#include <linux/completion.h>
> +#include <linux/io.h>
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/videobuf2-v4l2.h>
> +
> +#define MTK_FD_OUTPUT_MIN_WIDTH 26U
> +#define MTK_FD_OUTPUT_MIN_HEIGHT 26U
> +#define MTK_FD_OUTPUT_MAX_WIDTH 640U
> +#define MTK_FD_OUTPUT_MAX_HEIGHT 480U
> +
> +#define MTK_FD_HW_FMT_VYUY 2
> +#define MTK_FD_HW_FMT_UYVY 3
> +#define MTK_FD_HW_FMT_YVYU 4
> +#define MTK_FD_HW_FMT_YUYV 5
> +#define MTK_FD_HW_FMT_YVU_2P 6
> +#define MTK_FD_HW_FMT_YUV_2P 7
> +#define MTK_FD_HW_FMT_UNKNOWN 8
What is an unknown format?
> +
> +#define MTK_FD_IPI_CMD_INIT 0
> +#define MTK_FD_IPI_CMD_INIT_ACK 1
> +#define MTK_FD_IPI_CMD_ENQUEUE 2
> +#define MTK_FD_IPI_CMD_ENQ_ACK 3
> +#define MTK_FD_IPI_CMD_EXIT 4
> +#define MTK_FD_IPI_CMD_EXIT_ACK 5
> +#define MTK_FD_IPI_CMD_RESET 6
> +#define MTK_FD_IPI_CMD_RESET_ACK 7
> +
> +#define MTK_FD_REG_OFFSET_HW_ENABLE 0x4
> +#define MTK_FD_REG_OFFSET_INT_EN 0x15c
> +#define MTK_FD_REG_OFFSET_INT_VAL 0x168
> +#define MTK_FD_REG_OFFSET_RESULT 0x178
> +
> +#define MTK_FD_SET_HW_ENABLE 0x111
> +#define MTK_FD_RS_BUF_SIZE 2289664
> +#define MTK_FD_HW_WORK_BUF_SIZE 0x100000
How about using the SZ_1M?
> +#define MTK_FD_MAX_SPEEDUP 7
> +#define MTK_FD_MAX_RESULT_NUM 1026
Perhaps NUM_RESULTS?
> +
> +/* Max scale size counts */
> +#define MTK_FD_SCALE_ARR_NUM 15
Perhaps NUM_SCALE_SIZES?
> +
> +#define MTK_FD_HW_TIMEOUT 1000
What's the unit?
> +
> +enum face_angle {
> + MTK_FD_FACE_FRONT,
> + MTK_FD_FACE_RIGHT_50,
> + MTK_FD_FACE_LEFT_50,
> + MTK_FD_FACE_RIGHT_90,
> + MTK_FD_FACE_LEFT_90,
> + MTK_FD_FACE_ANGLE_NUM,
> +};
This enum seems to define values for the V4L2_CID_MTK_FD_DETECT_POSE
control. Considering that this is an enumeration and the values are
actually integers (-90, -50, 0, 50, 90), perhaps this should be an
INTEGER_MENU control instead?
> +
> +struct fd_buffer {
> + __u32 scp_addr; /* used by SCP */
> + __u32 dma_addr; /* used by DMA HW */
> +} __packed;
> +
> +struct fd_face_result {
> + char data[16];
> +};
> +
> +struct fd_user_output {
> + struct fd_face_result results[MTK_FD_MAX_RESULT_NUM];
> + __u16 number;
Is this perhaps the number of results? If so, would num_results be a better
name?
> +};
Since this struct is the meta buffer format, it is a part of the userspace
interface and should be defined in a header under include/uapi/linux/.
> +
> +struct user_param {
> + u8 fd_speedup;
> + u8 fd_extra_model;
> + u8 scale_img_num;
> + u8 src_img_fmt;
> + __u16 scale_img_width[MTK_FD_SCALE_ARR_NUM];
> + __u16 scale_img_height[MTK_FD_SCALE_ARR_NUM];
> + __u16 face_directions[MTK_FD_FACE_ANGLE_NUM];
Is this a user-facing definition or an interface between the kernel driver
and firmware? If the latter, the __ types shouldn't be used.
> +} __packed;
> +
> +struct fd_init_param {
> + struct fd_buffer fd_manager;
> + __u32 rs_dma_addr;
Ditto.
> +} __packed;
> +
> +struct fd_enq_param {
> + __u64 output_vaddr;
Ditto.
> + struct fd_buffer src_img[2];
> + struct fd_buffer user_result;
> + struct user_param user_param;
> +} __packed;
> +
> +struct fd_ack_param {
> + __u32 ret_code;
> + __u32 ret_msg;
Ditto.
> +} __packed;
[snip]
> +/* */
Was there supposed to be a comment here? :)
> +static int mtk_fd_hw_alloc_rs_dma_addr(struct mtk_fd_dev *fd)
> +{
> + struct device *dev = fd->dev;
> + void *va;
> + dma_addr_t dma_handle;
> +
> + va = dma_alloc_coherent(dev, MTK_FD_RS_BUF_SIZE, &dma_handle,
> + GFP_KERNEL);
> + if (!va) {
> + dev_err(dev, "dma_alloc null va\n");
No need to print errors for memory allocation failures, because one will be
printed automatically.
[snip]
> +static int mtk_fd_hw_connect(struct mtk_fd_dev *fd)
> +{
> + int ret;
> +
> + ret = rproc_boot(fd->rproc_handle);
> +
nit: Unnecessary blank line.
[snip]
> +static int mtk_fd_vb2_queue_setup(struct vb2_queue *vq,
> + unsigned int *num_buffers,
> + unsigned int *num_planes,
> + unsigned int sizes[],
> + struct device *alloc_devs[])
> +{
> + struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> + unsigned int size[2];
> + unsigned int plane;
> +
> + switch (vq->type) {
> + case V4L2_BUF_TYPE_META_CAPTURE:
> + size[0] = ctx->dst_fmt.buffersize;
> + break;
> + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> + size[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
> + if (*num_planes == 2)
> + size[1] = ctx->src_fmt.plane_fmt[1].sizeimage;
> + break;
> + }
Is this code above needed? The code below sets sizes[] and it uses a for loop,
without opencoded assignment for the second plane.
> +
> + if (*num_planes > 2)
> + return -EINVAL;
> + if (*num_planes == 0) {
> + if (vq->type == V4L2_BUF_TYPE_META_CAPTURE) {
> + sizes[0] = ctx->dst_fmt.buffersize;
> + *num_planes = 1;
> + return 0;
> + }
> +
> + *num_planes = ctx->src_fmt.num_planes;
> + for (plane = 0; plane < *num_planes; plane++)
> + sizes[plane] = ctx->src_fmt.plane_fmt[plane].sizeimage;
> + return 0;
> + }
> +
> + for (plane = 0; plane < *num_planes; plane++) {
> + if (sizes[plane] < size[plane])
> + return -EINVAL;
> + }
> + return 0;
> +}
[snip]
> +static int mtk_fd_enum_fmt_out_mp(struct file *file, void *fh,
> + struct v4l2_fmtdesc *f)
> +{
> + if (f->index >= NUM_FORMATS)
Please use ARRAY_SIZE().
> + return -EINVAL;
> +
> + f->pixelformat = mtk_fd_img_fmts[f->index].pixelformat;
> + return 0;
> +}
> +
> +static void mtk_fd_fill_pixfmt_mp(struct v4l2_pix_format_mplane *dfmt,
> + const struct v4l2_pix_format_mplane *sfmt)
> +{
> + dfmt->field = V4L2_FIELD_NONE;
> + dfmt->colorspace = V4L2_COLORSPACE_BT2020;
> + dfmt->num_planes = sfmt->num_planes;
> + dfmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> + dfmt->quantization = V4L2_QUANTIZATION_DEFAULT;
> + dfmt->xfer_func =
> + V4L2_MAP_XFER_FUNC_DEFAULT(dfmt->colorspace);
> +
> + /* Keep user setting as possible */
> + dfmt->width = clamp(dfmt->width,
> + MTK_FD_OUTPUT_MIN_WIDTH,
> + MTK_FD_OUTPUT_MAX_WIDTH);
> + dfmt->height = clamp(dfmt->height,
> + MTK_FD_OUTPUT_MIN_HEIGHT,
> + MTK_FD_OUTPUT_MAX_HEIGHT);
> +
> + if (sfmt->num_planes == 2) {
> + /* NV16M and NV61M has 1 byte per pixel */
> + dfmt->plane_fmt[0].bytesperline = dfmt->width;
> + dfmt->plane_fmt[1].bytesperline = dfmt->width;
> + } else {
> + /* 2 bytes per pixel */
> + dfmt->plane_fmt[0].bytesperline = dfmt->width * 2;
> + }
> +
> + dfmt->plane_fmt[0].sizeimage =
> + dfmt->height * dfmt->plane_fmt[0].bytesperline;
Could some of the code above be replaced with v4l2_fill_pixfmt_mp()?
> +}
> +
> +static const struct v4l2_pix_format_mplane *mtk_fd_find_fmt(u32 format)
> +{
> + unsigned int i;
> + const struct v4l2_pix_format_mplane *dev_fmt;
> +
> + for (i = 0; i < NUM_FORMATS; i++) {
Please use ARRAY_SIZE rather than a custom macro.
> + dev_fmt = &mtk_fd_img_fmts[i];
> + if (dev_fmt->pixelformat == format)
> + return dev_fmt;
> + }
> +
> + return NULL;
> +}
> +
> +static int mtk_fd_try_fmt_out_mp(struct file *file,
> + void *fh,
> + struct v4l2_format *f)
> +{
> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> + const struct v4l2_pix_format_mplane *fmt;
> +
> + fmt = mtk_fd_find_fmt(pix_mp->pixelformat);
> + if (!fmt)
> + fmt = &mtk_fd_img_fmts[0]; /* Get default img fmt */
nit: Please move the comment to a separate line and add braces.
> +
> + mtk_fd_fill_pixfmt_mp(pix_mp, fmt);
> + return 0;
> +}
[snip]
> +static unsigned int get_fd_img_fmt(unsigned int fourcc)
> +{
> + switch (fourcc) {
> + case V4L2_PIX_FMT_VYUY:
> + return MTK_FD_HW_FMT_VYUY;
> + case V4L2_PIX_FMT_YUYV:
> + return MTK_FD_HW_FMT_YUYV;
> + case V4L2_PIX_FMT_YVYU:
> + return MTK_FD_HW_FMT_YVYU;
> + case V4L2_PIX_FMT_UYVY:
> + return MTK_FD_HW_FMT_UYVY;
> + case V4L2_PIX_FMT_NV16M:
> + return MTK_FD_HW_FMT_YUV_2P;
> + case V4L2_PIX_FMT_NV61M:
> + return MTK_FD_HW_FMT_YVU_2P;
> + default:
> + return MTK_FD_HW_FMT_UNKNOWN;
If we want to be paranoid, we can just add a WARN here and return 0.
Shouldn't be a need to define an unknown format.
[snip]
> +static void mtk_fd_fill_user_param(struct user_param *user_param,
> + struct v4l2_ctrl_handler *hdl)
> +{
> + struct v4l2_ctrl *ctrl;
> + int i;
> +
> + ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_DOWN_IMG_WIDTH);
> + if (ctrl)
> + for (i = 0; i < ctrl->elems; i++)
> + user_param->scale_img_width[i] = ctrl->p_new.p_u16[i];
> + ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_DOWN_IMG_HEIGHT);
> + if (ctrl)
> + for (i = 0; i < ctrl->elems; i++)
> + user_param->scale_img_height[i] = ctrl->p_new.p_u16[i];
> + ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_SCALE_IMG_NUM);
> + if (ctrl)
> + user_param->scale_img_num = ctrl->val;
> +
nit: Either separate the code dealing with all controls from each other, or
none.
> + ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECT_POSE);
> + if (ctrl)
> + for (i = 0; i < ctrl->elems; i++)
> + user_param->face_directions[i] = ctrl->p_new.p_u16[i];
> + ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECT_SPEED);
> + if (ctrl)
> + user_param->fd_speedup = ctrl->val;
> + ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_FD_DETECTION_MODEL);
> + if (ctrl)
> + user_param->fd_extra_model = ctrl->val;
> +}
> +
> +static void mtk_fd_device_run(void *priv)
> +{
> + struct mtk_fd_ctx *ctx = priv;
> + struct mtk_fd_dev *fd = ctx->fd_dev;
> + struct vb2_v4l2_buffer *src_buf, *dst_buf;
> + struct fd_enq_param fd_param;
> + void *plane_vaddr;
> +
> + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
> +
> + fd_param.src_img[0].dma_addr =
> + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
> + fd_param.user_result.dma_addr =
> + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
> + plane_vaddr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
> + fd_param.output_vaddr = (u64)(unsigned long)plane_vaddr;
Why is the vaddr needed here? Specfically, it could pose a security problem
if CPU virtual addresses are exchanged with firmware.
> + fd_param.user_param.src_img_fmt =
> + get_fd_img_fmt(ctx->src_fmt.pixelformat);
> + if (ctx->src_fmt.num_planes == 2)
> + fd_param.src_img[1].dma_addr =
> + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
nit: Could this be moved above, to be just below src_img[0] initialization,
for readability reasons?
> + mtk_fd_fill_user_param(&fd_param.user_param, &ctx->hdl);
> +
> + /* Complete request controls if any */
> + v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, &ctx->hdl);
> +
> + fd->output = plane_vaddr;
> + mtk_fd_hw_job_exec(fd, &fd_param);
> +}
> +
> +static struct v4l2_m2m_ops fd_m2m_ops = {
> + .device_run = mtk_fd_device_run,
> +};
> +
> +static const struct media_device_ops fd_m2m_media_ops = {
> + .req_validate = vb2_request_validate,
> + .req_queue = v4l2_m2m_request_queue,
> +};
> +
> +static int mtk_fd_video_device_register(struct mtk_fd_dev *fd)
> +{
> + struct video_device *vfd = &fd->vfd;
> + struct v4l2_m2m_dev *m2m_dev = fd->m2m_dev;
> + struct device *dev = fd->dev;
> + int ret;
> +
> + vfd->fops = &fd_video_fops;
> + vfd->release = video_device_release;
> + vfd->lock = &fd->vfd_lock;
> + vfd->v4l2_dev = &fd->v4l2_dev;
> + vfd->vfl_dir = VFL_DIR_M2M;
> + vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT_MPLANE |
> + V4L2_CAP_META_CAPTURE;
> + vfd->ioctl_ops = &mtk_fd_v4l2_video_out_ioctl_ops;
> +
> + strscpy(vfd->name, dev_driver_string(dev), sizeof(vfd->name));
> +
> + video_set_drvdata(vfd, fd);
> +
> + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> + if (ret) {
> + dev_err(dev, "Failed to register video device\n");
> + goto err_free_dev;
> + }
> +
> + ret = v4l2_m2m_register_media_controller(m2m_dev, vfd,
> + MEDIA_ENT_F_PROC_VIDEO_STATISTICS);
> + if (ret) {
> + dev_err(dev, "Failed to init mem2mem media controller\n");
> + goto err_unreg_video;
> + }
> + return 0;
> +
> +err_unreg_video:
> + video_unregister_device(vfd);
> +err_free_dev:
> + video_device_release(vfd);
> + return ret;
> +}
> +
> +static int mtk_fd_dev_v4l2_init(struct mtk_fd_dev *fd)
> +{
> + struct media_device *mdev = &fd->mdev;
> + struct device *dev = fd->dev;
> + int ret;
> +
> + ret = v4l2_device_register(dev, &fd->v4l2_dev);
> + if (ret) {
> + dev_err(dev, "Failed to register v4l2 device\n");
> + return ret;
> + }
> +
> + fd->m2m_dev = v4l2_m2m_init(&fd_m2m_ops);
> + if (IS_ERR(fd->m2m_dev)) {
> + dev_err(dev, "Failed to init mem2mem device\n");
> + ret = PTR_ERR(fd->m2m_dev);
> + goto err_unreg_v4l2_dev;
> + }
> +
> + mdev->dev = dev;
> + strscpy(mdev->model, dev_driver_string(dev), sizeof(mdev->model));
> + snprintf(mdev->bus_info, sizeof(mdev->bus_info),
> + "platform:%s", dev_name(dev));
> + media_device_init(mdev);
> + mdev->ops = &fd_m2m_media_ops;
> + fd->v4l2_dev.mdev = mdev;
> +
> + ret = mtk_fd_video_device_register(fd);
> + if (ret) {
> + dev_err(dev, "Failed to register video device\n");
> + goto err_cleanup_mdev;
> + }
> +
> + ret = media_device_register(mdev);
> + if (ret) {
> + dev_err(dev, "Failed to register mem2mem media device\n");
> + goto err_unreg_vdev;
> + }
> +
> + return 0;
> +
> +err_unreg_vdev:
> + v4l2_m2m_unregister_media_controller(fd->m2m_dev);
> + video_unregister_device(&fd->vfd);
> + video_device_release(&fd->vfd);
This is inconsistent with the registration, which registers all of these in
a function. Perhaps it would be worth to move this cleanup to a
mtk_fd_video_device_unregister() function?
> +err_cleanup_mdev:
> + media_device_cleanup(mdev);
> + v4l2_m2m_release(fd->m2m_dev);
> +err_unreg_v4l2_dev:
> + v4l2_device_unregister(&fd->v4l2_dev);
> + return ret;
> +}
> +
> +static void mtk_fd_dev_v4l2_release(struct mtk_fd_dev *fd)
> +{
> + v4l2_m2m_unregister_media_controller(fd->m2m_dev);
> + video_unregister_device(&fd->vfd);
> + video_device_release(&fd->vfd);
> + media_device_cleanup(&fd->mdev);
> + v4l2_m2m_release(fd->m2m_dev);
> + v4l2_device_unregister(&fd->v4l2_dev);
> +}
> +
> +static irqreturn_t mtk_fd_irq(int irq, void *data)
> +{
> + struct mtk_fd_dev *fd = (struct mtk_fd_dev *)data;
> +
> + /* must read this register otherwise HW will keep sending irq */
> + readl(fd->fd_base + MTK_FD_REG_OFFSET_INT_VAL);
> + fd->output->number = readl(fd->fd_base + MTK_FD_REG_OFFSET_RESULT);
> + dev_dbg(fd->dev, "mtk_fd_face_num:%d\n", fd->output->number);
> +
> + mtk_fd_hw_done(fd, VB2_BUF_STATE_DONE);
> + return IRQ_HANDLED;
> +}
> +
> +static int mtk_fd_hw_get_scp_mem(struct mtk_fd_dev *fd)
> +{
> + struct device *dev = fd->dev;
> + dma_addr_t addr;
> + void *ptr;
> + u32 ret;
> +
> + /*
> + * Allocate coherent reserved memory for SCP firmware usage.
> + * The size of SCP composer's memory is fixed to 0x100000
> + * for the requirement of firmware.
> + */
> + ptr = dma_alloc_coherent(&fd->scp_pdev->dev,
> + MTK_FD_HW_WORK_BUF_SIZE, &addr, GFP_KERNEL);
> + if (!ptr)
> + return -ENOMEM;
> +
> + fd->scp_mem.scp_addr = addr;
> + fd->scp_mem_virt_addr = ptr;
> + dev_info(dev, "scp addr:%pad va:%pK\n", &addr, ptr);
These addresses are nothing a user should be concerned about, so please
don't use the _info level here. If you think this is an important debugging
information, please make it dev_dbg(). Otherwise, please just remove.
> +
> + /*
> + * This reserved memory is also be used by FD HW.
> + * Need to get iova address for FD DMA.
> + */
> + addr = dma_map_resource(dev, addr, MTK_FD_HW_WORK_BUF_SIZE,
> + DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
Note that the second argument to dma_map_resource() is phys_addr_t, but the
code above passes a DMA address. This works only by luck, because both
physical and SCP DMA address space have the same addresses. To be fully
correct, dma_map_single() needs to be used, with the kernel virtual address
passed to it, but currently it doesn't handle the memory from a reserved
pool. Thus, we can only keep the hack as is, but please add a comment
explaining it, e.g.
/*
* FIXME: Assume SCP DMA and physical addresses are the same until
* dma_map_single() is fixed to handle reserved memory allocations.
*/
> + if (dma_mapping_error(dev, addr)) {
> + dev_err(dev, "Failed to map scp iova\n");
> + ret = -ENOMEM;
> + goto fail_free_mem;
> + }
> + fd->scp_mem.dma_addr = addr;
> + dev_info(dev, "scp iova addr:%pad\n", &addr);
Ditto.
Best regards,
Tomasz
^ permalink raw reply
* Re: [PATCH v2 1/4] dt-bindings: iio: imu: bmi160: convert txt format to yaml
From: Jonathan Cameron @ 2020-05-21 18:27 UTC (permalink / raw)
To: Jonathan Albrieux
Cc: Jonathan Cameron, linux-kernel,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Hartmut Knaack, Lars-Peter Clausen,
open list:IIO SUBSYSTEM AND DRIVERS, Peter Meerwald-Stadler,
Rob Herring, Daniel Baluta
In-Reply-To: <20200520072423.GF3361@ict14-OptiPlex-980>
On Wed, 20 May 2020 09:24:23 +0200
Jonathan Albrieux <jonathan.albrieux@gmail.com> wrote:
> On Tue, May 19, 2020 at 06:49:33PM +0100, Jonathan Cameron wrote:
> > On Tue, 19 May 2020 09:50:57 +0200
> > Jonathan Albrieux <jonathan.albrieux@gmail.com> wrote:
> >
> > > Converts documentation from txt format to yaml
> > >
> > > Signed-off-by: Jonathan Albrieux <jonathan.albrieux@gmail.com>
> > > ---
> > > .../devicetree/bindings/iio/imu/bmi160.txt | 37 --------
> > > .../devicetree/bindings/iio/imu/bmi160.yaml | 84 +++++++++++++++++++
> > > 2 files changed, 84 insertions(+), 37 deletions(-)
> > > delete mode 100644 Documentation/devicetree/bindings/iio/imu/bmi160.txt
> > > create mode 100644 Documentation/devicetree/bindings/iio/imu/bmi160.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/iio/imu/bmi160.txt b/Documentation/devicetree/bindings/iio/imu/bmi160.txt
> > > deleted file mode 100644
> > > index 900c169de00f..000000000000
> > > --- a/Documentation/devicetree/bindings/iio/imu/bmi160.txt
> > > +++ /dev/null
> > > @@ -1,37 +0,0 @@
> > > -Bosch BMI160 - Inertial Measurement Unit with Accelerometer, Gyroscope
> > > -and externally connectable Magnetometer
> > > -
> > > -https://www.bosch-sensortec.com/bst/products/all_products/bmi160
> > > -
> > > -Required properties:
> > > - - compatible : should be "bosch,bmi160"
> > > - - reg : the I2C address or SPI chip select number of the sensor
> > > - - spi-max-frequency : set maximum clock frequency (only for SPI)
> > > -
> > > -Optional properties:
> > > - - interrupts : interrupt mapping for IRQ
> > > - - interrupt-names : set to "INT1" if INT1 pin should be used as interrupt
> > > - input, set to "INT2" if INT2 pin should be used instead
> > > - - drive-open-drain : set if the specified interrupt pin should be configured as
> > > - open drain. If not set, defaults to push-pull.
> > > -
> > > -Examples:
> > > -
> > > -bmi160@68 {
> > > - compatible = "bosch,bmi160";
> > > - reg = <0x68>;
> > > -
> > > - interrupt-parent = <&gpio4>;
> > > - interrupts = <12 IRQ_TYPE_EDGE_RISING>;
> > > - interrupt-names = "INT1";
> > > -};
> > > -
> > > -bmi160@0 {
> > > - compatible = "bosch,bmi160";
> > > - reg = <0>;
> > > - spi-max-frequency = <10000000>;
> > > -
> > > - interrupt-parent = <&gpio2>;
> > > - interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
> > > - interrupt-names = "INT2";
> > > -};
> > > diff --git a/Documentation/devicetree/bindings/iio/imu/bmi160.yaml b/Documentation/devicetree/bindings/iio/imu/bmi160.yaml
> > > new file mode 100644
> > > index 000000000000..6b464ce5ed0b
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/iio/imu/bmi160.yaml
> > > @@ -0,0 +1,84 @@
> > > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/iio/imu/bmi160.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Bosch BMI160
> > > +
> > > +maintainers:
> > > + - can't find a mantainer, author is Daniel Baluta <daniel.baluta@intel.com>
> >
> > Daniel is still active in the kernel, just not at Intel any more. +CC
> >
>
> Oh ok thank you! Daniel are you still maintaining this driver?
>
> > > +
> > > +description: |
> > > + Inertial Measurement Unit with Accelerometer, Gyroscope and externally
> > > + connectable Magnetometer
> > > + https://www.bosch-sensortec.com/bst/products/all_products/bmi160
> > > +
> > > +properties:
> > > + compatible:
> > > + const: bosch,bmi160
> > > +
> > > + reg:
> > > + maxItems: 1
> > > + description: the I2C address or SPI chip select number of the sensor
> >
> > As standard for i2c and spi, usually no need to have a description line for
> > this element.
> >
>
> Thank you, will remove the description then.
>
> > > +
> > > + spi-max-frequency:
> > > + maxItems: 1
> > > + description: set maximum clock frequency (required only for SPI)
> >
> > Standard spi binding. Probably doesn't need to be included here.
> >
>
> So should I completely remove it from properties?
Yes
Thanks,
Jonathan
^ permalink raw reply
* Re: [PATCH V1 2/3] mmc: sdhci-msm: Use internal voltage control
From: Bjorn Andersson @ 2020-05-21 18:21 UTC (permalink / raw)
To: Veerabhadrarao Badiganti
Cc: adrian.hunter, ulf.hansson, robh+dt, linux-mmc, linux-kernel,
linux-arm-msm, devicetree, Vijay Viswanath, Asutosh Das,
Andy Gross
In-Reply-To: <1f546a8b-7f10-95e7-efc2-8e3d5787aee6@codeaurora.org>
On Wed 20 May 04:16 PDT 2020, Veerabhadrarao Badiganti wrote:
>
> Thanks Bjorn for the review. For major comments I'm responding.
> Other comments, i will take care of them in my next patch-set.
>
> On 5/19/2020 1:27 AM, Bjorn Andersson wrote:
> > On Fri 15 May 04:18 PDT 2020, Veerabhadrarao Badiganti wrote:
[..]
> > > diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
[..]
> > > +static int sdhci_msm_set_vmmc(struct sdhci_msm_host *msm_host,
> > > + struct mmc_host *mmc, int level)
> > > +{
> > > + int load, ret;
> > > +
> > > + if (IS_ERR(mmc->supply.vmmc))
> > > + return 0;
> > > +
> > > + if (msm_host->vmmc_load) {
> > > + load = level ? msm_host->vmmc_load : 0;
> > > + ret = regulator_set_load(mmc->supply.vmmc, load);
> > I started on the comment about regulator_set_load() that you can find
> > below...
> >
> > > + if (ret)
> > > + goto out;
> > > + }
> > > +
> > > + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, mmc->ios.vdd);
> > ...but I don't see that mmc->ios.vdd necessarily is in sync with
> > "level". Or do you here simply set the load based on what the hardware
> > tell you and then orthogonally to that let the core enable/disable the
> > regulator?
> >
> > Perhaps I'm just missing something obvious, but if not I believe this
> > warrants a comment describing that you're lowering the power level
> > regardless of the actual power being disabled.
>
> ios.vdd will be in sync with level. Vdd will be either 0 or a valid voltage
> (3v).
>
> This indirectly gets triggered/invoked through power-irq when driver writes
> 0
> or valid voltage to SDHCI_POWER_CONTROL register from
> sdhci_set_power_noreg().
Ok, thanks for explaining.
> > > +out:
> > > + if (ret)
> > > + pr_err("%s: vmmc set load/ocr failed: %d\n",
> > > + mmc_hostname(mmc), ret);
> > Please use:
> > dev_err(mmc_dev(mmc), ...);
> >
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static int sdhci_msm_set_vqmmc(struct sdhci_msm_host *msm_host,
> > > + struct mmc_host *mmc, int level)
> > vqmmc_enabled is a bool and "level" sounds like an int with several
> > possible values. So please make level bool here as well, to make it
> > easer to read..
> >
> > > +{
> > > + int load, ret;
> > > + struct mmc_ios ios;
> > > +
> > > + if (IS_ERR(mmc->supply.vqmmc) ||
> > > + (mmc->ios.power_mode == MMC_POWER_UNDEFINED) ||
> > > + (msm_host->vqmmc_enabled == level))
> > > + return 0;
> > > +
> > > + if (msm_host->vqmmc_load) {
> > > + load = level ? msm_host->vqmmc_load : 0;
> > > + ret = regulator_set_load(mmc->supply.vqmmc, load);
> > Since v5.0 the "load" of a regulator consumer is only taken into
> > consideration if the consumer is enabled. So given that you're toggling
> > the regulator below there's no need to change this here.
> >
> > Just specify the "active load" at probe time.
>
> For eMMC case, we don't disable this Vccq2 regulator by having always-on
> flag
> in the regulator node. Only for SDcard Vccq2 will be disabled.
> Sice driver is common for both eMMC and SDcard, I have to set 0 load to make
> it generic and to ensure eMMC Vccq2 regulator will be in LPM mode.
>
You should still call regulator_enable()/regulator_disable() on your
consumer regulator in this driver. When you do this the regulator core
will conclude that the regulator_dev (i.e. the part that represents the
hardware) is marked always_on and will not enable/disable the regulator.
But it will still invoke _regulator_handle_consumer_enable() and
_regulator_handle_consumer_disable(), which will aggregate the "load" of
all client regulators and update the regulator's load.
So this will apply the load as you expect regardless of it being
supplied by a regulator marked as always_on.
> >
> > > + if (ret)
> > > + goto out;
> > > + }
> > > +
> > > + /*
> > > + * The IO voltage regulator may not always support a voltage close to
> > > + * vdd. Set IO voltage based on capability of the regulator.
> > > + */
> > Is this comment related to the if/else-if inside the conditional? If so
> > please move it one line down.
> >
> > > + if (level) {
> > > + if (msm_host->caps_0 & CORE_3_0V_SUPPORT)
> > > + ios.signal_voltage = MMC_SIGNAL_VOLTAGE_330;
> > > + else if (msm_host->caps_0 & CORE_1_8V_SUPPORT)
> > > + ios.signal_voltage = MMC_SIGNAL_VOLTAGE_180;
> > Please add a space here, to indicate that the if statement on the next
> > line is unrelated to the if/elseif above.
> >
> > > + if (msm_host->caps_0 & CORE_VOLT_SUPPORT) {
> > > + pr_debug("%s: %s: setting signal voltage: %d\n",
> > > + mmc_hostname(mmc), __func__,
> > > + ios.signal_voltage);
> > I strongly believe you should replace these debug prints with
> > tracepoints, throughout the driver.
> >
> > > + ret = mmc_regulator_set_vqmmc(mmc, &ios);
> > > + if (ret < 0)
> > > + goto out;
> > > + }
> > > + ret = regulator_enable(mmc->supply.vqmmc);
> > > + } else {
> > > + ret = regulator_disable(mmc->supply.vqmmc);
> > > + }
> > Given that you don't need to regulator_set_load() this function is now
> > just one large if/else condition on a constant passed as an argument.
> > Please split it into sdhci_msm_enable_vqmmc() and
> > sdhci_msm_disable_vqmmc().
>
>
> Same response as above
> For eMMC case, we don't disable this Vccq2 regulator by having always-on
> flag
> in the regulator node. Only for SDcard Vccq2 will be disabled.
> Sice driver is common for both eMMC and SDcard, I have to set 0 load to make
> it generic and to ensure eMMC Vccq2 regulator will be in LPM mode.
>
> > > +out:
> > > + if (ret)
> > > + pr_err("%s: vqmmc failed: %d\n", mmc_hostname(mmc), ret);
> > I think it would be useful to know if this error came from
> > mmc_regulator_set_vqmmc() or regulator_enable() - or
> > regulator_disable().
> >
> > So please move this up and add some context in the error message, and
> > please use dev_err().
> >
> > > + else
> > > + msm_host->vqmmc_enabled = level;
> > > +
> > > + return ret;
> > > +}
> > > +
> > > static inline void sdhci_msm_init_pwr_irq_wait(struct sdhci_msm_host *msm_host)
> > > {
> > > init_waitqueue_head(&msm_host->pwr_irq_wait);
> > > @@ -1401,8 +1478,9 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq)
> > > {
> > > struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> > > struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
> > > + struct mmc_host *mmc = host->mmc;
> > > u32 irq_status, irq_ack = 0;
> > > - int retry = 10;
> > > + int retry = 10, ret = 0;
> > There's no need to initialize ret, in all occasions it's assigned before
> > being read.
> >
> > > u32 pwr_state = 0, io_level = 0;
> > > u32 config;
> > > const struct sdhci_msm_offset *msm_offset = msm_host->offset;
> > > @@ -1438,14 +1516,35 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq)
> > > /* Handle BUS ON/OFF*/
> > > if (irq_status & CORE_PWRCTL_BUS_ON) {
> > > - pwr_state = REQ_BUS_ON;
> > > - io_level = REQ_IO_HIGH;
> > > - irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
> > > + ret = sdhci_msm_set_vmmc(msm_host, mmc, 1);
> > > + if (!ret)
> > > + ret = sdhci_msm_set_vqmmc(msm_host, mmc, 1);
> > I find this quite complex to follow. Wouldn't it be cleaner to retain
> > the 4 checks on irq_status as they are and then before the writel of
> > irq_ack check pwr_state and io_level and call sdhci_msm_set_{vmmc,vqmmc}
> > accordingly?
>
> I will see if i can update as you suggested.
>
> > > +
> > > + if (!ret) {
> > > + pwr_state = REQ_BUS_ON;
> > > + io_level = REQ_IO_HIGH;
> > > + irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
> > > + } else {
> > > + pr_err("%s: BUS_ON req failed(%d). irq_status: 0x%08x\n",
> > > + mmc_hostname(mmc), ret, irq_status);
> > You already printed that this failed in sdhci_msm_set_{vmmc,vqmmc}, no
> > need to print again.
> >
> > > + irq_ack |= CORE_PWRCTL_BUS_FAIL;
> > > + sdhci_msm_set_vmmc(msm_host, mmc, 0);
> > > + }
> > > }
> > > if (irq_status & CORE_PWRCTL_BUS_OFF) {
> > > - pwr_state = REQ_BUS_OFF;
> > > - io_level = REQ_IO_LOW;
> > > - irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
> > > + ret = sdhci_msm_set_vmmc(msm_host, mmc, 0);
> > > + if (!ret)
> > > + ret = sdhci_msm_set_vqmmc(msm_host, mmc, 0);
> > > +
> > > + if (!ret) {
> > > + pwr_state = REQ_BUS_OFF;
> > > + io_level = REQ_IO_LOW;
> > > + irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
> > > + } else {
> > > + pr_err("%s: BUS_ON req failed(%d). irq_status: 0x%08x\n",
> > > + mmc_hostname(mmc), ret, irq_status);
> > > + irq_ack |= CORE_PWRCTL_BUS_FAIL;
> > > + }
> > > }
> > > /* Handle IO LOW/HIGH */
> > > if (irq_status & CORE_PWRCTL_IO_LOW) {
> > > @@ -1457,6 +1556,15 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq)
> > > irq_ack |= CORE_PWRCTL_IO_SUCCESS;
> > > }
> > > + if (io_level && !IS_ERR(mmc->supply.vqmmc) && !pwr_state) {
> > > + ret = mmc_regulator_set_vqmmc(mmc, &mmc->ios);
> > Didn't you already call this through sdhci_msm_set_vqmmc()?
>
> No.sdhci_msm_set_vqmmc handles only vqmmc ON/OFF. While turning it ON it
> sets
> Vqmmc to possbile default IO level (1.8v or 3.0v).
> Where this is only to make IO lines high (3.0v) or Low (1.8v).
If you move both the regulator operations here (below the point where
you figure out pwr_state and io_level), wouldn't it be possible to avoid
the additional, nested, vqmmc voltage request?
> > > + if (ret < 0)
> > > + pr_err("%s: IO_level setting failed(%d). signal_voltage: %d, vdd: %d irq_status: 0x%08x\n",
> > > + mmc_hostname(mmc), ret,
> > > + mmc->ios.signal_voltage, mmc->ios.vdd,
> > > + irq_status);
> > > + }
> > > +
> > > /*
> > > * The driver has to acknowledge the interrupt, switch voltages and
> > > * report back if it succeded or not to this register. The voltage
> > > @@ -1833,6 +1941,91 @@ static void sdhci_msm_reset(struct sdhci_host *host, u8 mask)
> > > sdhci_reset(host, mask);
> > > }
> > > +static int sdhci_msm_register_vreg(struct sdhci_msm_host *msm_host)
> > > +{
> > > + int ret = 0;
> > No need to initialize ret, first use is an assignment.
> >
> > > + struct mmc_host *mmc = msm_host->mmc;
> > > +
> > > + ret = mmc_regulator_get_supply(msm_host->mmc);
> > > + if (ret)
> > > + return ret;
> > > + device_property_read_u32(&msm_host->pdev->dev,
> > > + "vmmc-max-load-microamp",
> > > + &msm_host->vmmc_load);
> > > + device_property_read_u32(&msm_host->pdev->dev,
> > > + "vqmmc-max-load-microamp",
> > > + &msm_host->vqmmc_load);
> > These properties are not documented. Do they vary enough to mandate
> > being read from DT or could they simply be approximated by a define
> > instead?
>
> I can use defines. But since these values are different for eMMC and SDcard
> I will have to maintain two sets and need to have logic during probe to
> identify SDcard or eMMC and use the assign the set accordingly.
> So we tought, getting from dt is simpler and clean.
> In case Rob didn't agree with dt entries, I will go with this approach.
>
Sounds reasonable, let's see what Rob says.
> > > +
> > > + sdhci_msm_set_regulator_caps(msm_host);
> > > + mmc->ios.power_mode = MMC_POWER_UNDEFINED;
> > > +
> > > + return 0;
> > > +
> > > +}
> > > +
> > > +static int sdhci_msm_start_signal_voltage_switch(struct mmc_host *mmc,
> > > + struct mmc_ios *ios)
> > > +{
> > > + struct sdhci_host *host = mmc_priv(mmc);
> > > + u16 ctrl;
> > > +
> > > + /*
> > > + * Signal Voltage Switching is only applicable for Host Controllers
> > > + * v3.00 and above.
> > > + */
> > > + if (host->version < SDHCI_SPEC_300)
> > > + return 0;
> > > +
> > > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > +
> > > + switch (ios->signal_voltage) {
> > > + case MMC_SIGNAL_VOLTAGE_330:
> > > + if (!(host->flags & SDHCI_SIGNALING_330))
> > > + return -EINVAL;
> > > + /* Set 1.8V Signal Enable in the Host Control2 register to 0 */
> > > + ctrl &= ~SDHCI_CTRL_VDD_180;
> > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > +
> > > + /* 3.3V regulator output should be stable within 5 ms */
> > What mechanism ensures that the readw won't return withing 5ms from the
> > writew above?
>
> Thanks for pointing this. This delay got missed. I will add it in next
> patchset.
Nice, thanks.
Regards,
Bjorn
^ permalink raw reply
* Re: [PATCH v14 2/2] iio: proximity: Add driver support for vcnl3020 proximity sensor
From: Jonathan Cameron @ 2020-05-21 18:14 UTC (permalink / raw)
To: Ivan Mikhaylov
Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
linux-iio, linux-kernel, devicetree, Mark Rutland, Rob Herring,
Andy Shevchenko
In-Reply-To: <20200510184537.10335-3-i.mikhaylov@yadro.com>
On Sun, 10 May 2020 21:45:37 +0300
Ivan Mikhaylov <i.mikhaylov@yadro.com> wrote:
> Proximity sensor driver based on light/vcnl4000.c code.
> For now supports only the single on-demand measurement.
>
> The VCNL3020 is a fully integrated proximity sensor. Fully
> integrated means that the infrared emitter is included in the
> package. It has 16-bit resolution. It includes a signal
> processing IC and features standard I2C communication
> interface. It features an interrupt function.
>
> Datasheet: http://www.vishay.com/docs/84150/vcnl3020.pdf
> Signed-off-by: Ivan Mikhaylov <i.mikhaylov@yadro.com>
> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Applied to the togreg branch of iio.git and pushed out as testing
or the autobuilders to play with it.
I 'might' manage to sneak a last pull request to Greg tomorrow
so this might make the coming merge window though we are cutting
it close so may go either way.
Thanks,
Jonathan
> ---
> drivers/iio/proximity/Kconfig | 11 ++
> drivers/iio/proximity/Makefile | 1 +
> drivers/iio/proximity/vcnl3020.c | 258 +++++++++++++++++++++++++++++++
> 3 files changed, 270 insertions(+)
> create mode 100644 drivers/iio/proximity/vcnl3020.c
>
> diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig
> index d53601447da4..b8d2b17e60ac 100644
> --- a/drivers/iio/proximity/Kconfig
> +++ b/drivers/iio/proximity/Kconfig
> @@ -112,6 +112,17 @@ config SRF08
> To compile this driver as a module, choose M here: the
> module will be called srf08.
>
> +config VCNL3020
> + tristate "VCNL3020 proximity sensor"
> + select REGMAP_I2C
> + depends on I2C
> + help
> + Say Y here if you want to build a driver for the Vishay VCNL3020
> + proximity sensor.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called vcnl3020.
> +
> config VL53L0X_I2C
> tristate "STMicroelectronics VL53L0X ToF ranger sensor (I2C)"
> depends on I2C
> diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
> index 0bb5f9de13d6..8245978ced30 100644
> --- a/drivers/iio/proximity/Makefile
> +++ b/drivers/iio/proximity/Makefile
> @@ -12,5 +12,6 @@ obj-$(CONFIG_RFD77402) += rfd77402.o
> obj-$(CONFIG_SRF04) += srf04.o
> obj-$(CONFIG_SRF08) += srf08.o
> obj-$(CONFIG_SX9500) += sx9500.o
> +obj-$(CONFIG_VCNL3020) += vcnl3020.o
> obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o
>
> diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c
> new file mode 100644
> index 000000000000..9ff1a164c2e6
> --- /dev/null
> +++ b/drivers/iio/proximity/vcnl3020.c
> @@ -0,0 +1,258 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Support for Vishay VCNL3020 proximity sensor on i2c bus.
> + * Based on Vishay VCNL4000 driver code.
> + *
> + * TODO: interrupts.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/err.h>
> +#include <linux/delay.h>
> +#include <linux/regmap.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +#define VCNL3020_PROD_ID 0x21
> +
> +#define VCNL_COMMAND 0x80 /* Command register */
> +#define VCNL_PROD_REV 0x81 /* Product ID and Revision ID */
> +#define VCNL_PROXIMITY_RATE 0x82 /* Rate of Proximity Measurement */
> +#define VCNL_LED_CURRENT 0x83 /* IR LED current for proximity mode */
> +#define VCNL_PS_RESULT_HI 0x87 /* Proximity result register, MSB */
> +#define VCNL_PS_RESULT_LO 0x88 /* Proximity result register, LSB */
> +#define VCNL_PS_ICR 0x89 /* Interrupt Control Register */
> +#define VCNL_PS_LO_THR_HI 0x8a /* High byte of low threshold value */
> +#define VCNL_PS_LO_THR_LO 0x8b /* Low byte of low threshold value */
> +#define VCNL_PS_HI_THR_HI 0x8c /* High byte of high threshold value */
> +#define VCNL_PS_HI_THR_LO 0x8d /* Low byte of high threshold value */
> +#define VCNL_ISR 0x8e /* Interrupt Status Register */
> +#define VCNL_PS_MOD_ADJ 0x8f /* Proximity Modulator Timing Adjustment */
> +
> +/* Bit masks for COMMAND register */
> +#define VCNL_PS_RDY BIT(5) /* proximity data ready? */
> +#define VCNL_PS_OD BIT(3) /* start on-demand proximity
> + * measurement
> + */
> +
> +#define VCNL_ON_DEMAND_TIMEOUT_US 100000
> +#define VCNL_POLL_US 20000
> +
> +/**
> + * struct vcnl3020_data - vcnl3020 specific data.
> + * @regmap: device register map.
> + * @dev: vcnl3020 device.
> + * @rev: revision id.
> + * @lock: lock for protecting access to device hardware registers.
> + */
> +struct vcnl3020_data {
> + struct regmap *regmap;
> + struct device *dev;
> + u8 rev;
> + struct mutex lock;
> +};
> +
> +/**
> + * struct vcnl3020_property - vcnl3020 property.
> + * @name: property name.
> + * @reg: i2c register offset.
> + * @conversion_func: conversion function.
> + */
> +struct vcnl3020_property {
> + const char *name;
> + u32 reg;
> + u32 (*conversion_func)(u32 *val);
> +};
> +
> +static u32 microamp_to_reg(u32 *val)
> +{
> + /*
> + * An example of conversion from uA to reg val:
> + * 200000 uA == 200 mA == 20
> + */
> + return *val /= 10000;
> +};
> +
> +static struct vcnl3020_property vcnl3020_led_current_property = {
> + .name = "vishay,led-current-microamp",
> + .reg = VCNL_LED_CURRENT,
> + .conversion_func = microamp_to_reg,
> +};
> +
> +static int vcnl3020_get_and_apply_property(struct vcnl3020_data *data,
> + struct vcnl3020_property prop)
> +{
> + int rc;
> + u32 val;
> +
> + rc = device_property_read_u32(data->dev, prop.name, &val);
> + if (rc)
> + return 0;
> +
> + if (prop.conversion_func)
> + prop.conversion_func(&val);
> +
> + rc = regmap_write(data->regmap, prop.reg, val);
> + if (rc) {
> + dev_err(data->dev, "Error (%d) setting property (%s)\n",
> + rc, prop.name);
> + }
> +
> + return rc;
> +}
> +
> +static int vcnl3020_init(struct vcnl3020_data *data)
> +{
> + int rc;
> + unsigned int reg;
> +
> + rc = regmap_read(data->regmap, VCNL_PROD_REV, ®);
> + if (rc) {
> + dev_err(data->dev,
> + "Error (%d) reading product revision\n", rc);
> + return rc;
> + }
> +
> + if (reg != VCNL3020_PROD_ID) {
> + dev_err(data->dev,
> + "Product id (%x) did not match vcnl3020 (%x)\n", reg,
> + VCNL3020_PROD_ID);
> + return -ENODEV;
> + }
> +
> + data->rev = reg;
> + mutex_init(&data->lock);
> +
> + return vcnl3020_get_and_apply_property(data,
> + vcnl3020_led_current_property);
> +};
> +
> +static int vcnl3020_measure_proximity(struct vcnl3020_data *data, int *val)
> +{
> + int rc;
> + unsigned int reg;
> + __be16 res;
> +
> + mutex_lock(&data->lock);
> +
> + rc = regmap_write(data->regmap, VCNL_COMMAND, VCNL_PS_OD);
> + if (rc)
> + goto err_unlock;
> +
> + /* wait for data to become ready */
> + rc = regmap_read_poll_timeout(data->regmap, VCNL_COMMAND, reg,
> + reg & VCNL_PS_RDY, VCNL_POLL_US,
> + VCNL_ON_DEMAND_TIMEOUT_US);
> + if (rc) {
> + dev_err(data->dev,
> + "Error (%d) reading vcnl3020 command register\n", rc);
> + goto err_unlock;
> + }
> +
> + /* high & low result bytes read */
> + rc = regmap_bulk_read(data->regmap, VCNL_PS_RESULT_HI, &res,
> + sizeof(res));
> + if (rc)
> + goto err_unlock;
> +
> + *val = be16_to_cpu(res);
> +
> +err_unlock:
> + mutex_unlock(&data->lock);
> +
> + return rc;
> +}
> +
> +static const struct iio_chan_spec vcnl3020_channels[] = {
> + {
> + .type = IIO_PROXIMITY,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> + },
> +};
> +
> +static int vcnl3020_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + int rc;
> + struct vcnl3020_data *data = iio_priv(indio_dev);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + rc = vcnl3020_measure_proximity(data, val);
> + if (rc)
> + return rc;
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static const struct iio_info vcnl3020_info = {
> + .read_raw = vcnl3020_read_raw,
> +};
> +
> +static const struct regmap_config vcnl3020_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = VCNL_PS_MOD_ADJ,
> +};
> +
> +static int vcnl3020_probe(struct i2c_client *client)
> +{
> + struct vcnl3020_data *data;
> + struct iio_dev *indio_dev;
> + struct regmap *regmap;
> + int rc;
> +
> + regmap = devm_regmap_init_i2c(client, &vcnl3020_regmap_config);
> + if (IS_ERR(regmap)) {
> + dev_err(&client->dev, "regmap_init failed\n");
> + return PTR_ERR(regmap);
> + }
> +
> + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + data = iio_priv(indio_dev);
> + i2c_set_clientdata(client, indio_dev);
> + data->regmap = regmap;
> + data->dev = &client->dev;
> +
> + rc = vcnl3020_init(data);
> + if (rc)
> + return rc;
> +
> + indio_dev->dev.parent = &client->dev;
> + indio_dev->info = &vcnl3020_info;
> + indio_dev->channels = vcnl3020_channels;
> + indio_dev->num_channels = ARRAY_SIZE(vcnl3020_channels);
> + indio_dev->name = "vcnl3020";
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + return devm_iio_device_register(&client->dev, indio_dev);
> +}
> +
> +static const struct of_device_id vcnl3020_of_match[] = {
> + {
> + .compatible = "vishay,vcnl3020",
> + },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, vcnl3020_of_match);
> +
> +static struct i2c_driver vcnl3020_driver = {
> + .driver = {
> + .name = "vcnl3020",
> + .of_match_table = vcnl3020_of_match,
> + },
> + .probe_new = vcnl3020_probe,
> +};
> +module_i2c_driver(vcnl3020_driver);
> +
> +MODULE_AUTHOR("Ivan Mikhaylov <i.mikhaylov@yadro.com>");
> +MODULE_DESCRIPTION("Vishay VCNL3020 proximity sensor driver");
> +MODULE_LICENSE("GPL");
^ permalink raw reply
* Re: [PATCH V6 2/7] soc: qcom-geni-se: Add interconnect support to fix earlycon crash
From: Matthias Kaehlcke @ 2020-05-21 18:12 UTC (permalink / raw)
To: Akash Asthana
Cc: gregkh, agross, bjorn.andersson, wsa, broonie, mark.rutland,
robh+dt, linux-i2c, linux-spi, devicetree, swboyd, mgautam,
linux-arm-msm, linux-serial, dianders, msavaliy, evgreen
In-Reply-To: <1590049764-20912-3-git-send-email-akashast@codeaurora.org>
Hi Akash,
On Thu, May 21, 2020 at 01:59:19PM +0530, Akash Asthana wrote:
> QUP core clock is shared among all the SE drivers present on particular
> QUP wrapper, the system will reset(unclocked access) if earlycon used after
> QUP core clock is put to 0 from other SE drivers before real console comes
> up.
>
> As earlycon can't vote for it's QUP core need, to fix this add ICC
> support to common/QUP wrapper driver and put vote for QUP core from
> probe on behalf of earlycon and remove vote during earlycon exit call.
>
> Signed-off-by: Akash Asthana <akashast@codeaurora.org>
> Reported-by: Matthias Kaehlcke <mka@chromium.org>
> ---
> Change in V3:
> - Add geni_remove_earlycon_icc_vote API that will be used by earlycon
> exit function to remove ICC vote for earlyconsole.
> - Remove suspend/resume hook for geni-se driver as we are no longer
> removing earlyconsole ICC vote from system suspend, we are removing
> from earlycon exit.
>
> Change in V4:
> - As per Matthias comment make 'earlycon_wrapper' as static structure.
>
> Changes in V5:
> - Vote for core path only after checking whether "qcom_geni" earlycon is
> actually present or not by traversing over structure "console_drivers".
>
> Changes in V6:
> - As per Matthias's comment removed NULL check for console_drivers global
> struct, added NULL check for earlycon_wrapper in _remove_earlycon_icc_vote
> API
> - Addressed nitpicks from Andy.
>
> drivers/soc/qcom/qcom-geni-se.c | 68 +++++++++++++++++++++++++++++++++++
> drivers/tty/serial/qcom_geni_serial.c | 7 ++++
> include/linux/qcom-geni-se.h | 2 ++
> 3 files changed, 77 insertions(+)
>
> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
> index 0b2526d..ac16bb1 100644
> --- a/drivers/soc/qcom/qcom-geni-se.c
> +++ b/drivers/soc/qcom/qcom-geni-se.c
> @@ -3,6 +3,7 @@
>
> #include <linux/acpi.h>
> #include <linux/clk.h>
> +#include <linux/console.h>
> #include <linux/slab.h>
> #include <linux/dma-mapping.h>
> #include <linux/io.h>
> @@ -90,11 +91,14 @@ struct geni_wrapper {
> struct device *dev;
> void __iomem *base;
> struct clk_bulk_data ahb_clks[NUM_AHB_CLKS];
> + struct geni_icc_path to_core;
> };
>
> static const char * const icc_path_names[] = {"qup-core", "qup-config",
> "qup-memory"};
>
> +static struct geni_wrapper *earlycon_wrapper;
> +
> #define QUP_HW_VER_REG 0x4
>
> /* Common SE registers */
> @@ -812,11 +816,38 @@ int geni_icc_disable(struct geni_se *se)
> }
> EXPORT_SYMBOL(geni_icc_disable);
>
> +void geni_remove_earlycon_icc_vote(void)
> +{
> + struct geni_wrapper *wrapper;
> + struct device_node *parent;
> + struct device_node *child;
> +
> + if (!earlycon_wrapper)
> + return;
> +
> + wrapper = earlycon_wrapper;
> + parent = of_get_next_parent(wrapper->dev->of_node);
> + for_each_child_of_node(parent, child) {
> + if (!of_device_is_compatible(child, "qcom,geni-se-qup"))
> + continue;
> + wrapper = platform_get_drvdata(of_find_device_by_node(child));
> + icc_put(wrapper->to_core.path);
> + wrapper->to_core.path = NULL;
> +
> + }
> + of_node_put(parent);
> +
> + earlycon_wrapper = NULL;
> +}
> +EXPORT_SYMBOL(geni_remove_earlycon_icc_vote);
> +
> static int geni_se_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> struct resource *res;
> struct geni_wrapper *wrapper;
> + struct console __maybe_unused *bcon;
> + bool __maybe_unused has_earlycon = false;
> int ret;
>
> wrapper = devm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL);
> @@ -839,6 +870,43 @@ static int geni_se_probe(struct platform_device *pdev)
> }
> }
>
> +#ifdef CONFIG_SERIAL_EARLYCON
> + for_each_console(bcon) {
> + if (!strcmp(bcon->name, "qcom_geni")) {
> + has_earlycon = true;
> + break;
> + }
> + }
> + if (!has_earlycon)
> + goto exit;
> +
> + wrapper->to_core.path = devm_of_icc_get(dev, "qup-core");
> + if (IS_ERR(wrapper->to_core.path))
> + return PTR_ERR(wrapper->to_core.path);
> + /*
> + * Put minmal BW request on core clocks on behalf of early console.
> + * The vote will be removed earlycon exit function.
> + *
> + * Note: We are putting vote on each QUP wrapper instead only to which
> + * earlycon is connected because QUP core clock of different wrapper
> + * share same voltage domain. If core1 is put to 0, then core2 will
> + * also run at 0, if not voted. Default ICC vote will be removed ASA
> + * we touch any of the core clock.
> + * core1 = core2 = max(core1, core2)
> + */
> + ret = icc_set_bw(wrapper->to_core.path, GENI_DEFAULT_BW,
> + GENI_DEFAULT_BW);
> + if (ret) {
> + dev_err(&pdev->dev, "%s: ICC BW voting failed for core :%d\n",
should be "... core: %d"
same for the other instances. I don't necessarily want to stall the series on
this, it can also be addressed with a follow up patch. Up to you if you want
to respin or not.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
^ permalink raw reply
* Re: [PATCH v14 1/2] dt-bindings: proximity: provide vcnl3020 device tree binding document
From: Jonathan Cameron @ 2020-05-21 18:11 UTC (permalink / raw)
To: Rob Herring
Cc: Ivan Mikhaylov, linux-kernel, Hartmut Knaack, linux-iio,
Peter Meerwald-Stadler, devicetree, Lars-Peter Clausen,
Mark Rutland, Andy Shevchenko, Rob Herring
In-Reply-To: <20200518204144.GA11276@bogus>
On Mon, 18 May 2020 14:41:44 -0600
Rob Herring <robh@kernel.org> wrote:
> On Sun, 10 May 2020 21:45:36 +0300, Ivan Mikhaylov wrote:
> > Mostly standard i2c driver with some additional led-current option
> > for vcnl3020.
> >
> > Signed-off-by: Ivan Mikhaylov <i.mikhaylov@yadro.com>
> > ---
> > .../iio/proximity/vishay,vcnl3020.yaml | 62 +++++++++++++++++++
> > 1 file changed, 62 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/iio/proximity/vishay,vcnl3020.yaml
> >
>
> Reviewed-by: Rob Herring <robh@kernel.org>
Applied to the togreg branch of iio.git and pushed out as testing for
the autobuilders to play with it.
Thanks,
Jonathan
^ permalink raw reply
* Re: [RFC PATCH net-next 1/4] dt-bindings: net: Add tx and rx internal delays
From: Florian Fainelli @ 2020-05-21 18:06 UTC (permalink / raw)
To: Dan Murphy, andrew, hkallweit1, davem, robh
Cc: netdev, linux-kernel, devicetree
In-Reply-To: <20200521174834.3234-2-dmurphy@ti.com>
On 5/21/2020 10:48 AM, Dan Murphy wrote:
> tx-internal-delays and rx-internal-delays are a common setting for RGMII
> capable devices.
>
> These properties are used when the phy-mode or phy-controller is set to
> rgmii-id, rgmii-rxid or rgmii-txid. These modes indicate to the
> controller that the PHY will add the internal delay for the connection.
>
> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> ---
> .../bindings/net/ethernet-controller.yaml | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
> index ac471b60ed6a..3f25066c339c 100644
> --- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml
> +++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
> @@ -143,6 +143,20 @@ properties:
> Specifies the PHY management type. If auto is set and fixed-link
> is not specified, it uses MDIO for management.
>
> + rx-internal-delay:
Please name this 'rx-internal-delay-ps'
> + $ref: /schemas/types.yaml#definitions/uint32
> + description: |
> + RGMII Receive PHY Clock Delay defined in pico seconds. This is used for
> + PHY's that have configurable RX internal delays. This property is only
> + used when the phy-mode or phy-connection-type is rgmii-id or rgmii-rxid.
> +
> + tx-internal-delay:
Likewise
> + $ref: /schemas/types.yaml#definitions/uint32
> + description: |
> + RGMII Transmit PHY Clock Delay defined in pico seconds. This is used for
> + PHY's that have configurable TX internal delays. This property is only
> + used when the phy-mode or phy-connection-type is rgmii-id or rgmii-txid.
> +
> fixed-link:
> allOf:
> - if:
>
--
Florian
^ permalink raw reply
* Re: [PATCH 3/4] remoteproc: add support for a new 64-bit trace version
From: Bjorn Andersson @ 2020-05-21 18:04 UTC (permalink / raw)
To: Suman Anna
Cc: Rob Herring, Mathieu Poirier, Clement Leger, Loic Pallardy,
Arnaud Pouliquen, Lokesh Vutla, linux-remoteproc, devicetree,
linux-arm-kernel, linux-kernel
In-Reply-To: <20200325204701.16862-4-s-anna@ti.com>
On Wed 25 Mar 13:47 PDT 2020, Suman Anna wrote:
> Introduce a new trace entry resource structure that accommodates
> a 64-bit device address to support 64-bit processors. This is to
> be used using an overloaded version value of 1 in the upper 32-bits
> of the previous resource type field. The new resource still uses
> 32-bits for the length field (followed by a 32-bit reserved field,
> so can be updated in the future), which is a sufficiently large
> trace buffer size. A 32-bit padding field also had to be added
> to align the device address on a 64-bit boundary, and match the
> usage on the firmware side.
>
> The remoteproc debugfs logic also has been adjusted accordingly.
>
> Signed-off-by: Suman Anna <s-anna@ti.com>
> ---
> drivers/remoteproc/remoteproc_core.c | 40 ++++++++++++++++++++-----
> drivers/remoteproc/remoteproc_debugfs.c | 37 ++++++++++++++++++-----
> include/linux/remoteproc.h | 26 ++++++++++++++++
> 3 files changed, 87 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
> index 53bc37c508c6..b9a097990862 100644
> --- a/drivers/remoteproc/remoteproc_core.c
> +++ b/drivers/remoteproc/remoteproc_core.c
> @@ -609,21 +609,45 @@ void rproc_vdev_release(struct kref *ref)
> *
> * Returns 0 on success, or an appropriate error code otherwise
> */
> -static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
> +static int rproc_handle_trace(struct rproc *rproc, void *rsc,
> int offset, int avail, u16 ver)
> {
> struct rproc_debug_trace *trace;
> struct device *dev = &rproc->dev;
> + struct fw_rsc_trace *rsc1;
> + struct fw_rsc_trace2 *rsc2;
> char name[15];
> + size_t rsc_size;
> + u32 reserved;
> + u64 da;
> + u32 len;
> +
> + if (!ver) {
This looks like a switch to me, but I also do think this looks rather
crude, if you spin off the tail of this function and call it from a
rproc_handle_trace() and rproc_handle_trace64() I believe this would be
cleaner.
> + rsc1 = (struct fw_rsc_trace *)rsc;
> + rsc_size = sizeof(*rsc1);
> + reserved = rsc1->reserved;
> + da = rsc1->da;
> + len = rsc1->len;
> + } else if (ver == 1) {
> + rsc2 = (struct fw_rsc_trace2 *)rsc;
> + rsc_size = sizeof(*rsc2);
> + reserved = rsc2->reserved;
> + da = rsc2->da;
> + len = rsc2->len;
> + } else {
> + dev_err(dev, "unsupported trace rsc version %d\n", ver);
If we use "type" to describe your 64-bit-da-trace then this sanity check
would have been taken care of by the core.
> + return -EINVAL;
> + }
>
> - if (sizeof(*rsc) > avail) {
> + if (rsc_size > avail) {
> dev_err(dev, "trace rsc is truncated\n");
> return -EINVAL;
> }
>
> /* make sure reserved bytes are zeroes */
> - if (rsc->reserved) {
> - dev_err(dev, "trace rsc has non zero reserved bytes\n");
> + if (reserved) {
> + dev_err(dev, "trace rsc has non zero reserved bytes, value = 0x%x\n",
> + reserved);
> return -EINVAL;
> }
>
> @@ -632,8 +656,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
> return -ENOMEM;
>
> /* set the trace buffer dma properties */
> - trace->trace_mem.len = rsc->len;
> - trace->trace_mem.da = rsc->da;
> + trace->trace_mem.len = len;
> + trace->trace_mem.da = da;
>
> /* set pointer on rproc device */
> trace->rproc = rproc;
> @@ -652,8 +676,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
>
> rproc->num_traces++;
>
> - dev_dbg(dev, "%s added: da 0x%x, len 0x%x\n",
> - name, rsc->da, rsc->len);
> + dev_dbg(dev, "%s added: da 0x%llx, len 0x%x\n",
> + name, da, len);
>
> return 0;
> }
> diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c
> index 3560eed7a360..ff43736db45a 100644
> --- a/drivers/remoteproc/remoteproc_debugfs.c
> +++ b/drivers/remoteproc/remoteproc_debugfs.c
> @@ -192,7 +192,8 @@ static int rproc_rsc_table_show(struct seq_file *seq, void *p)
> struct resource_table *table = rproc->table_ptr;
> struct fw_rsc_carveout *c;
> struct fw_rsc_devmem *d;
> - struct fw_rsc_trace *t;
> + struct fw_rsc_trace *t1;
> + struct fw_rsc_trace2 *t2;
> struct fw_rsc_vdev *v;
> int i, j;
>
> @@ -205,6 +206,7 @@ static int rproc_rsc_table_show(struct seq_file *seq, void *p)
> int offset = table->offset[i];
> struct fw_rsc_hdr *hdr = (void *)table + offset;
> void *rsc = (void *)hdr + sizeof(*hdr);
> + u16 ver = hdr->st.v;
>
> switch (hdr->st.t) {
> case RSC_CARVEOUT:
> @@ -230,13 +232,32 @@ static int rproc_rsc_table_show(struct seq_file *seq, void *p)
> seq_printf(seq, " Name %s\n\n", d->name);
> break;
> case RSC_TRACE:
> - t = rsc;
> - seq_printf(seq, "Entry %d is of type %s\n",
> - i, types[hdr->st.t]);
> - seq_printf(seq, " Device Address 0x%x\n", t->da);
> - seq_printf(seq, " Length 0x%x Bytes\n", t->len);
> - seq_printf(seq, " Reserved (should be zero) [%d]\n", t->reserved);
> - seq_printf(seq, " Name %s\n\n", t->name);
> + if (ver == 0) {
Again, this is a switch, here in a switch. Just defining a new
RSC_TRACE64 type would reduce the amount of code here...
> + t1 = rsc;
> + seq_printf(seq, "Entry %d is version %d of type %s\n",
> + i, ver, types[hdr->st.t]);
> + seq_printf(seq, " Device Address 0x%x\n",
> + t1->da);
> + seq_printf(seq, " Length 0x%x Bytes\n",
> + t1->len);
> + seq_printf(seq, " Reserved (should be zero) [%d]\n",
> + t1->reserved);
> + seq_printf(seq, " Name %s\n\n", t1->name);
> + } else if (ver == 1) {
> + t2 = rsc;
> + seq_printf(seq, "Entry %d is version %d of type %s\n",
> + i, ver, types[hdr->st.t]);
> + seq_printf(seq, " Device Address 0x%llx\n",
> + t2->da);
> + seq_printf(seq, " Length 0x%x Bytes\n",
> + t2->len);
> + seq_printf(seq, " Reserved (should be zero) [%d]\n",
> + t2->reserved);
> + seq_printf(seq, " Name %s\n\n", t2->name);
> + } else {
> + seq_printf(seq, "Entry %d is an unsupported version %d of type %s\n",
> + i, ver, types[hdr->st.t]);
> + }
> break;
> case RSC_VDEV:
> v = rsc;
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> index 526d3cb45e37..3b3bea42f8b1 100644
> --- a/include/linux/remoteproc.h
> +++ b/include/linux/remoteproc.h
> @@ -243,6 +243,32 @@ struct fw_rsc_trace {
> u8 name[32];
> } __packed;
>
> +/**
> + * struct fw_rsc_trace2 - trace buffer declaration supporting 64-bits
> + * @padding: initial padding after type field for aligned 64-bit access
> + * @da: device address (64-bit)
> + * @len: length (in bytes)
> + * @reserved: reserved (must be zero)
> + * @name: human-readable name of the trace buffer
> + *
> + * This resource entry is an enhanced version of the fw_rsc_trace resourec entry
> + * and the provides equivalent functionality but designed for 64-bit remote
> + * processors.
> + *
> + * @da specifies the device address of the buffer, @len specifies
> + * its size, and @name may contain a human readable name of the trace buffer.
> + *
> + * After booting the remote processor, the trace buffers are exposed to the
> + * user via debugfs entries (called trace0, trace1, etc..).
> + */
> +struct fw_rsc_trace2 {
Sounds more like fw_rsc_trace64 to me - in particular since the version
of trace2 is 1...
> + u32 padding;
> + u64 da;
> + u32 len;
> + u32 reserved;
What's the purpose of this reserved field?
Regards,
Bjorn
> + u8 name[32];
> +} __packed;
> +
> /**
> * struct fw_rsc_vdev_vring - vring descriptor entry
> * @da: device address
> --
> 2.23.0
>
^ permalink raw reply
* Re: [PATCH 09/12] iio: imu: inv_icm42600: add buffer support in iio devices
From: Jonathan Cameron @ 2020-05-21 17:56 UTC (permalink / raw)
To: Jean-Baptiste Maneyrol
Cc: Jonathan Cameron, robh+dt@kernel.org, robh@kernel.org,
mchehab+huawei@kernel.org, davem@davemloft.net,
gregkh@linuxfoundation.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <MN2PR12MB4422FF4727BB2D8344B9CAC8C4B80@MN2PR12MB4422.namprd12.prod.outlook.com>
On Mon, 18 May 2020 15:32:27 +0000
Jean-Baptiste Maneyrol <JManeyrol@invensense.com> wrote:
> Hi Jonathan,
>
> no problem with the comments.
>
> fifo_decode_packet will move to source code,
>
> For the following sleep comment:
> > + if (sleep_gyro > sleep_temp)
> > + sleep = sleep_gyro;
> > + else
> > + sleep = sleep_temp;
> > + if (sleep)
> > + msleep(sleep);
>
> if (sleep_gyro > sleep_temp)
> msleep(sleep_gyro);
> else
> msleep(sleep_temp);
>
> I am using an intermediate local variable to avoid a call to msleep(0) which is in fact sleeping for real for 1 jiffies as far as I understand. So is it OK to keep it as is?
Doh. I missed that for some reason. Absolutely fine as
it is!
J
>
> Thanks,
> JB
>
>
>
> From: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
>
> Sent: Friday, May 8, 2020 16:19
>
> To: Jean-Baptiste Maneyrol <JManeyrol@invensense.com>
>
> Cc: jic23@kernel.org <jic23@kernel.org>; robh+dt@kernel.org <robh+dt@kernel.org>; robh@kernel.org <robh@kernel.org>; mchehab+huawei@kernel.org <mchehab+huawei@kernel.org>; davem@davemloft.net <davem@davemloft.net>; gregkh@linuxfoundation.org <gregkh@linuxfoundation.org>;
> linux-iio@vger.kernel.org <linux-iio@vger.kernel.org>; devicetree@vger.kernel.org <devicetree@vger.kernel.org>; linux-kernel@vger.kernel.org <linux-kernel@vger.kernel.org>
>
> Subject: Re: [PATCH 09/12] iio: imu: inv_icm42600: add buffer support in iio devices
>
>
>
>
> CAUTION: This email originated from outside of the organization. Please make sure the sender is who they say they are and do not click links or open attachments unless you recognize the sender and know the content is safe.
>
>
>
> On Thu, 7 May 2020 16:42:19 +0200
>
> Jean-Baptiste Maneyrol <jmaneyrol@invensense.com> wrote:
>
>
>
> > Use triggered buffer by parsing FIFO data read in device trigger.
>
> > Support hwfifo watermark by multiplexing gyro and accel settings.
>
> > Support hwfifo flush.
>
> >
>
> > Simply use interrupt timestamp first.
>
> >
>
> > Signed-off-by: Jean-Baptiste Maneyrol <jmaneyrol@invensense.com>
>
> > ---
>
> > drivers/iio/imu/inv_icm42600/Kconfig | 3 +-
>
> > drivers/iio/imu/inv_icm42600/Makefile | 1 +
>
> > drivers/iio/imu/inv_icm42600/inv_icm42600.h | 8 +
>
> > .../iio/imu/inv_icm42600/inv_icm42600_accel.c | 183 +++++++++
>
> > .../imu/inv_icm42600/inv_icm42600_buffer.c | 353 ++++++++++++++++++
>
> > .../imu/inv_icm42600/inv_icm42600_buffer.h | 162 ++++++++
>
> > .../iio/imu/inv_icm42600/inv_icm42600_core.c | 23 ++
>
> > .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 183 +++++++++
>
> > .../imu/inv_icm42600/inv_icm42600_trigger.c | 15 +-
>
> > 9 files changed, 928 insertions(+), 3 deletions(-)
>
> > create mode 100644 drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
>
> > create mode 100644 drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
>
> >
>
> > diff --git a/drivers/iio/imu/inv_icm42600/Kconfig b/drivers/iio/imu/inv_icm42600/Kconfig
>
> > index 7b3eaeb2aa4a..8c0969319c49 100644
>
> > --- a/drivers/iio/imu/inv_icm42600/Kconfig
>
> > +++ b/drivers/iio/imu/inv_icm42600/Kconfig
>
> > @@ -2,7 +2,8 @@
>
> >
>
> > config INV_ICM42600
>
> > tristate
>
> > - select IIO_TRIGGER
>
> > + select IIO_BUFFER
>
> > + select IIO_TRIGGERED_BUFFER
>
> >
>
> > config INV_ICM42600_I2C
>
> > tristate "InvenSense ICM-426xx I2C driver"
>
> > diff --git a/drivers/iio/imu/inv_icm42600/Makefile b/drivers/iio/imu/inv_icm42600/Makefile
>
> > index e1f2aacbe888..d6732118010c 100644
>
> > --- a/drivers/iio/imu/inv_icm42600/Makefile
>
> > +++ b/drivers/iio/imu/inv_icm42600/Makefile
>
> > @@ -6,6 +6,7 @@ inv-icm42600-y += inv_icm42600_gyro.o
>
> > inv-icm42600-y += inv_icm42600_accel.o
>
> > inv-icm42600-y += inv_icm42600_temp.o
>
> > inv-icm42600-y += inv_icm42600_trigger.o
>
> > +inv-icm42600-y += inv_icm42600_buffer.o
>
> >
>
> > obj-$(CONFIG_INV_ICM42600_I2C) += inv-icm42600-i2c.o
>
> > inv-icm42600-i2c-y += inv_icm42600_i2c.o
>
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
>
> > index 175c1f67faee..947ca4dd245b 100644
>
> > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h
>
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
>
> > @@ -15,6 +15,8 @@
>
> > #include <linux/iio/iio.h>
>
> > #include <linux/iio/trigger.h>
>
> >
>
> > +#include "inv_icm42600_buffer.h"
>
> > +
>
> > enum inv_icm42600_chip {
>
> > INV_CHIP_ICM42600,
>
> > INV_CHIP_ICM42602,
>
> > @@ -124,6 +126,7 @@ struct inv_icm42600_suspended {
>
> > * @indio_gyro: gyroscope IIO device.
>
> > * @indio_accel: accelerometer IIO device.
>
> > * @trigger: device internal interrupt trigger
>
> > + * @fifo: FIFO management structure.
>
> > */
>
> > struct inv_icm42600_state {
>
> > struct mutex lock;
>
> > @@ -138,6 +141,7 @@ struct inv_icm42600_state {
>
> > struct iio_dev *indio_gyro;
>
> > struct iio_dev *indio_accel;
>
> > struct iio_trigger *trigger;
>
> > + struct inv_icm42600_fifo fifo;
>
> > };
>
> >
>
> > /* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
>
> > @@ -378,8 +382,12 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq,
>
> >
>
> > int inv_icm42600_gyro_init(struct inv_icm42600_state *st);
>
> >
>
> > +int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev, int64_t ts);
>
> > +
>
> > int inv_icm42600_accel_init(struct inv_icm42600_state *st);
>
> >
>
> > +int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev, int64_t ts);
>
> > +
>
> > int inv_icm42600_trigger_init(struct inv_icm42600_state *st, int irq,
>
> > int irq_type);
>
> >
>
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
>
> > index 74dac5f283d4..4206be54d057 100644
>
> > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
>
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
>
> > @@ -10,9 +10,13 @@
>
> > #include <linux/regmap.h>
>
> > #include <linux/delay.h>
>
> > #include <linux/iio/iio.h>
>
> > +#include <linux/iio/buffer.h>
>
> > +#include <linux/iio/triggered_buffer.h>
>
> > +#include <linux/iio/trigger_consumer.h>
>
> >
>
> > #include "inv_icm42600.h"
>
> > #include "inv_icm42600_temp.h"
>
> > +#include "inv_icm42600_buffer.h"
>
> >
>
> > #define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info) \
>
> > { \
>
> > @@ -46,6 +50,7 @@ enum inv_icm42600_accel_scan {
>
> > INV_ICM42600_ACCEL_SCAN_Y,
>
> > INV_ICM42600_ACCEL_SCAN_Z,
>
> > INV_ICM42600_ACCEL_SCAN_TEMP,
>
> > + INV_ICM42600_ACCEL_SCAN_TIMESTAMP,
>
> > };
>
> >
>
> > static const struct iio_chan_spec_ext_info inv_icm42600_accel_ext_infos[] = {
>
> > @@ -61,8 +66,100 @@ static const struct iio_chan_spec inv_icm42600_accel_channels[] = {
>
> > INV_ICM42600_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42600_ACCEL_SCAN_Z,
>
> > inv_icm42600_accel_ext_infos),
>
> > INV_ICM42600_TEMP_CHAN(INV_ICM42600_ACCEL_SCAN_TEMP),
>
> > + IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42600_ACCEL_SCAN_TIMESTAMP),
>
> > };
>
> >
>
> > +/* IIO buffer data */
>
> > +struct inv_icm42600_accel_buffer {
>
> > + struct inv_icm42600_fifo_sensor_data accel;
>
> > + int8_t temp;
>
> > + int64_t timestamp;
>
> > +};
>
> > +
>
> > +#define INV_ICM42600_SCAN_MASK_ACCEL_3AXIS \
>
> > + (BIT(INV_ICM42600_ACCEL_SCAN_X) | \
>
> > + BIT(INV_ICM42600_ACCEL_SCAN_Y) | \
>
> > + BIT(INV_ICM42600_ACCEL_SCAN_Z))
>
> > +
>
> > +#define INV_ICM42600_SCAN_MASK_TEMP BIT(INV_ICM42600_ACCEL_SCAN_TEMP)
>
> > +
>
> > +static const unsigned long inv_icm42600_accel_scan_masks[] = {
>
> > + /* 3-axis accel + temperature */
>
> > + INV_ICM42600_SCAN_MASK_ACCEL_3AXIS | INV_ICM42600_SCAN_MASK_TEMP,
>
> > + 0,
>
> > +};
>
> > +
>
> > +static irqreturn_t inv_icm42600_accel_handler(int irq, void *_data)
>
> > +{
>
> > + struct iio_poll_func *pf = _data;
>
> > + struct iio_dev *indio_dev = pf->indio_dev;
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + const size_t fifo_nb = st->fifo.nb.total;
>
> > + int ret;
>
> > +
>
> > + /* exit if no sample */
>
> > + if (fifo_nb == 0)
>
> > + goto out;
>
> > +
>
> > + ret = inv_icm42600_accel_parse_fifo(indio_dev, pf->timestamp);
>
> > + if (ret)
>
> > + dev_err(regmap_get_device(st->map), "accel fifo error %d\n",
>
> > + ret);
>
> > +
>
> > +out:
>
> > + iio_trigger_notify_done(indio_dev->trig);
>
> > + return IRQ_HANDLED;
>
> > +}
>
> > +
>
> > +/* enable accelerometer sensor and FIFO write */
>
> > +static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
>
> > + const unsigned long *scan_mask)
>
> > +{
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
>
> > + unsigned int fifo_en = 0;
>
> > + unsigned int sleep_temp = 0;
>
> > + unsigned int sleep_accel = 0;
>
> > + unsigned int sleep;
>
> > + int ret;
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
> > + if (*scan_mask & INV_ICM42600_SCAN_MASK_TEMP) {
>
> > + /* enable temp sensor */
>
> > + ret = inv_icm42600_set_temp_conf(st, true, &sleep_temp);
>
> > + if (ret)
>
> > + goto out_unlock;
>
> > + fifo_en |= INV_ICM42600_SENSOR_TEMP;
>
> > + }
>
> > +
>
> > + if (*scan_mask & INV_ICM42600_SCAN_MASK_ACCEL_3AXIS) {
>
> > + /* enable accel sensor */
>
> > + conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
>
> > + ret = inv_icm42600_set_accel_conf(st, &conf, &sleep_accel);
>
> > + if (ret)
>
> > + goto out_unlock;
>
> > + fifo_en |= INV_ICM42600_SENSOR_ACCEL;
>
> > + }
>
> > +
>
> > + /* update data FIFO write and FIFO watermark */
>
> > + ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
>
> > + if (ret)
>
> > + goto out_unlock;
>
> > + ret = inv_icm42600_buffer_update_watermark(st);
>
> > +
>
> > +out_unlock:
>
> > + mutex_unlock(&st->lock);
>
> > + /* sleep maximum required time */
>
> > + if (sleep_accel > sleep_temp)
>
> > + sleep = sleep_accel;
>
> > + else
>
> > + sleep = sleep_temp;
>
> > + if (sleep)
>
> > + msleep(sleep);
>
> > + return ret;
>
> > +}
>
> > +
>
> > static int inv_icm42600_accel_read_sensor(struct inv_icm42600_state *st,
>
> > struct iio_chan_spec const *chan,
>
> > int16_t *val)
>
> > @@ -250,6 +347,8 @@ static int inv_icm42600_accel_write_odr(struct inv_icm42600_state *st,
>
> > mutex_lock(&st->lock);
>
> > conf.odr = inv_icm42600_accel_odr_conv[idx / 2];
>
> > ret = inv_icm42600_set_accel_conf(st, &conf, NULL);
>
> > + inv_icm42600_buffer_update_fifo_period(st);
>
> > + inv_icm42600_buffer_update_watermark(st);
>
> > mutex_unlock(&st->lock);
>
> >
>
> > pm_runtime_mark_last_busy(dev);
>
> > @@ -512,12 +611,51 @@ static int inv_icm42600_accel_write_raw_get_fmt(struct iio_dev *indio_dev,
>
> > }
>
> > }
>
> >
>
> > +static int inv_icm42600_accel_hwfifo_set_watermark(struct iio_dev *indio_dev,
>
> > + unsigned int val)
>
> > +{
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + int ret;
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
> > + st->fifo.watermark.accel = val;
>
> > + ret = inv_icm42600_buffer_update_watermark(st);
>
> > +
>
> > + mutex_unlock(&st->lock);
>
> > +
>
> > + return ret;
>
> > +}
>
> > +
>
> > +static int inv_icm42600_accel_hwfifo_flush(struct iio_dev *indio_dev,
>
> > + unsigned int count)
>
> > +{
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + int ret;
>
> > +
>
> > + if (count == 0)
>
> > + return 0;
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
> > + ret = inv_icm42600_buffer_hwfifo_flush(st, count);
>
> > + if (!ret)
>
> > + ret = st->fifo.nb.accel;
>
> > +
>
> > + mutex_unlock(&st->lock);
>
> > +
>
> > + return ret;
>
> > +}
>
> > +
>
> > static const struct iio_info inv_icm42600_accel_info = {
>
> > .read_raw = inv_icm42600_accel_read_raw,
>
> > .read_avail = inv_icm42600_accel_read_avail,
>
> > .write_raw = inv_icm42600_accel_write_raw,
>
> > .write_raw_get_fmt = inv_icm42600_accel_write_raw_get_fmt,
>
> > .debugfs_reg_access = inv_icm42600_debugfs_reg,
>
> > + .update_scan_mode = inv_icm42600_accel_update_scan_mode,
>
> > + .hwfifo_set_watermark = inv_icm42600_accel_hwfifo_set_watermark,
>
> > + .hwfifo_flush_to_buffer = inv_icm42600_accel_hwfifo_flush,
>
> > };
>
> >
>
> > int inv_icm42600_accel_init(struct inv_icm42600_state *st)
>
> > @@ -525,6 +663,7 @@ int inv_icm42600_accel_init(struct inv_icm42600_state *st)
>
> > struct device *dev = regmap_get_device(st->map);
>
> > const char *name;
>
> > struct iio_dev *indio_dev;
>
> > + int ret;
>
> >
>
> > name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->name);
>
> > if (!name)
>
> > @@ -541,7 +680,51 @@ int inv_icm42600_accel_init(struct inv_icm42600_state *st)
>
> > indio_dev->modes = INDIO_DIRECT_MODE;
>
> > indio_dev->channels = inv_icm42600_accel_channels;
>
> > indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels);
>
> > + indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks;
>
> > +
>
> > + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
>
> > + inv_icm42600_accel_handler,
>
> > + &inv_icm42600_buffer_ops);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + indio_dev->trig = iio_trigger_get(st->trigger);
>
> >
>
> > st->indio_accel = indio_dev;
>
> > return devm_iio_device_register(dev, st->indio_accel);
>
> > }
>
> > +
>
> > +int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev, int64_t ts)
>
> > +{
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + const size_t accel_nb = st->fifo.nb.accel;
>
> > + ssize_t i, size;
>
> > + const void *accel, *gyro, *temp, *timestamp;
>
> > + unsigned int odr;
>
> > + struct inv_icm42600_accel_buffer buffer;
>
> > +
>
> > + /* exit if no accel sample */
>
> > + if (accel_nb == 0)
>
> > + return 0;
>
> > +
>
> > + /* parse all fifo packets */
>
> > + for (i = 0; i < st->fifo.count; i += size) {
>
> > + size = inv_icm42600_fifo_decode_packet(&st->fifo.data[i],
>
> > + &accel, &gyro, &temp, ×tamp, &odr);
>
> > + dev_dbg(regmap_get_device(st->map), "accel packet size = %zd\n",
>
> > + size);
>
> > + /* quit if error or FIFO is empty */
>
> > + if (size <= 0)
>
> > + return size;
>
> > + /* skip packet if no accel data or data is invalid */
>
> > + if (accel == NULL || !inv_icm42600_fifo_is_data_valid(accel)) {
>
> > + dev_dbg(regmap_get_device(st->map), "skip accel data\n");
>
> > + continue;
>
> > + }
>
> > + memcpy(&buffer.accel, accel, sizeof(buffer.accel));
>
> > + memcpy(&buffer.temp, temp, sizeof(buffer.temp));
>
> > + iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts);
>
> > + }
>
> > +
>
> > + return 0;
>
> > +}
>
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
>
> > new file mode 100644
>
> > index 000000000000..b428abdc92ee
>
> > --- /dev/null
>
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
>
> > @@ -0,0 +1,353 @@
>
> > +// SPDX-License-Identifier: GPL-2.0-or-later
>
> > +/*
>
> > + * Copyright (C) 2020 Invensense, Inc.
>
> > + */
>
> > +
>
> > +#include <linux/device.h>
>
> > +#include <linux/mutex.h>
>
> > +#include <linux/pm_runtime.h>
>
> > +#include <linux/regmap.h>
>
> > +#include <linux/delay.h>
>
> > +#include <linux/math64.h>
>
> > +#include <linux/iio/iio.h>
>
> > +#include <linux/iio/buffer.h>
>
> > +#include <linux/iio/triggered_buffer.h>
>
> > +#include <linux/iio/trigger_consumer.h>
>
> > +
>
> > +#include "inv_icm42600.h"
>
> > +#include "inv_icm42600_buffer.h"
>
> > +
>
> > +void inv_icm42600_buffer_update_fifo_period(struct inv_icm42600_state *st)
>
> > +{
>
> > + uint32_t period_gyro, period_accel, period;
>
> > +
>
> > + if (st->fifo.en & INV_ICM42600_SENSOR_GYRO)
>
> > + period_gyro = inv_icm42600_odr_to_period(st->conf.gyro.odr);
>
> > + else
>
> > + period_gyro = U32_MAX;
>
> > +
>
> > + if (st->fifo.en & INV_ICM42600_SENSOR_ACCEL)
>
> > + period_accel = inv_icm42600_odr_to_period(st->conf.accel.odr);
>
> > + else
>
> > + period_accel = U32_MAX;
>
> > +
>
> > + if (period_gyro <= period_accel)
>
> > + period = period_gyro;
>
> > + else
>
> > + period = period_accel;
>
> > +
>
> > + st->fifo.period = period;
>
> > +}
>
> > +
>
> > +int inv_icm42600_buffer_set_fifo_en(struct inv_icm42600_state *st,
>
> > + unsigned int fifo_en)
>
> > +{
>
> > + unsigned int mask, val;
>
> > + int ret;
>
> > +
>
> > + /* update only FIFO EN bits */
>
> > + mask = INV_ICM42600_FIFO_CONFIG1_TMST_FSYNC_EN |
>
> > + INV_ICM42600_FIFO_CONFIG1_TEMP_EN |
>
> > + INV_ICM42600_FIFO_CONFIG1_GYRO_EN |
>
> > + INV_ICM42600_FIFO_CONFIG1_ACCEL_EN;
>
> > +
>
> > + val = 0;
>
> > + if (fifo_en & INV_ICM42600_SENSOR_GYRO)
>
> > + val |= INV_ICM42600_FIFO_CONFIG1_GYRO_EN;
>
> > + if (fifo_en & INV_ICM42600_SENSOR_ACCEL)
>
> > + val |= INV_ICM42600_FIFO_CONFIG1_ACCEL_EN;
>
> > + if (fifo_en & INV_ICM42600_SENSOR_TEMP)
>
> > + val |= INV_ICM42600_FIFO_CONFIG1_TEMP_EN;
>
> > +
>
> > + ret = regmap_update_bits(st->map, INV_ICM42600_REG_FIFO_CONFIG1,
>
> > + mask, val);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + st->fifo.en = fifo_en;
>
> > + inv_icm42600_buffer_update_fifo_period(st);
>
> > +
>
> > + return 0;
>
> > +}
>
> > +
>
> > +static size_t inv_icm42600_get_packet_size(unsigned int fifo_en)
>
> > +{
>
> > + size_t packet_size;
>
> > +
>
> > + if ((fifo_en & INV_ICM42600_SENSOR_GYRO) &&
>
> > + (fifo_en & INV_ICM42600_SENSOR_ACCEL))
>
> > + packet_size = INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE;
>
> > + else
>
> > + packet_size = INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE;
>
> > +
>
> > + return packet_size;
>
> > +}
>
> > +
>
> > +static unsigned int inv_icm42600_wm_truncate(unsigned int watermark,
>
> > + size_t packet_size)
>
> > +{
>
> > + size_t wm_size;
>
> > + unsigned int wm;
>
> > +
>
> > + wm_size = watermark * packet_size;
>
> > + if (wm_size > INV_ICM42600_FIFO_WATERMARK_MAX)
>
> > + wm_size = INV_ICM42600_FIFO_WATERMARK_MAX;
>
> > +
>
> > + wm = wm_size / packet_size;
>
> > +
>
> > + return wm;
>
> > +}
>
> > +
>
> > +int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st)
>
> > +{
>
> > + size_t packet_size, wm_size;
>
> > + unsigned int wm_gyro, wm_accel, watermark;
>
> > + uint32_t period_gyro, period_accel, period;
>
> > + int64_t latency_gyro, latency_accel, latency;
>
> > + unsigned int mask, val;
>
> > + bool restore;
>
> > + __le16 raw_wm;
>
> > + int ret;
>
> > +
>
> > + packet_size = inv_icm42600_get_packet_size(st->fifo.en);
>
> > +
>
> > + /* get minimal latency, depending on sensor watermark and odr */
>
> > + wm_gyro = inv_icm42600_wm_truncate(st->fifo.watermark.gyro,
>
> > + packet_size);
>
> > + wm_accel = inv_icm42600_wm_truncate(st->fifo.watermark.accel,
>
> > + packet_size);
>
> > + period_gyro = inv_icm42600_odr_to_period(st->conf.gyro.odr);
>
> > + period_accel = inv_icm42600_odr_to_period(st->conf.accel.odr);
>
> > + latency_gyro = (int64_t)period_gyro * (int64_t)wm_gyro;
>
> > + latency_accel = (int64_t)period_accel * (int64_t)wm_accel;
>
> > + if (latency_gyro == 0) {
>
> > + latency = latency_accel;
>
> > + watermark = wm_accel;
>
> > + } else if (latency_accel == 0) {
>
> > + latency = latency_gyro;
>
> > + watermark = wm_gyro;
>
> > + } else {
>
> > + /* compute the smallest latency that is a multiple of both */
>
> > + if (latency_gyro <= latency_accel) {
>
> > + latency = latency_gyro;
>
> > + latency -= latency_accel % latency_gyro;
>
> > + } else {
>
> > + latency = latency_accel;
>
> > + latency -= latency_gyro % latency_accel;
>
> > + }
>
> > + /* use the shortest period */
>
> > + if (period_gyro <= period_accel)
>
> > + period = period_gyro;
>
> > + else
>
> > + period = period_accel;
>
> > + /* all this works because periods are multiple of each others */
>
> > + watermark = div_s64(latency, period);
>
> > + if (watermark < 1)
>
> > + watermark = 1;
>
> > + }
>
> > + wm_size = watermark * packet_size;
>
> > + dev_dbg(regmap_get_device(st->map), "watermark: %u (%zu)\n",
>
> > + watermark, wm_size);
>
> > +
>
> > + /* changing FIFO watermark requires to turn off watermark interrupt */
>
> > + mask = INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN;
>
> > + val = 0;
>
> > + ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0,
>
> > + mask, val, &restore);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + raw_wm = INV_ICM42600_FIFO_WATERMARK_VAL(wm_size);
>
> > + ret = regmap_bulk_write(st->map, INV_ICM42600_REG_FIFO_WATERMARK,
>
> > + &raw_wm, sizeof(raw_wm));
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* restore watermark interrupt */
>
> > + if (restore) {
>
> > + mask = INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN;
>
> > + val = INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN;
>
> > + ret = regmap_update_bits(st->map, INV_ICM42600_REG_INT_SOURCE0,
>
> > + mask, val);
>
> > + if (ret)
>
> > + return ret;
>
> > + }
>
> > +
>
> > + return 0;
>
> > +}
>
> > +
>
> > +static int inv_icm42600_buffer_preenable(struct iio_dev *indio_dev)
>
> > +{
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + struct device *dev = regmap_get_device(st->map);
>
> > +
>
> > + pm_runtime_get_sync(dev);
>
> > +
>
> > + return 0;
>
> > +}
>
> > +
>
> > +static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev)
>
> > +{
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + struct device *dev = regmap_get_device(st->map);
>
> > + unsigned int sensor;
>
> > + unsigned int *watermark;
>
> > + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
>
> > + unsigned int sleep = 0;
>
> > + int ret;
>
> > +
>
> > + if (indio_dev == st->indio_gyro) {
>
> > + sensor = INV_ICM42600_SENSOR_GYRO;
>
> > + watermark = &st->fifo.watermark.gyro;
>
> > + } else if (indio_dev == st->indio_accel) {
>
> > + sensor = INV_ICM42600_SENSOR_ACCEL;
>
> > + watermark = &st->fifo.watermark.accel;
>
> > + } else {
>
> > + return -EINVAL;
>
> > + }
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
> > + ret = inv_icm42600_buffer_set_fifo_en(st, st->fifo.en & ~sensor);
>
> > + if (ret)
>
> > + goto out_unlock;
>
> > +
>
> > + *watermark = 0;
>
> > + ret = inv_icm42600_buffer_update_watermark(st);
>
> > + if (ret)
>
> > + goto out_unlock;
>
> > +
>
> > + conf.mode = INV_ICM42600_SENSOR_MODE_OFF;
>
> > + if (sensor == INV_ICM42600_SENSOR_GYRO)
>
> > + ret = inv_icm42600_set_gyro_conf(st, &conf, &sleep);
>
> > + else
>
> > + ret = inv_icm42600_set_accel_conf(st, &conf, &sleep);
>
> > +
>
> > +out_unlock:
>
> > + mutex_unlock(&st->lock);
>
> > + if (sleep)
>
> > + msleep(sleep);
>
> > + pm_runtime_mark_last_busy(dev);
>
> > + pm_runtime_put_autosuspend(dev);
>
> > +
>
> > + return ret;
>
> > +}
>
> > +
>
> > +const struct iio_buffer_setup_ops inv_icm42600_buffer_ops = {
>
> > + .preenable = inv_icm42600_buffer_preenable,
>
> > + .postenable = iio_triggered_buffer_postenable,
>
> > + .predisable = iio_triggered_buffer_predisable,
>
> > + .postdisable = inv_icm42600_buffer_postdisable,
>
> > +};
>
> > +
>
> > +int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,
>
> > + unsigned int max)
>
> > +{
>
> > + struct device *dev = regmap_get_device(st->map);
>
> > + __be16 raw_fifo_count;
>
> > + size_t max_count;
>
> > + ssize_t i, size;
>
> > + const void *accel, *gyro, *temp, *timestamp;
>
> > + unsigned int odr;
>
> > + int ret;
>
> > +
>
> > + /* reset all samples counters */
>
> > + st->fifo.count = 0;
>
> > + st->fifo.nb.gyro = 0;
>
> > + st->fifo.nb.accel = 0;
>
> > + st->fifo.nb.total = 0;
>
> > +
>
> > + /* compute maximum FIFO read size */
>
> > + if (max == 0)
>
> > + max_count = sizeof(st->fifo.data);
>
> > + else
>
> > + max_count = max * inv_icm42600_get_packet_size(st->fifo.en);
>
> > +
>
> > + /* read FIFO count value */
>
> > + ret = regmap_bulk_read(st->map, INV_ICM42600_REG_FIFO_COUNT,
>
> > + &raw_fifo_count, sizeof(raw_fifo_count));
>
> > + if (ret)
>
> > + return ret;
>
> > + st->fifo.count = be16_to_cpu(raw_fifo_count);
>
> > + dev_dbg(dev, "FIFO count = %zu\n", st->fifo.count);
>
> > +
>
> > + /* check and sanitize FIFO count value */
>
> > + if (st->fifo.count == 0)
>
> > + return 0;
>
> > + if (st->fifo.count > max_count)
>
> > + st->fifo.count = max_count;
>
> > +
>
> > + /* read all FIFO data in internal buffer */
>
> > + ret = regmap_noinc_read(st->map, INV_ICM42600_REG_FIFO_DATA,
>
> > + st->fifo.data, st->fifo.count);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* compute number of samples for each sensor */
>
> > + for (i = 0; i < st->fifo.count; i += size) {
>
> > + size = inv_icm42600_fifo_decode_packet(&st->fifo.data[i],
>
> > + &accel, &gyro, &temp, ×tamp, &odr);
>
> > + if (size <= 0)
>
> > + break;
>
> > + if (gyro != NULL && inv_icm42600_fifo_is_data_valid(gyro))
>
> > + st->fifo.nb.gyro++;
>
> > + if (accel != NULL && inv_icm42600_fifo_is_data_valid(accel))
>
> > + st->fifo.nb.accel++;
>
> > + st->fifo.nb.total++;
>
> > + }
>
> > +
>
> > + return 0;
>
> > +}
>
> > +
>
> > +int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
>
> > + unsigned int count)
>
> > +{
>
> > + int64_t ts_gyro, ts_accel;
>
> > + int ret;
>
> > +
>
> > + dev_dbg(regmap_get_device(st->map), "FIFO flush %u\n", count);
>
> > +
>
> > + ts_gyro = iio_get_time_ns(st->indio_gyro);
>
> > + ts_accel = iio_get_time_ns(st->indio_accel);
>
> > + ret = inv_icm42600_buffer_fifo_read(st, count);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + if (st->fifo.nb.total == 0)
>
> > + return 0;
>
> > +
>
> > + ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro, ts_gyro);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + return inv_icm42600_accel_parse_fifo(st->indio_accel, ts_accel);
>
> > +}
>
> > +
>
> > +int inv_icm42600_buffer_init(struct inv_icm42600_state *st)
>
> > +{
>
> > + unsigned int mask, val;
>
> > + int ret;
>
> > +
>
> > + /*
>
> > + * Default FIFO configuration (bits 7 to 5)
>
> > + * - use invalid value
>
> > + * - FIFO count in bytes
>
> > + * - FIFO count in big endian
>
> > + */
>
> > + mask = GENMASK(7, 5);
>
> > + val = INV_ICM42600_INTF_CONFIG0_FIFO_COUNT_ENDIAN;
>
> > + ret = regmap_update_bits(st->map, INV_ICM42600_REG_INTF_CONFIG0,
>
> > + mask, val);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /*
>
> > + * Enable FIFO partial read and continuous watermark interrupt.
>
> > + * Disable all FIFO EN bits.
>
> > + */
>
> > + mask = GENMASK(6, 5) | GENMASK(3, 0);
>
> > + val = INV_ICM42600_FIFO_CONFIG1_RESUME_PARTIAL_RD |
>
> > + INV_ICM42600_FIFO_CONFIG1_WM_GT_TH;
>
> > + return regmap_update_bits(st->map, INV_ICM42600_REG_FIFO_CONFIG1,
>
> > + mask, val);
>
> > +}
>
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
>
> > new file mode 100644
>
> > index 000000000000..74b91c0e664b
>
> > --- /dev/null
>
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
>
> > @@ -0,0 +1,162 @@
>
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
>
> > +/*
>
> > + * Copyright (C) 2020 Invensense, Inc.
>
> > + */
>
> > +
>
> > +#ifndef INV_ICM42600_BUFFER_H_
>
> > +#define INV_ICM42600_BUFFER_H_
>
> > +
>
> > +#include <linux/kernel.h>
>
> > +#include <linux/bits.h>
>
> > +
>
> > +struct inv_icm42600_state;
>
> > +
>
> > +#define INV_ICM42600_SENSOR_GYRO BIT(0)
>
> > +#define INV_ICM42600_SENSOR_ACCEL BIT(1)
>
> > +#define INV_ICM42600_SENSOR_TEMP BIT(2)
>
> > +
>
> > +struct inv_icm42600_fifo {
>
> > + unsigned int en;
>
> > + uint32_t period;
>
> > + struct {
>
> > + unsigned int gyro;
>
> > + unsigned int accel;
>
> > + } watermark;
>
> > + size_t count;
>
> > + struct {
>
> > + size_t gyro;
>
> > + size_t accel;
>
> > + size_t total;
>
> > + } nb;
>
> > + uint8_t data[2080];
>
> > +};
>
> > +
>
> > +/* FIFO header: 1 byte */
>
> > +#define INV_ICM42600_FIFO_HEADER_MSG BIT(7)
>
> > +#define INV_ICM42600_FIFO_HEADER_ACCEL BIT(6)
>
> > +#define INV_ICM42600_FIFO_HEADER_GYRO BIT(5)
>
> > +#define INV_ICM42600_FIFO_HEADER_TMST_FSYNC GENMASK(3, 2)
>
> > +#define INV_ICM42600_FIFO_HEADER_ODR_ACCEL BIT(1)
>
> > +#define INV_ICM42600_FIFO_HEADER_ODR_GYRO BIT(0)
>
> > +
>
> > +/* FIFO data packet */
>
> > +struct inv_icm42600_fifo_sensor_data {
>
> > + __be16 x;
>
> > + __be16 y;
>
> > + __be16 z;
>
> > +} __packed;
>
> > +#define INV_ICM42600_FIFO_DATA_INVALID -32768
>
> > +
>
> > +struct inv_icm42600_fifo_1sensor_packet {
>
> > + uint8_t header;
>
> > + struct inv_icm42600_fifo_sensor_data data;
>
> > + int8_t temp;
>
> > +} __packed;
>
> > +#define INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE 8
>
> > +
>
> > +struct inv_icm42600_fifo_2sensors_packet {
>
> > + uint8_t header;
>
> > + struct inv_icm42600_fifo_sensor_data accel;
>
> > + struct inv_icm42600_fifo_sensor_data gyro;
>
> > + int8_t temp;
>
> > + __be16 timestamp;
>
> > +} __packed;
>
> > +#define INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE 16
>
> > +
>
> > +static inline int16_t inv_icm42600_fifo_get_sensor_data(__be16 d)
>
> > +{
>
> > + return be16_to_cpu(d);
>
> > +}
>
> > +
>
> > +static inline bool
>
> > +inv_icm42600_fifo_is_data_valid(const struct inv_icm42600_fifo_sensor_data *s)
>
> > +{
>
> > + int16_t x, y, z;
>
> > +
>
> > + x = inv_icm42600_fifo_get_sensor_data(s->x);
>
> > + y = inv_icm42600_fifo_get_sensor_data(s->y);
>
> > + z = inv_icm42600_fifo_get_sensor_data(s->z);
>
> > +
>
> > + if (x == INV_ICM42600_FIFO_DATA_INVALID &&
>
> > + y == INV_ICM42600_FIFO_DATA_INVALID &&
>
> > + z == INV_ICM42600_FIFO_DATA_INVALID)
>
> > + return false;
>
> > +
>
> > + return true;
>
> > +}
>
> > +
>
> > +static inline ssize_t inv_icm42600_fifo_decode_packet(const void *packet,
>
>
>
> Bit big to be in the header..
>
>
>
> > + const void **accel, const void **gyro, const void **temp,
>
> > + const void **timestamp, unsigned int *odr)
>
> > +{
>
> > + const struct inv_icm42600_fifo_1sensor_packet *pack1 = packet;
>
> > + const struct inv_icm42600_fifo_2sensors_packet *pack2 = packet;
>
> > + uint8_t header = *((const uint8_t *)packet);
>
> > +
>
> > + /* FIFO empty */
>
> > + if (header & INV_ICM42600_FIFO_HEADER_MSG) {
>
> > + *accel = NULL;
>
> > + *gyro = NULL;
>
> > + *temp = NULL;
>
> > + *timestamp = NULL;
>
> > + *odr = 0;
>
> > + return 0;
>
> > + }
>
> > +
>
> > + /* handle odr flags */
>
> > + *odr = 0;
>
> > + if (header & INV_ICM42600_FIFO_HEADER_ODR_GYRO)
>
> > + *odr |= INV_ICM42600_SENSOR_GYRO;
>
> > + if (header & INV_ICM42600_FIFO_HEADER_ODR_ACCEL)
>
> > + *odr |= INV_ICM42600_SENSOR_ACCEL;
>
> > +
>
> > + /* accel + gyro */
>
> > + if ((header & INV_ICM42600_FIFO_HEADER_ACCEL) &&
>
> > + (header & INV_ICM42600_FIFO_HEADER_GYRO)) {
>
> > + *accel = &pack2->accel;
>
> > + *gyro = &pack2->gyro;
>
> > + *temp = &pack2->temp;
>
> > + *timestamp = &pack2->timestamp;
>
> > + return INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE;
>
> > + }
>
> > +
>
> > + /* accel only */
>
> > + if (header & INV_ICM42600_FIFO_HEADER_ACCEL) {
>
> > + *accel = &pack1->data;
>
> > + *gyro = NULL;
>
> > + *temp = &pack1->temp;
>
> > + *timestamp = NULL;
>
> > + return INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE;
>
> > + }
>
> > +
>
> > + /* gyro only */
>
> > + if (header & INV_ICM42600_FIFO_HEADER_GYRO) {
>
> > + *accel = NULL;
>
> > + *gyro = &pack1->data;
>
> > + *temp = &pack1->temp;
>
> > + *timestamp = NULL;
>
> > + return INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE;
>
> > + }
>
> > +
>
> > + /* invalid packet if here */
>
> > + return -EINVAL;
>
> > +}
>
> > +
>
> > +extern const struct iio_buffer_setup_ops inv_icm42600_buffer_ops;
>
> > +
>
> > +int inv_icm42600_buffer_init(struct inv_icm42600_state *st);
>
> > +
>
> > +void inv_icm42600_buffer_update_fifo_period(struct inv_icm42600_state *st);
>
> > +
>
> > +int inv_icm42600_buffer_set_fifo_en(struct inv_icm42600_state *st,
>
> > + unsigned int fifo_en);
>
> > +
>
> > +int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st);
>
> > +
>
> > +int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,
>
> > + unsigned int max);
>
> > +
>
> > +int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
>
> > + unsigned int count);
>
> > +
>
> > +#endif
>
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
>
> > index 1102c54396e3..689089065ff9 100644
>
> > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
>
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
>
> > @@ -14,6 +14,7 @@
>
> > #include <linux/iio/iio.h>
>
> >
>
> > #include "inv_icm42600.h"
>
> > +#include "inv_icm42600_buffer.h"
>
> >
>
> > static const struct regmap_range_cfg inv_icm42600_regmap_ranges[] = {
>
> > {
>
> > @@ -515,6 +516,11 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq,
>
> > if (ret)
>
> > return ret;
>
> >
>
> > + /* setup FIFO buffer */
>
> > + ret = inv_icm42600_buffer_init(st);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > /* setup interrupt trigger */
>
> > ret = inv_icm42600_trigger_init(st, irq, irq_type);
>
> > if (ret)
>
> > @@ -559,6 +565,16 @@ static int __maybe_unused inv_icm42600_suspend(struct device *dev)
>
> > goto out_unlock;
>
> > }
>
> >
>
> > + /* disable FIFO data streaming */
>
> > + if (iio_buffer_enabled(st->indio_gyro) ||
>
> > + iio_buffer_enabled(st->indio_accel)) {
>
> > + /* set FIFO in bypass mode */
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG,
>
> > + INV_ICM42600_FIFO_CONFIG_BYPASS);
>
> > + if (ret)
>
> > + goto out_unlock;
>
> > + }
>
> > +
>
> > ret = inv_icm42600_set_pwr_mgmt0(st, INV_ICM42600_SENSOR_MODE_OFF,
>
> > INV_ICM42600_SENSOR_MODE_OFF, false,
>
> > NULL);
>
> > @@ -594,6 +610,13 @@ static int __maybe_unused inv_icm42600_resume(struct device *dev)
>
> > if (ret)
>
> > goto out_unlock;
>
> >
>
> > + /* restore FIFO data streaming */
>
> > + if (iio_buffer_enabled(st->indio_gyro) ||
>
> > + iio_buffer_enabled(st->indio_accel)) {
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG,
>
> > + INV_ICM42600_FIFO_CONFIG_STREAM);
>
> > + }
>
> > +
>
> > out_unlock:
>
> > mutex_unlock(&st->lock);
>
> > return ret;
>
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
>
> > index c0164ab2830e..dafb104abc77 100644
>
> > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
>
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
>
> > @@ -10,9 +10,13 @@
>
> > #include <linux/regmap.h>
>
> > #include <linux/delay.h>
>
> > #include <linux/iio/iio.h>
>
> > +#include <linux/iio/buffer.h>
>
> > +#include <linux/iio/triggered_buffer.h>
>
> > +#include <linux/iio/trigger_consumer.h>
>
> >
>
> > #include "inv_icm42600.h"
>
> > #include "inv_icm42600_temp.h"
>
> > +#include "inv_icm42600_buffer.h"
>
> >
>
> > #define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \
>
> > { \
>
> > @@ -46,6 +50,7 @@ enum inv_icm42600_gyro_scan {
>
> > INV_ICM42600_GYRO_SCAN_Y,
>
> > INV_ICM42600_GYRO_SCAN_Z,
>
> > INV_ICM42600_GYRO_SCAN_TEMP,
>
> > + INV_ICM42600_GYRO_SCAN_TIMESTAMP,
>
> > };
>
> >
>
> > static const struct iio_chan_spec_ext_info inv_icm42600_gyro_ext_infos[] = {
>
> > @@ -61,8 +66,100 @@ static const struct iio_chan_spec inv_icm42600_gyro_channels[] = {
>
> > INV_ICM42600_GYRO_CHAN(IIO_MOD_Z, INV_ICM42600_GYRO_SCAN_Z,
>
> > inv_icm42600_gyro_ext_infos),
>
> > INV_ICM42600_TEMP_CHAN(INV_ICM42600_GYRO_SCAN_TEMP),
>
> > + IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42600_GYRO_SCAN_TIMESTAMP),
>
> > };
>
> >
>
> > +/* IIO buffer data */
>
> > +struct inv_icm42600_gyro_buffer {
>
> > + struct inv_icm42600_fifo_sensor_data gyro;
>
> > + int8_t temp;
>
> > + int64_t timestamp;
>
> > +};
>
> > +
>
> > +#define INV_ICM42600_SCAN_MASK_GYRO_3AXIS \
>
> > + (BIT(INV_ICM42600_GYRO_SCAN_X) | \
>
> > + BIT(INV_ICM42600_GYRO_SCAN_Y) | \
>
> > + BIT(INV_ICM42600_GYRO_SCAN_Z))
>
> > +
>
> > +#define INV_ICM42600_SCAN_MASK_TEMP BIT(INV_ICM42600_GYRO_SCAN_TEMP)
>
> > +
>
> > +static const unsigned long inv_icm42600_gyro_scan_masks[] = {
>
> > + /* 3-axis gyro + temperature */
>
> > + INV_ICM42600_SCAN_MASK_GYRO_3AXIS | INV_ICM42600_SCAN_MASK_TEMP,
>
> > + 0,
>
> > +};
>
> > +
>
> > +static irqreturn_t inv_icm42600_gyro_handler(int irq, void *_data)
>
> > +{
>
> > + struct iio_poll_func *pf = _data;
>
> > + struct iio_dev *indio_dev = pf->indio_dev;
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + const size_t fifo_nb = st->fifo.nb.total;
>
> > + int ret;
>
> > +
>
> > + /* exit if no sample */
>
> > + if (fifo_nb == 0)
>
> > + goto out;
>
> > +
>
> > + ret = inv_icm42600_gyro_parse_fifo(indio_dev, pf->timestamp);
>
> > + if (ret)
>
> > + dev_err(regmap_get_device(st->map), "gyro fifo error %d\n",
>
> > + ret);
>
> > +
>
> > +out:
>
> > + iio_trigger_notify_done(indio_dev->trig);
>
> > + return IRQ_HANDLED;
>
> > +}
>
> > +
>
> > +/* enable gyroscope sensor and FIFO write */
>
> > +static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev,
>
> > + const unsigned long *scan_mask)
>
> > +{
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
>
> > + unsigned int fifo_en = 0;
>
> > + unsigned int sleep_gyro = 0;
>
> > + unsigned int sleep_temp = 0;
>
> > + unsigned int sleep;
>
> > + int ret;
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
> > + if (*scan_mask & INV_ICM42600_SCAN_MASK_TEMP) {
>
> > + /* enable temp sensor */
>
> > + ret = inv_icm42600_set_temp_conf(st, true, &sleep_temp);
>
> > + if (ret)
>
> > + goto out_unlock;
>
> > + fifo_en |= INV_ICM42600_SENSOR_TEMP;
>
> > + }
>
> > +
>
> > + if (*scan_mask & INV_ICM42600_SCAN_MASK_GYRO_3AXIS) {
>
> > + /* enable gyro sensor */
>
> > + conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
>
> > + ret = inv_icm42600_set_gyro_conf(st, &conf, &sleep_gyro);
>
> > + if (ret)
>
> > + goto out_unlock;
>
> > + fifo_en |= INV_ICM42600_SENSOR_GYRO;
>
> > + }
>
> > +
>
> > + /* update data FIFO write and FIFO watermark */
>
> > + ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
>
> > + if (ret)
>
> > + goto out_unlock;
>
>
>
> blank line
>
>
>
> > + ret = inv_icm42600_buffer_update_watermark(st);
>
> > +
>
> > +out_unlock:
>
> > + mutex_unlock(&st->lock);
>
> > + /* sleep maximum required time */
>
> > + if (sleep_gyro > sleep_temp)
>
> > + sleep = sleep_gyro;
>
> > + else
>
> > + sleep = sleep_temp;
>
> > + if (sleep)
>
> > + msleep(sleep);
>
>
>
> if (sleep_gyro > sleep_temp)
>
> msleep(sleep_gyro);
>
> else
>
> msleep(sleep_temp);
>
>
>
> > + return ret;
>
> > +}
>
> > +
>
> > static int inv_icm42600_gyro_read_sensor(struct inv_icm42600_state *st,
>
> > struct iio_chan_spec const *chan,
>
> > int16_t *val)
>
> > @@ -262,6 +359,8 @@ static int inv_icm42600_gyro_write_odr(struct inv_icm42600_state *st,
>
> > mutex_lock(&st->lock);
>
> > conf.odr = inv_icm42600_gyro_odr_conv[idx / 2];
>
> > ret = inv_icm42600_set_gyro_conf(st, &conf, NULL);
>
> > + inv_icm42600_buffer_update_fifo_period(st);
>
> > + inv_icm42600_buffer_update_watermark(st);
>
> > mutex_unlock(&st->lock);
>
> >
>
> > pm_runtime_mark_last_busy(dev);
>
> > @@ -524,12 +623,51 @@ static int inv_icm42600_gyro_write_raw_get_fmt(struct iio_dev *indio_dev,
>
> > }
>
> > }
>
> >
>
> > +static int inv_icm42600_gyro_hwfifo_set_watermark(struct iio_dev *indio_dev,
>
> > + unsigned int val)
>
> > +{
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + int ret;
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
> > + st->fifo.watermark.gyro = val;
>
> > + ret = inv_icm42600_buffer_update_watermark(st);
>
> > +
>
> > + mutex_unlock(&st->lock);
>
> > +
>
> > + return ret;
>
> > +}
>
> > +
>
> > +static int inv_icm42600_gyro_hwfifo_flush(struct iio_dev *indio_dev,
>
> > + unsigned int count)
>
> > +{
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + int ret;
>
> > +
>
> > + if (count == 0)
>
> > + return 0;
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
> > + ret = inv_icm42600_buffer_hwfifo_flush(st, count);
>
> > + if (!ret)
>
> > + ret = st->fifo.nb.gyro;
>
> > +
>
> > + mutex_unlock(&st->lock);
>
> > +
>
> > + return ret;
>
> > +}
>
> > +
>
> > static const struct iio_info inv_icm42600_gyro_info = {
>
> > .read_raw = inv_icm42600_gyro_read_raw,
>
> > .read_avail = inv_icm42600_gyro_read_avail,
>
> > .write_raw = inv_icm42600_gyro_write_raw,
>
> > .write_raw_get_fmt = inv_icm42600_gyro_write_raw_get_fmt,
>
> > .debugfs_reg_access = inv_icm42600_debugfs_reg,
>
> > + .update_scan_mode = inv_icm42600_gyro_update_scan_mode,
>
> > + .hwfifo_set_watermark = inv_icm42600_gyro_hwfifo_set_watermark,
>
> > + .hwfifo_flush_to_buffer = inv_icm42600_gyro_hwfifo_flush,
>
> > };
>
> >
>
> > int inv_icm42600_gyro_init(struct inv_icm42600_state *st)
>
> > @@ -537,6 +675,7 @@ int inv_icm42600_gyro_init(struct inv_icm42600_state *st)
>
> > struct device *dev = regmap_get_device(st->map);
>
> > const char *name;
>
> > struct iio_dev *indio_dev;
>
> > + int ret;
>
> >
>
> > name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name);
>
> > if (!name)
>
> > @@ -553,7 +692,51 @@ int inv_icm42600_gyro_init(struct inv_icm42600_state *st)
>
> > indio_dev->modes = INDIO_DIRECT_MODE;
>
> > indio_dev->channels = inv_icm42600_gyro_channels;
>
> > indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_gyro_channels);
>
> > + indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks;
>
> > +
>
> > + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
>
> > + inv_icm42600_gyro_handler,
>
> > + &inv_icm42600_buffer_ops);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + indio_dev->trig = iio_trigger_get(st->trigger);
>
> >
>
> > st->indio_gyro = indio_dev;
>
> > return devm_iio_device_register(dev, st->indio_gyro);
>
> > }
>
> > +
>
> > +int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev, int64_t ts)
>
> > +{
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + const size_t gyro_nb = st->fifo.nb.gyro;
>
> > + ssize_t i, size;
>
> > + const void *accel, *gyro, *temp, *timestamp;
>
> > + unsigned int odr;
>
> > + struct inv_icm42600_gyro_buffer buffer;
>
> > +
>
> > + /* exit if no gyro sample */
>
> > + if (gyro_nb == 0)
>
> > + return 0;
>
> > +
>
> > + /* parse all fifo packets */
>
> > + for (i = 0; i < st->fifo.count; i += size) {
>
> > + size = inv_icm42600_fifo_decode_packet(&st->fifo.data[i],
>
> > + &accel, &gyro, &temp, ×tamp, &odr);
>
> > + dev_dbg(regmap_get_device(st->map), "gyro packet size = %zd\n",
>
> > + size);
>
> > + /* quit if error or FIFO is empty */
>
> > + if (size <= 0)
>
> > + return size;
>
>
>
> blank line here.
>
>
>
> > + /* skip packet if no gyro data or data is invalid */
>
> > + if (gyro == NULL || !inv_icm42600_fifo_is_data_valid(gyro)) {
>
> > + dev_dbg(regmap_get_device(st->map), "skip gyro data\n");
>
>
>
> Very noisy logging. I'd drop it for the final version of the driver.
>
>
>
> > + continue;
>
> > + }
>
> > + memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
>
> > + memcpy(&buffer.temp, temp, sizeof(buffer.temp));
>
>
>
> buffer.temp = temp;
>
>
>
> > + iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts);
>
> > + }
>
> > +
>
> > + return 0;
>
> > +}
>
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_trigger.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_trigger.c
>
> > index 7a5e76305f0b..5667e0204722 100644
>
> > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_trigger.c
>
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_trigger.c
>
> > @@ -13,6 +13,7 @@
>
> > #include <linux/iio/trigger_consumer.h>
>
> >
>
> > #include "inv_icm42600.h"
>
> > +#include "inv_icm42600_buffer.h"
>
> >
>
> > static irqreturn_t inv_icm42600_trigger_timestamp(int irq, void *_data)
>
> > {
>
> > @@ -45,8 +46,18 @@ static irqreturn_t inv_icm42600_trigger_int_handler(int irq, void *_data)
>
> > dev_warn(dev, "FIFO full data lost!\n");
>
> >
>
> > /* FIFO threshold reached */
>
> > - if (status & INV_ICM42600_INT_STATUS_FIFO_THS)
>
> > - iio_trigger_poll_chained(st->trigger);
>
> > + if (status & INV_ICM42600_INT_STATUS_FIFO_THS) {
>
> > + ret = inv_icm42600_buffer_fifo_read(st, 0);
>
> > + if (ret)
>
> > + dev_err(dev, "FIFO read error %d\n", ret);
>
> > + } else {
>
> > + st->fifo.count = 0;
>
> > + st->fifo.nb.gyro = 0;
>
> > + st->fifo.nb.accel = 0;
>
> > + st->fifo.nb.total = 0;
>
> > + }
>
> > +
>
> > + iio_trigger_poll_chained(st->trigger);
>
> >
>
> > out_unlock:
>
> > mutex_unlock(&st->lock);
>
>
>
>
>
>
^ permalink raw reply
* Re: [PATCH 2/4] remoteproc: introduce version element into resource type field
From: Bjorn Andersson @ 2020-05-21 17:54 UTC (permalink / raw)
To: Suman Anna
Cc: Rob Herring, Mathieu Poirier, Clement Leger, Loic Pallardy,
Arnaud Pouliquen, Lokesh Vutla, linux-remoteproc, devicetree,
linux-arm-kernel, linux-kernel
In-Reply-To: <20200325204701.16862-3-s-anna@ti.com>
On Wed 25 Mar 13:46 PDT 2020, Suman Anna wrote:
> The current remoteproc core has supported only 32-bit remote
> processors and as such some of the current resource structures
> may not scale well for 64-bit remote processors, and would
> require new versions of resource types. Each resource is currently
> identified by a 32-bit type field. Introduce the concept of version
> for these resource types by overloading this 32-bit type field
> into two 16-bit version and type fields with the existing resources
> behaving as version 0 thereby providing backward compatibility.
>
> The version field is passed as an additional argument to each of
> the handler functions, and all the existing handlers are updated
> accordingly. Each specific handler will be updated on a need basis
> when a new version of the resource type is added.
>
I really would prefer that we add additional types for the new
structures, neither side will be compatible with new versions without
enhancements to their respective implementations anyways.
> An alternate way would be to introduce the new types as completely
> new resource types which would require additional customization of
> the resource handlers based on the 32-bit or 64-bit mode of a remote
> processor, and introduction of an additional mode flag to the rproc
> structure.
>
What would this "mode" indicate? If it's version 0 or 1?
> Signed-off-by: Suman Anna <s-anna@ti.com>
> ---
> drivers/remoteproc/remoteproc_core.c | 25 +++++++++++++++----------
> drivers/remoteproc/remoteproc_debugfs.c | 17 ++++++++++-------
> include/linux/remoteproc.h | 8 +++++++-
> 3 files changed, 32 insertions(+), 18 deletions(-)
>
[..]
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> index 77788a4bb94e..526d3cb45e37 100644
> --- a/include/linux/remoteproc.h
> +++ b/include/linux/remoteproc.h
> @@ -86,7 +86,13 @@ struct resource_table {
> * this header, and it should be parsed according to the resource type.
> */
> struct fw_rsc_hdr {
> - u32 type;
> + union {
> + u32 type;
> + struct {
> + u16 t;
> + u16 v;
> + } st;
I see your "type" is little endian...
Regards,
Bjorn
^ permalink raw reply
* Re: [PATCH 04/12] iio: imu: inv_icm42600: add gyroscope IIO device
From: Jonathan Cameron @ 2020-05-21 17:55 UTC (permalink / raw)
To: Jean-Baptiste Maneyrol
Cc: Jonathan Cameron, robh+dt@kernel.org, robh@kernel.org,
mchehab+huawei@kernel.org, davem@davemloft.net,
gregkh@linuxfoundation.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <MN2PR12MB4422B32CB3C4BFD0AF5FFF3CC4B80@MN2PR12MB4422.namprd12.prod.outlook.com>
On Mon, 18 May 2020 15:27:28 +0000
Jean-Baptiste Maneyrol <JManeyrol@invensense.com> wrote:
> Hi Jonathan,
>
> I agree with all comments.
>
> For regmap_bulk_read, by looking at source code it doesn't seem to requires specific alignment, except if bus read callback is expecting that. But I can see numerous drivers calling regmap_bulk_read with a data buffer on the stack and not particularly aligned.
Absolutely. e.g. i2c you don't need to be aligned for
but for SPI you do. If there are drivers
using regmap for spi that don't there is probably
a bug there.
Jonathan
>
> And we definitely can read calibration offset registers while running, the lock is indeed not needed.
>
> Thanks,
> JB
> ________________________________
> From: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
> Sent: Friday, May 8, 2020 16:01
> To: Jean-Baptiste Maneyrol <JManeyrol@invensense.com>
> Cc: jic23@kernel.org <jic23@kernel.org>; robh+dt@kernel.org <robh+dt@kernel.org>; robh@kernel.org <robh@kernel.org>; mchehab+huawei@kernel.org <mchehab+huawei@kernel.org>; davem@davemloft.net <davem@davemloft.net>; gregkh@linuxfoundation.org <gregkh@linuxfoundation.org>; linux-iio@vger.kernel.org <linux-iio@vger.kernel.org>; devicetree@vger.kernel.org <devicetree@vger.kernel.org>; linux-kernel@vger.kernel.org <linux-kernel@vger.kernel.org>
> Subject: Re: [PATCH 04/12] iio: imu: inv_icm42600: add gyroscope IIO device
>
> CAUTION: This email originated from outside of the organization. Please make sure the sender is who they say they are and do not click links or open attachments unless you recognize the sender and know the content is safe.
>
> On Thu, 7 May 2020 16:42:14 +0200
> Jean-Baptiste Maneyrol <jmaneyrol@invensense.com> wrote:
>
> > Add IIO device for gyroscope sensor with data polling interface.
> > Attributes: raw, scale, sampling_frequency, calibbias.
> >
> > Gyroscope in low noise mode.
> >
> > Signed-off-by: Jean-Baptiste Maneyrol <jmaneyrol@invensense.com>
> Few trivial things and questions inline.
>
> J
>
> > ---
> > drivers/iio/imu/inv_icm42600/inv_icm42600.h | 4 +
> > .../iio/imu/inv_icm42600/inv_icm42600_core.c | 5 +
> > .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 549 ++++++++++++++++++
> > 3 files changed, 558 insertions(+)
> > create mode 100644 drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> >
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
> > index 8da4c8249aed..ca41a9d6404a 100644
> > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
> > @@ -120,6 +120,7 @@ struct inv_icm42600_suspended {
> > * @orientation: sensor chip orientation relative to main hardware.
> > * @conf: chip sensors configurations.
> > * @suspended: suspended sensors configuration.
> > + * @indio_gyro: gyroscope IIO device.
> > */
> > struct inv_icm42600_state {
> > struct mutex lock;
> > @@ -131,6 +132,7 @@ struct inv_icm42600_state {
> > struct iio_mount_matrix orientation;
> > struct inv_icm42600_conf conf;
> > struct inv_icm42600_suspended suspended;
> > + struct iio_dev *indio_gyro;
> > };
> >
> > /* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
> > @@ -369,4 +371,6 @@ int inv_icm42600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
> > int inv_icm42600_core_probe(struct regmap *regmap, int chip,
> > inv_icm42600_bus_setup bus_setup);
> >
> > +int inv_icm42600_gyro_init(struct inv_icm42600_state *st);
> > +
> > #endif
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
> > index 35bdf4f9d31e..151257652ce6 100644
> > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
> > @@ -503,6 +503,11 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip,
> > if (ret)
> > return ret;
> >
> > + /* create and init gyroscope iio device */
>
> 'Kind' of obvious from function name? Maybe drop the comment?
>
> > + ret = inv_icm42600_gyro_init(st);
> > + if (ret)
> > + return ret;
> > +
> > /* setup runtime power management */
> > ret = pm_runtime_set_active(dev);
> > if (ret)
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> > new file mode 100644
> > index 000000000000..74aa2b5fa611
> > --- /dev/null
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> > @@ -0,0 +1,549 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Copyright (C) 2020 Invensense, Inc.
> > + */
> > +
> > +#include <linux/device.h>
> > +#include <linux/mutex.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/delay.h>
> > +#include <linux/iio/iio.h>
> > +
> > +#include "inv_icm42600.h"
> > +
> > +#define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \
> > + { \
> > + .type = IIO_ANGL_VEL, \
> > + .modified = 1, \
> > + .channel2 = _modifier, \
> > + .info_mask_separate = \
> > + BIT(IIO_CHAN_INFO_RAW) | \
> > + BIT(IIO_CHAN_INFO_CALIBBIAS), \
> > + .info_mask_shared_by_type = \
> > + BIT(IIO_CHAN_INFO_SCALE), \
> > + .info_mask_shared_by_type_available = \
> > + BIT(IIO_CHAN_INFO_SCALE), \
> > + .info_mask_shared_by_all = \
> > + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> > + .info_mask_shared_by_all_available = \
> > + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> > + .scan_index = _index, \
> > + .scan_type = { \
> > + .sign = 's', \
> > + .realbits = 16, \
> > + .storagebits = 16, \
> > + .shift = 0, \
>
> Shift has the 'obviously' default of 0, so normally we don't bother explicitly
> setting it to 0 like this.
>
> > + .endianness = IIO_BE, \
> > + }, \
> > + .ext_info = _ext_info, \
> > + }
> > +
> > +enum inv_icm42600_gyro_scan {
> > + INV_ICM42600_GYRO_SCAN_X,
> > + INV_ICM42600_GYRO_SCAN_Y,
> > + INV_ICM42600_GYRO_SCAN_Z,
> > +};
> > +
> > +static const struct iio_chan_spec_ext_info inv_icm42600_gyro_ext_infos[] = {
> > + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42600_get_mount_matrix),
> > + {},
> > +};
> > +
> > +static const struct iio_chan_spec inv_icm42600_gyro_channels[] = {
> > + INV_ICM42600_GYRO_CHAN(IIO_MOD_X, INV_ICM42600_GYRO_SCAN_X,
> > + inv_icm42600_gyro_ext_infos),
> > + INV_ICM42600_GYRO_CHAN(IIO_MOD_Y, INV_ICM42600_GYRO_SCAN_Y,
> > + inv_icm42600_gyro_ext_infos),
> > + INV_ICM42600_GYRO_CHAN(IIO_MOD_Z, INV_ICM42600_GYRO_SCAN_Z,
> > + inv_icm42600_gyro_ext_infos),
> > +};
> > +
> > +static int inv_icm42600_gyro_read_sensor(struct inv_icm42600_state *st,
> > + struct iio_chan_spec const *chan,
> > + int16_t *val)
> > +{
> > + struct device *dev = regmap_get_device(st->map);
> > + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
> > + unsigned int reg;
> > + __be16 data;
> > + int ret;
> > +
> > + if (chan->type != IIO_ANGL_VEL)
> > + return -EINVAL;
> > +
> > + switch (chan->channel2) {
> > + case IIO_MOD_X:
> > + reg = INV_ICM42600_REG_GYRO_DATA_X;
> > + break;
> > + case IIO_MOD_Y:
> > + reg = INV_ICM42600_REG_GYRO_DATA_Y;
> > + break;
> > + case IIO_MOD_Z:
> > + reg = INV_ICM42600_REG_GYRO_DATA_Z;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + pm_runtime_get_sync(dev);
> > + mutex_lock(&st->lock);
> > +
> > + /* enable gyro sensor */
> > + conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
> > + ret = inv_icm42600_set_gyro_conf(st, &conf, NULL);
> > + if (ret)
> > + goto exit;
> > +
> > + /* read gyro register data */
> > + ret = regmap_bulk_read(st->map, reg, &data, sizeof(data));
>
> IIRC bulk reads need to be to dma safe buffers. So typically on the stack and
> in appropriately aligned location in any containing structure.
>
> > + if (ret)
> > + goto exit;
> > +
> > + *val = (int16_t)be16_to_cpu(data);
> > + if (*val == INV_ICM42600_DATA_INVALID)
> > + ret = -EINVAL;
> > +exit:
> > + mutex_unlock(&st->lock);
> > + pm_runtime_mark_last_busy(dev);
> > + pm_runtime_put_autosuspend(dev);
> > + return ret;
> > +}
> > +
> > +/* IIO format int + nano */
> > +static const int inv_icm42600_gyro_scale[] = {
> > + /* +/- 2000dps => 0.001065264 rad/s */
> > + [2 * INV_ICM42600_GYRO_FS_2000DPS] = 0,
> > + [2 * INV_ICM42600_GYRO_FS_2000DPS + 1] = 1065264,
> > + /* +/- 1000dps => 0.000532632 rad/s */
> > + [2 * INV_ICM42600_GYRO_FS_1000DPS] = 0,
> > + [2 * INV_ICM42600_GYRO_FS_1000DPS + 1] = 532632,
> > + /* +/- 500dps => 0.000266316 rad/s */
> > + [2 * INV_ICM42600_GYRO_FS_500DPS] = 0,
> > + [2 * INV_ICM42600_GYRO_FS_500DPS + 1] = 266316,
> > + /* +/- 250dps => 0.000133158 rad/s */
> > + [2 * INV_ICM42600_GYRO_FS_250DPS] = 0,
> > + [2 * INV_ICM42600_GYRO_FS_250DPS + 1] = 133158,
> > + /* +/- 125dps => 0.000066579 rad/s */
> > + [2 * INV_ICM42600_GYRO_FS_125DPS] = 0,
> > + [2 * INV_ICM42600_GYRO_FS_125DPS + 1] = 66579,
> > + /* +/- 62.5dps => 0.000033290 rad/s */
> > + [2 * INV_ICM42600_GYRO_FS_62_5DPS] = 0,
> > + [2 * INV_ICM42600_GYRO_FS_62_5DPS + 1] = 33290,
> > + /* +/- 31.25dps => 0.000016645 rad/s */
> > + [2 * INV_ICM42600_GYRO_FS_31_25DPS] = 0,
> > + [2 * INV_ICM42600_GYRO_FS_31_25DPS + 1] = 16645,
> > + /* +/- 15.625dps => 0.000008322 rad/s */
> > + [2 * INV_ICM42600_GYRO_FS_15_625DPS] = 0,
> > + [2 * INV_ICM42600_GYRO_FS_15_625DPS + 1] = 8322,
> > +};
> > +
> > +static int inv_icm42600_gyro_read_scale(struct inv_icm42600_state *st,
> > + int *val, int *val2)
> > +{
> > + unsigned int idx;
> > +
> > + mutex_lock(&st->lock);
> > + idx = st->conf.gyro.fs;
>
> Seems like we shouldn't need the lock to retrieve a single value.
> Is there some odd intermediate state somewhere I'm missing?
>
> > + mutex_unlock(&st->lock);
> > +
> > + *val = inv_icm42600_gyro_scale[2 * idx];
> > + *val2 = inv_icm42600_gyro_scale[2 * idx + 1];
> > + return IIO_VAL_INT_PLUS_NANO;
> > +}
> > +
> > +static int inv_icm42600_gyro_write_scale(struct inv_icm42600_state *st,
> > + int val, int val2)
> > +{
> > + struct device *dev = regmap_get_device(st->map);
> > + unsigned int idx;
> > + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
> > + int ret;
> > +
> > + for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_gyro_scale); idx += 2) {
> > + if (val == inv_icm42600_gyro_scale[idx] &&
> > + val2 == inv_icm42600_gyro_scale[idx + 1])
>
> Alignment of code seems odd.
>
> > + break;
> > + }
> > + if (idx >= ARRAY_SIZE(inv_icm42600_gyro_scale))
> > + return -EINVAL;
> > +
> > + /* update gyro fs */
> > + pm_runtime_get_sync(dev);
> > +
> > + mutex_lock(&st->lock);
> > + conf.fs = idx / 2;
> > + ret = inv_icm42600_set_gyro_conf(st, &conf, NULL);
> > + mutex_unlock(&st->lock);
> > +
> > + pm_runtime_mark_last_busy(dev);
> > + pm_runtime_put_autosuspend(dev);
> > +
> > + return ret;
> > +}
> > +
> > +/* IIO format int + micro */
> > +static const int inv_icm42600_gyro_odr[] = {
> > + /* 12.5Hz */
> > + 12, 500000,
> > + /* 25Hz */
> > + 25, 0,
> > + /* 50Hz */
> > + 50, 0,
> > + /* 100Hz */
> > + 100, 0,
> > + /* 200Hz */
> > + 200, 0,
> > + /* 1kHz */
> > + 1000, 0,
> > + /* 2kHz */
> > + 2000, 0,
> > + /* 4kHz */
> > + 4000, 0,
> > +};
> > +
> > +static const int inv_icm42600_gyro_odr_conv[] = {
> > + INV_ICM42600_ODR_12_5HZ,
> > + INV_ICM42600_ODR_25HZ,
> > + INV_ICM42600_ODR_50HZ,
> > + INV_ICM42600_ODR_100HZ,
> > + INV_ICM42600_ODR_200HZ,
> > + INV_ICM42600_ODR_1KHZ_LN,
> > + INV_ICM42600_ODR_2KHZ_LN,
> > + INV_ICM42600_ODR_4KHZ_LN,
> > +};
> > +
> > +static int inv_icm42600_gyro_read_odr(struct inv_icm42600_state *st,
> > + int *val, int *val2)
> > +{
> > + unsigned int odr;
> > + unsigned int i;
> > +
> > + mutex_lock(&st->lock);
> > + odr = st->conf.gyro.odr;
> > + mutex_unlock(&st->lock);
> > +
> > + for (i = 0; i < ARRAY_SIZE(inv_icm42600_gyro_odr_conv); ++i) {
> > + if (inv_icm42600_gyro_odr_conv[i] == odr)
> > + break;
> > + }
> > + if (i >= ARRAY_SIZE(inv_icm42600_gyro_odr_conv))
> > + return -EINVAL;
> > +
> > + *val = inv_icm42600_gyro_odr[2 * i];
> > + *val2 = inv_icm42600_gyro_odr[2 * i + 1];
> > +
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +}
> > +
> > +static int inv_icm42600_gyro_write_odr(struct inv_icm42600_state *st,
> > + int val, int val2)
> > +{
> > + struct device *dev = regmap_get_device(st->map);
> > + unsigned int idx;
> > + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
> > + int ret;
> > +
> > + for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_gyro_odr); idx += 2) {
> > + if (val == inv_icm42600_gyro_odr[idx] &&
> > + val2 == inv_icm42600_gyro_odr[idx + 1])
> > + break;
> > + }
> > + if (idx >= ARRAY_SIZE(inv_icm42600_gyro_odr))
> > + return -EINVAL;
> > +
> > + /* update gyro odr */
> > + pm_runtime_get_sync(dev);
> > +
> > + mutex_lock(&st->lock);
> > + conf.odr = inv_icm42600_gyro_odr_conv[idx / 2];
> > + ret = inv_icm42600_set_gyro_conf(st, &conf, NULL);
> > + mutex_unlock(&st->lock);
> > +
> > + pm_runtime_mark_last_busy(dev);
> > + pm_runtime_put_autosuspend(dev);
> > +
> > + return ret;
> > +}
> > +
> > +static int inv_icm42600_gyro_read_offset(struct inv_icm42600_state *st,
> > + struct iio_chan_spec const *chan,
> > + int16_t *val)
> > +{
> > + struct device *dev = regmap_get_device(st->map);
> > + unsigned int reg;
> > + uint8_t data[2];
> > + int ret;
> > +
> > + if (chan->type != IIO_ANGL_VEL)
> > + return -EINVAL;
> > +
> > + switch (chan->channel2) {
> > + case IIO_MOD_X:
> > + reg = INV_ICM42600_REG_OFFSET_USER0;
> > + break;
> > + case IIO_MOD_Y:
> > + reg = INV_ICM42600_REG_OFFSET_USER1;
> > + break;
> > + case IIO_MOD_Z:
> > + reg = INV_ICM42600_REG_OFFSET_USER3;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + pm_runtime_get_sync(dev);
> > +
> > + /* read gyro offset data */
> > + mutex_lock(&st->lock);
> > + ret = regmap_bulk_read(st->map, reg, &data, sizeof(data));
> > + mutex_unlock(&st->lock);
> > + if (ret)
> > + goto exit;
> > +
> > + switch (chan->channel2) {
> > + case IIO_MOD_X:
> > + *val = (int16_t)(((data[1] & 0x0F) << 8) | data[0]);
>
> This doesn't look right for negative values. You would be better
> off with a sign extend of the 12 bit value.
>
> > + break;
> > + case IIO_MOD_Y:
> > + *val = (int16_t)(((data[0] & 0xF0) << 4) | data[1]);
> > + break;
> > + case IIO_MOD_Z:
> > + *val = (int16_t)(((data[1] & 0x0F) << 8) | data[0]);
> > + break;
> > + default:
> > + ret = -EINVAL;
> > + break;
> > + }
> > +
> > +exit:
> > + pm_runtime_mark_last_busy(dev);
> > + pm_runtime_put_autosuspend(dev);
> > + return ret;
> > +}
> > +
> > +static int inv_icm42600_gyro_write_offset(struct inv_icm42600_state *st,
> > + struct iio_chan_spec const *chan,
> > + int val)
> > +{
> > + struct device *dev = regmap_get_device(st->map);
> > + unsigned int reg, regval;
> > + uint8_t data[2];
> > + int ret;
> > +
> > + if (chan->type != IIO_ANGL_VEL)
> > + return -EINVAL;
> > +
> > + switch (chan->channel2) {
> > + case IIO_MOD_X:
> > + reg = INV_ICM42600_REG_OFFSET_USER0;
> > + break;
> > + case IIO_MOD_Y:
> > + reg = INV_ICM42600_REG_OFFSET_USER1;
> > + break;
> > + case IIO_MOD_Z:
> > + reg = INV_ICM42600_REG_OFFSET_USER3;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + /* value is limited to 12 bits signed */
> > + if (val < -2048 || val > 2047)
> > + return -EINVAL;
>
> Perhaps worth an available callback to give the range?
>
> > +
> > + pm_runtime_get_sync(dev);
> > + mutex_lock(&st->lock);
> > +
> > + switch (chan->channel2) {
> > + case IIO_MOD_X:
> > + /* OFFSET_USER1 register is shared */
> > + ret = regmap_read(st->map, INV_ICM42600_REG_OFFSET_USER1,
> > + ®val);
> > + if (ret)
> > + goto out_unlock;
> > + data[0] = val & 0xFF;
> > + data[1] = (regval & 0xF0) | ((val & 0xF00) >> 8);
> > + break;
> > + case IIO_MOD_Y:
> > + /* OFFSET_USER1 register is shared */
> > + ret = regmap_read(st->map, INV_ICM42600_REG_OFFSET_USER1,
> > + ®val);
> > + if (ret)
> > + goto out_unlock;
> > + data[0] = ((val & 0xF00) >> 4) | (regval & 0x0F);
> > + data[1] = val & 0xFF;
> > + break;
> > + case IIO_MOD_Z:
> > + /* OFFSET_USER4 register is shared */
> > + ret = regmap_read(st->map, INV_ICM42600_REG_OFFSET_USER4,
> > + ®val);
> > + if (ret)
> > + goto out_unlock;
> > + data[0] = val & 0xFF;
> > + data[1] = (regval & 0xF0) | ((val & 0xF00) >> 8);
> > + break;
> > + default:
> > + ret = -EINVAL;
> > + goto out_unlock;
> > + }
> > +
> > + ret = regmap_bulk_write(st->map, reg, data, sizeof(data));
> > +
> > +out_unlock:
> > + mutex_unlock(&st->lock);
> > + pm_runtime_mark_last_busy(dev);
> > + pm_runtime_put_autosuspend(dev);
> > + return ret;
> > +}
> > +
> > +static int inv_icm42600_gyro_read_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int *val, int *val2, long mask)
> > +{
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
> > + int16_t data;
> > + int ret;
> > +
> > + if (chan->type != IIO_ANGL_VEL)
> > + return -EINVAL;
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_RAW:
> > + ret = iio_device_claim_direct_mode(indio_dev);
> > + if (ret)
> > + return ret;
> > + ret = inv_icm42600_gyro_read_sensor(st, chan, &data);
> > + iio_device_release_direct_mode(indio_dev);
> > + if (ret)
> > + return ret;
> > + *val = data;
> > + return IIO_VAL_INT;
> > + case IIO_CHAN_INFO_SCALE:
> > + return inv_icm42600_gyro_read_scale(st, val, val2);
> > + case IIO_CHAN_INFO_SAMP_FREQ:
> > + return inv_icm42600_gyro_read_odr(st, val, val2);
> > + case IIO_CHAN_INFO_CALIBBIAS:
> > + ret = iio_device_claim_direct_mode(indio_dev);
> > + if (ret)
> > + return ret;
>
> I'm curious. Why can't we read back a calibration offset whilst doing
> buffered capture?
>
> > + ret = inv_icm42600_gyro_read_offset(st, chan, &data);
> > + iio_device_release_direct_mode(indio_dev);
> > + if (ret)
> > + return ret;
> > + *val = data;
> > + return IIO_VAL_INT;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int inv_icm42600_gyro_read_avail(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + const int **vals,
> > + int *type, int *length, long mask)
> > +{
> > + if (chan->type != IIO_ANGL_VEL)
> > + return -EINVAL;
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_SCALE:
> > + *vals = inv_icm42600_gyro_scale;
> > + *type = IIO_VAL_INT_PLUS_NANO;
> > + *length = ARRAY_SIZE(inv_icm42600_gyro_scale);
> > + return IIO_AVAIL_LIST;
> > + case IIO_CHAN_INFO_SAMP_FREQ:
> > + *vals = inv_icm42600_gyro_odr;
> > + *type = IIO_VAL_INT_PLUS_MICRO;
> > + *length = ARRAY_SIZE(inv_icm42600_gyro_odr);
> > + return IIO_AVAIL_LIST;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int inv_icm42600_gyro_write_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int val, int val2, long mask)
> > +{
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
> > + int ret;
> > +
> > + if (chan->type != IIO_ANGL_VEL)
> > + return -EINVAL;
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_SCALE:
> > + ret = iio_device_claim_direct_mode(indio_dev);
> > + if (ret)
> > + return ret;
> > + ret = inv_icm42600_gyro_write_scale(st, val, val2);
> > + iio_device_release_direct_mode(indio_dev);
> > + return ret;
> > + case IIO_CHAN_INFO_SAMP_FREQ:
> > + return inv_icm42600_gyro_write_odr(st, val, val2);
> > + case IIO_CHAN_INFO_CALIBBIAS:
> > + ret = iio_device_claim_direct_mode(indio_dev);
> > + if (ret)
> > + return ret;
> > + ret = inv_icm42600_gyro_write_offset(st, chan, val);
> > + iio_device_release_direct_mode(indio_dev);
> > + return ret;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int inv_icm42600_gyro_write_raw_get_fmt(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + long mask)
> > +{
> > + if (chan->type != IIO_ANGL_VEL)
> > + return -EINVAL;
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_SCALE:
> > + return IIO_VAL_INT_PLUS_NANO;
> > + case IIO_CHAN_INFO_SAMP_FREQ:
> > + return IIO_VAL_INT_PLUS_MICRO;
> > + case IIO_CHAN_INFO_CALIBBIAS:
> > + return IIO_VAL_INT;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static const struct iio_info inv_icm42600_gyro_info = {
> > + .read_raw = inv_icm42600_gyro_read_raw,
> > + .read_avail = inv_icm42600_gyro_read_avail,
> > + .write_raw = inv_icm42600_gyro_write_raw,
> > + .write_raw_get_fmt = inv_icm42600_gyro_write_raw_get_fmt,
> > + .debugfs_reg_access = inv_icm42600_debugfs_reg,
> > +};
> > +
> > +int inv_icm42600_gyro_init(struct inv_icm42600_state *st)
> > +{
> > + struct device *dev = regmap_get_device(st->map);
> > + const char *name;
> > + struct iio_dev *indio_dev;
> > +
> > + name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name);
> > + if (!name)
> > + return -ENOMEM;
> > +
> > + indio_dev = devm_iio_device_alloc(dev, 0);
> > + if (!indio_dev)
> > + return -ENOMEM;
> > +
> > + iio_device_set_drvdata(indio_dev, st);
> > + indio_dev->dev.parent = dev;
> > + indio_dev->name = name;
> > + indio_dev->info = &inv_icm42600_gyro_info;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > + indio_dev->channels = inv_icm42600_gyro_channels;
> > + indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_gyro_channels);
> > +
> > + st->indio_gyro = indio_dev;
> > + return devm_iio_device_register(dev, st->indio_gyro);
> > +}
>
>
^ permalink raw reply
* Re: [PATCH 02/12] iio: imu: inv_icm42600: add I2C driver for inv_icm42600 driver
From: Jonathan Cameron @ 2020-05-21 17:50 UTC (permalink / raw)
To: Jean-Baptiste Maneyrol
Cc: Jonathan Cameron, robh+dt@kernel.org, robh@kernel.org,
mchehab+huawei@kernel.org, davem@davemloft.net,
gregkh@linuxfoundation.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <MN2PR12MB44228DAD29335621D4047260C4B80@MN2PR12MB4422.namprd12.prod.outlook.com>
On Mon, 18 May 2020 14:19:11 +0000
Jean-Baptiste Maneyrol <JManeyrol@invensense.com> wrote:
> Hi Jonathan,
>
> I am using generic device_get_match_data because I was thinking it was now the way to go. But since only of is supported with the driver, I can switch to using of_device_get_match_data instead.
>
> Tell me what do you think is better.
Leave it as you have it. We can look at if we can tidy up
what I was rambling about as a separate exercise.
J
>
> I could definitely use the new probe interface indeed, good idea.
>
> Thanks,
> JB
>
>
>
> From: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
>
> Sent: Friday, May 8, 2020 15:44
>
> To: Jean-Baptiste Maneyrol <JManeyrol@invensense.com>
>
> Cc: jic23@kernel.org <jic23@kernel.org>; robh+dt@kernel.org <robh+dt@kernel.org>; robh@kernel.org <robh@kernel.org>; mchehab+huawei@kernel.org <mchehab+huawei@kernel.org>; davem@davemloft.net <davem@davemloft.net>; gregkh@linuxfoundation.org <gregkh@linuxfoundation.org>;
> linux-iio@vger.kernel.org <linux-iio@vger.kernel.org>; devicetree@vger.kernel.org <devicetree@vger.kernel.org>; linux-kernel@vger.kernel.org <linux-kernel@vger.kernel.org>
>
> Subject: Re: [PATCH 02/12] iio: imu: inv_icm42600: add I2C driver for inv_icm42600 driver
>
>
>
>
> CAUTION: This email originated from outside of the organization. Please make sure the sender is who they say they are and do not click links or open attachments unless you recognize the sender and know the content is safe.
>
>
>
> On Thu, 7 May 2020 16:42:12 +0200
>
> Jean-Baptiste Maneyrol <jmaneyrol@invensense.com> wrote:
>
>
>
> > Add I2C driver for InvenSense ICM-426xxx devices.
>
> >
>
> > Configure bus signal slew rates as indicated in the datasheet.
>
> >
>
> > Signed-off-by: Jean-Baptiste Maneyrol <jmaneyrol@invensense.com>
>
> Some incoherent rambling inline. + a few comments
>
>
>
> Jonathan
>
>
>
> > ---
>
> > .../iio/imu/inv_icm42600/inv_icm42600_i2c.c | 117 ++++++++++++++++++
>
> > 1 file changed, 117 insertions(+)
>
> > create mode 100644 drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
>
> >
>
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
>
> > new file mode 100644
>
> > index 000000000000..b61f993beacf
>
> > --- /dev/null
>
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
>
> > @@ -0,0 +1,117 @@
>
> > +// SPDX-License-Identifier: GPL-2.0-or-later
>
> > +/*
>
> > + * Copyright (C) 2020 InvenSense, Inc.
>
> > + */
>
> > +
>
> > +#include <linux/device.h>
>
> > +#include <linux/module.h>
>
> > +#include <linux/i2c.h>
>
> > +#include <linux/regmap.h>
>
> > +#include <linux/of_device.h>
>
>
>
> Why? Looks like you need the table and the device property stuff neither
>
> of which are in that file.
>
>
>
> linux/mod_devicetable.h
>
> linux/property.h
>
>
>
>
>
> > +
>
> > +#include "inv_icm42600.h"
>
> > +
>
> > +static int inv_icm42600_i2c_bus_setup(struct inv_icm42600_state *st)
>
> > +{
>
> > + unsigned int mask, val;
>
> > + int ret;
>
> > +
>
> > + /* setup interface registers */
>
> > + mask = INV_ICM42600_INTF_CONFIG6_MASK;
>
> > + val = INV_ICM42600_INTF_CONFIG6_I3C_EN;
>
> > + ret = regmap_update_bits(st->map, INV_ICM42600_REG_INTF_CONFIG6,
>
> > + mask, val);
>
>
>
> I'd put the values inline where they are simple like these rather than
>
> using local variables.
>
>
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + mask = INV_ICM42600_INTF_CONFIG4_I3C_BUS_ONLY;
>
> > + val = 0;
>
> > + ret = regmap_update_bits(st->map, INV_ICM42600_REG_INTF_CONFIG4,
>
> > + mask, val);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* set slew rates for I2C and SPI */
>
> > + mask = INV_ICM42600_DRIVE_CONFIG_I2C_MASK |
>
> > + INV_ICM42600_DRIVE_CONFIG_SPI_MASK;
>
> > + val = INV_ICM42600_DRIVE_CONFIG_I2C(INV_ICM42600_SLEW_RATE_12_36NS) |
>
> > + INV_ICM42600_DRIVE_CONFIG_SPI(INV_ICM42600_SLEW_RATE_12_36NS);
>
> > + ret = regmap_update_bits(st->map, INV_ICM42600_REG_DRIVE_CONFIG,
>
> > + mask, val);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* disable SPI bus */
>
> > + mask = INV_ICM42600_INTF_CONFIG0_UI_SIFS_CFG_MASK;
>
> > + val = INV_ICM42600_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS;
>
> > + return regmap_update_bits(st->map, INV_ICM42600_REG_INTF_CONFIG0,
>
> > + mask, val);
>
> > +}
>
> > +
>
> > +static int inv_icm42600_probe(struct i2c_client *client,
>
> > + const struct i2c_device_id *id)
>
> > +{
>
> > + const void *match;
>
> > + enum inv_icm42600_chip chip;
>
> > + struct regmap *regmap;
>
> > +
>
> > + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
>
> > + return -ENOTSUPP;
>
> > +
>
> > + match = device_get_match_data(&client->dev);
>
>
>
> Hmm. Annoyingly if one were to call the of specific option
>
> of i2c_of_match_device it would handle the old style i2c match just fine without
>
> needing special handling. However, it would fail to handle PRP0001 ACPI.
>
>
>
> Rather feels like there should be something similar for
>
> device_get_match_data so we could use the probe_new version of i2c device
>
> probing.
>
>
>
> Oh well. I guess thats a separate question for another day ;)
>
>
>
> Mind you can we actually probe this driver via the sysfs route?
>
> If not why do we need to handle the non firmware case at all?
>
>
>
> > + if (match)
>
> > + chip = (enum inv_icm42600_chip)match;
>
> > + else if (id)
>
> > + chip = (enum inv_icm42600_chip)id->driver_data;
>
> > + else
>
> > + return -EINVAL;
>
> > +
>
> > + regmap = devm_regmap_init_i2c(client, &inv_icm42600_regmap_config);
>
> > + if (IS_ERR(regmap))
>
> > + return PTR_ERR(regmap);
>
> > +
>
> > + return inv_icm42600_core_probe(regmap, chip,
>
> > + inv_icm42600_i2c_bus_setup);
>
> > +}
>
> > +
>
> > +static const struct of_device_id inv_icm42600_of_matches[] = {
>
> > + {
>
> > + .compatible = "invensense,icm42600",
>
> > + .data = (void *)INV_CHIP_ICM42600,
>
> > + }, {
>
> > + .compatible = "invensense,icm42602",
>
> > + .data = (void *)INV_CHIP_ICM42602,
>
> > + }, {
>
> > + .compatible = "invensense,icm42605",
>
> > + .data = (void *)INV_CHIP_ICM42605,
>
> > + }, {
>
> > + .compatible = "invensense,icm42622",
>
> > + .data = (void *)INV_CHIP_ICM42622,
>
> > + },
>
> > + {}
>
> > +};
>
> > +MODULE_DEVICE_TABLE(of, inv_icm42600_of_matches);
>
> > +
>
> > +static const struct i2c_device_id inv_icm42600_ids[] = {
>
> > + {"icm42600", INV_CHIP_ICM42600},
>
> > + {"icm42602", INV_CHIP_ICM42602},
>
> > + {"icm42605", INV_CHIP_ICM42605},
>
> > + {"icm42622", INV_CHIP_ICM42622},
>
> > + {}
>
> > +};
>
> > +MODULE_DEVICE_TABLE(i2c, inv_icm42600_ids);
>
> > +
>
> > +static struct i2c_driver inv_icm42600_driver = {
>
> > + .probe = inv_icm42600_probe,
>
> > + .id_table = inv_icm42600_ids,
>
> > + .driver = {
>
> > + .of_match_table = inv_icm42600_of_matches,
>
> > + .name = "inv-icm42600-i2c",
>
> > + .pm = &inv_icm42600_pm_ops,
>
> > + },
>
> > +};
>
> > +module_i2c_driver(inv_icm42600_driver);
>
> > +
>
> > +MODULE_AUTHOR("InvenSense, Inc.");
>
> > +MODULE_DESCRIPTION("InvenSense ICM-426xx I2C driver");
>
> > +MODULE_LICENSE("GPL");
>
>
>
>
>
^ permalink raw reply
* Re: [PATCH 01/12] iio: imu: inv_icm42600: add core of new inv_icm42600 driver
From: Jonathan Cameron @ 2020-05-21 17:47 UTC (permalink / raw)
To: Jean-Baptiste Maneyrol
Cc: Jonathan Cameron, robh+dt@kernel.org, robh@kernel.org,
mchehab+huawei@kernel.org, davem@davemloft.net,
gregkh@linuxfoundation.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <MN2PR12MB44227D5AEA06F37269C03BCBC4B80@MN2PR12MB4422.namprd12.prod.outlook.com>
On Mon, 18 May 2020 14:14:26 +0000
Jean-Baptiste Maneyrol <JManeyrol@invensense.com> wrote:
> Hi Jonathan,
>
> thanks for the feedbacks, I'm sorry but I will not be able to have a correct email formatting to respond you inline.
>
> No problem with all the comments. For iio_device_get_drvdata, it would make more sense to use a const struct iio_dev * as argument. I am obliged to do the pointer conversion since iio_get_mount_matrix requires the use of a const struct iio_dev *.
Absolutely. My argument was that we should change
iio_device_get_drvdata to take a const struct iio_dev * as well.
Seems logical. dev_get_drvdata takes a const device * and
that's all that is being called inside there.
That change can happen in parallel to this set so no problem
if you would rather leave it alone for now.
Jonathan
>
> For resume/suspend, I will add commentaries to explain what it is really doing and for which purpose. Sensor states save and restore will remain in this patch, since it makes more sense to have it as a core functionnality, as much as gyro/accel turn on/off.
>
> Thanks.
> JB
>
>
> From: linux-iio-owner@vger.kernel.org <linux-iio-owner@vger.kernel.org> on behalf of Jonathan Cameron <Jonathan.Cameron@Huawei.com>
>
> Sent: Friday, May 8, 2020 15:28
>
> To: Jean-Baptiste Maneyrol <JManeyrol@invensense.com>
>
> Cc: jic23@kernel.org <jic23@kernel.org>; robh+dt@kernel.org <robh+dt@kernel.org>; robh@kernel.org <robh@kernel.org>; mchehab+huawei@kernel.org <mchehab+huawei@kernel.org>; davem@davemloft.net <davem@davemloft.net>; gregkh@linuxfoundation.org <gregkh@linuxfoundation.org>;
> linux-iio@vger.kernel.org <linux-iio@vger.kernel.org>; devicetree@vger.kernel.org <devicetree@vger.kernel.org>; linux-kernel@vger.kernel.org <linux-kernel@vger.kernel.org>
>
> Subject: Re: [PATCH 01/12] iio: imu: inv_icm42600: add core of new inv_icm42600 driver
>
>
>
>
> CAUTION: This email originated from outside of the organization. Please make sure the sender is who they say they are and do not click links or open attachments unless you recognize the sender and know the content is safe.
>
>
>
> On Thu, 7 May 2020 16:42:11 +0200
>
> Jean-Baptiste Maneyrol <jmaneyrol@invensense.com> wrote:
>
>
>
> > Core component of a new driver for InvenSense ICM-426xx devices.
>
> > It includes registers definition, main probe/setup, and device
>
> > utility functions.
>
> >
>
> > ICM-426xx devices are latest generation of 6-axis IMU,
>
> > gyroscope+accelerometer and temperature sensor. This device
>
> > includes a 2K FIFO, supports I2C/I3C/SPI, and provides
>
> > intelligent motion features like pedometer, tilt detection,
>
> > and tap detection.
>
> >
>
> > Signed-off-by: Jean-Baptiste Maneyrol <jmaneyrol@invensense.com>
>
>
>
> Hi Jean-Baptiste,
>
>
>
> A few minor things inline.
>
>
>
> Thanks,
>
>
>
> Jonathan
>
>
>
> > ---
>
> > drivers/iio/imu/inv_icm42600/inv_icm42600.h | 372 +++++++++++
>
> > .../iio/imu/inv_icm42600/inv_icm42600_core.c | 618 ++++++++++++++++++
>
> > 2 files changed, 990 insertions(+)
>
> > create mode 100644 drivers/iio/imu/inv_icm42600/inv_icm42600.h
>
> > create mode 100644 drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
>
> >
>
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
>
> > new file mode 100644
>
> > index 000000000000..8da4c8249aed
>
> > --- /dev/null
>
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
>
> > @@ -0,0 +1,372 @@
>
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
>
> > +/*
>
> > + * Copyright (C) 2020 Invensense, Inc.
>
> > + */
>
> > +
>
> > +#ifndef INV_ICM42600_H_
>
> > +#define INV_ICM42600_H_
>
> > +
>
> > +#include <linux/bits.h>
>
> > +#include <linux/bitfield.h>
>
> > +#include <linux/regmap.h>
>
> > +#include <linux/mutex.h>
>
> > +#include <linux/regulator/consumer.h>
>
> > +#include <linux/pm.h>
>
> > +#include <linux/iio/iio.h>
>
> > +
>
> > +enum inv_icm42600_chip {
>
> > + INV_CHIP_ICM42600,
>
> > + INV_CHIP_ICM42602,
>
> > + INV_CHIP_ICM42605,
>
> > + INV_CHIP_ICM42622,
>
> > + INV_CHIP_NB,
>
> > +};
>
> > +
>
> > +/* serial bus slew rates */
>
> > +enum inv_icm42600_slew_rate {
>
> > + INV_ICM42600_SLEW_RATE_20_60NS,
>
> > + INV_ICM42600_SLEW_RATE_12_36NS,
>
> > + INV_ICM42600_SLEW_RATE_6_18NS,
>
> > + INV_ICM42600_SLEW_RATE_4_12NS,
>
> > + INV_ICM42600_SLEW_RATE_2_6NS,
>
> > + INV_ICM42600_SLEW_RATE_INF_2NS,
>
> > +};
>
> > +
>
> > +enum inv_icm42600_sensor_mode {
>
> > + INV_ICM42600_SENSOR_MODE_OFF,
>
> > + INV_ICM42600_SENSOR_MODE_STANDBY,
>
> > + INV_ICM42600_SENSOR_MODE_LOW_POWER,
>
> > + INV_ICM42600_SENSOR_MODE_LOW_NOISE,
>
> > + INV_ICM42600_SENSOR_MODE_NB,
>
> > +};
>
> > +
>
> > +/* gyroscope fullscale values */
>
> > +enum inv_icm42600_gyro_fs {
>
> > + INV_ICM42600_GYRO_FS_2000DPS,
>
> > + INV_ICM42600_GYRO_FS_1000DPS,
>
> > + INV_ICM42600_GYRO_FS_500DPS,
>
> > + INV_ICM42600_GYRO_FS_250DPS,
>
> > + INV_ICM42600_GYRO_FS_125DPS,
>
> > + INV_ICM42600_GYRO_FS_62_5DPS,
>
> > + INV_ICM42600_GYRO_FS_31_25DPS,
>
> > + INV_ICM42600_GYRO_FS_15_625DPS,
>
> > + INV_ICM42600_GYRO_FS_NB,
>
> > +};
>
> > +
>
> > +/* accelerometer fullscale values */
>
> > +enum inv_icm42600_accel_fs {
>
> > + INV_ICM42600_ACCEL_FS_16G,
>
> > + INV_ICM42600_ACCEL_FS_8G,
>
> > + INV_ICM42600_ACCEL_FS_4G,
>
> > + INV_ICM42600_ACCEL_FS_2G,
>
> > + INV_ICM42600_ACCEL_FS_NB,
>
> > +};
>
> > +
>
> > +/* ODR suffixed by LN or LP are Low-Noise or Low-Power mode only */
>
> > +enum inv_icm42600_odr {
>
> > + INV_ICM42600_ODR_8KHZ_LN = 3,
>
> > + INV_ICM42600_ODR_4KHZ_LN,
>
> > + INV_ICM42600_ODR_2KHZ_LN,
>
> > + INV_ICM42600_ODR_1KHZ_LN,
>
> > + INV_ICM42600_ODR_200HZ,
>
> > + INV_ICM42600_ODR_100HZ,
>
> > + INV_ICM42600_ODR_50HZ,
>
> > + INV_ICM42600_ODR_25HZ,
>
> > + INV_ICM42600_ODR_12_5HZ,
>
> > + INV_ICM42600_ODR_6_25HZ_LP,
>
> > + INV_ICM42600_ODR_3_125HZ_LP,
>
> > + INV_ICM42600_ODR_1_5625HZ_LP,
>
> > + INV_ICM42600_ODR_500HZ,
>
> > + INV_ICM42600_ODR_NB,
>
> > +};
>
> > +
>
> > +enum inv_icm42600_filter {
>
> > + /* Low-Noise mode sensor data filter (3rd order filter by default) */
>
> > + INV_ICM42600_FILTER_BW_ODR_DIV_2,
>
> > +
>
> > + /* Low-Power mode sensor data filter (averaging) */
>
> > + INV_ICM42600_FILTER_AVG_1X = 1,
>
> > + INV_ICM42600_FILTER_AVG_16X = 6,
>
> > +};
>
> > +
>
> > +struct inv_icm42600_sensor_conf {
>
> > + int mode;
>
> > + int fs;
>
> > + int odr;
>
> > + int filter;
>
> > +};
>
> > +#define INV_ICM42600_SENSOR_CONF_INIT {-1, -1, -1, -1}
>
> > +
>
> > +struct inv_icm42600_conf {
>
> > + struct inv_icm42600_sensor_conf gyro;
>
> > + struct inv_icm42600_sensor_conf accel;
>
> > + bool temp_en;
>
> > +};
>
> > +
>
> > +struct inv_icm42600_suspended {
>
> > + enum inv_icm42600_sensor_mode gyro;
>
> > + enum inv_icm42600_sensor_mode accel;
>
> > + bool temp;
>
> > +};
>
> > +
>
> > +/*
>
> /**
>
>
>
> It's valid kernel doc so lets mark it as such.
>
>
>
> > + * struct inv_icm42600_state - driver state variables
>
> > + * @lock: chip access lock.
>
>
>
> Nice to be a bit more specific on that. What about the chip needs
>
> a lock at this level as opposed to bus locks etc?
>
>
>
> > + * @chip: chip identifier.
>
> > + * @name: chip name.
>
> > + * @map: regmap pointer.
>
> > + * @vdd_supply: VDD voltage regulator for the chip.
>
> > + * @vddio_supply: I/O voltage regulator for the chip.
>
> > + * @orientation: sensor chip orientation relative to main hardware.
>
> > + * @conf: chip sensors configurations.
>
> > + * @suspended: suspended sensors configuration.
>
> > + */
>
> > +struct inv_icm42600_state {
>
> > + struct mutex lock;
>
> > + enum inv_icm42600_chip chip;
>
> > + const char *name;
>
> > + struct regmap *map;
>
> > + struct regulator *vdd_supply;
>
> > + struct regulator *vddio_supply;
>
> > + struct iio_mount_matrix orientation;
>
> > + struct inv_icm42600_conf conf;
>
> > + struct inv_icm42600_suspended suspended;
>
> > +};
>
> > +
>
> > +/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
>
> > +
>
> > +/* Bank selection register, available in all banks */
>
> > +#define INV_ICM42600_REG_BANK_SEL 0x76
>
> > +#define INV_ICM42600_BANK_SEL_MASK GENMASK(2, 0)
>
> > +
>
> > +/* User bank 0 (MSB 0x00) */
>
> > +#define INV_ICM42600_REG_DEVICE_CONFIG 0x0011
>
> > +#define INV_ICM42600_DEVICE_CONFIG_SOFT_RESET BIT(0)
>
> > +
>
> > +#define INV_ICM42600_REG_DRIVE_CONFIG 0x0013
>
> > +#define INV_ICM42600_DRIVE_CONFIG_I2C_MASK GENMASK(5, 3)
>
> > +#define INV_ICM42600_DRIVE_CONFIG_I2C(_rate) \
>
> > + FIELD_PREP(INV_ICM42600_DRIVE_CONFIG_I2C_MASK, (_rate))
>
> > +#define INV_ICM42600_DRIVE_CONFIG_SPI_MASK GENMASK(2, 0)
>
> > +#define INV_ICM42600_DRIVE_CONFIG_SPI(_rate) \
>
> > + FIELD_PREP(INV_ICM42600_DRIVE_CONFIG_SPI_MASK, (_rate))
>
> > +
>
> > +#define INV_ICM42600_REG_INT_CONFIG 0x0014
>
> > +#define INV_ICM42600_INT_CONFIG_INT2_LATCHED BIT(5)
>
> > +#define INV_ICM42600_INT_CONFIG_INT2_PUSH_PULL BIT(4)
>
> > +#define INV_ICM42600_INT_CONFIG_INT2_ACTIVE_HIGH BIT(3)
>
> > +#define INV_ICM42600_INT_CONFIG_INT2_ACTIVE_LOW 0x00
>
> > +#define INV_ICM42600_INT_CONFIG_INT1_LATCHED BIT(2)
>
> > +#define INV_ICM42600_INT_CONFIG_INT1_PUSH_PULL BIT(1)
>
> > +#define INV_ICM42600_INT_CONFIG_INT1_ACTIVE_HIGH BIT(0)
>
> > +#define INV_ICM42600_INT_CONFIG_INT1_ACTIVE_LOW 0x00
>
> > +
>
> > +#define INV_ICM42600_REG_FIFO_CONFIG 0x0016
>
> > +#define INV_ICM42600_FIFO_CONFIG_MASK GENMASK(7, 6)
>
> > +#define INV_ICM42600_FIFO_CONFIG_BYPASS \
>
> > + FIELD_PREP(INV_ICM42600_FIFO_CONFIG_MASK, 0)
>
> > +#define INV_ICM42600_FIFO_CONFIG_STREAM \
>
> > + FIELD_PREP(INV_ICM42600_FIFO_CONFIG_MASK, 1)
>
> > +#define INV_ICM42600_FIFO_CONFIG_STOP_ON_FULL \
>
> > + FIELD_PREP(INV_ICM42600_FIFO_CONFIG_MASK, 2)
>
> > +
>
> > +/* all sensor data are 16 bits (2 registers wide) in big-endian */
>
> > +#define INV_ICM42600_REG_TEMP_DATA 0x001D
>
> > +#define INV_ICM42600_REG_ACCEL_DATA_X 0x001F
>
> > +#define INV_ICM42600_REG_ACCEL_DATA_Y 0x0021
>
> > +#define INV_ICM42600_REG_ACCEL_DATA_Z 0x0023
>
> > +#define INV_ICM42600_REG_GYRO_DATA_X 0x0025
>
> > +#define INV_ICM42600_REG_GYRO_DATA_Y 0x0027
>
> > +#define INV_ICM42600_REG_GYRO_DATA_Z 0x0029
>
> > +#define INV_ICM42600_DATA_INVALID -32768
>
> > +
>
> > +#define INV_ICM42600_REG_INT_STATUS 0x002D
>
> > +#define INV_ICM42600_INT_STATUS_UI_FSYNC BIT(6)
>
> > +#define INV_ICM42600_INT_STATUS_PLL_RDY BIT(5)
>
> > +#define INV_ICM42600_INT_STATUS_RESET_DONE BIT(4)
>
> > +#define INV_ICM42600_INT_STATUS_DATA_RDY BIT(3)
>
> > +#define INV_ICM42600_INT_STATUS_FIFO_THS BIT(2)
>
> > +#define INV_ICM42600_INT_STATUS_FIFO_FULL BIT(1)
>
> > +#define INV_ICM42600_INT_STATUS_AGC_RDY BIT(0)
>
> > +
>
> > +/*
>
> > + * FIFO access registers
>
> > + * FIFO count is 16 bits (2 registers) big-endian
>
> > + * FIFO data is a continuous read register to read FIFO content
>
> > + */
>
> > +#define INV_ICM42600_REG_FIFO_COUNT 0x002E
>
> > +#define INV_ICM42600_REG_FIFO_DATA 0x0030
>
> > +
>
> > +#define INV_ICM42600_REG_SIGNAL_PATH_RESET 0x004B
>
> > +#define INV_ICM42600_SIGNAL_PATH_RESET_DMP_INIT_EN BIT(6)
>
> > +#define INV_ICM42600_SIGNAL_PATH_RESET_DMP_MEM_RESET BIT(5)
>
> > +#define INV_ICM42600_SIGNAL_PATH_RESET_RESET BIT(3)
>
> > +#define INV_ICM42600_SIGNAL_PATH_RESET_TMST_STROBE BIT(2)
>
> > +#define INV_ICM42600_SIGNAL_PATH_RESET_FIFO_FLUSH BIT(1)
>
> > +
>
> > +/* default configuration: all data big-endian and fifo count in bytes */
>
> > +#define INV_ICM42600_REG_INTF_CONFIG0 0x004C
>
> > +#define INV_ICM42600_INTF_CONFIG0_FIFO_HOLD_LAST_DATA BIT(7)
>
> > +#define INV_ICM42600_INTF_CONFIG0_FIFO_COUNT_REC BIT(6)
>
> > +#define INV_ICM42600_INTF_CONFIG0_FIFO_COUNT_ENDIAN BIT(5)
>
> > +#define INV_ICM42600_INTF_CONFIG0_SENSOR_DATA_ENDIAN BIT(4)
>
> > +#define INV_ICM42600_INTF_CONFIG0_UI_SIFS_CFG_MASK GENMASK(1, 0)
>
> > +#define INV_ICM42600_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS \
>
> > + FIELD_PREP(INV_ICM42600_INTF_CONFIG0_UI_SIFS_CFG_MASK, 2)
>
> > +#define INV_ICM42600_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS \
>
> > + FIELD_PREP(INV_ICM42600_INTF_CONFIG0_UI_SIFS_CFG_MASK, 3)
>
> > +
>
> > +#define INV_ICM42600_REG_INTF_CONFIG1 0x004D
>
> > +#define INV_ICM42600_INTF_CONFIG1_ACCEL_LP_CLK_RC BIT(3)
>
> > +
>
> > +#define INV_ICM42600_REG_PWR_MGMT0 0x004E
>
> > +#define INV_ICM42600_PWR_MGMT0_TEMP_DIS BIT(5)
>
> > +#define INV_ICM42600_PWR_MGMT0_IDLE BIT(4)
>
> > +#define INV_ICM42600_PWR_MGMT0_GYRO(_mode) \
>
> > + FIELD_PREP(GENMASK(3, 2), (_mode))
>
> > +#define INV_ICM42600_PWR_MGMT0_ACCEL(_mode) \
>
> > + FIELD_PREP(GENMASK(1, 0), (_mode))
>
> > +
>
> > +#define INV_ICM42600_REG_GYRO_CONFIG0 0x004F
>
> > +#define INV_ICM42600_GYRO_CONFIG0_FS(_fs) \
>
> > + FIELD_PREP(GENMASK(7, 5), (_fs))
>
> > +#define INV_ICM42600_GYRO_CONFIG0_ODR(_odr) \
>
> > + FIELD_PREP(GENMASK(3, 0), (_odr))
>
> > +
>
> > +#define INV_ICM42600_REG_ACCEL_CONFIG0 0x0050
>
> > +#define INV_ICM42600_ACCEL_CONFIG0_FS(_fs) \
>
> > + FIELD_PREP(GENMASK(7, 5), (_fs))
>
> > +#define INV_ICM42600_ACCEL_CONFIG0_ODR(_odr) \
>
> > + FIELD_PREP(GENMASK(3, 0), (_odr))
>
> > +
>
> > +#define INV_ICM42600_REG_GYRO_ACCEL_CONFIG0 0x0052
>
> > +#define INV_ICM42600_GYRO_ACCEL_CONFIG0_ACCEL_FILT(_f) \
>
> > + FIELD_PREP(GENMASK(7, 4), (_f))
>
> > +#define INV_ICM42600_GYRO_ACCEL_CONFIG0_GYRO_FILT(_f) \
>
> > + FIELD_PREP(GENMASK(3, 0), (_f))
>
> > +
>
> > +#define INV_ICM42600_REG_TMST_CONFIG 0x0054
>
> > +#define INV_ICM42600_TMST_CONFIG_MASK GENMASK(4, 0)
>
> > +#define INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN BIT(4)
>
> > +#define INV_ICM42600_TMST_CONFIG_TMST_RES_16US BIT(3)
>
> > +#define INV_ICM42600_TMST_CONFIG_TMST_DELTA_EN BIT(2)
>
> > +#define INV_ICM42600_TMST_CONFIG_TMST_FSYNC_EN BIT(1)
>
> > +#define INV_ICM42600_TMST_CONFIG_TMST_EN BIT(0)
>
> > +
>
> > +#define INV_ICM42600_REG_FIFO_CONFIG1 0x005F
>
> > +#define INV_ICM42600_FIFO_CONFIG1_RESUME_PARTIAL_RD BIT(6)
>
> > +#define INV_ICM42600_FIFO_CONFIG1_WM_GT_TH BIT(5)
>
> > +#define INV_ICM42600_FIFO_CONFIG1_TMST_FSYNC_EN BIT(3)
>
> > +#define INV_ICM42600_FIFO_CONFIG1_TEMP_EN BIT(2)
>
> > +#define INV_ICM42600_FIFO_CONFIG1_GYRO_EN BIT(1)
>
> > +#define INV_ICM42600_FIFO_CONFIG1_ACCEL_EN BIT(0)
>
> > +
>
> > +/* FIFO watermark is 16 bits (2 registers wide) in little-endian */
>
> > +#define INV_ICM42600_REG_FIFO_WATERMARK 0x0060
>
> > +#define INV_ICM42600_FIFO_WATERMARK_VAL(_wm) \
>
> > + cpu_to_le16((_wm) & GENMASK(11, 0))
>
> > +/* FIFO is 2048 bytes, let 12 samples for reading latency */
>
> > +#define INV_ICM42600_FIFO_WATERMARK_MAX (2048 - 12 * 16)
>
> > +
>
> > +#define INV_ICM42600_REG_INT_CONFIG1 0x0064
>
> > +#define INV_ICM42600_INT_CONFIG1_TPULSE_DURATION BIT(6)
>
> > +#define INV_ICM42600_INT_CONFIG1_TDEASSERT_DISABLE BIT(5)
>
> > +#define INV_ICM42600_INT_CONFIG1_ASYNC_RESET BIT(4)
>
> > +
>
> > +#define INV_ICM42600_REG_INT_SOURCE0 0x0065
>
> > +#define INV_ICM42600_INT_SOURCE0_UI_FSYNC_INT1_EN BIT(6)
>
> > +#define INV_ICM42600_INT_SOURCE0_PLL_RDY_INT1_EN BIT(5)
>
> > +#define INV_ICM42600_INT_SOURCE0_RESET_DONE_INT1_EN BIT(4)
>
> > +#define INV_ICM42600_INT_SOURCE0_UI_DRDY_INT1_EN BIT(3)
>
> > +#define INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN BIT(2)
>
> > +#define INV_ICM42600_INT_SOURCE0_FIFO_FULL_INT1_EN BIT(1)
>
> > +#define INV_ICM42600_INT_SOURCE0_UI_AGC_RDY_INT1_EN BIT(0)
>
> > +
>
> > +#define INV_ICM42600_REG_WHOAMI 0x0075
>
> > +#define INV_ICM42600_WHOAMI_ICM42600 0x40
>
> > +#define INV_ICM42600_WHOAMI_ICM42602 0x41
>
> > +#define INV_ICM42600_WHOAMI_ICM42605 0x42
>
> > +#define INV_ICM42600_WHOAMI_ICM42622 0x46
>
> > +
>
> > +/* User bank 1 (MSB 0x10) */
>
> > +#define INV_ICM42600_REG_SENSOR_CONFIG0 0x1003
>
> > +#define INV_ICM42600_SENSOR_CONFIG0_ZG_DISABLE BIT(5)
>
> > +#define INV_ICM42600_SENSOR_CONFIG0_YG_DISABLE BIT(4)
>
> > +#define INV_ICM42600_SENSOR_CONFIG0_XG_DISABLE BIT(3)
>
> > +#define INV_ICM42600_SENSOR_CONFIG0_ZA_DISABLE BIT(2)
>
> > +#define INV_ICM42600_SENSOR_CONFIG0_YA_DISABLE BIT(1)
>
> > +#define INV_ICM42600_SENSOR_CONFIG0_XA_DISABLE BIT(0)
>
> > +
>
> > +/* Timestamp value is 20 bits (3 registers) in little-endian */
>
> > +#define INV_ICM42600_REG_TMSTVAL 0x1062
>
> > +#define INV_ICM42600_TMSTVAL_MASK GENMASK(19, 0)
>
> > +
>
> > +#define INV_ICM42600_REG_INTF_CONFIG4 0x107A
>
> > +#define INV_ICM42600_INTF_CONFIG4_I3C_BUS_ONLY BIT(6)
>
> > +#define INV_ICM42600_INTF_CONFIG4_SPI_AP_4WIRE BIT(1)
>
> > +
>
> > +#define INV_ICM42600_REG_INTF_CONFIG6 0x107C
>
> > +#define INV_ICM42600_INTF_CONFIG6_MASK GENMASK(4, 0)
>
> > +#define INV_ICM42600_INTF_CONFIG6_I3C_EN BIT(4)
>
> > +#define INV_ICM42600_INTF_CONFIG6_I3C_IBI_BYTE_EN BIT(3)
>
> > +#define INV_ICM42600_INTF_CONFIG6_I3C_IBI_EN BIT(2)
>
> > +#define INV_ICM42600_INTF_CONFIG6_I3C_DDR_EN BIT(1)
>
> > +#define INV_ICM42600_INTF_CONFIG6_I3C_SDR_EN BIT(0)
>
> > +
>
> > +/* User bank 4 (MSB 0x40) */
>
> > +#define INV_ICM42600_REG_INT_SOURCE8 0x404F
>
> > +#define INV_ICM42600_INT_SOURCE8_FSYNC_IBI_EN BIT(5)
>
> > +#define INV_ICM42600_INT_SOURCE8_PLL_RDY_IBI_EN BIT(4)
>
> > +#define INV_ICM42600_INT_SOURCE8_UI_DRDY_IBI_EN BIT(3)
>
> > +#define INV_ICM42600_INT_SOURCE8_FIFO_THS_IBI_EN BIT(2)
>
> > +#define INV_ICM42600_INT_SOURCE8_FIFO_FULL_IBI_EN BIT(1)
>
> > +#define INV_ICM42600_INT_SOURCE8_AGC_RDY_IBI_EN BIT(0)
>
> > +
>
> > +#define INV_ICM42600_REG_OFFSET_USER0 0x4077
>
> > +#define INV_ICM42600_REG_OFFSET_USER1 0x4078
>
> > +#define INV_ICM42600_REG_OFFSET_USER2 0x4079
>
> > +#define INV_ICM42600_REG_OFFSET_USER3 0x407A
>
> > +#define INV_ICM42600_REG_OFFSET_USER4 0x407B
>
> > +#define INV_ICM42600_REG_OFFSET_USER5 0x407C
>
> > +#define INV_ICM42600_REG_OFFSET_USER6 0x407D
>
> > +#define INV_ICM42600_REG_OFFSET_USER7 0x407E
>
> > +#define INV_ICM42600_REG_OFFSET_USER8 0x407F
>
> > +
>
> > +/* Sleep times required by the driver */
>
> > +#define INV_ICM42600_POWER_UP_TIME_MS 100
>
> > +#define INV_ICM42600_RESET_TIME_MS 1
>
> > +#define INV_ICM42600_ACCEL_STARTUP_TIME_MS 20
>
> > +#define INV_ICM42600_GYRO_STARTUP_TIME_MS 60
>
> > +#define INV_ICM42600_GYRO_STOP_TIME_MS 150
>
> > +#define INV_ICM42600_TEMP_STARTUP_TIME_MS 14
>
> > +#define INV_ICM42600_SUSPEND_DELAY_MS 2000
>
> > +
>
> > +typedef int (*inv_icm42600_bus_setup)(struct inv_icm42600_state *);
>
> > +
>
> > +extern const struct regmap_config inv_icm42600_regmap_config;
>
> > +extern const struct dev_pm_ops inv_icm42600_pm_ops;
>
> > +
>
> > +const struct iio_mount_matrix *
>
> > +inv_icm42600_get_mount_matrix(const struct iio_dev *indio_dev,
>
> > + const struct iio_chan_spec *chan);
>
> > +
>
> > +uint32_t inv_icm42600_odr_to_period(enum inv_icm42600_odr odr);
>
> > +
>
> > +int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st,
>
> > + struct inv_icm42600_sensor_conf *conf,
>
> > + unsigned int *sleep);
>
> > +
>
> > +int inv_icm42600_set_gyro_conf(struct inv_icm42600_state *st,
>
> > + struct inv_icm42600_sensor_conf *conf,
>
> > + unsigned int *sleep);
>
> > +
>
> > +int inv_icm42600_set_temp_conf(struct inv_icm42600_state *st, bool enable,
>
> > + unsigned int *sleep);
>
> > +
>
> > +int inv_icm42600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
>
> > + unsigned int writeval, unsigned int *readval);
>
> > +
>
> > +int inv_icm42600_core_probe(struct regmap *regmap, int chip,
>
> > + inv_icm42600_bus_setup bus_setup);
>
> > +
>
> > +#endif
>
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
>
> > new file mode 100644
>
> > index 000000000000..35bdf4f9d31e
>
> > --- /dev/null
>
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
>
> > @@ -0,0 +1,618 @@
>
> > +// SPDX-License-Identifier: GPL-2.0-or-later
>
> > +/*
>
> > + * Copyright (C) 2020 Invensense, Inc.
>
> > + */
>
> > +
>
> > +#include <linux/device.h>
>
> > +#include <linux/module.h>
>
> > +#include <linux/slab.h>
>
> > +#include <linux/delay.h>
>
> > +#include <linux/interrupt.h>
>
> > +#include <linux/regulator/consumer.h>
>
> > +#include <linux/pm_runtime.h>
>
> > +#include <linux/regmap.h>
>
> > +#include <linux/iio/iio.h>
>
> > +
>
> > +#include "inv_icm42600.h"
>
> > +
>
> > +static const struct regmap_range_cfg inv_icm42600_regmap_ranges[] = {
>
> > + {
>
> > + .name = "user banks",
>
> > + .range_min = 0x0000,
>
> > + .range_max = 0x4FFF,
>
> > + .selector_reg = INV_ICM42600_REG_BANK_SEL,
>
> > + .selector_mask = INV_ICM42600_BANK_SEL_MASK,
>
> > + .selector_shift = 0,
>
> > + .window_start = 0,
>
> > + .window_len = 0x1000,
>
> > + },
>
> > +};
>
> > +
>
> > +const struct regmap_config inv_icm42600_regmap_config = {
>
> > + .reg_bits = 8,
>
> > + .val_bits = 8,
>
> > + .max_register = 0x4FFF,
>
> > + .ranges = inv_icm42600_regmap_ranges,
>
> > + .num_ranges = ARRAY_SIZE(inv_icm42600_regmap_ranges),
>
> > +};
>
> > +EXPORT_SYMBOL_GPL(inv_icm42600_regmap_config);
>
> > +
>
> > +struct inv_icm42600_hw {
>
> > + uint8_t whoami;
>
> > + const char *name;
>
> > + const struct inv_icm42600_conf *conf;
>
> > +};
>
> > +
>
> > +/* chip initial default configuration */
>
> > +static const struct inv_icm42600_conf inv_icm42600_default_conf = {
>
> > + .gyro = {
>
> > + .mode = INV_ICM42600_SENSOR_MODE_OFF,
>
> > + .fs = INV_ICM42600_GYRO_FS_2000DPS,
>
> > + .odr = INV_ICM42600_ODR_50HZ,
>
> > + .filter = INV_ICM42600_FILTER_BW_ODR_DIV_2,
>
> > + },
>
> > + .accel = {
>
> > + .mode = INV_ICM42600_SENSOR_MODE_OFF,
>
> > + .fs = INV_ICM42600_ACCEL_FS_16G,
>
> > + .odr = INV_ICM42600_ODR_50HZ,
>
> > + .filter = INV_ICM42600_FILTER_BW_ODR_DIV_2,
>
> > + },
>
> > + .temp_en = false,
>
> > +};
>
> > +
>
> > +static const struct inv_icm42600_hw inv_icm42600_hw[] = {
>
> > + [INV_CHIP_ICM42600] = {
>
> > + .whoami = INV_ICM42600_WHOAMI_ICM42600,
>
> > + .name = "icm42600",
>
> > + .conf = &inv_icm42600_default_conf,
>
> > + },
>
> > + [INV_CHIP_ICM42602] = {
>
> > + .whoami = INV_ICM42600_WHOAMI_ICM42602,
>
> > + .name = "icm42602",
>
> > + .conf = &inv_icm42600_default_conf,
>
> > + },
>
> > + [INV_CHIP_ICM42605] = {
>
> > + .whoami = INV_ICM42600_WHOAMI_ICM42605,
>
> > + .name = "icm42605",
>
> > + .conf = &inv_icm42600_default_conf,
>
> > + },
>
> > + [INV_CHIP_ICM42622] = {
>
> > + .whoami = INV_ICM42600_WHOAMI_ICM42622,
>
> > + .name = "icm42622",
>
> > + .conf = &inv_icm42600_default_conf,
>
> > + },
>
> > +};
>
> > +
>
> > +const struct iio_mount_matrix *
>
> > +inv_icm42600_get_mount_matrix(const struct iio_dev *indio_dev,
>
> > + const struct iio_chan_spec *chan)
>
> > +{
>
> > + const struct inv_icm42600_state *st =
>
> > + iio_device_get_drvdata((struct iio_dev *)indio_dev);
>
>
>
> Interesting... iio_device_get_drvdata is never going to modify
>
> the struct iio_dev. Should we just change that to take a
>
> const struct iio_dev * ?
>
>
>
> > +
>
> > + return &st->orientation;
>
> > +}
>
> > +
>
> > +uint32_t inv_icm42600_odr_to_period(enum inv_icm42600_odr odr)
>
> > +{
>
> > + static uint32_t odr_periods[INV_ICM42600_ODR_NB] = {
>
> > + /* reserved values */
>
> > + 0, 0, 0,
>
> > + /* 8kHz */
>
> > + 125000,
>
> > + /* 4kHz */
>
> > + 250000,
>
> > + /* 2kHz */
>
> > + 500000,
>
> > + /* 1kHz */
>
> > + 1000000,
>
> > + /* 200Hz */
>
> > + 5000000,
>
> > + /* 100Hz */
>
> > + 10000000,
>
> > + /* 50Hz */
>
> > + 20000000,
>
> > + /* 25Hz */
>
> > + 40000000,
>
> > + /* 12.5Hz */
>
> > + 80000000,
>
> > + /* 6.25Hz */
>
> > + 160000000,
>
> > + /* 3.125Hz */
>
> > + 320000000,
>
> > + /* 1.5625Hz */
>
> > + 640000000,
>
> > + /* 500Hz */
>
> > + 2000000,
>
> > + };
>
> > +
>
> > + return odr_periods[odr];
>
> > +}
>
> > +
>
> > +static int inv_icm42600_set_pwr_mgmt0(struct inv_icm42600_state *st,
>
> > + enum inv_icm42600_sensor_mode gyro,
>
> > + enum inv_icm42600_sensor_mode accel,
>
> > + bool temp, unsigned int *sleep)
>
>
>
> msleep or similar that indicates the units of the sleep time.
>
>
>
> > +{
>
> > + enum inv_icm42600_sensor_mode oldgyro = st->conf.gyro.mode;
>
> > + enum inv_icm42600_sensor_mode oldaccel = st->conf.accel.mode;
>
> > + bool oldtemp = st->conf.temp_en;
>
> > + unsigned int sleepval;
>
> > + unsigned int val;
>
> > + int ret;
>
> > +
>
> > + /* if nothing changed, exit */
>
> > + if (gyro == oldgyro && accel == oldaccel && temp == oldtemp)
>
> > + return 0;
>
> > +
>
> > + val = INV_ICM42600_PWR_MGMT0_GYRO(gyro) |
>
> > + INV_ICM42600_PWR_MGMT0_ACCEL(accel);
>
> > + if (!temp)
>
> > + val |= INV_ICM42600_PWR_MGMT0_TEMP_DIS;
>
> > + dev_dbg(regmap_get_device(st->map), "pwr_mgmt0: %#02x\n", val);
>
>
>
> I wonder if you have a little too much in the way of debug prints.
>
> These are internal to the code and so could only be wrong due to a local
>
> bug. Once you've finished writing the driver I'd hope we won't need these!
>
>
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_PWR_MGMT0, val);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + st->conf.gyro.mode = gyro;
>
> > + st->conf.accel.mode = accel;
>
> > + st->conf.temp_en = temp;
>
> > +
>
> > + /* compute required wait time for sensors to stabilize */
>
> > + sleepval = 0;
>
> > + /* temperature stabilization time */
>
> > + if (temp && !oldtemp) {
>
> > + if (sleepval < INV_ICM42600_TEMP_STARTUP_TIME_MS)
>
> > + sleepval = INV_ICM42600_TEMP_STARTUP_TIME_MS;
>
> > + }
>
> > + /* accel startup time */
>
> > + if (accel != oldaccel && oldaccel == INV_ICM42600_SENSOR_MODE_OFF) {
>
> > + /* block any register write for at least 200 µs */
>
> > + usleep_range(200, 300);
>
> > + if (sleepval < INV_ICM42600_ACCEL_STARTUP_TIME_MS)
>
> > + sleepval = INV_ICM42600_ACCEL_STARTUP_TIME_MS;
>
> > + }
>
> > + if (gyro != oldgyro) {
>
> > + /* gyro startup time */
>
> > + if (oldgyro == INV_ICM42600_SENSOR_MODE_OFF) {
>
> > + /* block any register write for at least 200 µs */
>
> > + usleep_range(200, 300);
>
> > + if (sleepval < INV_ICM42600_GYRO_STARTUP_TIME_MS)
>
> > + sleepval = INV_ICM42600_GYRO_STARTUP_TIME_MS;
>
> > + /* gyro stop time */
>
> > + } else if (gyro == INV_ICM42600_SENSOR_MODE_OFF) {
>
> > + if (sleepval < INV_ICM42600_GYRO_STOP_TIME_MS)
>
> > + sleepval = INV_ICM42600_GYRO_STOP_TIME_MS;
>
> > + }
>
> > + }
>
> > +
>
> > + /* deferred sleep value if sleep pointer is provided or direct sleep */
>
> > + if (sleep)
>
> > + *sleep = sleepval;
>
> > + else if (sleepval)
>
> > + msleep(sleepval);
>
> > +
>
> > + return 0;
>
> > +}
>
> > +
>
> > +int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st,
>
> > + struct inv_icm42600_sensor_conf *conf,
>
> > + unsigned int *sleep)
>
> > +{
>
> > + struct inv_icm42600_sensor_conf *oldconf = &st->conf.accel;
>
> > + unsigned int val;
>
> > + int ret;
>
> > +
>
> > + /* Sanitize missing values with current values */
>
> > + if (conf->mode < 0)
>
> > + conf->mode = oldconf->mode;
>
> > + if (conf->fs < 0)
>
> > + conf->fs = oldconf->fs;
>
> > + if (conf->odr < 0)
>
> > + conf->odr = oldconf->odr;
>
> > + if (conf->filter < 0)
>
> > + conf->filter = oldconf->filter;
>
> > +
>
> > + /* set ACCEL_CONFIG0 register (accel fullscale & odr) */
>
> > + if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
>
> > + val = INV_ICM42600_ACCEL_CONFIG0_FS(conf->fs) |
>
> > + INV_ICM42600_ACCEL_CONFIG0_ODR(conf->odr);
>
> > + dev_dbg(regmap_get_device(st->map), "accel_config0: %#02x\n", val);
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_ACCEL_CONFIG0, val);
>
> > + if (ret)
>
> > + return ret;
>
> > + oldconf->fs = conf->fs;
>
> > + oldconf->odr = conf->odr;
>
> > + }
>
> > +
>
> > + /* set GYRO_ACCEL_CONFIG0 register (accel filter) */
>
> > + if (conf->filter != oldconf->filter) {
>
> > + val = INV_ICM42600_GYRO_ACCEL_CONFIG0_ACCEL_FILT(conf->filter) |
>
> > + INV_ICM42600_GYRO_ACCEL_CONFIG0_GYRO_FILT(st->conf.gyro.filter);
>
> > + dev_dbg(regmap_get_device(st->map), "gyro_accel_config0: %#02x\n", val);
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_ACCEL_CONFIG0, val);
>
> > + if (ret)
>
> > + return ret;
>
> > + oldconf->filter = conf->filter;
>
> > + }
>
> > +
>
> > + /* set PWR_MGMT0 register (accel sensor mode) */
>
> > + return inv_icm42600_set_pwr_mgmt0(st, st->conf.gyro.mode, conf->mode,
>
> > + st->conf.temp_en, sleep);
>
> > +}
>
> > +
>
> > +int inv_icm42600_set_gyro_conf(struct inv_icm42600_state *st,
>
> > + struct inv_icm42600_sensor_conf *conf,
>
> > + unsigned int *sleep)
>
> > +{
>
> > + struct inv_icm42600_sensor_conf *oldconf = &st->conf.gyro;
>
> > + unsigned int val;
>
> > + int ret;
>
> > +
>
> > + /* sanitize missing values with current values */
>
> > + if (conf->mode < 0)
>
> > + conf->mode = oldconf->mode;
>
> > + if (conf->fs < 0)
>
> > + conf->fs = oldconf->fs;
>
> > + if (conf->odr < 0)
>
> > + conf->odr = oldconf->odr;
>
> > + if (conf->filter < 0)
>
> > + conf->filter = oldconf->filter;
>
> > +
>
> > + /* set GYRO_CONFIG0 register (gyro fullscale & odr) */
>
> > + if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
>
> > + val = INV_ICM42600_GYRO_CONFIG0_FS(conf->fs) |
>
> > + INV_ICM42600_GYRO_CONFIG0_ODR(conf->odr);
>
> > + dev_dbg(regmap_get_device(st->map), "gyro_config0: %#02x\n", val);
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_CONFIG0, val);
>
> > + if (ret)
>
> > + return ret;
>
> > + oldconf->fs = conf->fs;
>
> > + oldconf->odr = conf->odr;
>
> > + }
>
> > +
>
> > + /* set GYRO_ACCEL_CONFIG0 register (gyro filter) */
>
> > + if (conf->filter != oldconf->filter) {
>
> > + val = INV_ICM42600_GYRO_ACCEL_CONFIG0_ACCEL_FILT(st->conf.accel.filter) |
>
> > + INV_ICM42600_GYRO_ACCEL_CONFIG0_GYRO_FILT(conf->filter);
>
> > + dev_dbg(regmap_get_device(st->map), "gyro_accel_config0: %#02x\n", val);
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_ACCEL_CONFIG0, val);
>
> > + if (ret)
>
> > + return ret;
>
> > + oldconf->filter = conf->filter;
>
> > + }
>
> > +
>
> > + /* set PWR_MGMT0 register (gyro sensor mode) */
>
> > + return inv_icm42600_set_pwr_mgmt0(st, conf->mode, st->conf.accel.mode,
>
> > + st->conf.temp_en, sleep);
>
> > +
>
> > + return 0;
>
> > +}
>
> > +
>
> > +int inv_icm42600_set_temp_conf(struct inv_icm42600_state *st, bool enable,
>
> > + unsigned int *sleep)
>
> > +{
>
> > + return inv_icm42600_set_pwr_mgmt0(st, st->conf.gyro.mode,
>
> > + st->conf.accel.mode, enable,
>
> > + sleep);
>
> > +}
>
> > +
>
> > +int inv_icm42600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
>
> > + unsigned int writeval, unsigned int *readval)
>
> > +{
>
> > + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
>
> > + int ret;
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
> > + if (readval)
>
> > + ret = regmap_read(st->map, reg, readval);
>
> > + else
>
> > + ret = regmap_write(st->map, reg, writeval);
>
> > +
>
> > + mutex_unlock(&st->lock);
>
> > +
>
> > + return ret;
>
> > +}
>
> > +
>
> > +static int inv_icm42600_set_conf(struct inv_icm42600_state *st,
>
> > + const struct inv_icm42600_conf *conf)
>
> > +{
>
> > + unsigned int val;
>
> > + int ret;
>
> > +
>
> > + /* set PWR_MGMT0 register (gyro & accel sensor mode, temp enabled) */
>
> > + val = INV_ICM42600_PWR_MGMT0_GYRO(conf->gyro.mode) |
>
> > + INV_ICM42600_PWR_MGMT0_ACCEL(conf->accel.mode);
>
> > + if (!conf->temp_en)
>
> > + val |= INV_ICM42600_PWR_MGMT0_TEMP_DIS;
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_PWR_MGMT0, val);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* set GYRO_CONFIG0 register (gyro fullscale & odr) */
>
> > + val = INV_ICM42600_GYRO_CONFIG0_FS(conf->gyro.fs) |
>
> > + INV_ICM42600_GYRO_CONFIG0_ODR(conf->gyro.odr);
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_CONFIG0, val);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* set ACCEL_CONFIG0 register (accel fullscale & odr) */
>
> > + val = INV_ICM42600_ACCEL_CONFIG0_FS(conf->accel.fs) |
>
> > + INV_ICM42600_ACCEL_CONFIG0_ODR(conf->accel.odr);
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_ACCEL_CONFIG0, val);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* set GYRO_ACCEL_CONFIG0 register (gyro & accel filters) */
>
> > + val = INV_ICM42600_GYRO_ACCEL_CONFIG0_ACCEL_FILT(conf->accel.filter) |
>
> > + INV_ICM42600_GYRO_ACCEL_CONFIG0_GYRO_FILT(conf->gyro.filter);
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_ACCEL_CONFIG0, val);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* update internal conf */
>
> > + st->conf = *conf;
>
> > +
>
> > + return 0;
>
> > +}
>
> > +
>
> > +/**
>
> > + * inv_icm42600_setup() - check and setup chip.
>
>
>
> If doing kernel-doc (which is good) you should do it all.
>
> So document the parameters as well.
>
> It's worth running the kernel-doc script over any file where
>
> you put some and fixing up any warnings / errors.
>
>
>
> > + */
>
> > +static int inv_icm42600_setup(struct inv_icm42600_state *st,
>
> > + inv_icm42600_bus_setup bus_setup)
>
> > +{
>
> > + const struct inv_icm42600_hw *hw = &inv_icm42600_hw[st->chip];
>
> > + const struct device *dev = regmap_get_device(st->map);
>
> > + unsigned int mask, val;
>
> > + int ret;
>
> > +
>
> > + /* check chip self-identification value */
>
> > + ret = regmap_read(st->map, INV_ICM42600_REG_WHOAMI, &val);
>
> > + if (ret)
>
> > + return ret;
>
> > + if (val != hw->whoami) {
>
> > + dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n",
>
> > + val, hw->whoami, hw->name);
>
> > + return -ENODEV;
>
> > + }
>
> > + dev_info(dev, "found %s (%#02x)\n", hw->name, hw->whoami);
>
>
>
> Hmm. I'm never that keen on this sort of log noise. Why do you need it
>
> except for initial debug?
>
>
>
> > + st->name = hw->name;
>
> > +
>
> > + /* reset to make sure previous state are not there */
>
> > + ret = regmap_write(st->map, INV_ICM42600_REG_DEVICE_CONFIG,
>
> > + INV_ICM42600_DEVICE_CONFIG_SOFT_RESET);
>
> > + if (ret)
>
> > + return ret;
>
> > + msleep(INV_ICM42600_RESET_TIME_MS);
>
>
>
> blank line here to separate two logical blocks of code.
>
> Slightly helps readability.
>
>
>
> > + ret = regmap_read(st->map, INV_ICM42600_REG_INT_STATUS, &val);
>
> > + if (ret)
>
> > + return ret;
>
> > + if (!(val & INV_ICM42600_INT_STATUS_RESET_DONE)) {
>
> > + dev_err(dev, "reset error, reset done bit not set\n");
>
> > + return -ENODEV;
>
> > + }
>
> > +
>
> > + /* set chip bus configuration */
>
> > + ret = bus_setup(st);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* sensor data in big-endian (default) */
>
> > + mask = INV_ICM42600_INTF_CONFIG0_SENSOR_DATA_ENDIAN;
>
> > + val = INV_ICM42600_INTF_CONFIG0_SENSOR_DATA_ENDIAN;
>
> > + ret = regmap_update_bits(st->map, INV_ICM42600_REG_INTF_CONFIG0,
>
> > + mask, val);
>
>
>
> Long line, but I'd rather you just didn't bother will local variables
>
> in cases like this where you just set them to a constant.
>
> Take the 80 chars thing as guidance not a rule :)
>
>
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + return inv_icm42600_set_conf(st, hw->conf);
>
> > +}
>
> > +
>
> > +static int inv_icm42600_enable_regulator_vddio(struct inv_icm42600_state *st)
>
> > +{
>
> > + int ret;
>
> > +
>
> > + ret = regulator_enable(st->vddio_supply);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* wait a little for supply ramp */
>
> > + usleep_range(3000, 4000);
>
> > +
>
> > + return 0;
>
> > +}
>
> > +
>
> > +static void inv_icm42600_disable_regulators(void *_data)
>
> > +{
>
> > + struct inv_icm42600_state *st = _data;
>
> > + const struct device *dev = regmap_get_device(st->map);
>
> > + int ret;
>
> > +
>
> > + ret = regulator_disable(st->vddio_supply);
>
> > + if (ret)
>
> > + dev_err(dev, "failed to disable vddio error %d\n", ret);
>
> > +
>
> > + ret = regulator_disable(st->vdd_supply);
>
> > + if (ret)
>
> > + dev_err(dev, "failed to disable vdd error %d\n", ret);
>
> > +}
>
> > +
>
> > +static void inv_icm42600_disable_pm(void *_data)
>
> > +{
>
> > + struct device *dev = _data;
>
> > +
>
> > + pm_runtime_put_sync(dev);
>
> > + pm_runtime_disable(dev);
>
> > +}
>
> > +
>
> > +int inv_icm42600_core_probe(struct regmap *regmap, int chip,
>
> > + inv_icm42600_bus_setup bus_setup)
>
> > +{
>
> > + struct device *dev = regmap_get_device(regmap);
>
> > + struct inv_icm42600_state *st;
>
> > + int ret;
>
> > +
>
> > + BUILD_BUG_ON(ARRAY_SIZE(inv_icm42600_hw) != INV_CHIP_NB);
>
>
>
> Why not just give the array an explicit size when you define it above?
>
> I guess it would in theory be possible to not instantiate all of the array
>
> but relying on different size of a variable length array seems less than
>
> ideal.
>
>
>
> > + if (chip < 0 || chip >= INV_CHIP_NB) {
>
> > + dev_err(dev, "invalid chip = %d\n", chip);
>
> > + return -ENODEV;
>
> > + }
>
> > +
>
> > + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
>
> > + if (!st)
>
> > + return -ENOMEM;
>
> nitpick: blank line here.
>
>
>
> > + dev_set_drvdata(dev, st);
>
> > + mutex_init(&st->lock);
>
> > + st->chip = chip;
>
> > + st->map = regmap;
>
> > +
>
> > + ret = iio_read_mount_matrix(dev, "mount-matrix", &st->orientation);
>
> > + if (ret) {
>
> > + dev_err(dev, "failed to retrieve mounting matrix %d\n", ret);
>
> > + return ret;
>
> > + }
>
> > +
>
> > + st->vdd_supply = devm_regulator_get(dev, "vdd");
>
> > + if (IS_ERR(st->vdd_supply))
>
> > + return PTR_ERR(st->vdd_supply);
>
> > +
>
> > + st->vddio_supply = devm_regulator_get(dev, "vddio");
>
> > + if (IS_ERR(st->vddio_supply))
>
> > + return PTR_ERR(st->vddio_supply);
>
> > +
>
> > + ret = regulator_enable(st->vdd_supply);
>
> > + if (ret)
>
> > + return ret;
>
> > + msleep(INV_ICM42600_POWER_UP_TIME_MS);
>
> > +
>
> > + ret = inv_icm42600_enable_regulator_vddio(st);
>
> > + if (ret) {
>
> > + regulator_disable(st->vdd_supply);
>
> > + return ret;
>
> > + }
>
> > +
>
> > + ret = devm_add_action_or_reset(dev, inv_icm42600_disable_regulators,
>
> > + st);
>
>
>
> I'd prefer to see two devm_add_action_or_reset calls. One for each regulator.
>
> That means you don't have to do the extra disable logic above which is
>
> a bit fragile in amongst a whole load of device managed calls.
>
>
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* setup chip registers */
>
> > + ret = inv_icm42600_setup(st, bus_setup);
>
> > + if (ret)
>
> > + return ret;
>
> > +
>
> > + /* setup runtime power management */
>
> > + ret = pm_runtime_set_active(dev);
>
> > + if (ret)
>
> > + return ret;
>
> > + pm_runtime_get_noresume(dev);
>
> > + pm_runtime_enable(dev);
>
> > + pm_runtime_set_autosuspend_delay(dev, INV_ICM42600_SUSPEND_DELAY_MS);
>
> > + pm_runtime_use_autosuspend(dev);
>
> > + pm_runtime_put(dev);
>
> > +
>
> > + return devm_add_action_or_reset(dev, inv_icm42600_disable_pm, dev);
>
> > +}
>
> > +EXPORT_SYMBOL_GPL(inv_icm42600_core_probe);
>
> > +
>
> > +static int __maybe_unused inv_icm42600_suspend(struct device *dev)
>
> > +{
>
> > + struct inv_icm42600_state *st = dev_get_drvdata(dev);
>
> > + int ret;
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
> > + st->suspended.gyro = st->conf.gyro.mode;
>
> > + st->suspended.accel = st->conf.accel.mode;
>
> > + st->suspended.temp = st->conf.temp_en;
>
> > + if (pm_runtime_suspended(dev)) {
>
> > + ret = 0;
>
> > + goto out_unlock;
>
> > + }
>
> > +
>
> > + ret = inv_icm42600_set_pwr_mgmt0(st, INV_ICM42600_SENSOR_MODE_OFF,
>
> > + INV_ICM42600_SENSOR_MODE_OFF, false,
>
> > + NULL);
>
> > + if (ret)
>
> > + goto out_unlock;
>
> > +
>
> > + regulator_disable(st->vddio_supply);
>
> > +
>
> > +out_unlock:
>
> > + mutex_unlock(&st->lock);
>
> > + return ret;
>
> > +}
>
> > +
>
> > +static int __maybe_unused inv_icm42600_resume(struct device *dev)
>
> > +{
>
> > + struct inv_icm42600_state *st = dev_get_drvdata(dev);
>
> > + int ret;
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
> > + ret = inv_icm42600_enable_regulator_vddio(st);
>
> > + if (ret)
>
> > + goto out_unlock;
>
> > +
>
> > + pm_runtime_disable(dev);
>
> > + pm_runtime_set_active(dev);
>
> > + pm_runtime_enable(dev);
>
> > +
>
> > + /* restore sensors state */
>
> > + ret = inv_icm42600_set_pwr_mgmt0(st, st->suspended.gyro,
>
> > + st->suspended.accel,
>
> > + st->suspended.temp, NULL);
>
> > + if (ret)
>
> > + goto out_unlock;
>
>
>
> You may need this later, but for now it's a bit comic so ideally introduce
>
> it only when needed.
>
>
>
> > +
>
> > +out_unlock:
>
> > + mutex_unlock(&st->lock);
>
> > + return ret;
>
> > +}
>
> > +
>
> > +static int __maybe_unused inv_icm42600_runtime_suspend(struct device *dev)
>
> > +{
>
> > + struct inv_icm42600_state *st = dev_get_drvdata(dev);
>
> > + int ret;
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
> > + /* disable all sensors */
>
> > + ret = inv_icm42600_set_pwr_mgmt0(st, INV_ICM42600_SENSOR_MODE_OFF,
>
> > + INV_ICM42600_SENSOR_MODE_OFF, false,
>
> > + NULL);
>
> > + if (ret)
>
> > + goto error_unlock;
>
> > +
>
> > + regulator_disable(st->vddio_supply);
>
> > +
>
> > +error_unlock:
>
> > + mutex_unlock(&st->lock);
>
> > + return ret;
>
> > +}
>
> > +
>
> > +static int __maybe_unused inv_icm42600_runtime_resume(struct device *dev)
>
> > +{
>
> > + struct inv_icm42600_state *st = dev_get_drvdata(dev);
>
> > + int ret;
>
> > +
>
> > + mutex_lock(&st->lock);
>
> > +
>
>
>
> Why don't we need to reenable all the sensors we disabled in runtime suspend?
>
> I can guess why we might not, but a comment here to explain would save on
>
> possible confusion..
>
>
>
> > + ret = inv_icm42600_enable_regulator_vddio(st);
>
> > +
>
> > + mutex_unlock(&st->lock);
>
> > + return ret;
>
> > +}
>
> > +
>
> > +const struct dev_pm_ops inv_icm42600_pm_ops = {
>
> > + SET_SYSTEM_SLEEP_PM_OPS(inv_icm42600_suspend, inv_icm42600_resume)
>
> > + SET_RUNTIME_PM_OPS(inv_icm42600_runtime_suspend,
>
> > + inv_icm42600_runtime_resume, NULL)
>
> > +};
>
> > +EXPORT_SYMBOL_GPL(inv_icm42600_pm_ops);
>
> > +
>
> > +MODULE_AUTHOR("InvenSense, Inc.");
>
> > +MODULE_DESCRIPTION("InvenSense ICM-426xx device driver");
>
> > +MODULE_LICENSE("GPL");
>
>
>
>
>
^ permalink raw reply
* [RFC PATCH net-next 1/4] dt-bindings: net: Add tx and rx internal delays
From: Dan Murphy @ 2020-05-21 17:48 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem, robh
Cc: netdev, linux-kernel, devicetree, Dan Murphy
In-Reply-To: <20200521174834.3234-1-dmurphy@ti.com>
tx-internal-delays and rx-internal-delays are a common setting for RGMII
capable devices.
These properties are used when the phy-mode or phy-controller is set to
rgmii-id, rgmii-rxid or rgmii-txid. These modes indicate to the
controller that the PHY will add the internal delay for the connection.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
.../bindings/net/ethernet-controller.yaml | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
index ac471b60ed6a..3f25066c339c 100644
--- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml
+++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
@@ -143,6 +143,20 @@ properties:
Specifies the PHY management type. If auto is set and fixed-link
is not specified, it uses MDIO for management.
+ rx-internal-delay:
+ $ref: /schemas/types.yaml#definitions/uint32
+ description: |
+ RGMII Receive PHY Clock Delay defined in pico seconds. This is used for
+ PHY's that have configurable RX internal delays. This property is only
+ used when the phy-mode or phy-connection-type is rgmii-id or rgmii-rxid.
+
+ tx-internal-delay:
+ $ref: /schemas/types.yaml#definitions/uint32
+ description: |
+ RGMII Transmit PHY Clock Delay defined in pico seconds. This is used for
+ PHY's that have configurable TX internal delays. This property is only
+ used when the phy-mode or phy-connection-type is rgmii-id or rgmii-txid.
+
fixed-link:
allOf:
- if:
--
2.26.2
^ permalink raw reply related
* [RFC PATCH net-next 3/4] dt-bindings: net: Add RGMII internal delay for DP83869
From: Dan Murphy @ 2020-05-21 17:48 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem, robh
Cc: netdev, linux-kernel, devicetree, Dan Murphy
In-Reply-To: <20200521174834.3234-1-dmurphy@ti.com>
Add the internal delay values into the header and update the binding
with the internal delay properties.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
.../devicetree/bindings/net/ti,dp83869.yaml | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/ti,dp83869.yaml b/Documentation/devicetree/bindings/net/ti,dp83869.yaml
index 5b69ef03bbf7..b2547b43e103 100644
--- a/Documentation/devicetree/bindings/net/ti,dp83869.yaml
+++ b/Documentation/devicetree/bindings/net/ti,dp83869.yaml
@@ -64,6 +64,20 @@ properties:
Operational mode for the PHY. If this is not set then the operational
mode is set by the straps. see dt-bindings/net/ti-dp83869.h for values
+ rx-internal-delay:
+ $ref: "#/properties/rx-internal-delay"
+ description: Delay is in pico seconds
+ enum: [ 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000,
+ 3250, 3500, 3750, 4000 ]
+ default: 2000
+
+ tx-internal-delay:
+ $ref: "#/properties/tx-internal-delay"
+ description: Delay is in pico seconds
+ enum: [ 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000,
+ 3250, 3500, 3750, 4000 ]
+ default: 2000
+
required:
- reg
@@ -80,5 +94,7 @@ examples:
ti,op-mode = <DP83869_RGMII_COPPER_ETHERNET>;
ti,max-output-impedance = "true";
ti,clk-output-sel = <DP83869_CLK_O_SEL_CHN_A_RCLK>;
+ rx-internal-delay = <2000>;
+ tx-internal-delay = <2000>;
};
};
--
2.26.2
^ permalink raw reply related
* [RFC PATCH net-next 0/4] RGMII Internal delay common property
From: Dan Murphy @ 2020-05-21 17:48 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem, robh
Cc: netdev, linux-kernel, devicetree, Dan Murphy
Hello
The RGMII internal delay is a common setting found in most RGMII capable PHY
devices. It was found that many vendor specific device tree properties exist
to do the same function. This creates a common property to be used for PHY's
that have tunable internal delays for the Rx and Tx paths.
Dan Murphy (4):
dt-bindings: net: Add tx and rx internal delays
net: phy: Add a helper to return the index for of the internal delay
dt-bindings: net: Add RGMII internal delay for DP83869
net: dp83869: Add RGMII internal delay configuration
.../bindings/net/ethernet-controller.yaml | 14 +++
.../devicetree/bindings/net/ti,dp83869.yaml | 16 +++
drivers/net/phy/dp83869.c | 101 ++++++++++++++++++
drivers/net/phy/phy_device.c | 45 ++++++++
include/linux/phy.h | 2 +
5 files changed, 178 insertions(+)
--
2.26.2
^ permalink raw reply
* [RFC PATCH net-next 2/4] net: phy: Add a helper to return the index for of the internal delay
From: Dan Murphy @ 2020-05-21 17:48 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem, robh
Cc: netdev, linux-kernel, devicetree, Dan Murphy
In-Reply-To: <20200521174834.3234-1-dmurphy@ti.com>
Add a helper function that will return the index in the array for the
passed in internal delay value. The helper requires the array, size and
delay value.
The helper will then return the index for the exact match or return the
index for the index to the closest smaller value.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
drivers/net/phy/phy_device.c | 45 ++++++++++++++++++++++++++++++++++++
include/linux/phy.h | 2 ++
2 files changed, 47 insertions(+)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 7481135d27ab..40f53b379d2b 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -2661,6 +2661,51 @@ void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause)
}
EXPORT_SYMBOL(phy_get_pause);
+/**
+ * phy_get_delay_index - returns the index of the internal delay
+ * @phydev: phy_device struct
+ * @delay_values: array of delays the PHY supports
+ * @size: the size of the delay array
+ * @delay: the delay to be looked up
+ *
+ * Returns the index within the array of internal delay passed in.
+ */
+int phy_get_delay_index(struct phy_device *phydev, int *delay_values, int size,
+ int delay)
+{
+ int i;
+
+ if (size <= 0)
+ return -EINVAL;
+
+ if (delay <= delay_values[0])
+ return 0;
+
+ if (delay > delay_values[size - 1])
+ return size - 1;
+
+ for (i = 0; i < size; i++) {
+ if (delay == delay_values[i])
+ return i;
+
+ /* Find an approximate index by looking up the table */
+ if (delay > delay_values[i - 1] &&
+ delay < delay_values[i]) {
+ if (delay - delay_values[i - 1] < delay_values[i] - delay)
+ return i - 1;
+ else
+ return i;
+ }
+
+ }
+
+ phydev_err(phydev, "error finding internal delay index for %d\n",
+ delay);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(phy_get_delay_index);
+
static bool phy_drv_supports_irq(struct phy_driver *phydrv)
{
return phydrv->config_intr && phydrv->ack_interrupt;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 467aa8bf9f64..78b7b77f9a67 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1406,6 +1406,8 @@ void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx);
bool phy_validate_pause(struct phy_device *phydev,
struct ethtool_pauseparam *pp);
void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause);
+int phy_get_delay_index(struct phy_device *phydev, int *delay_values,
+ int size, int delay);
void phy_resolve_pause(unsigned long *local_adv, unsigned long *partner_adv,
bool *tx_pause, bool *rx_pause);
--
2.26.2
^ permalink raw reply related
* [RFC PATCH net-next 4/4] net: dp83869: Add RGMII internal delay configuration
From: Dan Murphy @ 2020-05-21 17:48 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem, robh
Cc: netdev, linux-kernel, devicetree, Dan Murphy
In-Reply-To: <20200521174834.3234-1-dmurphy@ti.com>
Add RGMII internal delay configuration for Rx and Tx.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
drivers/net/phy/dp83869.c | 101 ++++++++++++++++++++++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c
index cfb22a21a2e6..40c34fefffe4 100644
--- a/drivers/net/phy/dp83869.c
+++ b/drivers/net/phy/dp83869.c
@@ -99,6 +99,14 @@
#define DP83869_OP_MODE_MII BIT(5)
#define DP83869_SGMII_RGMII_BRIDGE BIT(6)
+/* RGMIIDCTL bits */
+#define DP83869_RGMII_TX_CLK_DELAY_SHIFT 4
+#define DP83869_RGMII_CLK_DELAY_INV 0
+
+static int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500, 1750,
+ 2000, 2250, 2500, 2750, 3000, 3250,
+ 3500, 3750, 4000};
+
enum {
DP83869_PORT_MIRRORING_KEEP,
DP83869_PORT_MIRRORING_EN,
@@ -108,6 +116,8 @@ enum {
struct dp83869_private {
int tx_fifo_depth;
int rx_fifo_depth;
+ u32 rx_id_delay;
+ u32 tx_id_delay;
int io_impedance;
int port_mirroring;
bool rxctrl_strap_quirk;
@@ -182,6 +192,7 @@ static int dp83869_of_init(struct phy_device *phydev)
struct dp83869_private *dp83869 = phydev->priv;
struct device *dev = &phydev->mdio.dev;
struct device_node *of_node = dev->of_node;
+ int delay_size = ARRAY_SIZE(dp83869_internal_delay);
int ret;
if (!of_node)
@@ -232,6 +243,26 @@ static int dp83869_of_init(struct phy_device *phydev)
&dp83869->tx_fifo_depth))
dp83869->tx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB;
+ dp83869->rx_id_delay = DP83869_RGMII_CLK_DELAY_INV;
+ ret = of_property_read_u32(of_node, "rx-internal-delay",
+ &dp83869->rx_id_delay);
+ if (!ret && dp83869->rx_id_delay > dp83869_internal_delay[delay_size]) {
+ phydev_err(phydev,
+ "rx-internal-delay value of %u out of range\n",
+ dp83869->rx_id_delay);
+ return -EINVAL;
+ }
+
+ dp83869->tx_id_delay = DP83869_RGMII_CLK_DELAY_INV;
+ ret = of_property_read_u32(of_node, "tx-internal-delay",
+ &dp83869->tx_id_delay);
+ if (!ret && dp83869->tx_id_delay > dp83869_internal_delay[delay_size]) {
+ phydev_err(phydev,
+ "tx-internal-delay value of %u out of range\n",
+ dp83869->tx_id_delay);
+ return -EINVAL;
+ }
+
return ret;
}
#else
@@ -270,6 +301,29 @@ static int dp83869_configure_rgmii(struct phy_device *phydev,
return ret;
}
+static int dp83869_verify_rgmii_cfg(struct phy_device *phydev)
+{
+ struct dp83869_private *dp83869 = phydev->priv;
+
+ /* RX delay *must* be specified if internal delay of RX is used. */
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) &&
+ dp83869->rx_id_delay == DP83869_RGMII_CLK_DELAY_INV) {
+ phydev_err(phydev, "ti,rx-internal-delay must be specified\n");
+ return -EINVAL;
+ }
+
+ /* TX delay *must* be specified if internal delay of TX is used. */
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) &&
+ dp83869->tx_id_delay == DP83869_RGMII_CLK_DELAY_INV) {
+ phydev_err(phydev, "ti,tx-internal-delay must be specified\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int dp83869_configure_mode(struct phy_device *phydev,
struct dp83869_private *dp83869)
{
@@ -371,6 +425,12 @@ static int dp83869_config_init(struct phy_device *phydev)
{
struct dp83869_private *dp83869 = phydev->priv;
int ret, val;
+ int delay_size = ARRAY_SIZE(dp83869_internal_delay);
+ int delay = 0;
+
+ ret = dp83869_verify_rgmii_cfg(phydev);
+ if (ret)
+ return ret;
ret = dp83869_configure_mode(phydev, dp83869);
if (ret)
@@ -394,6 +454,47 @@ static int dp83869_config_init(struct phy_device *phydev)
dp83869->clk_output_sel <<
DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT);
+ if (phy_interface_is_rgmii(phydev)) {
+ val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL);
+
+ val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN | DP83869_RGMII_RX_CLK_DELAY_EN);
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ val |= (DP83869_RGMII_TX_CLK_DELAY_EN | DP83869_RGMII_RX_CLK_DELAY_EN);
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+ val |= DP83869_RGMII_TX_CLK_DELAY_EN;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+ val |= DP83869_RGMII_RX_CLK_DELAY_EN;
+
+ phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL, val);
+
+ if (dp83869->rx_id_delay) {
+ val = phy_get_delay_index(phydev,
+ &dp83869_internal_delay[0],
+ delay_size,
+ dp83869->rx_id_delay);
+ if (val < 0)
+ return val;
+
+ delay |= val;
+ }
+
+ if (dp83869->tx_id_delay) {
+ val = phy_get_delay_index(phydev,
+ &dp83869_internal_delay[0],
+ delay_size,
+ dp83869->tx_id_delay);
+ if (val < 0)
+ return val;
+
+ delay |= val << DP83869_RGMII_TX_CLK_DELAY_SHIFT;
+ }
+
+ phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL,
+ delay);
+ }
+
return ret;
}
--
2.26.2
^ permalink raw reply related
* [PATCH net-next v3 1/2] net: phy: dp83869: Update port-mirroring to read straps
From: Dan Murphy @ 2020-05-21 17:47 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem
Cc: netdev, linux-kernel, devicetree, Dan Murphy
In-Reply-To: <20200521174738.3151-1-dmurphy@ti.com>
The device tree may not have the property set for port mirroring
because the hardware may have it strapped. If the property is not in the
DT then check the straps and set the port mirroring bit appropriately.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
drivers/net/phy/dp83869.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c
index 7996a4aea8d2..073a0f7754a5 100644
--- a/drivers/net/phy/dp83869.c
+++ b/drivers/net/phy/dp83869.c
@@ -66,6 +66,7 @@
/* STRAP_STS1 bits */
#define DP83869_STRAP_STS1_RESERVED BIT(11)
+#define DP83869_STRAP_MIRROR_ENABLED BIT(12)
/* PHYCTRL bits */
#define DP83869_RX_FIFO_SHIFT 12
@@ -191,10 +192,18 @@ static int dp83869_of_init(struct phy_device *phydev)
else if (of_property_read_bool(of_node, "ti,min-output-impedance"))
dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN;
- if (of_property_read_bool(of_node, "enet-phy-lane-swap"))
+ if (of_property_read_bool(of_node, "enet-phy-lane-swap")) {
dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN;
- else
- dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS;
+ } else {
+ /* If the lane swap is not in the DT then check the straps */
+ ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1);
+ if (ret < 0)
+ return ret;
+ if (ret & DP83869_STRAP_MIRROR_ENABLED)
+ dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN;
+ else
+ dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS;
+ }
if (of_property_read_u32(of_node, "rx-fifo-depth",
&dp83869->rx_fifo_depth))
--
2.26.2
^ permalink raw reply related
* [PATCH net-next v3 2/2] net: phy: dp83869: Set opmode from straps
From: Dan Murphy @ 2020-05-21 17:47 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem
Cc: netdev, linux-kernel, devicetree, Dan Murphy
In-Reply-To: <20200521174738.3151-1-dmurphy@ti.com>
If the op-mode for the device is not set in the device tree then set
the strapped op-mode and store it for later configuration.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
drivers/net/phy/dp83869.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c
index 073a0f7754a5..cfb22a21a2e6 100644
--- a/drivers/net/phy/dp83869.c
+++ b/drivers/net/phy/dp83869.c
@@ -65,6 +65,7 @@
#define DP83869_RGMII_RX_CLK_DELAY_EN BIT(0)
/* STRAP_STS1 bits */
+#define DP83869_STRAP_OP_MODE_MASK GENMASK(2, 0)
#define DP83869_STRAP_STS1_RESERVED BIT(11)
#define DP83869_STRAP_MIRROR_ENABLED BIT(12)
@@ -161,6 +162,20 @@ static int dp83869_config_port_mirroring(struct phy_device *phydev)
DP83869_CFG3_PORT_MIRROR_EN);
}
+static int dp83869_set_strapped_mode(struct phy_device *phydev)
+{
+ struct dp83869_private *dp83869 = phydev->priv;
+ int val;
+
+ val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1);
+ if (val < 0)
+ return val;
+
+ dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK;
+
+ return 0;
+}
+
#ifdef CONFIG_OF_MDIO
static int dp83869_of_init(struct phy_device *phydev)
{
@@ -185,6 +200,10 @@ static int dp83869_of_init(struct phy_device *phydev)
if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET ||
dp83869->mode > DP83869_SGMII_COPPER_ETHERNET)
return -EINVAL;
+ } else {
+ ret = dp83869_set_strapped_mode(phydev);
+ if (ret)
+ return ret;
}
if (of_property_read_bool(of_node, "ti,max-output-impedance"))
@@ -218,7 +237,7 @@ static int dp83869_of_init(struct phy_device *phydev)
#else
static int dp83869_of_init(struct phy_device *phydev)
{
- return 0;
+ return dp83869_set_strapped_mode(phydev);
}
#endif /* CONFIG_OF_MDIO */
--
2.26.2
^ permalink raw reply related
* [PATCH net-next v3 0/2] DP83869 Enhancements
From: Dan Murphy @ 2020-05-21 17:47 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem
Cc: netdev, linux-kernel, devicetree, Dan Murphy
Hello
These are improvements to the DP83869 Ethernet PHY driver. OP-mode and port
mirroring may be strapped on the device but the software only retrives these
settings from the device tree. Reading the straps and initializing the
associated stored variables so when setting the PHY up and down the PHY's
configuration values will be retained.
Dan Murphy (2):
net: phy: dp83869: Update port-mirroring to read straps
net: phy: dp83869: Set opmode from straps
drivers/net/phy/dp83869.c | 36 ++++++++++++++++++++++++++++++++----
1 file changed, 32 insertions(+), 4 deletions(-)
--
2.26.2
^ permalink raw reply
* Re: [PATCH v3 00/16] spi: dw: Add generic DW DMA controller support
From: Andy Shevchenko @ 2020-05-21 17:46 UTC (permalink / raw)
To: Serge Semin
Cc: Mark Brown, Serge Semin, Georgy Vlasov, Ramil Zaripov,
Alexey Malahov, Maxim Kaurkin, Pavel Parkhomenko,
Ekaterina Skachko, Vadim Vlasov, Alexey Kolotnikov,
Thomas Bogendoerfer, Paul Burton, Ralf Baechle, Arnd Bergmann,
Rob Herring, linux-mips, linux-spi, devicetree, linux-kernel
In-Reply-To: <20200521012206.14472-1-Sergey.Semin@baikalelectronics.ru>
On Thu, May 21, 2020 at 04:21:50AM +0300, Serge Semin wrote:
> Baikal-T1 SoC provides a DW DMA controller to perform low-speed peripherals
> Mem-to-Dev and Dev-to-Mem transaction. This is also applicable to the DW
> APB SSI devices embedded into the SoC. Currently the DMA-based transfers
> are supported by the DW APB SPI driver only as a middle layer code for
> Intel MID/Elkhart PCI devices. Seeing the same code can be used for normal
> platform DMAC device we introduced a set of patches to fix it within this
> series.
>
> First of all we need to add the Tx and Rx DMA channels support into the DW
> APB SSI binding. Then there are several fixes and cleanups provided as a
> initial preparation for the Generic DMA support integration: add Tx/Rx
> finish wait methods, clear DMAC register when done or stopped, Fix native
> CS being unset, enable interrupts in accordance with DMA xfer mode,
> discard static DW DMA slave structures, discard unused void priv pointer
> and dma_width member of the dw_spi structure, provide the DMA Tx/Rx burst
> length parametrisation and make sure it's optionally set in accordance
> with the DMA max-burst capability.
>
> In order to have the DW APB SSI MMIO driver working with DMA we need to
> initialize the paddr field with the physical base address of the DW APB SSI
> registers space. Then we unpin the Intel MID specific code from the
> generic DMA one and placed it into the spi-dw-pci.c driver, which is a
> better place for it anyway. After that the naming cleanups are performed
> since the code is going to be used for a generic DMAC device. Finally the
> Generic DMA initialization can be added to the generic version of the
> DW APB SSI IP.
>
> Last but not least we traditionally convert the legacy plain text-based
> dt-binding file with yaml-based one and as a cherry on a cake replace
> the manually written DebugFS registers read method with a ready-to-use
> for the same purpose regset32 DebugFS interface usage.
I have given tags where I agree with content and I left disputed ones untouched
(1st patch, nevertheless, is fine for me, but I'm waiting for Feng).
So, now it seems settled in a way that I will send couple of cleanups
afterwards to avoid blocking this series to go.
Thanks for your work!
> This patchset is rebased and tested on the spi/for-next (5.7-rc5):
> base-commit: fe9fce6b2cf3 ("Merge remote-tracking branch 'spi/for-5.8' into spi-next")
>
> Link: https://lore.kernel.org/linux-spi/20200508132943.9826-1-Sergey.Semin@baikalelectronics.ru/
> Changelog v2:
> - Rebase on top of the spi repository for-next branch.
> - Move bindings conversion patch to the tail of the series.
> - Move fixes to the head of the series.
> - Apply as many changes as possible to be applied the Generic DMA
> functionality support is added and the spi-dw-mid is moved to the
> spi-dw-dma driver.
> - Discard patch "spi: dw: Fix dma_slave_config used partly uninitialized"
> since the problem has already been fixed.
> - Add new patch "spi: dw: Discard unused void priv pointer".
> - Add new patch "spi: dw: Discard dma_width member of the dw_spi structure".
> n_bytes member of the DW SPI data can be used instead.
> - Build the DMA functionality into the DW APB SSI core if required instead
> of creating a separate kernel module.
> - Use conditional statement instead of the ternary operator in the ref
> clock getter.
>
> Link: https://lore.kernel.org/linux-spi/20200515104758.6934-1-Sergey.Semin@baikalelectronics.ru/
> Changelog v3:
> - Use spi_delay_exec() method to wait for the DMA operation completion.
> - Explicitly initialize the dw_dma_slave members on stack.
> - Discard the dws->fifo_len utilization in the Tx FIFO DMA threshold
> setting from the patch where we just add the default burst length
> constants.
> - Use min() method to calculate the optimal burst values.
> - Add new patch which moves the spi-dw.c source file to spi-dw-core.c in
> order to preserve the DW APB SSI core driver name.
> - Add commas in the debugfs_reg32 structure initializer and after the last
> entry of the dw_spi_dbgfs_regs array.
>
> Co-developed-by: Georgy Vlasov <Georgy.Vlasov@baikalelectronics.ru>
> Signed-off-by: Georgy Vlasov <Georgy.Vlasov@baikalelectronics.ru>
> Co-developed-by: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
> Signed-off-by: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
> Cc: Maxim Kaurkin <Maxim.Kaurkin@baikalelectronics.ru>
> Cc: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
> Cc: Ekaterina Skachko <Ekaterina.Skachko@baikalelectronics.ru>
> Cc: Vadim Vlasov <V.Vlasov@baikalelectronics.ru>
> Cc: Alexey Kolotnikov <Alexey.Kolotnikov@baikalelectronics.ru>
> Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
> Cc: Paul Burton <paulburton@kernel.org>
> Cc: Ralf Baechle <ralf@linux-mips.org>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: linux-mips@vger.kernel.org
> Cc: linux-spi@vger.kernel.org
> Cc: devicetree@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
>
> Serge Semin (16):
> spi: dw: Add Tx/Rx finish wait methods to the MID DMA
> spi: dw: Enable interrupts in accordance with DMA xfer mode
> spi: dw: Discard static DW DMA slave structures
> spi: dw: Discard unused void priv pointer
> spi: dw: Discard dma_width member of the dw_spi structure
> spi: dw: Parameterize the DMA Rx/Tx burst length
> spi: dw: Use DMA max burst to set the request thresholds
> spi: dw: Fix Rx-only DMA transfers
> spi: dw: Add core suffix to the DW APB SSI core source file
> spi: dw: Move Non-DMA code to the DW PCIe-SPI driver
> spi: dw: Remove DW DMA code dependency from DW_DMAC_PCI
> spi: dw: Add DW SPI DMA/PCI/MMIO dependency on the DW SPI core
> spi: dw: Cleanup generic DW DMA code namings
> spi: dw: Add DMA support to the DW SPI MMIO driver
> spi: dw: Use regset32 DebugFS method to create regdump file
> dt-bindings: spi: Convert DW SPI binding to DT schema
>
> .../bindings/spi/snps,dw-apb-ssi.txt | 44 ---
> .../bindings/spi/snps,dw-apb-ssi.yaml | 127 ++++++++
> .../devicetree/bindings/spi/spi-dw.txt | 24 --
> drivers/spi/Kconfig | 15 +-
> drivers/spi/Makefile | 5 +-
> drivers/spi/{spi-dw.c => spi-dw-core.c} | 88 ++----
> drivers/spi/{spi-dw-mid.c => spi-dw-dma.c} | 276 +++++++++++-------
> drivers/spi/spi-dw-mmio.c | 4 +
> drivers/spi/spi-dw-pci.c | 50 +++-
> drivers/spi/spi-dw.h | 33 ++-
> 10 files changed, 407 insertions(+), 259 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt
> create mode 100644 Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
> delete mode 100644 Documentation/devicetree/bindings/spi/spi-dw.txt
> rename drivers/spi/{spi-dw.c => spi-dw-core.c} (82%)
> rename drivers/spi/{spi-dw-mid.c => spi-dw-dma.c} (53%)
>
> --
> 2.25.1
>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v2 0/3] Re-introduce TX FIFO resize for larger EP bursting
From: Wesley Cheng @ 2020-05-21 17:37 UTC (permalink / raw)
To: agross, bjorn.andersson, robh+dt, balbi, gregkh
Cc: linux-arm-msm, devicetree, linux-kernel, linux-usb, jackp
In-Reply-To: <1590050169-30747-1-git-send-email-wcheng@codeaurora.org>
On 5/21/2020 1:36 AM, Wesley Cheng wrote:
> Changes in V2:
> - Modified TXFIFO resizing logic to ensure that each EP is reserved a
> FIFO.
> - Removed dev_dbg() prints and fixed typos from patches
> - Added some more description on the dt-bindings commit message
>
> Reviewed-by: Felipe Balbi <balbi@kernel.org>
> Reviewed-by: Rob Herring <robh@kernel.org>
>
Sorry, please disregard the Reviewed-by tags in the patches. I added
those mistakenly.
> Currently, there is no functionality to allow for resizing the TXFIFOs, and
> relying on the HW default setting for the TXFIFO depth. In most cases, the
> HW default is probably sufficient, but for USB compositions that contain
> multiple functions that require EP bursting, the default settings
> might not be enough. Also to note, the current SW will assign an EP to a
> function driver w/o checking to see if the TXFIFO size for that particular
> EP is large enough. (this is a problem if there are multiple HW defined
> values for the TXFIFO size)
>
> It is mentioned in the SNPS databook that a minimum of TX FIFO depth = 3
> is required for an EP that supports bursting. Otherwise, there may be
> frequent occurences of bursts ending. For high bandwidth functions,
> such as data tethering (protocols that support data aggregation), mass
> storage, and media transfer protocol (over FFS), the bMaxBurst value can be
> large, and a bigger TXFIFO depth may prove to be beneficial in terms of USB
> throughput. (which can be associated to system access latency, etc...) It
> allows for a more consistent burst of traffic, w/o any interruptions, as
> data is readily available in the FIFO.
>
> With testing done using the mass storage function driver, the results show
> that with a larger TXFIFO depth, the bandwidth increased significantly.
>
> Test Parameters:
> - Platform: Qualcomm SM8150
> - bMaxBurst = 6
> - USB req size = 256kB
> - Num of USB reqs = 16
> - USB Speed = Super-Speed
> - Function Driver: Mass Storage (w/ ramdisk)
> - Test Application: CrystalDiskMark
>
> Results:
>
> TXFIFO Depth = 3 max packets
>
> Test Case | Data Size | AVG tput (in MB/s)
> -------------------------------------------
> Sequential|1 GB x |
> Read |9 loops | 193.60
> | | 195.86
> | | 184.77
> | | 193.60
> -------------------------------------------
>
> TXFIFO Depth = 6 max packets
>
> Test Case | Data Size | AVG tput (in MB/s)
> -------------------------------------------
> Sequential|1 GB x |
> Read |9 loops | 287.35
> | | 304.94
> | | 289.64
> | | 293.61
> -------------------------------------------
>
> Wesley Cheng (3):
> usb: dwc3: Resize TX FIFOs to meet EP bursting requirements
> arm64: boot: dts: qcom: sm8150: Enable dynamic TX FIFO resize logic
> dt-bindings: usb: dwc3: Add entry for tx-fifo-resize
>
> Documentation/devicetree/bindings/usb/dwc3.txt | 2 +-
> arch/arm64/boot/dts/qcom/sm8150.dtsi | 1 +
> drivers/usb/dwc3/core.c | 2 +
> drivers/usb/dwc3/core.h | 8 ++
> drivers/usb/dwc3/ep0.c | 37 ++++++++-
> drivers/usb/dwc3/gadget.c | 111 +++++++++++++++++++++++++
> 6 files changed, 159 insertions(+), 2 deletions(-)
>
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox