devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Karthik Ramasubramanian <kramasub@codeaurora.org>
To: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: corbet@lwn.net, andy.gross@linaro.org, david.brown@linaro.org,
	robh+dt@kernel.org, mark.rutland@arm.com, wsa@the-dreams.de,
	gregkh@linuxfoundation.org, linux-doc@vger.kernel.org,
	linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org,
	linux-i2c@vger.kernel.org, linux-serial@vger.kernel.org,
	jslaby@suse.com, Sagar Dharia <sdharia@codeaurora.org>,
	Girish Mahadevan <girishm@codeaurora.org>
Subject: Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
Date: Wed, 31 Jan 2018 11:58:29 -0700	[thread overview]
Message-ID: <1faee481-8cb6-4dc6-ac0f-eb89445227e1@codeaurora.org> (raw)
In-Reply-To: <20180117062002.GA6620@minitux>



On 1/16/2018 11:20 PM, Bjorn Andersson wrote:
> On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> 
>> This driver manages the Generic Interface (GENI) firmware based Qualcomm
>> Universal Peripheral (QUP) Wrapper. GENI based QUP is the next generation
>> programmable module composed of multiple Serial Engines (SE) and supports
>> a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. This
>> driver also enables managing the serial interface independent aspects of
>> Serial Engines.
>>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>> ---
>>   drivers/soc/qcom/Kconfig        |    8 +
>>   drivers/soc/qcom/Makefile       |    1 +
>>   drivers/soc/qcom/qcom-geni-se.c | 1016 +++++++++++++++++++++++++++++++++++++++
> 
> I'm not aware of any non-serial-engine "geni" at Qualcomm, so can we
> drop the "se" throughout this driver?
Currently GENI is used to program just the serial engines. But it is not 
restricted to that. It can be used to program other hardware cores. 
Hence keeping "se" will help to clarify that this driver is meant for 
GENI based serial engines only.
> 
>>   include/linux/qcom-geni-se.h    |  807 +++++++++++++++++++++++++++++++
>>   4 files changed, 1832 insertions(+)
>>   create mode 100644 drivers/soc/qcom/qcom-geni-se.c
>>   create mode 100644 include/linux/qcom-geni-se.h
>>
>> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
>> index b81374b..b306d51 100644
>> --- a/drivers/soc/qcom/Kconfig
>> +++ b/drivers/soc/qcom/Kconfig
>> @@ -3,6 +3,14 @@
>>   #
>>   menu "Qualcomm SoC drivers"
>>   
>> +config QCOM_GENI_SE
>> +	tristate "QCOM GENI Serial Engine Driver"
> 
> Options in drivers/soc/qcom/Kconfig should have "depends on ARCH_QCOM",
> as the makefile in this directory won't be processed when !ARCH_QCOM.
I will update the config to depend on ARCH_QCOM.
> 
>> +	help
>> +	  This module is used to manage Generic Interface (GENI) firmware based
>> +	  Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This
>> +	  module is also used to manage the common aspects of multiple Serial
>> +	  Engines present in the QUP.
>> +
>>   config QCOM_GLINK_SSR
>>   	tristate "Qualcomm Glink SSR driver"
>>   	depends on RPMSG
>> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
>> index 40c56f6..74d5db8 100644
>> --- a/drivers/soc/qcom/Makefile
>> +++ b/drivers/soc/qcom/Makefile
>> @@ -1,4 +1,5 @@
>>   # SPDX-License-Identifier: GPL-2.0
>> +obj-$(CONFIG_QCOM_GENI_SE) +=	qcom-geni-se.o
>>   obj-$(CONFIG_QCOM_GLINK_SSR) +=	glink_ssr.o
>>   obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>>   obj-$(CONFIG_QCOM_MDT_LOADER)	+= mdt_loader.o
>> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
>> new file mode 100644
>> index 0000000..3f43582
>> --- /dev/null
>> +++ b/drivers/soc/qcom/qcom-geni-se.c
>> @@ -0,0 +1,1016 @@
>> +/*
>> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
> 
> Please use SPDX style license header, i.e. this file should start with:
> 
> // SPDX-License-Identifier: GPL-2.0
> /*
>   * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
>   */
> 
>> +
>> +#include <linux/clk.h>
>> +#include <linux/slab.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/io.h>
>> +#include <linux/list.h>
> 
> This isn't used.
> 
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/pm_runtime.h>
> 
> Is this used?
> 
>> +#include <linux/qcom-geni-se.h>
>> +#include <linux/spinlock.h>
> 
> Is this used?
I will remove including unused header files.
> 
>> +
>> +#define MAX_CLK_PERF_LEVEL 32
>> +
>> +/**
>> + * @struct geni_se_device - Data structure to represent the QUP Wrapper Core
> 
> Make this name indicate that this is the wrapper; e.g. struct geni_wrapper
geni_wrapper makes sense.
> 
>> + * @dev:		Device pointer of the QUP wrapper core.
>> + * @base:		Base address of this instance of QUP wrapper core.
>> + * @m_ahb_clk:		Handle to the primary AHB clock.
>> + * @s_ahb_clk:		Handle to the secondary AHB clock.
>> + * @geni_dev_lock:	Lock to protect the device elements.
>> + * @num_clk_levels:	Number of valid clock levels in clk_perf_tbl.
>> + * @clk_perf_tbl:	Table of clock frequency input to Serial Engine clock.
>> + */
>> +struct geni_se_device {
>> +	struct device *dev;
>> +	void __iomem *base;
>> +	struct clk *m_ahb_clk;
>> +	struct clk *s_ahb_clk;
> 
> As these two clocks always seems to be operated in tandem use
> clk_bulk_data to keep track of them and use the clk_bulk*() operations
> to simplify your prepare/unprepare sites.
I will use the clk_bulk* approach.
> 
>> +	struct mutex geni_dev_lock;
> 
> There's only one lock and it should be obvious from context that it's
> the lock of the geni_se_device, so naming this "lock" should be
> sufficient.
I will rename it to "lock".
> 
>> +	unsigned int num_clk_levels;
>> +	unsigned long *clk_perf_tbl;
>> +};
>> +
>> +/* Offset of QUP Hardware Version Register */
>> +#define QUP_HW_VER (0x4)
> 
> Drop the parenthesis. It would be nice if this define indicated that
> this is a register and not a value. E.g. call it QUP_HW_VER_REG.
Ok.
> 
>> +
>> +#define HW_VER_MAJOR_MASK GENMASK(31, 28)
>> +#define HW_VER_MAJOR_SHFT 28
>> +#define HW_VER_MINOR_MASK GENMASK(27, 16)
>> +#define HW_VER_MINOR_SHFT 16
>> +#define HW_VER_STEP_MASK GENMASK(15, 0)
>> +
>> +/**
>> + * geni_read_reg_nolog() - Helper function to read from a GENI register
>> + * @base:	Base address of the serial engine's register block.
>> + * @offset:	Offset within the serial engine's register block.
>> + *
>> + * Return:	Return the contents of the register.
>> + */
>> +unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
> 
> Remove this function.
Ok.
> 
>> +{
>> +	return readl_relaxed(base + offset);
>> +}
>> +EXPORT_SYMBOL(geni_read_reg_nolog);
>> +
>> +/**
>> + * geni_write_reg_nolog() - Helper function to write into a GENI register
>> + * @value:	Value to be written into the register.
>> + * @base:	Base address of the serial engine's register block.
>> + * @offset:	Offset within the serial engine's register block.
>> + */
>> +void geni_write_reg_nolog(unsigned int value, void __iomem *base, int offset)
> 
> Ditto
Ok.
> 
>> +{
>> +	return writel_relaxed(value, (base + offset));
>> +}
>> +EXPORT_SYMBOL(geni_write_reg_nolog);
>> +
>> +/**
>> + * geni_read_reg() - Helper function to read from a GENI register
>> + * @base:	Base address of the serial engine's register block.
>> + * @offset:	Offset within the serial engine's register block.
>> + *
>> + * Return:	Return the contents of the register.
> 
> Drop the extra spaces after "Return:" in all your kerneldoc.
Ok.
> 
>> + */
>> +unsigned int geni_read_reg(void __iomem *base, int offset)
>> +{
>> +	return readl_relaxed(base + offset);
>> +}
>> +EXPORT_SYMBOL(geni_read_reg);
>> +
>> +/**
>> + * geni_write_reg() - Helper function to write into a GENI register
>> + * @value:	Value to be written into the register.
>> + * @base:	Base address of the serial engine's register block.
>> + * @offset:	Offset within the serial engine's register block.
>> + */
>> +void geni_write_reg(unsigned int value, void __iomem *base, int offset)
>> +{
>> +	return writel_relaxed(value, (base + offset));
> 
> As written, this function adds no value and I find it quite confusing
> that this is used both for read/writes on the wrapper as well as the
> individual functions.Ok.
> 
> Compare:
> 
> 	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
> 
> with just inlining:
> 
> 	writel(common_geni_m_irq_en, base + SE_GENI_M_IRQ_EN);
> 
>> +}
>> +EXPORT_SYMBOL(geni_write_reg);
>> +
>> +/**
>> + * geni_get_qup_hw_version() - Read the QUP wrapper Hardware version
>> + * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
>> + * @major:		Buffer for Major Version field.
>> + * @minor:		Buffer for Minor Version field.
>> + * @step:		Buffer for Step Version field.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_get_qup_hw_version(struct device *wrapper_dev, unsigned int *major,
>> +			    unsigned int *minor, unsigned int *step)
>> +{
>> +	unsigned int version;
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (!wrapper_dev || !major || !minor || !step)
>> +		return -EINVAL;
> 
> This would only happen during development, as the engineer calls the
> function incorrectly. Consider it a contract that these has to be valid
> and skip the checks.
Ok.
> 
>> +
>> +	geni_se_dev = dev_get_drvdata(wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
>> +		return -ENODEV;
> 
> Make the children acquire the geni_se_dev handle (keep the type opaque)
> and pass that instead of wrapper_dev. Then you can just drop this chunk.
Ok.
> 
>> +
>> +	version = geni_read_reg(geni_se_dev->base, QUP_HW_VER);
>> +	*major = (version & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT;
>> +	*minor = (version & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT;
>> +	*step = version & HW_VER_STEP_MASK;
>> +	return 0;
> 
> If you implement these two changes there's no way this function can
> fail, so you don't have to return a value here.
Ok.
> 
>> +}
>> +EXPORT_SYMBOL(geni_get_qup_hw_version);
>> +
>> +/**
>> + * geni_se_get_proto() - Read the protocol configured for a serial engine
>> + * @base:	Base address of the serial engine's register block.
>> + *
>> + * Return:	Protocol value as configured in the serial engine.
>> + */
>> +int geni_se_get_proto(void __iomem *base)
> 
> I keep reading this as "geni set get proto", you should be fine just
> dropping _se_ from these function names. And if you want to denote that
> it's the Qualcomm GENI then make it qcom_geni_*.
"_se" prefix is used to help differentiate operations on a serial engine 
from those on the wrapper. I can rename it as geni_se_read_proto to 
avoid the confusion.
> 
> But more importantly, it is not obvious when reading this driver that
> the typeless "base" being passed is that of the individual functional
> block, and not the wrapper.
> 
> If you want to do this in an "object oriented" fashion create a struct
> that's common for all geni instances, include it in the context of the
> individual function devices and pass it into these functions.
There is a structure named geni_se_rsc to group resources associated 
with a serial engine. I can rename it to geni_serial_engine and include 
the opaque "base" address in that structure. That structure can be 
passed as an input parameter to all the serial engine operations.
> 
>> +{
>> +	int proto;
>> +
>> +	proto = ((geni_read_reg(base, GENI_FW_REVISION_RO)
>> +			& FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT);
> 
> Don't do everything in one go, as you can't fit it on one line anyways.
> Writing it like this instead makes it easier to read:
> 
> 	u32 val;
> 
> 	val = readl(base + GENI_FW_S_REVISION_RO);
> 
> 	return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
Ok.
> 
>> +	return proto;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_proto);
>> +
>> +static int geni_se_irq_en(void __iomem *base)
>> +{
>> +	unsigned int common_geni_m_irq_en;
>> +	unsigned int common_geni_s_irq_en;
> 
> The size of these values isn't unsigned int, it's u32. And if you give
> them a shorter name the function becomes easier to read.
> 
> Further more, as you don't care about ordering you don't need two of
> them either. So you should be able to just do:
> 
> 	u32 val;
> 
> 	val = readl(base + SE_GENI_M_IRQ_EN);
> 	val |= M_COMMON_GENI_M_IRQ_EN;
> 	writel(val, base + SE_GENI_M_IRQ_EN);
> 
> 	val = readl(base + SE_GENI_S_IRQ_EN);
> 	val |= S_COMMON_GENI_S_IRQ_EN;
> 	writel(val, base + SE_GENI_S_IRQ_EN);
Ok.
> 
>> +
>> +	common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
>> +	common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
>> +	/* Common to all modes */
>> +	common_geni_m_irq_en |= M_COMMON_GENI_M_IRQ_EN;
>> +	common_geni_s_irq_en |= S_COMMON_GENI_S_IRQ_EN;
>> +
>> +	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
>> +	geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
>> +	return 0;
> 
> And as this can't fail there's no reason to have a return value.
Ok.
> 
>> +}
>> +
>> +
>> +static void geni_se_set_rx_rfr_wm(void __iomem *base, unsigned int rx_wm,
>> +				  unsigned int rx_rfr)
>> +{
>> +	geni_write_reg(rx_wm, base, SE_GENI_RX_WATERMARK_REG);
>> +	geni_write_reg(rx_rfr, base, SE_GENI_RX_RFR_WATERMARK_REG);
>> +}
>> +
>> +static int geni_se_io_set_mode(void __iomem *base)
>> +{
>> +	unsigned int io_mode;
>> +	unsigned int geni_dma_mode;
>> +
>> +	io_mode = geni_read_reg(base, SE_IRQ_EN);
>> +	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
>> +
>> +	io_mode |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
>> +	io_mode |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
>> +	geni_dma_mode &= ~GENI_DMA_MODE_EN;
>> +
>> +	geni_write_reg(io_mode, base, SE_IRQ_EN);
>> +	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
>> +	geni_write_reg(0, base, SE_GSI_EVENT_EN);
>> +	return 0;
> 
> As this function can't fail, don't return a value.
Ok.
> 
>> +}
>> +
>> +static void geni_se_io_init(void __iomem *base)
>> +{
>> +	unsigned int io_op_ctrl;
>> +	unsigned int geni_cgc_ctrl;
>> +	unsigned int dma_general_cfg;
> 
> These are all u32, be explicit.
Ok.
> 
>> +
>> +	geni_cgc_ctrl = geni_read_reg(base, GENI_CGC_CTRL);
>> +	dma_general_cfg = geni_read_reg(base, SE_DMA_GENERAL_CFG);
>> +	geni_cgc_ctrl |= DEFAULT_CGC_EN;
>> +	dma_general_cfg |= (AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON |
>> +			DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON);
> 
> Drop the parenthesis and there's no harm in making multiple assignments
> in favour of splitting the line.
Ok.
> 
>> +	io_op_ctrl = DEFAULT_IO_OUTPUT_CTRL_MSK;
>> +	geni_write_reg(geni_cgc_ctrl, base, GENI_CGC_CTRL);
>> +	geni_write_reg(dma_general_cfg, base, SE_DMA_GENERAL_CFG);
> 
> Is there a reason why this chunk of code is a mix of 3 independent
> register updates?
I am not sure I understand the context of your question. This is how the 
hardware programming manual is describing to program the registers as 
part of initializing a serial engine. Please let me know if this is not 
the information you are looking for.
> 
>> +
>> +	geni_write_reg(io_op_ctrl, base, GENI_OUTPUT_CTRL);
>> +	geni_write_reg(FORCE_DEFAULT, base, GENI_FORCE_DEFAULT_REG);
>> +}
>> +
>> +/**
>> + * geni_se_init() - Initialize the GENI Serial Engine
>> + * @base:	Base address of the serial engine's register block.
>> + * @rx_wm:	Receive watermark to be configured.
>> + * @rx_rfr_wm:	Ready-for-receive watermark to be configured.
> 
> What's the unit for these?
The unit is the number of hardware FIFO words.
> 
>> + *
>> + * This function is used to initialize the GENI serial engine, configure
>> + * receive watermark and ready-for-receive watermarks.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
> 
> As neither geni_se_io_set_mode() nor geni_se_irq_en() can fail there's
> no way geni_se_init() can fail and as such you don't need a return value
> of this function.
Ok.
> 
>> + */
>> +int geni_se_init(void __iomem *base, unsigned int rx_wm, unsigned int rx_rfr)
>> +{
>> +	int ret;
>> +
>> +	geni_se_io_init(base);
>> +	ret = geni_se_io_set_mode(base);
>> +	if (ret)
>> +		return ret;
>> +
>> +	geni_se_set_rx_rfr_wm(base, rx_wm, rx_rfr);
>> +	ret = geni_se_irq_en(base);
> 
> Just inline these two functions here.
> 
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_init);
> 
> This is an excellent candidate for initializing a common "geni
> function"-struct shared among the children, i.e. make this:
> 
> void geni_init_port(struct geni_port *port, struct device *dev,
> 		    size_t rx_wm, rfr_wm);
> 
> And have this do the ioremap of the memory of dev and initialize some
> common "geni_port" struct, then you can pass this to some set of
> geni_port_*() helper functions.
Sure, this sounds like a good place to do ioremap of children and 
initialize common serial engine resources.
> 
>> +
>> +static int geni_se_select_fifo_mode(void __iomem *base)
>> +{
>> +	int proto = geni_se_get_proto(base);
>> +	unsigned int common_geni_m_irq_en;
>> +	unsigned int common_geni_s_irq_en;
>> +	unsigned int geni_dma_mode;
> 
> These are of type u32.
> 
> If you use more succinct names the function will be easier to read; what
> about master, slave and mode? (Or m_en, s_en and mode)
Ok.
> 
>> +
>> +	geni_write_reg(0, base, SE_GSI_EVENT_EN);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);
> 
> Use lowercase for the hex constants.
Ok.
> 
>> +
>> +	common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
>> +	common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
>> +	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
>> +	if (proto != UART) {
>> +		common_geni_m_irq_en |=
>> +			(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN |
>> +			M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
> 
> Drop the parenthesis, split the assignment in multiple to make things
> not span 3 lines.
Ok.
> 
>> +		common_geni_s_irq_en |= S_CMD_DONE_EN;
>> +	}
>> +	geni_dma_mode &= ~GENI_DMA_MODE_EN;
> 
> Please group things that are related.
> 
> The compiler doesn't care if you stretch the life time of your local
> variables through your functions, but the brain does.
Ok.
> 
>> +
>> +	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
>> +	geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
>> +	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
>> +	return 0;
> 
> This can't fail, and you ignore the returned value in
> geni_se_select_mode().
Ok.
> 
>> +}
>> +
>> +static int geni_se_select_dma_mode(void __iomem *base)
>> +{
>> +	unsigned int geni_dma_mode = 0;
> 
> This is u32, can be shorten to "mode" and it's first use is an
> assignment - so you don't have to initialize it here.
Ok.
> 
>> +
>> +	geni_write_reg(0, base, SE_GSI_EVENT_EN);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
>> +	geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);
> 
> Please lower case your hex constants.
Ok.
> 
>> +
>> +	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
>> +	geni_dma_mode |= GENI_DMA_MODE_EN;
>> +	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
>> +	return 0;
> 
> Can't fail.
Ok.
> 
>> +}
>> +
>> +/**
>> + * geni_se_select_mode() - Select the serial engine transfer mode
>> + * @base:	Base address of the serial engine's register block.
>> + * @mode:	Transfer mode to be selected.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure.
>> + */
>> +int geni_se_select_mode(void __iomem *base, int mode)
>> +{
>> +	int ret = 0;
> 
> Drop the return value and "ret", if you want to help the developer of
> child devices you can start off by a WARN_ON(mode != FIFO_MODE && mode
> != SE_DMA);
Ok.
> 
>> +
>> +	switch (mode) {
>> +	case FIFO_MODE:
>> +		geni_se_select_fifo_mode(base);
>> +		break;
>> +	case SE_DMA:
>> +		geni_se_select_dma_mode(base);
>> +		break;
>> +	default:
>> +		ret = -EINVAL;
>> +		break;
>> +	}
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_select_mode);
>> +
>> +/**
>> + * geni_se_setup_m_cmd() - Setup the primary sequencer
>> + * @base:	Base address of the serial engine's register block.
>> + * @cmd:	Command/Operation to setup in the primary sequencer.
>> + * @params:	Parameter for the sequencer command.
>> + *
>> + * This function is used to configure the primary sequencer with the
>> + * command and its assoicated parameters.
>> + */
>> +void geni_se_setup_m_cmd(void __iomem *base, u32 cmd, u32 params)
>> +{
>> +	u32 m_cmd = (cmd << M_OPCODE_SHFT);
>> +
>> +	m_cmd |= (params & M_PARAMS_MSK);
> 
> Rather than initializing the variable during declaration and then
> amending the value it's cleaner if you do:
> 
> 	val = (cmd << M_OPCODE_SHFT) | (params & M_PARAMS_MSK);
Ok.
> 
>> +	geni_write_reg(m_cmd, base, SE_GENI_M_CMD0);
>> +}
>> +EXPORT_SYMBOL(geni_se_setup_m_cmd);
>> +
> [..]
>> +/**
>> + * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
>> + * @base:	Base address of the serial engine's register block.
>> + *
>> + * This function is used to get the depth i.e. number of elements in the
>> + * TX fifo of the serial engine.
>> + *
>> + * Return:	TX fifo depth in units of FIFO words.
>> + */
>> +int geni_se_get_tx_fifo_depth(void __iomem *base)
>> +{
>> +	int tx_fifo_depth;
>> +
>> +	tx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_0)
>> +			& TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT);
> 
> It easier to read this way:
> 
> 	u32 val;
> 
> 	val = readl(base, SE_HW_PARAM_0);
> 
> 	return (val & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT;
Ok.
> 
>> +	return tx_fifo_depth;
>> +}
>> +EXPORT_SYMBOL(geni_se_get_tx_fifo_depth);
>> +
>> +/**
>> + * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
>> + * @base:	Base address of the serial engine's register block.
>> + *
>> + * This function is used to get the width i.e. word size per element in the
>> + * TX fifo of the serial engine.
>> + *
>> + * Return:	TX fifo width in bits
>> + */
>> +int geni_se_get_tx_fifo_width(void __iomem *base)
>> +{
>> +	int tx_fifo_width;
>> +
>> +	tx_fifo_width = ((geni_read_reg(base, SE_HW_PARAM_0)
>> +			& TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT);
>> +	return tx_fifo_width;
> 
> Ditto.
Ok.
> 
>> +}
>> +EXPORT_SYMBOL(geni_se_get_tx_fifo_width);
>> +
>> +/**
>> + * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
>> + * @base:	Base address of the serial engine's register block.
>> + *
>> + * This function is used to get the depth i.e. number of elements in the
>> + * RX fifo of the serial engine.
>> + *
>> + * Return:	RX fifo depth in units of FIFO words
>> + */
>> +int geni_se_get_rx_fifo_depth(void __iomem *base)
>> +{
>> +	int rx_fifo_depth;
>> +
>> +	rx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_1)
>> +			& RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT);
>> +	return rx_fifo_depth;
> 
> Ditto.
Ok.
> 
>> +}
>> +EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);
>> +
>> +/**
>> + * geni_se_get_packing_config() - Get the packing configuration based on input
>> + * @bpw:	Bits of data per transfer word.
>> + * @pack_words:	Number of words per fifo element.
>> + * @msb_to_lsb:	Transfer from MSB to LSB or vice-versa.
>> + * @cfg0:	Output buffer to hold the first half of configuration.
>> + * @cfg1:	Output buffer to hold the second half of configuration.
>> + *
>> + * This function is used to calculate the packing configuration based on
>> + * the input packing requirement and the configuration logic.
>> + */
>> +void geni_se_get_packing_config(int bpw, int pack_words, bool msb_to_lsb,
>> +				unsigned long *cfg0, unsigned long *cfg1)
> 
> This function is used only from geni_se_config_packing() which writes
> the new config to the associated registers and from the serial driver
> that does the same - but here pack_words differ for rx and tx.
> 
> If you improve geni_se_config_packing() to take rx and tx fifo sizes
> independently you don't have to expose this function to the client
> drivers and you don't need to return cfg0 and cfg1 like you do here.
Ok.
> 
>> +{
>> +	u32 cfg[4] = {0};
>> +	int len;
>> +	int temp_bpw = bpw;
>> +	int idx_start = (msb_to_lsb ? (bpw - 1) : 0);
>> +	int idx = idx_start;
>> +	int idx_delta = (msb_to_lsb ? -BITS_PER_BYTE : BITS_PER_BYTE);
>> +	int ceil_bpw = ((bpw & (BITS_PER_BYTE - 1)) ?
>> +			((bpw & ~(BITS_PER_BYTE - 1)) + BITS_PER_BYTE) : bpw);
>> +	int iter = (ceil_bpw * pack_words) >> 3;
>> +	int i;
>> +
>> +	if (unlikely(iter <= 0 || iter > 4)) {
> 
> Don't use unlikely(), this function is not called one per port, there's
> no need to clutter the code to "optimize" it.
> 
>> +		*cfg0 = 0;
>> +		*cfg1 = 0;
>> +		return;
>> +	}
>> +
>> +	for (i = 0; i < iter; i++) {
>> +		len = (temp_bpw < BITS_PER_BYTE) ?
>> +				(temp_bpw - 1) : BITS_PER_BYTE - 1;
>> +		cfg[i] = ((idx << 5) | (msb_to_lsb << 4) | (len << 1));
>> +		idx = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
>> +				((i + 1) * BITS_PER_BYTE) + idx_start :
>> +				idx + idx_delta;
>> +		temp_bpw = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
>> +				bpw : (temp_bpw - BITS_PER_BYTE);
> 
> Each operation in this loop depend on temp_bpw being smaller or larger
> than BITS_PER_BYTE, please rewrite it with a single if/else based on
> this.
> 
>> +	}
>> +	cfg[iter - 1] |= 1;
> 
> The 1 you're writing here looks like an "EOF". It would be nice with a
> comment describing the format of these 4 registers and the meaning of
> BIT(0).
> 
>> +	*cfg0 = cfg[0] | (cfg[1] << 10);
>> +	*cfg1 = cfg[2] | (cfg[3] << 10);
>> +}
>> +EXPORT_SYMBOL(geni_se_get_packing_config);
>> +
>> +/**
>> + * geni_se_config_packing() - Packing configuration of the serial engine
>> + * @base:	Base address of the serial engine's register block.
>> + * @bpw:	Bits of data per transfer word.
>> + * @pack_words:	Number of words per fifo element.
>> + * @msb_to_lsb:	Transfer from MSB to LSB or vice-versa.
>> + *
>> + * This function is used to configure the packing rules for the current
>> + * transfer.
>> + */
>> +void geni_se_config_packing(void __iomem *base, int bpw,
>> +			    int pack_words, bool msb_to_lsb)
>> +{
>> +	unsigned long cfg0, cfg1;
> 
> These are u32.
Ok.
> 
>> +
>> +	geni_se_get_packing_config(bpw, pack_words, msb_to_lsb, &cfg0, &cfg1);
>> +	geni_write_reg(cfg0, base, SE_GENI_TX_PACKING_CFG0);
>> +	geni_write_reg(cfg1, base, SE_GENI_TX_PACKING_CFG1);
>> +	geni_write_reg(cfg0, base, SE_GENI_RX_PACKING_CFG0);
>> +	geni_write_reg(cfg1, base, SE_GENI_RX_PACKING_CFG1);
>> +	if (pack_words || bpw == 32)
>> +		geni_write_reg((bpw >> 4), base, SE_GENI_BYTE_GRAN);
>> +}
>> +EXPORT_SYMBOL(geni_se_config_packing);
>> +
>> +static void geni_se_clks_off(struct geni_se_rsc *rsc)
>> +{
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (unlikely(!rsc || !rsc->wrapper_dev))
> 
> Drop the unlikely(). Why would you be passed a rsc with wrapper_dev
> NULL?
Used it just to handle any development time issues. I will remove the 
unlikely().
> 
>> +		return;
>> +
>> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
>> +		return;
>> +
>> +	clk_disable_unprepare(rsc->se_clk);
>> +	clk_disable_unprepare(geni_se_dev->s_ahb_clk);
>> +	clk_disable_unprepare(geni_se_dev->m_ahb_clk);
>> +}
>> +
>> +/**
>> + * geni_se_resources_off() - Turn off resources associated with the serial
>> + *                           engine
>> + * @rsc:	Handle to resources associated with the serial engine.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_resources_off(struct geni_se_rsc *rsc)
>> +{
>> +	int ret = 0;
> 
> No need to initialize ret.
Ok.
> 
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (unlikely(!rsc || !rsc->wrapper_dev))
>> +		return -EINVAL;
>> +
>> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
> 
> 
> Only child devices would call this, so it's unlikely that this is a
> probe defer.
> 
> Also the returned value is not used.
> 
> And drop the unlikely()
Ok.
> 
>> +		return -ENODEV;
>> +
>> +	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
>> +	if (ret)
>> +		return ret;
>> +
>> +	geni_se_clks_off(rsc);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_resources_off);
>> +
>> +static int geni_se_clks_on(struct geni_se_rsc *rsc)
>> +{
>> +	int ret;
>> +	struct geni_se_device *geni_se_dev;
> 
> If you name this "geni" instead, or "wrapper" the rest will be cleaner.
Ok.
> 
>> +
>> +	if (unlikely(!rsc || !rsc->wrapper_dev))
>> +		return -EINVAL;
>> +
>> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
>> +		return -EPROBE_DEFER;
>> +
>> +	ret = clk_prepare_enable(geni_se_dev->m_ahb_clk);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = clk_prepare_enable(geni_se_dev->s_ahb_clk);
>> +	if (ret) {
>> +		clk_disable_unprepare(geni_se_dev->m_ahb_clk);
>> +		return ret;
>> +	}
> 
> These two could be dealt with in a single clk_bulk_prepare_enable().
Ok.
> 
>> +
>> +	ret = clk_prepare_enable(rsc->se_clk);
>> +	if (ret) {
>> +		clk_disable_unprepare(geni_se_dev->s_ahb_clk);
>> +		clk_disable_unprepare(geni_se_dev->m_ahb_clk);
>> +	}
>> +	return ret;
>> +}
>> +
>> +/**
>> + * geni_se_resources_on() - Turn on resources associated with the serial
>> + *                          engine
>> + * @rsc:	Handle to resources associated with the serial engine.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_resources_on(struct geni_se_rsc *rsc)
>> +{
>> +	int ret = 0;
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (unlikely(!rsc || !rsc->wrapper_dev))
>> +		return -EINVAL;
>> +
>> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
> 
> As with geni_se_resources_off()
Ok.
> 
>> +		return -EPROBE_DEFER;
>> +
>> +	ret = geni_se_clks_on(rsc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_active);
> 
> pinctrl_pm_select_default_state(rsc->dev);
> 
> So you need the dev associated with the rsc.
> 
>> +	if (ret)
>> +		geni_se_clks_off(rsc);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_resources_on);
>> +
>> +/**
>> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
>> + * @rsc:	Resource for which the clock table is requested.
>> + * @tbl:	Table in which the output is returned.
>> + *
>> + * This function is called by the protocol drivers to determine the different
>> + * clock frequencies supported by Serail Engine Core Clock. The protocol
> 
> s/Serail/Serial/
Ok.
> 
>> + * drivers use the output to determine the clock frequency index to be
>> + * programmed into DFS.
>> + *
>> + * Return:	number of valid performance levels in the table on success,
>> + *		standard Linux error codes on failure.
>> + */
>> +int geni_se_clk_tbl_get(struct geni_se_rsc *rsc, unsigned long **tbl)
>> +{
>> +	struct geni_se_device *geni_se_dev;
>> +	int i;
>> +	unsigned long prev_freq = 0;
>> +	int ret = 0;
>> +
>> +	if (unlikely(!rsc || !rsc->wrapper_dev || !rsc->se_clk || !tbl))
> 
> Drop the "unlikely", you're only calling this once.
Ok.
> 
>> +		return -EINVAL;
>> +
>> +	*tbl = NULL;
> 
> Don't touch tbl on error.
Ok.
> 
>> +	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
>> +	if (unlikely(!geni_se_dev))
>> +		return -EPROBE_DEFER;
> 
> How would this even be possible? This function should only be called by
> child devices, which per definition means that this device been probed.
I agree. Prior to this patchset, the serial engine nodes were not 
children nodes of wrapper devices. Hence the check existed. Now that the 
device tree nodes are re-structured, this check can be removed.
> 
>> +
>> +	mutex_lock(&geni_se_dev->geni_dev_lock);
>> +	if (geni_se_dev->clk_perf_tbl) {
>> +		*tbl = geni_se_dev->clk_perf_tbl;
>> +		ret = geni_se_dev->num_clk_levels;
>> +		goto exit_se_clk_tbl_get;
>> +	}
> 
> You're never freeing clk_perf_tbl, and the only reason you have
> geni_dev_lock is to protect the "cached" array in geni_se_dev.
> 
> Move the allocation and generation of clk_perf_tbl to geni_se_probe()
> and store the value, then make this function just return that array.
> That way you can drop the lock and the code becomes cleaner.
Currently this driver is initializing in arch_init. It can be moved to 
module_init stage as long as the clk_round_rate() returns -EPROBE_DEFER 
when it is not ready. I will look into it and do the appropriate change.
> 
>> +
>> +	geni_se_dev->clk_perf_tbl = kzalloc(sizeof(*geni_se_dev->clk_perf_tbl) *
>> +						MAX_CLK_PERF_LEVEL, GFP_KERNEL);
> 
> Use kcalloc()
Ok.
> 
>> +	if (!geni_se_dev->clk_perf_tbl) {
>> +		ret = -ENOMEM;
>> +		goto exit_se_clk_tbl_get;
>> +	}
>> +
>> +	for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
>> +		geni_se_dev->clk_perf_tbl[i] = clk_round_rate(rsc->se_clk,
>> +								prev_freq + 1);
>> +		if (geni_se_dev->clk_perf_tbl[i] == prev_freq) {
>> +			geni_se_dev->clk_perf_tbl[i] = 0;
> 
> Use a local variable to keep the return value of clk_round_rate(), that
> way you don't have to revert the value here (not that it matters...) and
> you don't need to line wrap the assignment above.
Ok.
> 
>> +			break;
>> +		}
>> +		prev_freq = geni_se_dev->clk_perf_tbl[i];
> 
> ...and then you don't need a separate local variable to keep track of
> the last value...
Ok.
> 
>> +	}
>> +	geni_se_dev->num_clk_levels = i;
>> +	*tbl = geni_se_dev->clk_perf_tbl;
>> +	ret = geni_se_dev->num_clk_levels;
>> +exit_se_clk_tbl_get:
> 
> Name your labels based on what action they perform, e.g. "out_unlock"
> (not err_unlock here because it's the successful path as well)
Ok.
> 
>> +	mutex_unlock(&geni_se_dev->geni_dev_lock);
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(geni_se_clk_tbl_get);
>> +
>> +/**
>> + * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
>> + * @rsc:	Resource for which the clock frequency is requested.
>> + * @req_freq:	Requested clock frequency.
>> + * @index:	Index of the resultant frequency in the table.
>> + * @res_freq:	Resultant frequency which matches or is closer to the
>> + *		requested frequency.
>> + * @exact:	Flag to indicate exact multiple requirement of the requested
>> + *		frequency .
>> + *
>> + * This function is called by the protocol drivers to determine the matching
>> + * or closest frequency of the Serial Engine clock to be selected in order
>> + * to meet the performance requirements.
> 
> What's the benefit compared to calling clk_round_rate()?
> 
> Given that the function can return a rate that is a fraction of the
> requested frequency even though "exact" is set, this description isn't
> explaining the entire picture.
I agree, the description makes it look like clk_round_rate(). But it is 
different from clk_round_rate() in the sense that it will try to return 
the matching or exact multiple of the requested frequency. If there is 
no matching or exact multiple value found, then it returns the closest 
floor frequency available. I will update the description accordingly.
> 
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure.
> 
> Returning the new clockrate would make more sense.
> 
>> + */
>> +int geni_se_clk_freq_match(struct geni_se_rsc *rsc, unsigned long req_freq,
>> +			   unsigned int *index, unsigned long *res_freq,
>> +			   bool exact)
>> +{
>> +	unsigned long *tbl;
>> +	int num_clk_levels;
>> +	int i;
>> +
>> +	num_clk_levels = geni_se_clk_tbl_get(rsc, &tbl);
>> +	if (num_clk_levels < 0)
>> +		return num_clk_levels;
>> +
>> +	if (num_clk_levels == 0)
>> +		return -EFAULT;
>> +
>> +	*res_freq = 0;
>> +	for (i = 0; i < num_clk_levels; i++) {
>> +		if (!(tbl[i] % req_freq)) {
>> +			*index = i;
>> +			*res_freq = tbl[i];
> 
> So this will return a frequency that divides the requested frequency
> without a remainder, will index be used to calculate a multiplier from
> this?
The serial interface drivers need to program the hardware register with 
the index of the clock rate from the clock controller source. Hence the 
index is returned.
> 
>> +			return 0;
>> +		}
>> +
>> +		if (!(*res_freq) || ((tbl[i] > *res_freq) &&
>> +				     (tbl[i] < req_freq))) {
>> +			*index = i;
>> +			*res_freq = tbl[i];
> 
> What if there's a previous value in tbl that given some multiplier has a
> smaller difference to the requested frequency?
At this point the requirement is to return matching or exact multiple of 
the requested frequency. If those criteria are not met, return the 
closest floor frequency.
> 
>> +		}
>> +	}
>> +
>> +	if (exact || !(*res_freq))
> 
> *res_freq can't be 0, because in the case that num_clk_levels is 0 you
> already returned -EFAULT above, in all other cases you will assign it at
> least once.
Ok.
> 
>> +		return -ENOKEY;
>> +
>> +	return 0;
> 
> Why not use the return value for the calculated frequency?
With frequency being unsigned long, I dont see a clean way to return any 
error.
> 
>> +}
>> +EXPORT_SYMBOL(geni_se_clk_freq_match);
>> +
>> +/**
>> + * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
>> + * @wrapper_dev:	QUP Wrapper Device to which the TX buffer is mapped.
>> + * @base:		Base address of the SE register block.
>> + * @tx_buf:		Pointer to the TX buffer.
>> + * @tx_len:		Length of the TX buffer.
>> + * @tx_dma:		Pointer to store the mapped DMA address.
>> + *
>> + * This function is used to prepare the buffers for DMA TX.
>> + *
>> + * Return:	0 on success, standard Linux error codes on error/failure.
>> + */
>> +int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base,
>> +			void *tx_buf, int tx_len, dma_addr_t *tx_dma)
> 
> All child devices will have a "base", so if you move that into what you
> today call a geni_se_rsc and you pass that to all these functions
> operating on behalf of the child device you have the first two
> parameters passed in the same object.
> 
> A "tx_len" is typically of type size_t.
> 
> Also note that there are no other buf than tx_buf, no other len than
> tx_len and no other dma address than tx_dma in this function, so you can
> drop "tx_" without loosing any information - but improving code
> cleanliness.
Ok.
> 
>> +{
>> +	int ret;
>> +
>> +	if (unlikely(!wrapper_dev || !base || !tx_buf || !tx_len || !tx_dma))
>> +		return -EINVAL;
> 
> Reduce the error checking here.
Ok.
> 
>> +
>> +	ret = geni_se_map_buf(wrapper_dev, tx_dma, tx_buf, tx_len,
>> +				    DMA_TO_DEVICE);
> 
> I think you should pass the wrapper itself to geni_se_tx_dma_prep() and
> if you name this "wrapper" (instead of wrapper_dev) you're under 80
> chars and don't need the line break.
Ok.
> 
>> +	if (ret)
>> +		return ret;
>> +
>> +	geni_write_reg(7, base, SE_DMA_TX_IRQ_EN_SET);
> 
> So that's DMA_RX_IRQ_EN | DMA_TX_IRQ_EN | GENI_M_IRQ_EN?
No, that is DMA_DONE_EN (BIT 0), EOT_EN (BIT 1) and AHB_ERR_EN (BIT 2).
> 
>> +	geni_write_reg((u32)(*tx_dma), base, SE_DMA_TX_PTR_L);
>> +	geni_write_reg((u32)((*tx_dma) >> 32), base, SE_DMA_TX_PTR_H);
> 
> If you return the dma_addr_t from this function you will have the happy
> side effect of being able to use tx_dma directly instead of *tx_dma and
> you can remove some craziness from these calls.
> 
> 
> 
>> +	geni_write_reg(1, base, SE_DMA_TX_ATTR);
> 
> This "1" would be nice to have some clarification on.
This indicates to the hardware that the current buffer is of 
End-of-Transfer Type. I will add a comment here or replace the magic 1 
with an appropriate preprocessor macro.
> 
>> +	geni_write_reg(tx_len, base, SE_DMA_TX_LEN);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
>> +
>> +/**
>> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
>> + * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
>> + * @base:		Base address of the SE register block.
>> + * @rx_buf:		Pointer to the RX buffer.
>> + * @rx_len:		Length of the RX buffer.
>> + * @rx_dma:		Pointer to store the mapped DMA address.
>> + *
>> + * This function is used to prepare the buffers for DMA RX.
>> + *
>> + * Return:	0 on success, standard Linux error codes on error/failure.
> 
> The only real error that can come out of this is dma_mapping_error(), so
> just return the dma_addr_t.
Ok.
> 
>> + */
>> +int geni_se_rx_dma_prep(struct device *wrapper_dev, void __iomem *base,
>> +			void *rx_buf, int rx_len, dma_addr_t *rx_dma)
> 
> As with tx, drop all the "rx_". And pass that geni_se_rsc object
> instead.
Ok.
> 
>> +{
>> +	int ret;
>> +
>> +	if (unlikely(!wrapper_dev || !base || !rx_buf || !rx_len || !rx_dma))
>> +		return -EINVAL;
>> +
>> +	ret = geni_se_map_buf(wrapper_dev, rx_dma, rx_buf, rx_len,
>> +				    DMA_FROM_DEVICE);
>> +	if (ret)
>> +		return ret;
>> +
>> +	geni_write_reg(7, base, SE_DMA_RX_IRQ_EN_SET);
> 
> We enable same interrupts for rx as tx? Why enable TX interrupt here?
No, the interrupts enabled are DMA_DONE_EN, EOT_EN and AHB_ERR_EN.
> 
>> +	geni_write_reg((u32)(*rx_dma), base, SE_DMA_RX_PTR_L);
>> +	geni_write_reg((u32)((*rx_dma) >> 32), base, SE_DMA_RX_PTR_H);
>> +	/* RX does not have EOT bit */
> 
> Okay? How does this relate to the 0 being written into RX_ATTR?
Instead of EOT bit, this register has RX DMA mode bits. Writing 0 means 
the buffer is a single buffer. Writing 1 means the data is present as 
part of the command itself and 2 means the buffer is scatter-gather buffer.
> 
>> +	geni_write_reg(0, base, SE_DMA_RX_ATTR);
>> +	geni_write_reg(rx_len, base, SE_DMA_RX_LEN);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_rx_dma_prep);
>> +
>> +/**
>> + * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
>> + * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
>> + * @tx_dma:		DMA address of the TX buffer.
>> + * @tx_len:		Length of the TX buffer.
>> + *
>> + * This function is used to unprepare the DMA buffers after DMA TX.
>> + */
>> +void geni_se_tx_dma_unprep(struct device *wrapper_dev,
>> +			   dma_addr_t tx_dma, int tx_len)
>> +{
>> +	if (tx_dma)
>> +		geni_se_unmap_buf(wrapper_dev, &tx_dma, tx_len,
>> +						DMA_TO_DEVICE);
>> +}
>> +EXPORT_SYMBOL(geni_se_tx_dma_unprep);
>> +
>> +/**
>> + * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
>> + * @wrapper_dev:	QUP Wrapper Device to which the RX buffer is mapped.
>> + * @rx_dma:		DMA address of the RX buffer.
>> + * @rx_len:		Length of the RX buffer.
>> + *
>> + * This function is used to unprepare the DMA buffers after DMA RX.
>> + */
>> +void geni_se_rx_dma_unprep(struct device *wrapper_dev,
>> +			   dma_addr_t rx_dma, int rx_len)
>> +{
>> +	if (rx_dma)
>> +		geni_se_unmap_buf(wrapper_dev, &rx_dma, rx_len,
>> +						DMA_FROM_DEVICE);
>> +}
>> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
>> +
>> +/**
>> + * geni_se_map_buf() - Map a single buffer into QUP wrapper device
> 
> I find the mixture of prepare and map in the API (where prepare also
> maps) confusing, but no-one calls genbi_se_map_buf() can it be made
> internal?
Sure it can be made internal and later exposed when the serial interface 
drivers need it.
> 
>> + * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
>> + * @iova:		Pointer in which the mapped virtual address is stored.
>> + * @buf:		Address of the buffer that needs to be mapped.
>> + * @size:		Size of the buffer.
>> + * @dir:		Direction of the DMA transfer.
>> + *
>> + * This function is used to map an already allocated buffer into the
>> + * QUP device space.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_map_buf(struct device *wrapper_dev, dma_addr_t *iova,
>> +		    void *buf, size_t size, enum dma_data_direction dir)
>> +{
>> +	struct device *dev_p;
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (!wrapper_dev || !iova || !buf || !size)
>> +		return -EINVAL;
> 
> No need to check that the programmer of the intermediate function
> checked that the programmer of the client filled all these out
> correctly.
Ok.
> 
>> +
>> +	geni_se_dev = dev_get_drvdata(wrapper_dev);
>> +	if (!geni_se_dev || !geni_se_dev->dev)
>> +		return -ENODEV;
> 
> Pass the geni_se_device from the child driver, to remove the need for
> this.
Ok.
> 
>> +
>> +	dev_p = geni_se_dev->dev;
> 
> Name your variables more succinct and you don't need this local alias.
Ok.
> 
>> +
>> +	*iova = dma_map_single(dev_p, buf, size, dir);
> 
> Just inline dma_map_single() in the functions calling this.
Ok.
> 
>> +	if (dma_mapping_error(dev_p, *iova))
>> +		return -EIO;
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_map_buf);
>> +
>> +/**
>> + * geni_se_unmap_buf() - Unmap a single buffer from QUP wrapper device
>> + * @wrapper_dev:	Pointer to the corresponding QUP wrapper core.
>> + * @iova:		Pointer in which the mapped virtual address is stored.
>> + * @size:		Size of the buffer.
>> + * @dir:		Direction of the DMA transfer.
>> + *
>> + * This function is used to unmap an already mapped buffer from the
>> + * QUP device space.
>> + *
>> + * Return:	0 on success, standard Linux error codes on failure/error.
>> + */
>> +int geni_se_unmap_buf(struct device *wrapper_dev, dma_addr_t *iova,
>> +		      size_t size, enum dma_data_direction dir)
> 
> There's no reason for iova to be a pointer. Just pass the dma_addr_t as
> is.
Ok.
> 
> Should this function really be exposed to the clients?
It can be made internal based on the current use-case.
> 
>> +{
>> +	struct device *dev_p;
>> +	struct geni_se_device *geni_se_dev;
>> +
>> +	if (!wrapper_dev || !iova || !size)
>> +		return -EINVAL;
> 
> Reduce the error checking.
Ok.
> 
>> +
>> +	geni_se_dev = dev_get_drvdata(wrapper_dev);
>> +	if (!geni_se_dev || !geni_se_dev->dev)
>> +		return -ENODEV;
> 
> And pass the geni_se_rsc to this function for symmetry purposes, which
> would give you the wrapper by just following the pointer and then the
> device from there.
Ok.
> 
>> +
>> +	dev_p = geni_se_dev->dev;
>> +	dma_unmap_single(dev_p, *iova, size, dir);
> 
> Just inline dma_unmap_single() in the calling functions.
Ok.
> 
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(geni_se_unmap_buf);
>> +
>> +static const struct of_device_id geni_se_dt_match[] = {
>> +	{ .compatible = "qcom,geni-se-qup", },
>> +	{}
>> +};
> 
> Move this next to the geni_se_driver below and don't forget the
> MODULE_DEVICE_TABLE()
Ok.
> 
>> +
>> +static int geni_se_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct resource *res;
>> +	struct geni_se_device *geni_se_dev;
> 
> Just name this "geni".
Ok.
> 
>> +	int ret = 0;
> 
> No need to initialize ret, it's only ever used after assignment.
Ok.
> 
>> +
>> +	geni_se_dev = devm_kzalloc(dev, sizeof(*geni_se_dev), GFP_KERNEL);
>> +	if (!geni_se_dev)
>> +		return -ENOMEM;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(dev, "%s: Mandatory resource info not found\n",
>> +			__func__);
>> +		devm_kfree(dev, geni_se_dev);
>> +		return -EINVAL;
>> +	}
> 
> It's idiomatic to not check for errors of platform_get_resource() as
> devm_ioremap_resource() will fail gracefully if this happens.
Ok, I will remove the check and use the error returned by 
devm_ioremap_resource, if any.
> 
>> +
>> +	geni_se_dev->base = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR_OR_NULL(geni_se_dev->base)) {
> 
> This should be IS_ERR() only.
Ok.
> 
>> +		dev_err(dev, "%s: Error mapping the resource\n", __func__);
>> +		devm_kfree(dev, geni_se_dev);
>> +		return -EFAULT;
>> +	}
>> +
>> +	geni_se_dev->m_ahb_clk = devm_clk_get(dev, "m-ahb");
>> +	if (IS_ERR(geni_se_dev->m_ahb_clk)) {
>> +		ret = PTR_ERR(geni_se_dev->m_ahb_clk);
>> +		dev_err(dev, "Err getting M AHB clk %d\n", ret);
>> +		devm_iounmap(dev, geni_se_dev->base);
>> +		devm_kfree(dev, geni_se_dev);
> 
> The reason we use the devm_ versions of these is so that we don't have
> to release our resources explicitly.
Ok, I will remove releasing the resources explicitly.
> 
>> +		return ret;
>> +	}
>> +
>> +	geni_se_dev->s_ahb_clk = devm_clk_get(dev, "s-ahb");
>> +	if (IS_ERR(geni_se_dev->s_ahb_clk)) {
>> +		ret = PTR_ERR(geni_se_dev->s_ahb_clk);
>> +		dev_err(dev, "Err getting S AHB clk %d\n", ret);
>> +		devm_clk_put(dev, geni_se_dev->m_ahb_clk);
>> +		devm_iounmap(dev, geni_se_dev->base);
>> +		devm_kfree(dev, geni_se_dev);
>> +		return ret;
>> +	}
> 
> Use devm_clk_bulk_get().
Ok.
> 
>> +
>> +	geni_se_dev->dev = dev;
>> +	mutex_init(&geni_se_dev->geni_dev_lock);
>> +	dev_set_drvdata(dev, geni_se_dev);
>> +	dev_dbg(dev, "GENI SE Driver probed\n");
>> +	return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
> 
> You're not depopulating these devices when the wrapper goes away. Use
> devm_of_platform_populate() here instead to make that happen.
Ok.
> 
>> +}
>> +
>> +static int geni_se_remove(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct geni_se_device *geni_se_dev = dev_get_drvdata(dev);
>> +
>> +	devm_clk_put(dev, geni_se_dev->s_ahb_clk);
>> +	devm_clk_put(dev, geni_se_dev->m_ahb_clk);
>> +	devm_iounmap(dev, geni_se_dev->base);
>> +	devm_kfree(dev, geni_se_dev);
> 
> Again, the reason to use devm_* is so that you don't have to free
> things...so if this is what you have here you don't even need a remove
> function.
Ok.
> 
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver geni_se_driver = {
>> +	.driver = {
>> +		.name = "geni_se_qup",
>> +		.of_match_table = geni_se_dt_match,
>> +	},
>> +	.probe = geni_se_probe,
>> +	.remove = geni_se_remove,
>> +};
>> +
>> +static int __init geni_se_driver_init(void)
>> +{
>> +	return platform_driver_register(&geni_se_driver);
>> +}
>> +arch_initcall(geni_se_driver_init);
>> +
>> +static void __exit geni_se_driver_exit(void)
>> +{
>> +	platform_driver_unregister(&geni_se_driver);
>> +}
>> +module_exit(geni_se_driver_exit);
> 
> Should be fine to just use module_platform_driver(), you need separate
> support for earlycon regardless.
Ok.
> 
>> +
>> +MODULE_DESCRIPTION("GENI Serial Engine Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
>> new file mode 100644
>> index 0000000..5695da9
>> --- /dev/null
>> +++ b/include/linux/qcom-geni-se.h
>> @@ -0,0 +1,807 @@
>> +/*
>> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> [..]
>> + */
>> +
> 
> Use SPDX header as above.
Ok.
> 
>> +#ifndef _LINUX_QCOM_GENI_SE
>> +#define _LINUX_QCOM_GENI_SE
>> +#include <linux/clk.h>
>> +#include <linux/dma-direction.h>
>> +#include <linux/io.h>
>> +#include <linux/list.h>
> 
> I don't think there's a need for io.h or list.h here.
Ok.
> 
>> +
>> +/* Transfer mode supported by GENI Serial Engines */
>> +enum geni_se_xfer_mode {
>> +	INVALID,
>> +	FIFO_MODE,
>> +	SE_DMA,
> 
> These are quite bad names for things in a header file.
I am not sure if you are suggesting me to use a prefix to make these 
definitions look GENI specific. I would prefer some clarification in 
this regard.
> 
>> +};
>> +
>> +/* Protocols supported by GENI Serial Engines */
>> +enum geni_se_protocol_types {
>> +	NONE,
>> +	SPI,
>> +	UART,
>> +	I2C,
>> +	I3C
> 
> Ditto
> 
>> +};
>> +
>> +/**
>> + * struct geni_se_rsc - GENI Serial Engine Resource
>> + * @wrapper_dev:	Pointer to the parent QUP Wrapper core.
>> + * @se_clk:		Handle to the core serial engine clock.
>> + * @geni_pinctrl:	Handle to the pinctrl configuration.
>> + * @geni_gpio_active:	Handle to the default/active pinctrl state.
>> + * @geni_gpi_sleep:	Handle to the sleep pinctrl state.
>> + */
>> +struct geni_se_rsc {
> 
> This looks like the common geni_port or geni_device I requested above.
Yes that is right. I will use it to include the serial engine base address.
> 
>> +	struct device *wrapper_dev;
>> +	struct clk *se_clk;
> 
> The one and only clock can be named "clk".
Ok.
> 
>> +	struct pinctrl *geni_pinctrl;
>> +	struct pinctrl_state *geni_gpio_active;
>> +	struct pinctrl_state *geni_gpio_sleep;
> 
> The associated struct device already has init, default, idle and sleep
> pinctrl states associated through the device->pins struct. Typically
> this means that if you provide a "default" and "sleep" state, the
> default will be selected when the device probes.
> 
> In order to select between the states call
> pinctrl_pm_select_{default,sleep,idle}_state(dev);
Ok.
> 
>> +};
>> +
>> +#define PINCTRL_DEFAULT	"default"
>> +#define PINCTRL_SLEEP	"sleep"
>> +
>> +/* Common SE registers */
> 
> The purpose of the helper functions in the wrapper driver is to hide
> the common details from the individual function drivers, so move these
> defines into the c-file as they shouldn't be needed in the individual
> drivers.
I will move the register definitions that are accessed through helper 
functions inside the source file. I may have to keep those register 
definitions that are common and are directly accessed by the serial 
interface drivers here so that there are no duplicate definitions.
> 
>> +#define GENI_INIT_CFG_REVISION		(0x0)
> 
> Drop the parenthesis.
Ok.
> 
> [..]
>> +#ifdef CONFIG_QCOM_GENI_SE
>> +/**
>> + * geni_read_reg_nolog() - Helper function to read from a GENI register
>> + * @base:	Base address of the serial engine's register block.
>> + * @offset:	Offset within the serial engine's register block.
>> + *
>> + * Return:	Return the contents of the register.
>> + */
> 
> The kerneldoc goes in the implementation, not the header file. And you
> already have a copy there, so remove it from here.
Ok.
> 
>> +unsigned int geni_read_reg_nolog(void __iomem *base, int offset);
> [..]
>> +#else
> 
> I see no point in providing dummy functions for these, just make the
> individual drivers either depend or select the core helpers.
Ok.
> 
>> +static inline unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
>> +{
>> +	return 0;
>> +}
> 
> Regards,
> Bjorn
> 
Regards,
Karthik.
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

  parent reply	other threads:[~2018-01-31 18:58 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-01-13  1:05 [PATCH v2 0/7] Introduce GENI SE Controller Driver Karthikeyan Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary Karthikeyan Ramasubramanian
2018-01-16 16:55   ` Bjorn Andersson
2018-01-29 21:52     ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 2/7] dt-bindings: soc: qcom: Add device tree binding for GENI SE Karthikeyan Ramasubramanian
2018-01-17  6:25   ` Bjorn Andersson
2018-01-19 22:53     ` Rob Herring
2018-02-26 21:24     ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 4/7] dt-bindings: i2c: Add device tree bindings for GENI I2C Controller Karthikeyan Ramasubramanian
     [not found]   ` <1515805547-22816-5-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-17  6:31     ` Bjorn Andersson
2018-02-26 21:28       ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 6/7] dt-bindings: serial: Add bindings for GENI based UART Controller Karthikeyan Ramasubramanian
2018-01-17  6:35   ` Bjorn Andersson
2018-02-27 13:25     ` Karthik Ramasubramanian
2018-01-13  1:05 ` [PATCH v2 7/7] tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP Karthikeyan Ramasubramanian
2018-01-18 19:43   ` Bjorn Andersson
2018-01-19  7:12   ` Bjorn Andersson
2018-02-27 15:07     ` Karthik Ramasubramanian
2018-01-24 23:07   ` [v2, " Evan Green
2018-02-14 11:04   ` [PATCH v2 " Amit Kucheria
2018-02-23 18:06   ` [v2, " Guenter Roeck
2018-02-27 13:23     ` Karthik Ramasubramanian
2018-02-23 19:05   ` Doug Anderson
     [not found] ` <1515805547-22816-1-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-13  1:05   ` [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver Karthikeyan Ramasubramanian
2018-01-17  6:20     ` Bjorn Andersson
2018-01-18  9:13       ` Rajendra Nayak
2018-01-18 16:57         ` Bjorn Andersson
2018-01-19 22:57           ` Rob Herring
2018-01-31 19:02             ` Karthik Ramasubramanian
     [not found]               ` <1abb0679-1997-9b70-30bd-d3472cea7053-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-02-05 23:53                 ` Bjorn Andersson
2018-01-31 18:58       ` Karthik Ramasubramanian [this message]
2018-02-05 23:50         ` Bjorn Andersson
     [not found]     ` <1515805547-22816-4-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-24 23:06       ` [v2,3/7] " Evan Green
2018-01-26 19:38     ` Doug Anderson
2018-02-14 11:07     ` [PATCH v2 3/7] " Amit Kucheria
2018-02-16 20:44       ` Karthik Ramasubramanian
2018-01-13  1:05   ` [PATCH v2 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller Karthikeyan Ramasubramanian
     [not found]     ` <1515805547-22816-6-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2018-01-18  5:23       ` Bjorn Andersson
2018-02-27 13:16         ` Karthik Ramasubramanian
2018-01-24 23:07     ` [v2, " Evan Green
2018-01-19 18:32   ` [PATCH v2 0/7] Introduce GENI SE Controller Driver Randy Dunlap
2018-01-31 18:59     ` Karthik Ramasubramanian

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1faee481-8cb6-4dc6-ac0f-eb89445227e1@codeaurora.org \
    --to=kramasub@codeaurora.org \
    --cc=andy.gross@linaro.org \
    --cc=bjorn.andersson@linaro.org \
    --cc=corbet@lwn.net \
    --cc=david.brown@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=girishm@codeaurora.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=jslaby@suse.com \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=robh+dt@kernel.org \
    --cc=sdharia@codeaurora.org \
    --cc=wsa@the-dreams.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).