RE: [RFC] omap: hwspinlock: Added hwspinlock driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




>-----Original Message-----
>From: linux-omap-owner@xxxxxxxxxxxxxxx [mailto:linux-omap-
>owner@xxxxxxxxxxxxxxx] On Behalf Of Que, Simon
>Sent: Thursday, June 24, 2010 7:40 PM
>To: linux-omap@xxxxxxxxxxxxxxx
>Cc: Kanigeri, Hari; Ohad Ben-Cohen
>Subject: [RFC] omap: hwspinlock: Added hwspinlock driver
>
>
>Hi,
>
<snip>

>diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
>index 6725b3a..14af19a 100644
>--- a/arch/arm/mach-omap2/Makefile
>+++ b/arch/arm/mach-omap2/Makefile
>@@ -170,3 +170,5 @@ obj-y                                       += $(nand-
>m) $(nand-y)
>
> smc91x-$(CONFIG_SMC91X)                        := gpmc-smc91x.o
> obj-y                                  += $(smc91x-m) $(smc91x-y)
>+
>+obj-y                                  += hwspinlocks.o

If this block is only on OMAP4, why build it for omap2 and omap3?

>\ No newline at end of file
>diff --git a/arch/arm/mach-omap2/hwspinlocks.c b/arch/arm/mach-
>omap2/hwspinlocks.c
>new file mode 100644
>index 0000000..de813a0
>--- /dev/null
>+++ b/arch/arm/mach-omap2/hwspinlocks.c
>@@ -0,0 +1,126 @@
>+/*
>+ * OMAP hardware spinlock driver
>+ *
>+ * Copyright (C) 2010 Texas Instruments. All rights reserved.
>+ *
>+ * Contact: Simon Que <sque@xxxxxx>
>+ *
>+ * This program is free software; you can redistribute it and/or
>+ * modify it under the terms of the GNU General Public License
>+ * 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.
>+ *
>+ * You should have received a copy of the GNU General Public License
>+ * along with this program; if not, write to the Free Software
>+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>+ * 02110-1301 USA
>+ *
>+ */
>+
>+#include <linux/module.h>
>+#include <linux/interrupt.h>
>+#include <linux/device.h>
>+#include <linux/delay.h>
>+#include <linux/io.h>
>+#include <linux/slab.h>
>+
>+#include <plat/hwspinlock.h>
>+
>+/* Base address of HW spinlock module */
>+#define HWSPINLOCK_BASE                (L4_44XX_BASE + 0xF6000)
>+#define HWSPINLOCK_REGADDR(reg)                                \
>+                       OMAP2_L4_IO_ADDRESS(HWSPINLOCK_BASE + (reg))
>+
>+/* Spinlock register offsets */
>+#define HWSPINLOCK_REVISION                    0x0000
>+#define HWSPINLOCK_SYSCONFIG                   0x0010
>+#define HWSPINLOCK_SYSSTATUS                   0x0014
>+#define HWSPINLOCK_LOCK_BASE                   0x0800
>+
>+/* Spinlock register addresses */
>+#define HWSPINLOCK_REVISION_REG                        \
>+       HWSPINLOCK_REGADDR(HWSPINLOCK_REVISION)
>+#define HWSPINLOCK_SYSCONFIG_REG                       \
>+       HWSPINLOCK_REGADDR(HWSPINLOCK_SYSCONFIG)
>+#define HWSPINLOCK_SYSSTATUS_REG                       \
>+       HWSPINLOCK_REGADDR(HWSPINLOCK_SYSSTATUS)
>+#define HWSPINLOCK_LOCK_REG(i)                 \
>+       HWSPINLOCK_REGADDR(HWSPINLOCK_LOCK_BASE + 0x4 * (i))
>+
>+/* Spinlock count code */
>+#define HWSPINLOCK_32_REGS                             1
>+#define HWSPINLOCK_64_REGS                             2
>+#define HWSPINLOCK_128_REGS                            4
>+#define HWSPINLOCK_256_REGS                            8
>+#define HWSPINLOCK_NUMLOCKS_OFFSET                     24
>+
>+
>+/* Initialization function */
>+int __init hwspinlocks_init(void)
>+{
>+       int i;
>+       int retval = 0;
>+
>+       struct platform_device *pdev;
>+       struct hwspinlock_plat_info *pdata;
>+       void __iomem *base;
>+       int num_locks;
>+       bool is_reserved;
>+
>+       /* Determine number of locks */
>+       switch (__raw_readl(HWSPINLOCK_SYSSTATUS_REG) >>
>+                                       HWSPINLOCK_NUMLOCKS_OFFSET) {
>+       case HWSPINLOCK_32_REGS:
>+               num_locks = 32;
>+               break;
>+       case HWSPINLOCK_64_REGS:
>+               num_locks = 64;
>+               break;
>+       case HWSPINLOCK_128_REGS:
>+               num_locks = 128;
>+               break;
>+       case HWSPINLOCK_256_REGS:
>+               num_locks = 256;
>+               break;
>+       default:
>+               return -EINVAL; /* Invalid spinlock count code */
>+       }
>+
>+       /* Device drivers */
>+       for (i = 0; i < num_locks; i++) {
>+               pdev = platform_device_alloc("hwspinlock", i);
>+
>+               base = HWSPINLOCK_LOCK_REG(i);  /* Get register address */
>+
>+               /* Some locks are reserved for system use */
>+               if (i < CONFIG_OMAP_HWSPINLOCK_NUM_RESERVED)
>+                       is_reserved = true;
>+               else
>+                       is_reserved = false;
>+
>+               /* Pass data to device initialization */
>+               pdata = kzalloc(sizeof(struct hwspinlock_plat_info),
>+
>GFP_KERNEL);
>+               pdata->num_locks = num_locks;
>+               pdata->io_base = base;

Any reason not to have the driver ioremap the HWSPINLOCK address and rather pass the physical pointer to driver?


>+               pdata->is_reserved = is_reserved;
>+               retval = platform_device_add_data(pdev, pdata,
>sizeof(*pdata));
>+               if (retval)
>+                       goto device_add_fail;
>+
>+               retval = platform_device_add(pdev);
>+               if (retval)
>+                       goto device_add_fail;
>+               continue;
>+device_add_fail:
>+               platform_device_put(pdev);
>+       }
>+
>+       return retval;
>+}
>+module_init(hwspinlocks_init);
>+
>diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile
>index a37abf5..fb98ff9 100644
>--- a/arch/arm/plat-omap/Makefile
>+++ b/arch/arm/plat-omap/Makefile
>@@ -32,4 +32,5 @@ obj-y += $(i2c-omap-m) $(i2c-omap-y)
> obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox.o
> obj-$(CONFIG_OMAP_REMOTE_PROC) += remoteproc.o
>
>-obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-noop.o
>\ No newline at end of file
>+obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-noop.o
>+obj-y += hwspinlock.o

Same comment as above - why build for all platforms?

>\ No newline at end of file
>diff --git a/arch/arm/plat-omap/hwspinlock.c b/arch/arm/plat-
>omap/hwspinlock.c
>new file mode 100644
>index 0000000..327a524
>--- /dev/null
>+++ b/arch/arm/plat-omap/hwspinlock.c
>@@ -0,0 +1,331 @@
>+/*
>+ * OMAP hardware spinlock driver
>+ *
>+ * Copyright (C) 2010 Texas Instruments. All rights reserved.
>+ *
>+ * Contact: Simon Que <sque@xxxxxx>
>+ *
>+ * This program is free software; you can redistribute it and/or
>+ * modify it under the terms of the GNU General Public License
>+ * 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.
>+ *
>+ * You should have received a copy of the GNU General Public License
>+ * along with this program; if not, write to the Free Software
>+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>+ * 02110-1301 USA
>+ *
>+ */
>+
>+#include <linux/module.h>
>+#include <linux/interrupt.h>
>+#include <linux/device.h>
>+#include <linux/delay.h>
>+#include <linux/io.h>
>+#include <linux/slab.h>
>+#include <linux/spinlock.h>
>+
>+#include <plat/hwspinlock.h>
>+
>+/* for managing a hardware spinlock module */
>+struct hwspinlock_state {
>+       bool is_init;                   /* For first-time initialization */
>+       int num_locks;                  /* Total number of locks in system
>*/
>+       spinlock_t local_lock;          /* Local protection */
>+};
>+
>+/* Points to the hardware spinlock module */
>+static struct hwspinlock_state hwspinlock_state;
>+static struct hwspinlock_state *hwspinlock_module = &hwspinlock_state;
>+
>+/* Spinlock object */
>+struct hwspinlock {
>+       void __iomem *io_base;
>+       bool is_reserved;
>+       bool is_allocated;
>+       struct platform_device *pdev;
>+};
>+
>+/* Array of spinlocks */
>+static struct hwspinlock *hwspinlocks;
>+
>+/* API functions */
>+
>+/* Busy loop to acquire a spinlock */
>+int hwspinlock_lock(struct hwspinlock *handle)
>+{
>+       int retval;
>+
>+       if (WARN_ON(handle == NULL))
>+               return -EINVAL;
>+
>+       if (WARN_ON(in_atomic() || in_irq()))
>+               return -EPERM;
>+
>+       /* Attempt to acquire the lock by reading from it */
>+       do {
>+               retval = __raw_readl(handle->io_base);
>+       } while (retval == HWSPINLOCK_BUSY);
>+
>+       return 0;
>+}
>+EXPORT_SYMBOL(hwspinlock_lock);
>+
>+/* Attempt to acquire a spinlock once */
>+int hwspinlock_trylock(struct hwspinlock *handle)
>+{
>+       int retval = 0;
>+
>+       if (WARN_ON(handle == NULL))
>+               return -EINVAL;
>+
>+       if (WARN_ON(in_atomic() || in_irq()))
>+               return -EPERM;
>+
>+       /* Attempt to acquire the lock by reading from it */
>+       retval = __raw_readl(handle->io_base);
>+
>+       return retval;
>+}
>+EXPORT_SYMBOL(hwspinlock_trylock);
>+
>+/* Release a spinlock */
>+int hwspinlock_unlock(struct hwspinlock *handle)
>+{
>+       if (WARN_ON(handle == NULL))
>+               return -EINVAL;
>+
>+       /* Release it by writing 0 to it */
>+       __raw_writel(0, handle->io_base);
>+
>+       return 0;
>+}
>+EXPORT_SYMBOL(hwspinlock_unlock);
>+
>+/* Busy loop to acquire a spinlock, disabling interrupts/preemption */
>+int hwspinlock_lock_irqsave(struct hwspinlock *handle, unsigned long
>*flags)
>+{
>+       int retval;
>+       unsigned long temp_flags;
>+
>+       if (WARN_ON(handle == NULL))
>+               return -EINVAL;
>+
>+       if (WARN_ON(in_atomic() || in_irq()))
>+               return -EPERM;
>+
>+       if (WARN_ON(flags == NULL))
>+               return -EINVAL;
>+
>+       /* Attempt to acquire the lock by reading from it */
>+       do {
>+               preempt_disable();                      /* Disable
>preemption */
>+               local_irq_save(temp_flags);             /* Disable
>interrupts */
>+
>+               retval = __raw_readl(handle->io_base);
>+
>+               /* Restore interrupts and preemption if not successful */
>+               if (retval == HWSPINLOCK_BUSY) {
>+                       local_irq_restore(temp_flags);
>+                       preempt_enable();
>+               }
>+       } while (retval == HWSPINLOCK_BUSY);
>+
>+       *flags = temp_flags;            /* Return IRQ state */
>+
>+       return 0;
>+}
>+EXPORT_SYMBOL(hwspinlock_lock_irqsave);
>+
>+/* Attempt to acquire a spinlock once, disabling interrupts/preemption */
>+int hwspinlock_trylock_irqsave(struct hwspinlock *handle, unsigned long
>*flags)
>+{
>+       int retval = 0;
>+       unsigned long temp_flags;
>+
>+       if (WARN_ON(handle == NULL))
>+               return -EINVAL;
>+
>+       if (WARN_ON(in_atomic() || in_irq()))
>+               return -EPERM;
>+
>+       if (WARN_ON(flags == NULL))
>+               return -EINVAL;
>+
>+       preempt_disable();                      /* Disable preemption */

Any hard need to disable PREEMPTION?

>+       local_irq_save(temp_flags);             /* Disable interrupts */
>+
>+       /* Attempt to acquire the lock by reading from it */
>+       retval = __raw_readl(handle->io_base);
>+
>+       /* Restore interrupts and preemption if not successful */
>+       if (retval == HWSPINLOCK_BUSY) {
>+               local_irq_restore(temp_flags);
>+               preempt_enable();
>+       } else
>+               *flags = temp_flags;    /* Return IRQ state */
>+
>+       return retval;
>+}
>+EXPORT_SYMBOL(hwspinlock_trylock_irqsave);
>+
>+/* Unlock a spinlock that was locked with irq/preempt disabled */
>+int hwspinlock_unlock_irqrestore(struct hwspinlock *handle, unsigned long
>+
>flags)
>+{
>+       if (WARN_ON(handle == NULL))
>+               return -EINVAL;
>+
>+       /* Release it by writing 0 to it */
>+       __raw_writel(0, handle->io_base);
>+
>+       /* Restore interrupts and preemption */
>+       local_irq_restore(flags);
>+       preempt_enable();
>+
>+       return 0;
>+}
>+EXPORT_SYMBOL(hwspinlock_unlock_irqrestore);
>+
>+/* Request an unclaimed spinlock */
>+struct hwspinlock *hwspinlock_request(void)
>+{
>+       int i;
>+       bool found = false;
>+       struct hwspinlock *handle = NULL;
>+       unsigned long flags;
>+
>+       spin_lock_irqsave(&hwspinlock_module->local_lock, flags);
>+       /* Search for an unclaimed, unreserved lock */
>+       for (i = 0; i < hwspinlock_module->num_locks && !found; i++) {
>+               if (!hwspinlocks[i].is_allocated &&
>+
>!hwspinlocks[i].is_reserved) {
>+                       found = true;
>+                       handle = &hwspinlocks[i];
>+               }
>+       }
>+       spin_unlock_irqrestore(&hwspinlock_module->local_lock, flags);
>+
>+       /* Return error if no more locks available */
>+       if (!found)
>+               return NULL;
>+
>+       handle->is_allocated = true;
>+
>+       return handle;
>+}
>+EXPORT_SYMBOL(hwspinlock_request);
>+
>+/* Request an unclaimed spinlock by ID */
>+struct hwspinlock *hwspinlock_request_specific(unsigned int id)
>+{
>+       struct hwspinlock *handle = NULL;
>+       unsigned long flags;
>+
>+       spin_lock_irqsave(&hwspinlock_module->local_lock, flags);
>+
>+       if (WARN_ON(!hwspinlocks[id].is_reserved))
>+               goto exit;
>+
>+       if (WARN_ON(hwspinlocks[id].is_allocated))
>+               goto exit;
>+
>+       handle = &hwspinlocks[id];
>+       handle->is_allocated = true;
>+
>+exit:
>+       spin_unlock_irqrestore(&hwspinlock_module->local_lock, flags);
>+       return handle;
>+}
>+EXPORT_SYMBOL(hwspinlock_request_specific);
>+
>+/* Release a claimed spinlock */
>+int hwspinlock_free(struct hwspinlock *handle)
>+{
>+       if (WARN_ON(handle == NULL))
>+               return -EINVAL;
>+
>+       if (WARN_ON(!handle->is_allocated))
>+               return -ENOMEM;
>+
>+       handle->is_allocated = false;
>+
>+       return 0;
>+}
>+EXPORT_SYMBOL(hwspinlock_free);
>+
>+/* Probe function */
>+static int __devinit hwspinlock_probe(struct platform_device *pdev)
>+{
>+       struct hwspinlock_plat_info *pdata = pdev->dev.platform_data;
>+       int id = pdev->id;
>+
>+       /* Set up the spinlock count and array */
>+       if (!hwspinlock_module->is_init) {
>+               hwspinlock_module->num_locks = pdata->num_locks;
>+
>+               /* Allocate spinlock device objects */
>+               hwspinlocks = kmalloc(sizeof(struct hwspinlock) *
>+                               hwspinlock_module->num_locks, GFP_KERNEL);
>+               if (WARN_ON(hwspinlocks == NULL))
>+                       return -ENOMEM;
>+
>+               /* Initialize local lock */
>+               spin_lock_init(&hwspinlock_module->local_lock);
>+
>+               /* Only do initialization once */
>+               hwspinlock_module->is_init = true;
>+       }
>+
>+       hwspinlocks[id].pdev                    = pdev;
>+
>+       hwspinlocks[id].is_reserved             = pdata->is_reserved;
>+       hwspinlocks[id].is_allocated            = false;
>+       hwspinlocks[id].io_base                 = pdata->io_base;
>+
>+       return 0;
>+}
>+
>+static struct platform_driver hwspinlock_driver = {
>+       .probe          = hwspinlock_probe,
>+       .driver         = {
>+               .name   = "hwspinlock",
>+       },
>+};
>+
>+/* Initialization function */
>+static int __init hwspinlock_init(void)
>+{
>+       int retval = 0;
>+
>+       /* Register spinlock driver */
>+       retval = platform_driver_register(&hwspinlock_driver);
>+
>+       /* Make sure the it was properly initialized */
>+       if (WARN_ON(!hwspinlock_module->is_init))
>+               return -EACCES;
>+
>+       return retval;
>+}
>+
>+/* Cleanup function */
>+static void __exit hwspinlock_exit(void)
>+{
>+       platform_driver_unregister(&hwspinlock_driver);
>+
>+       /* Free spinlock device objects */
>+       if (hwspinlock_module->is_init)
>+               kfree(hwspinlocks);
>+}
>+
>+module_init(hwspinlock_init);
>+module_exit(hwspinlock_exit);
>+
>+MODULE_LICENSE("GPL v2");
>+MODULE_DESCRIPTION("Hardware spinlock driver");
>+MODULE_AUTHOR("Simon Que");
>+MODULE_AUTHOR("Hari Kanigeri");
>diff --git a/arch/arm/plat-omap/include/plat/hwspinlock.h b/arch/arm/plat-
>omap/include/plat/hwspinlock.h
>new file mode 100644
>index 0000000..1cdf7a8
>--- /dev/null
>+++ b/arch/arm/plat-omap/include/plat/hwspinlock.h
>@@ -0,0 +1,35 @@
>+/* hwspinlock.h */
>+
>+#ifndef HWSPINLOCK_H
>+#define HWSPINLOCK_H
>+
>+#include <linux/platform_device.h>
>+#include <plat/omap44xx.h>
>+
>+/* Read values from the spinlock register */
>+#define HWSPINLOCK_ACQUIRED 0
>+#define HWSPINLOCK_BUSY 1
>+
>+/* Device data */
>+struct hwspinlock_plat_info {
>+       int num_locks;                  /* Number of locks (initialization)
>*/
>+       void __iomem *io_base;          /* Address of spinlock register */
>+       bool is_reserved;               /* Reserved for system use? */
>+};
>+
>+struct hwspinlock;
>+
>+int hwspinlock_lock(struct hwspinlock *handle);
>+int hwspinlock_trylock(struct hwspinlock *handle);
>+int hwspinlock_unlock(struct hwspinlock *handle);
>+
>+int hwspinlock_lock_irqsave(struct hwspinlock *handle, unsigned long
>*flags);
>+int hwspinlock_trylock_irqsave(struct hwspinlock *handle, unsigned long
>*flags);
>+int hwspinlock_unlock_irqrestore(struct hwspinlock *handle, unsigned long
>+
>flags);
>+
>+struct hwspinlock *hwspinlock_request(void);
>+struct hwspinlock *hwspinlock_request_specific(unsigned int id);
>+int hwspinlock_free(struct hwspinlock *hwspinlock_ptr);
>+
>+#endif /* HWSPINLOCK_H */
>--
>1.7.0

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux