On Fri 11 Dec 08:40 CST 2020, Samuel Holland wrote:

> On 12/11/20 3:03 AM, Maxime Ripard wrote:
> > Hi,
> > 
> > On Fri, Dec 11, 2020 at 09:24:15AM +0100, Wilken Gottwalt wrote:
> >> Adds the sun8i_hwspinlock driver for the hardware spinlock unit found in
> >> most of the sun8i compatible SoCs.
> >>
> >> This unit provides at least 32 spinlocks in hardware. The implementation
> >> supports 32, 64, 128 or 256 32bit registers. A lock can be taken by
> >> reading a register and released by writing a 0 to it. This driver
> >> supports all 4 spinlock setups, but for now only the first setup (32
> >> locks) seem to exist in available devices. This spinlock unit is shared
> >> between all ARM cores and the embedded OpenRisc AR100 core. All of them
> >> can take/release a lock with a single cycle operation. It can be used to
> >> sync access to devices shared by the ARM cores and the OpenRisc core.
> >>
> >> There are two ways to check if a lock is taken. The first way is to read
> >> a lock. If a 0 is returned, the lock was free and is taken now. If an 1
> >> is returned, the caller has to try again. Which means the lock is taken.
> >> The second way is to read a 32bit wide status register where every bit
> >> represents one of the 32 first locks. According to the datasheets this
> >> status register supports only the 32 first locks. This is the reason the
> >> first way (lock read/write) approach is used to be able to cover all 256
> >> locks in future devices. The driver also reports the amount of supported
> >> locks via debugfs.
> >>
> >> Signed-off-by: Wilken Gottwalt <wilken.gottw...@posteo.net>
> >> ---
> >> Changes in v4:
> >>   - further simplified driver
> >>   - fixed an add_action_and_reset_ function issue
> >>   - changed bindings from sun8i-hwspinlock to sun8i-a33-hwspinlock
> >>
> >> Changes in v3:
> >>   - moved test description to cover letter
> >>   - changed name and symbols from sunxi to sun8i
> >>   - improved driver description
> >>   - further simplified driver
> >>   - fully switched to devm_* and devm_add_action_* functions
> >>
> >> Changes in v2:
> >>   - added suggestions from Bjorn Andersson and Maxime Ripard
> >>   - provided better driver and test description
> >> ---
> >>  MAINTAINERS                           |   6 +
> >>  drivers/hwspinlock/Kconfig            |   9 ++
> >>  drivers/hwspinlock/Makefile           |   1 +
> >>  drivers/hwspinlock/sun8i_hwspinlock.c | 197 ++++++++++++++++++++++++++
> >>  4 files changed, 213 insertions(+)
> >>  create mode 100644 drivers/hwspinlock/sun8i_hwspinlock.c
> >>
> >> diff --git a/MAINTAINERS b/MAINTAINERS
> >> index ebe4829cdd4d..46846113f1eb 100644
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -722,6 +722,12 @@ L:    linux-cry...@vger.kernel.org
> >>  S:        Maintained
> >>  F:        drivers/crypto/allwinner/
> >>  
> >> +ALLWINNER HARDWARE SPINLOCK SUPPORT
> >> +M:        Wilken Gottwalt <wilken.gottw...@posteo.net>
> >> +S:        Maintained
> >> +F:        Documentation/devicetree/bindings/hwlock/sun8i-hwspinlock.yaml
> >> +F:        drivers/hwspinlock/sun8i_hwspinlock.c
> >> +
> >>  ALLWINNER THERMAL DRIVER
> >>  M:        Vasily Khoruzhick <anars...@gmail.com>
> >>  M:        Yangtao Li <tiny.win...@gmail.com>
> >> diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig
> >> index 32cd26352f38..b03fd99aab32 100644
> >> --- a/drivers/hwspinlock/Kconfig
> >> +++ b/drivers/hwspinlock/Kconfig
> >> @@ -55,6 +55,15 @@ config HWSPINLOCK_STM32
> >>  
> >>      If unsure, say N.
> >>  
> >> +config HWSPINLOCK_SUN8I
> >> +  tristate "SUN8I Hardware Spinlock device"
> >> +  depends on ARCH_SUNXI || COMPILE_TEST
> >> +  help
> >> +    Say y here to support the SUN8I Hardware Spinlock device which can be
> >> +    found in most of the sun8i compatible Allwinner SoCs.
> >> +
> >> +    If unsure, say N.
> >> +
> >>  config HSEM_U8500
> >>    tristate "STE Hardware Semaphore functionality"
> >>    depends on ARCH_U8500 || COMPILE_TEST
> >> diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile
> >> index ed053e3f02be..3496648d9257 100644
> >> --- a/drivers/hwspinlock/Makefile
> >> +++ b/drivers/hwspinlock/Makefile
> >> @@ -9,4 +9,5 @@ obj-$(CONFIG_HWSPINLOCK_QCOM)              += 
> >> qcom_hwspinlock.o
> >>  obj-$(CONFIG_HWSPINLOCK_SIRF)             += sirf_hwspinlock.o
> >>  obj-$(CONFIG_HWSPINLOCK_SPRD)             += sprd_hwspinlock.o
> >>  obj-$(CONFIG_HWSPINLOCK_STM32)            += stm32_hwspinlock.o
> >> +obj-$(CONFIG_HWSPINLOCK_SUN8I)            += sun8i_hwspinlock.o
> >>  obj-$(CONFIG_HSEM_U8500)          += u8500_hsem.o
> >> diff --git a/drivers/hwspinlock/sun8i_hwspinlock.c 
> >> b/drivers/hwspinlock/sun8i_hwspinlock.c
> >> new file mode 100644
> >> index 000000000000..878dae6f1763
> >> --- /dev/null
> >> +++ b/drivers/hwspinlock/sun8i_hwspinlock.c
> >> @@ -0,0 +1,197 @@
> >> +// SPDX-License-Identifier: GPL-2.0-or-later
> >> +/*
> >> + * sun8i_hwspinlock.c - hardware spinlock driver for sun8i compatible 
> >> Allwinner SoCs
> >> + * Copyright (C) 2020 Wilken Gottwalt <wilken.gottw...@posteo.net>
> >> + */
> >> +
> >> +#include <linux/clk.h>
> >> +#include <linux/debugfs.h>
> >> +#include <linux/errno.h>
> >> +#include <linux/hwspinlock.h>
> >> +#include <linux/io.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/reset.h>
> >> +#include <linux/slab.h>
> >> +#include <linux/spinlock.h>
> >> +#include <linux/types.h>
> >> +
> >> +#include "hwspinlock_internal.h"
> >> +
> >> +#define DRIVER_NAME               "sun8i_hwspinlock"
> >> +
> >> +#define SPINLOCK_BASE_ID  0 /* there is only one hwspinlock device per 
> >> SoC */
> >> +#define SPINLOCK_SYSSTATUS_REG    0x0000
> >> +#define SPINLOCK_LOCK_REGN        0x0100
> >> +#define SPINLOCK_NOTTAKEN 0
> >> +
> >> +struct sun8i_hwspinlock_data {
> >> +  struct hwspinlock_device *bank;
> >> +  struct reset_control *reset;
> >> +  struct clk *ahb_clk;
> >> +  struct dentry *debugfs;
> >> +  int nlocks;
> >> +};
> >> +
> >> +#ifdef CONFIG_DEBUG_FS
> >> +
> >> +static int hwlocks_supported_show(struct seq_file *seqf, void *unused)
> >> +{
> >> +  struct sun8i_hwspinlock_data *priv = seqf->private;
> >> +
> >> +  seq_printf(seqf, "%d\n", priv->nlocks);
> >> +
> >> +  return 0;
> >> +}
> >> +DEFINE_SHOW_ATTRIBUTE(hwlocks_supported);
> >> +
> >> +static void sun8i_hwspinlock_debugfs_init(struct sun8i_hwspinlock_data 
> >> *priv)
> >> +{
> >> +  priv->debugfs = debugfs_create_dir(DRIVER_NAME, NULL);
> > 
> > debugfs_create_dir can return an error pointer, you should check that
> > and return the error code if it does
> > 
> >> +  debugfs_create_file("supported", 0444, priv->debugfs, priv, 
> >> &hwlocks_supported_fops);
> > 
> > And debugfs_create_file can fail as well
> 
> My understanding was that you're not supposed to check the return values
> of debugfs_create_* functions, because debugfs is just for debugging and
> shouldn't prevent the driver from loading.
> 

That is correct, the debugfs api is written with the expectation that
you shouldn't do error handling.

> >> +}
> >> +
> >> +#else
> >> +
> >> +static void sun8i_hwspinlock_debugfs_init(struct sun8i_hwspinlock_data 
> >> *priv)
> >> +{
> >> +}
> >> +
> >> +#endif
> >> +
> >> +static int sun8i_hwspinlock_trylock(struct hwspinlock *lock)
> >> +{
> >> +  void __iomem *lock_addr = lock->priv;
> >> +
> >> +  return (readl(lock_addr) == SPINLOCK_NOTTAKEN);
> >> +}
> >> +
> >> +static void sun8i_hwspinlock_unlock(struct hwspinlock *lock)
> >> +{
> >> +  void __iomem *lock_addr = lock->priv;
> >> +
> >> +  writel(SPINLOCK_NOTTAKEN, lock_addr);
> >> +}
> >> +
> >> +static const struct hwspinlock_ops sun8i_hwspinlock_ops = {
> >> +  .trylock        = sun8i_hwspinlock_trylock,
> >> +  .unlock         = sun8i_hwspinlock_unlock,
> >> +};
> >> +
> >> +static void sun8i_hwspinlock_disable(void *data)
> >> +{
> >> +  struct sun8i_hwspinlock_data *priv = data;
> >> +
> >> +  debugfs_remove_recursive(priv->debugfs);
> >> +  reset_control_assert(priv->reset);
> >> +  clk_disable_unprepare(priv->ahb_clk);
> >> +}
> >> +
> >> +static int sun8i_hwspinlock_probe(struct platform_device *pdev)
> >> +{
> >> +  struct sun8i_hwspinlock_data *priv;
> >> +  struct hwspinlock *hwlock;
> >> +  void __iomem *io_base;
> >> +  u32 num_banks;
> >> +  int err, i;
> >> +
> >> +  io_base = devm_platform_ioremap_resource(pdev, SPINLOCK_BASE_ID);
> >> +  if (IS_ERR(io_base))
> >> +          return PTR_ERR(io_base);
> >> +
> >> +  priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> >> +  if (!priv)
> >> +          return -ENOMEM;
> >> +
> >> +  priv->ahb_clk = devm_clk_get(&pdev->dev, "ahb");
> >> +  if (IS_ERR(priv->ahb_clk)) {
> >> +          err = PTR_ERR(priv->ahb_clk);
> >> +          dev_err(&pdev->dev, "unable to get AHB clock (%d)\n", err);
> >> +          return err;
> >> +  }
> >> +
> >> +  priv->reset = devm_reset_control_get(&pdev->dev, "ahb");
> >> +  if (IS_ERR(priv->reset))
> >> +          return dev_err_probe(&pdev->dev, PTR_ERR(priv->reset),
> >> +                               "unable to get reset control\n");
> >> +
> >> +  err = reset_control_deassert(priv->reset);
> >> +  if (err) {
> >> +          dev_err(&pdev->dev, "deassert reset control failure (%d)\n", 
> >> err);
> >> +          return err;
> >> +  }
> >> +
> >> +  err = clk_prepare_enable(priv->ahb_clk);
> >> +  if (err) {
> >> +          dev_err(&pdev->dev, "unable to prepare AHB clk (%d)\n", err);
> >> +          return err;
> >> +  }
> >> +
> >> +  sun8i_hwspinlock_debugfs_init(priv);
> >> +
> >> +  err = devm_add_action_or_reset(&pdev->dev, sun8i_hwspinlock_disable, 
> >> priv);
> >> +  if (err) {
> >> +          dev_err(&pdev->dev, "unable to add disable action\n");
> >> +          return err;
> >> +  }
> > 
> > That part still doesn't really work: if clk_prepare_enable fails, you'll
> > end up removing the debugfs files that haven't been added yet, and
> > you'll disable the clock that hasn't been enabled.
> > 
> > I'm not sure you need to be that smart: just add a label and a goto to
> > the proper cleaning stuff
> 
> I would also suggest implementing the driver's .remove callback instead
> of allocating additional memory for a devm action.
> 

I have a feeling that I might have asked for the use of devm helpers.
But afaict we still need a remove function to remove the debugfs stuff.

So I agree with your ask.

Regards,
Bjorn

Reply via email to