* [RFC] omap: hwspinlock: Added hwspinlock driver
@ 2010-06-25 0:40 Que, Simon
2010-06-25 8:11 ` Cousson, Benoit
` (2 more replies)
0 siblings, 3 replies; 12+ messages in thread
From: Que, Simon @ 2010-06-25 0:40 UTC (permalink / raw)
To: linux-omap@vger.kernel.org; +Cc: Kanigeri, Hari, Ohad Ben-Cohen
[-- Attachment #1: Type: text/plain, Size: 21173 bytes --]
Hi,
We are introducing a kernel driver for hardware spinlock, called hwspinlock.
It is designed to interface with the OMAP4 hardware spinlock module. This
driver supports:
- Reserved spinlocks for internal use
- Dynamic allocation of unreserved locks
- Lock, unlock, and trylock functions, with or without disabling irqs/preempt
- Registered as a platform device driver
The device initialization will set aside some spinlocks as reserved for
special-purpose use. All other locks can be used by anyone. This is
configurable using a Kconfig option. The device initialization file is:
arch/arm/mach-omap2/hwspinlocks.c
The driver takes in data passed in device initialization. The function
hwspinlock_probe() initializes the array of spinlock structures, each
containing a spinlock register address provided by the device initialization.
The device driver file is:
arch/arm/plat-omap/hwspinlock.c
Here's an API summary:
int hwspinlock_lock(struct hwspinlock *);
Attempt to lock a hardware spinlock. If it is busy, the function will
keep trying until it succeeds. This is a blocking function.
int hwspinlock_trylock(struct hwspinlock *);
Attempt to lock a hardware spinlock. If it is busy, the function will
return BUSY. If it succeeds in locking, the function will return
ACQUIRED. This is a non-blocking function
int hwspinlock_unlock(struct hwspinlock *);
Unlock a hardware spinlock.
int hwspinlock_lock_irqsave(struct hwspinlock *, unsigned long *);
Same as hwspinlock_lock, but disables interrupts and preemption
int hwspinlock_trylock_irqsave(struct hwspinlock *, unsigned long *);
Same as hwspinlock_trylock, but disables interrupts and preemption
int hwspinlock_unlock_irqrestore(struct hwspinlock *, unsigned long);
Same as hwspinlock_unlock, but restores interrupts and preemption
struct hwspinlock *hwspinlock_request(void);
Provides for "dynamic allocation" of an unreserved hardware spinlock.
If no more locks are available, returns NULL.
struct hwspinlock *hwspinlock_request_specific(unsigned int);
Provides for "static allocation" of a reserved hardware spinlock. This
allows the system to use a specific reserved lock, identified by an ID.
If the ID is invalid or if the desired lock is already allocated, this
will return NULL.
int hwspinlock_free(struct hwspinlock *);
Frees an allocated hardware spinlock (either reserved or unreserved).
Please see the below patch contents, or the attached patch, and provide feedback.
Signed-off-by: Simon Que <sque@ti.com>
Cc: Hari Kanigeri <h-kanigeri2@ti.com>
=====================================================================
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 9f73d79..a13c188 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -182,3 +182,13 @@ config OMAP3_SDRC_AC_TIMING
wish to say no. Selecting yes without understanding what is
going on could result in system crashes;
+config OMAP_HWSPINLOCK_NUM_RESERVED
+ int "Number of hardware spinlocks reserved for system use"
+ depends on ARCH_OMAP
+ default 8
+ range 0 32
+ help
+ Choose a number of hardware spinlocks to reserve for internal use.
+ The rest will be unreserved and availble for general use. Make
+ that the number of reserved locks does not exceed the total number
+ available locks.
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
\ 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@ti.com>
+ *
+ * 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;
+ 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
\ 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@ti.com>
+ *
+ * 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 */
+ 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
[-- Attachment #2: 0001-omap-hwspinlock-Added-hwspinlock-driver.patch --]
[-- Type: application/octet-stream, Size: 18143 bytes --]
From 9d967fabfd300bae13fa2233a803b6e30477baf8 Mon Sep 17 00:00:00 2001
From: Simon Que <sque@ti.com>
Date: Wed, 23 Jun 2010 18:40:30 -0500
Subject: [PATCH] omap: hwspinlock: Added hwspinlock driver
Created driver for OMAP hardware spinlock. This driver supports:
- Reserved spinlocks for internal use
- Dynamic allocation of unreserved locks
- Lock, unlock, and trylock functions, with or without disabling irqs/preempt
- Registered as a platform device driver
The device initialization will set aside some spinlocks as reserved for
special-purpose use. All other locks can be used by anyone. This is
configurable using a Kconfig option. The device initialization file is:
arch/arm/mach-omap2/hwspinlocks.c
The driver takes in data passed in device initialization. The function
hwspinlock_probe() initializes the array of spinlock structures, each
containing a spinlock register address provided by the device initialization.
The device driver file is:
arch/arm/plat-omap/hwspinlock.c
Here's an API summary:
int hwspinlock_lock(struct hwspinlock *);
Attempt to lock a hardware spinlock. If it is busy, the function will
keep trying until it succeeds. This is a blocking function.
int hwspinlock_trylock(struct hwspinlock *);
Attempt to lock a hardware spinlock. If it is busy, the function will
return BUSY. If it succeeds in locking, the function will return
ACQUIRED. This is a non-blocking function
int hwspinlock_unlock(struct hwspinlock *);
Unlock a hardware spinlock.
int hwspinlock_lock_irqsave(struct hwspinlock *, unsigned long *);
Same as hwspinlock_lock, but disables interrupts and preemption
int hwspinlock_trylock_irqsave(struct hwspinlock *, unsigned long *);
Same as hwspinlock_trylock, but disables interrupts and preemption
int hwspinlock_unlock_irqrestore(struct hwspinlock *, unsigned long);
Same as hwspinlock_unlock, but restores interrupts and preemption
struct hwspinlock *hwspinlock_request(void);
Provides for "dynamic allocation" of an unreserved hardware spinlock.
If no more locks are available, returns NULL.
struct hwspinlock *hwspinlock_request_specific(unsigned int);
Provides for "static allocation" of a reserved hardware spinlock. This
allows the system to use a specific reserved lock, identified by an ID.
If the ID is invalid or if the desired lock is already allocated, this
will return NULL.
int hwspinlock_free(struct hwspinlock *);
Frees an allocated hardware spinlock (either reserved or unreserved).
Signed-off-by: Simon Que <sque@ti.com>
---
arch/arm/mach-omap2/Kconfig | 10 +
arch/arm/mach-omap2/Makefile | 2 +
arch/arm/mach-omap2/hwspinlocks.c | 126 ++++++++++
arch/arm/plat-omap/Makefile | 3 +-
arch/arm/plat-omap/hwspinlock.c | 331 ++++++++++++++++++++++++++
arch/arm/plat-omap/include/plat/hwspinlock.h | 35 +++
6 files changed, 506 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/mach-omap2/hwspinlocks.c
create mode 100644 arch/arm/plat-omap/hwspinlock.c
create mode 100644 arch/arm/plat-omap/include/plat/hwspinlock.h
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 9f73d79..a13c188 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -182,3 +182,13 @@ config OMAP3_SDRC_AC_TIMING
wish to say no. Selecting yes without understanding what is
going on could result in system crashes;
+config OMAP_HWSPINLOCK_NUM_RESERVED
+ int "Number of hardware spinlocks reserved for system use"
+ depends on ARCH_OMAP
+ default 8
+ range 0 32
+ help
+ Choose a number of hardware spinlocks to reserve for internal use.
+ The rest will be unreserved and availble for general use. Make
+ that the number of reserved locks does not exceed the total number
+ available locks.
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
\ 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@ti.com>
+ *
+ * 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;
+ 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
\ 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@ti.com>
+ *
+ * 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 */
+ 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
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [RFC] omap: hwspinlock: Added hwspinlock driver
2010-06-25 0:40 [RFC] omap: hwspinlock: Added hwspinlock driver Que, Simon
@ 2010-06-25 8:11 ` Cousson, Benoit
2010-06-25 18:13 ` Que, Simon
2010-06-25 18:26 ` Pandita, Vikram
2010-06-25 18:33 ` Shilimkar, Santosh
2 siblings, 1 reply; 12+ messages in thread
From: Cousson, Benoit @ 2010-06-25 8:11 UTC (permalink / raw)
To: Que, Simon; +Cc: linux-omap@vger.kernel.org, Kanigeri, Hari, Ohad Ben-Cohen
Hi Simon,
On 6/25/2010 2:40 AM, Que, Simon wrote:
>
> Hi,
>
> We are introducing a kernel driver for hardware spinlock, called hwspinlock.
> It is designed to interface with the OMAP4 hardware spinlock module. This
> driver supports:
> - Reserved spinlocks for internal use
> - Dynamic allocation of unreserved locks
> - Lock, unlock, and trylock functions, with or without disabling irqs/preempt
> - Registered as a platform device driver
>
> The device initialization will set aside some spinlocks as reserved for
> special-purpose use. All other locks can be used by anyone. This is
> configurable using a Kconfig option. The device initialization file is:
> arch/arm/mach-omap2/hwspinlocks.c
Why using a Kconfig option in that case? You can reserve the locks at
run time based on other driver request. The goal of the hwspinlock is to
protect data shared between various processors inside OMAP4 like ipu,
iva, dsp and mpu. So in anycase the syslink driver can request the
needed locks at init time on behalf of the dsp or the ipu.
Since you don't even know the number of locks because it is determined
at init time, you cannot know at build time that information.
You should add an API to reserve some locks at run time instead of doing
that.
>
> The driver takes in data passed in device initialization. The function
> hwspinlock_probe() initializes the array of spinlock structures, each
> containing a spinlock register address provided by the device initialization.
> The device driver file is:
> arch/arm/plat-omap/hwspinlock.c
>
> Here's an API summary:
> int hwspinlock_lock(struct hwspinlock *);
> Attempt to lock a hardware spinlock. If it is busy, the function will
> keep trying until it succeeds. This is a blocking function.
> int hwspinlock_trylock(struct hwspinlock *);
> Attempt to lock a hardware spinlock. If it is busy, the function will
> return BUSY. If it succeeds in locking, the function will return
> ACQUIRED. This is a non-blocking function
> int hwspinlock_unlock(struct hwspinlock *);
> Unlock a hardware spinlock.
>
> int hwspinlock_lock_irqsave(struct hwspinlock *, unsigned long *);
> Same as hwspinlock_lock, but disables interrupts and preemption
> int hwspinlock_trylock_irqsave(struct hwspinlock *, unsigned long *);
> Same as hwspinlock_trylock, but disables interrupts and preemption
> int hwspinlock_unlock_irqrestore(struct hwspinlock *, unsigned long);
> Same as hwspinlock_unlock, but restores interrupts and preemption
>
> struct hwspinlock *hwspinlock_request(void);
> Provides for "dynamic allocation" of an unreserved hardware spinlock.
> If no more locks are available, returns NULL.
> struct hwspinlock *hwspinlock_request_specific(unsigned int);
> Provides for "static allocation" of a reserved hardware spinlock. This
> allows the system to use a specific reserved lock, identified by an ID.
> If the ID is invalid or if the desired lock is already allocated, this
> will return NULL.
> int hwspinlock_free(struct hwspinlock *);
> Frees an allocated hardware spinlock (either reserved or unreserved).
>
> Please see the below patch contents, or the attached patch, and provide feedback.
>
> Signed-off-by: Simon Que<sque@ti.com>
> Cc: Hari Kanigeri<h-kanigeri2@ti.com>
>
> =====================================================================
>
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index 9f73d79..a13c188 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -182,3 +182,13 @@ config OMAP3_SDRC_AC_TIMING
> wish to say no. Selecting yes without understanding what is
> going on could result in system crashes;
>
> +config OMAP_HWSPINLOCK_NUM_RESERVED
> + int "Number of hardware spinlocks reserved for system use"
> + depends on ARCH_OMAP
> + default 8
> + range 0 32
> + help
> + Choose a number of hardware spinlocks to reserve for internal use.
> + The rest will be unreserved and availble for general use. Make
> + that the number of reserved locks does not exceed the total number
> + available locks.
> 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
> \ 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@ti.com>
> + *
> + * 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)
It is a details, but why are you renaming the registers with the HW
prefix? The module name is spinlock not hwspinlock.
The names should be that:
#define SPINLOCK_REVISION 0x0000
#define SPINLOCK_SYSCONFIG 0x0010
#define SPINLOCK_SYSSTATUS 0x0014
#define SPINLOCK_LOCK_BASE 0x0800
or even that since you are not sharing these defines:
#define REVISION 0x0000
#define SYSCONFIG 0x0010
#define SYSSTATUS 0x0014
#define LOCK_BASE 0x0800
Every IPs are by definition an HW module, so that information is useless.
> +#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);
Since it is a new driver for a new IP, why don't you use directly the
omap_device / omap_hwmod abstraction?
Regards,
Benoit
^ permalink raw reply [flat|nested] 12+ messages in thread* RE: [RFC] omap: hwspinlock: Added hwspinlock driver
2010-06-25 8:11 ` Cousson, Benoit
@ 2010-06-25 18:13 ` Que, Simon
2010-06-25 18:38 ` Kevin Hilman
2010-06-25 21:02 ` Cousson, Benoit
0 siblings, 2 replies; 12+ messages in thread
From: Que, Simon @ 2010-06-25 18:13 UTC (permalink / raw)
To: Cousson, Benoit
Cc: linux-omap@vger.kernel.org, Kanigeri, Hari, Ohad Ben-Cohen
Benoit,
Thanks for the comments. My responses are below.
> Why using a Kconfig option in that case? You can reserve the locks at
> run time based on other driver request. The goal of the hwspinlock is to
> protect data shared between various processors inside OMAP4 like ipu,
> iva, dsp and mpu. So in anycase the syslink driver can request the
> needed locks at init time on behalf of the dsp or the ipu.
>
> Since you don't even know the number of locks because it is determined
> at init time, you cannot know at build time that information.
>
> You should add an API to reserve some locks at run time instead of doing
> that.
The reserved locks are not meant for syslink use only. It will also be used by i2c, which does not go through syslink. If we were to follow your suggestion, who should call the function to establish number of reserved locks? We can instead think of the config option as requesting a particular number of reserved locks for system use, independent of how many locks actually exist in the system. How many reserved locks there are, and which driver is using which reserved locks -- that is a system integration issue that should be sorted out at a higher level.
> It is a details, but why are you renaming the registers with the HW
> prefix? The module name is spinlock not hwspinlock.
>
> The names should be that:
> #define SPINLOCK_REVISION 0x0000
> #define SPINLOCK_SYSCONFIG 0x0010
> #define SPINLOCK_SYSSTATUS 0x0014
> #define SPINLOCK_LOCK_BASE 0x0800
>
> or even that since you are not sharing these defines:
> #define REVISION 0x0000
> #define SYSCONFIG 0x0010
> #define SYSSTATUS 0x0014
> #define LOCK_BASE 0x0800
>
> Every IPs are by definition an HW module, so that information is useless.
You have a good point, I will make this change.
>
> Since it is a new driver for a new IP, why don't you use directly the
> omap_device / omap_hwmod abstraction?
hwmod for spinlock is not currently ready. We want to integrate with the platform/driver/device system for now, as a first step. hwmod can be implemented later.
Simon
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC] omap: hwspinlock: Added hwspinlock driver
2010-06-25 18:13 ` Que, Simon
@ 2010-06-25 18:38 ` Kevin Hilman
2010-06-25 21:02 ` Cousson, Benoit
1 sibling, 0 replies; 12+ messages in thread
From: Kevin Hilman @ 2010-06-25 18:38 UTC (permalink / raw)
To: Que, Simon
Cc: Cousson, Benoit, linux-omap@vger.kernel.org, Kanigeri, Hari,
Ohad Ben-Cohen
"Que, Simon" <sque@ti.com> writes:
[...]
>> Why using a Kconfig option in that case? You can reserve the locks at
>> run time based on other driver request. The goal of the hwspinlock is to
>> protect data shared between various processors inside OMAP4 like ipu,
>> iva, dsp and mpu. So in anycase the syslink driver can request the
>> needed locks at init time on behalf of the dsp or the ipu.
>>
>> Since you don't even know the number of locks because it is determined
>> at init time, you cannot know at build time that information.
>>
>> You should add an API to reserve some locks at run time instead of doing
>> that.
>
> The reserved locks are not meant for syslink use only. It will also
> be used by i2c, which does not go through syslink. If we were to
> follow your suggestion, who should call the function to establish
> number of reserved locks?
Board code.
> We can instead think of the config option as requesting a particular
> number of reserved locks for system use, independent of how many locks
> actually exist in the system. How many reserved locks there are, and
> which driver is using which reserved locks -- that is a system
> integration issue that should be sorted out at a higher level.
Still, Benoit is right. It should be a run-time decision, not a
compile-time decision, and it could be done by a board file as needed.
Also, you wouldn't even need a new API for this. Just have the board
code request the specific spinlocks it would like to reserve.
>>
>> Since it is a new driver for a new IP, why don't you use directly the
>> omap_device / omap_hwmod abstraction?
>
> hwmod for spinlock is not currently ready.
Then please help get it ready. We need to work together to get hwmods
ready and validated for all IP blocks.
> We want to integrate with the platform/driver/device system for now,
> as a first step. hwmod can be implemented later.
As we are now switching to to hwmods, new drivers will not be merged
without hwmod support.
As a starint point, you can base your changes on the pm-wip/hwmods-omap4
branch of my linux-omap-pm git tree[1]
Many other folks are currently working hwmod conversions for several
other drivers using this as a base. Please help us with that effort.
Thanks,
Kevin
[1] git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-omap-pm.git
^ permalink raw reply [flat|nested] 12+ messages in thread
* RE: [RFC] omap: hwspinlock: Added hwspinlock driver
2010-06-25 18:13 ` Que, Simon
2010-06-25 18:38 ` Kevin Hilman
@ 2010-06-25 21:02 ` Cousson, Benoit
1 sibling, 0 replies; 12+ messages in thread
From: Cousson, Benoit @ 2010-06-25 21:02 UTC (permalink / raw)
To: Que, Simon; +Cc: linux-omap@vger.kernel.org, Kanigeri, Hari, Ohad Ben-Cohen
>From: Que, Simon
>Sent: Friday, June 25, 2010 8:14 PM
>
>Benoit,
>
>Thanks for the comments. My responses are below.
>
>> Why using a Kconfig option in that case? You can reserve the locks at
>> run time based on other driver request. The goal of the
>hwspinlock is to
>> protect data shared between various processors inside OMAP4 like ipu,
>> iva, dsp and mpu. So in anycase the syslink driver can request the
>> needed locks at init time on behalf of the dsp or the ipu.
>>
>> Since you don't even know the number of locks because it is
>determined
>> at init time, you cannot know at build time that information.
>>
>> You should add an API to reserve some locks at run time
>instead of doing
>> that.
>
>The reserved locks are not meant for syslink use only. It
>will also be used by i2c, which does not go through syslink.
>If we were to follow your suggestion, who should call the
>function to establish number of reserved locks? We can
>instead think of the config option as requesting a particular
>number of reserved locks for system use, independent of how
>many locks actually exist in the system. How many reserved
>locks there are, and which driver is using which reserved
>locks -- that is a system integration issue that should be
>sorted out at a higher level.
Syslink was just an example, any driver including i2c can reserve any number of locks.
The MPU is always the master, so no other processor will start if the MPU didn't
enable them previously. You can always reserve lock at runtime, during board init.
BTW, the spinlock is not a system wide resources but more a Soc resource.
>> It is a details, but why are you renaming the registers with the HW
>> prefix? The module name is spinlock not hwspinlock.
>>
>> The names should be that:
>> #define SPINLOCK_REVISION 0x0000
>> #define SPINLOCK_SYSCONFIG 0x0010
>> #define SPINLOCK_SYSSTATUS 0x0014
>> #define SPINLOCK_LOCK_BASE 0x0800
>>
>> or even that since you are not sharing these defines:
>> #define REVISION 0x0000
>> #define SYSCONFIG 0x0010
>> #define SYSSTATUS 0x0014
>> #define LOCK_BASE 0x0800
>>
>> Every IPs are by definition an HW module, so that
>information is useless.
>
>You have a good point, I will make this change.
>
>>
>> Since it is a new driver for a new IP, why don't you use directly the
>> omap_device / omap_hwmod abstraction?
>
>hwmod for spinlock is not currently ready. We want to
>integrate with the platform/driver/device system for now, as a
>first step. hwmod can be implemented later.
Not ready??? The data was in my git tree for months and is available in Kevin's tree for a while. http://git.kernel.org/?p=linux/kernel/git/khilman/linux-omap-pm.git;a=blob;f=arch/arm/mach-omap2/omap_hwmod_44xx_data.c;h=20f5f8c2a67e6e82854f66761f08faf7f65d6ee1;hb=7aefc6cd5c93afe464bcf527d20fdac81d00af77#l3597
Why did you think it was not ready?
Even if it was not there, it takes about 30 minutes to write the hwmod for such a basic IP.
Doing the platform driver first was perhaps needed for legacy code, in your case, you are probably wasting your time writing code that was be already there and that you will have to remove later.
Benoit
Texas Instruments France SA, 821 Avenue Jack Kilby, 06270 Villeneuve Loubet. 036 420 040 R.C.S Antibes. Capital de EUR 753.920
^ permalink raw reply [flat|nested] 12+ messages in thread
* RE: [RFC] omap: hwspinlock: Added hwspinlock driver
2010-06-25 0:40 [RFC] omap: hwspinlock: Added hwspinlock driver Que, Simon
2010-06-25 8:11 ` Cousson, Benoit
@ 2010-06-25 18:26 ` Pandita, Vikram
2010-06-25 18:33 ` Shilimkar, Santosh
2 siblings, 0 replies; 12+ messages in thread
From: Pandita, Vikram @ 2010-06-25 18:26 UTC (permalink / raw)
To: Que, Simon, linux-omap@vger.kernel.org; +Cc: Kanigeri, Hari, Ohad Ben-Cohen
>-----Original Message-----
>From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
>owner@vger.kernel.org] On Behalf Of Que, Simon
>Sent: Thursday, June 24, 2010 7:40 PM
>To: linux-omap@vger.kernel.org
>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@ti.com>
>+ *
>+ * 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@ti.com>
>+ *
>+ * 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
^ permalink raw reply [flat|nested] 12+ messages in thread* RE: [RFC] omap: hwspinlock: Added hwspinlock driver
2010-06-25 0:40 [RFC] omap: hwspinlock: Added hwspinlock driver Que, Simon
2010-06-25 8:11 ` Cousson, Benoit
2010-06-25 18:26 ` Pandita, Vikram
@ 2010-06-25 18:33 ` Shilimkar, Santosh
2010-06-25 20:31 ` Que, Simon
2 siblings, 1 reply; 12+ messages in thread
From: Shilimkar, Santosh @ 2010-06-25 18:33 UTC (permalink / raw)
To: Que, Simon, linux-omap@vger.kernel.org; +Cc: Kanigeri, Hari, Ohad Ben-Cohen
Simon,
> -----Original Message-----
> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-owner@vger.kernel.org] On Behalf Of Que,
> Simon
> Sent: Friday, June 25, 2010 6:10 AM
> To: linux-omap@vger.kernel.org
> Cc: Kanigeri, Hari; Ohad Ben-Cohen
> Subject: [RFC] omap: hwspinlock: Added hwspinlock driver
>
>
> Hi,
>
> We are introducing a kernel driver for hardware spinlock, called hwspinlock.
> It is designed to interface with the OMAP4 hardware spinlock module. This
> driver supports:
> - Reserved spinlocks for internal use
> - Dynamic allocation of unreserved locks
> - Lock, unlock, and trylock functions, with or without disabling irqs/preempt
> - Registered as a platform device driver
>
> The device initialization will set aside some spinlocks as reserved for
> special-purpose use. All other locks can be used by anyone. This is
> configurable using a Kconfig option. The device initialization file is:
> arch/arm/mach-omap2/hwspinlocks.c
>
> The driver takes in data passed in device initialization. The function
> hwspinlock_probe() initializes the array of spinlock structures, each
> containing a spinlock register address provided by the device initialization.
> The device driver file is:
> arch/arm/plat-omap/hwspinlock.c
>
> Here's an API summary:
> int hwspinlock_lock(struct hwspinlock *);
> Attempt to lock a hardware spinlock. If it is busy, the function will
> keep trying until it succeeds. This is a blocking function.
> int hwspinlock_trylock(struct hwspinlock *);
> Attempt to lock a hardware spinlock. If it is busy, the function will
> return BUSY. If it succeeds in locking, the function will return
> ACQUIRED. This is a non-blocking function
> int hwspinlock_unlock(struct hwspinlock *);
> Unlock a hardware spinlock.
>
> int hwspinlock_lock_irqsave(struct hwspinlock *, unsigned long *);
> Same as hwspinlock_lock, but disables interrupts and preemption
> int hwspinlock_trylock_irqsave(struct hwspinlock *, unsigned long *);
> Same as hwspinlock_trylock, but disables interrupts and preemption
> int hwspinlock_unlock_irqrestore(struct hwspinlock *, unsigned long);
> Same as hwspinlock_unlock, but restores interrupts and preemption
>
> struct hwspinlock *hwspinlock_request(void);
> Provides for "dynamic allocation" of an unreserved hardware spinlock.
> If no more locks are available, returns NULL.
> struct hwspinlock *hwspinlock_request_specific(unsigned int);
> Provides for "static allocation" of a reserved hardware spinlock. This
> allows the system to use a specific reserved lock, identified by an ID.
> If the ID is invalid or if the desired lock is already allocated, this
> will return NULL.
> int hwspinlock_free(struct hwspinlock *);
> Frees an allocated hardware spinlock (either reserved or unreserved).
>
> Please see the below patch contents, or the attached patch, and provide feedback.
>
> Signed-off-by: Simon Que <sque@ti.com>
> Cc: Hari Kanigeri <h-kanigeri2@ti.com>
>
> =====================================================================
>
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index 9f73d79..a13c188 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -182,3 +182,13 @@ config OMAP3_SDRC_AC_TIMING
> wish to say no. Selecting yes without understanding what is
> going on could result in system crashes;
>
> +config OMAP_HWSPINLOCK_NUM_RESERVED
> + int "Number of hardware spinlocks reserved for system use"
> + depends on ARCH_OMAP
It should have been "ARCH_OMAP4" but I think this KCONFIG should be killed
> + default 8
> + range 0 32
> + help
> + Choose a number of hardware spinlocks to reserve for internal use.
> + The rest will be unreserved and availble for general use. Make
> + that the number of reserved locks does not exceed the total number
> + available locks.
This reservation KCONFIG isn't necessary. HWSPINLOCK IP is not really for very generic use
but specific usages where you need to protect the data between independent softwares
running on different masters.
For other Linux only side software even with dual core, kernel spinlock library
is sufficient.
> 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
Are you building this for all OMAP's ??
> \ 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@ti.com>
> + *
> + * 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)
Move this base address to plat/omap44xx.h and use it from there
> +#define HWSPINLOCK_REGADDR(reg) \
> + OMAP2_L4_IO_ADDRESS(HWSPINLOCK_BASE + (reg))
" OMAP2_L4_IO_ADDRESS" this should not be used anymore. Rather, use
"ioremap and readl/writel"
> +
> +/* 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;
> + 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
> \ No newline at end of file
> diff --git a/arch/arm/plat-omap/hwspinlock.c b/arch/arm/plat-omap/hwspinlock.c
Why do you have two files ? This IP is not part of OMAP1 and all the code can
go in mach-omap and can be exported from there.
> 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@ti.com>
> + *
> + * 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 */
> + 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
This too can be moved to
arch/arm/mach-omap2/include/mach/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
^ permalink raw reply [flat|nested] 12+ messages in thread* RE: [RFC] omap: hwspinlock: Added hwspinlock driver
2010-06-25 18:33 ` Shilimkar, Santosh
@ 2010-06-25 20:31 ` Que, Simon
2010-06-27 6:58 ` Shilimkar, Santosh
0 siblings, 1 reply; 12+ messages in thread
From: Que, Simon @ 2010-06-25 20:31 UTC (permalink / raw)
To: Shilimkar, Santosh, linux-omap@vger.kernel.org
Cc: Kanigeri, Hari, Ohad Ben-Cohen
Santosh,
> This reservation KCONFIG isn't necessary. HWSPINLOCK IP is not really for
> very generic use
> but specific usages where you need to protect the data between independent
> softwares
> running on different masters.
> For other Linux only side software even with dual core, kernel spinlock
> library
> is sufficient.
We will consider this.
> Are you building this for all OMAP's ??
> Move this base address to plat/omap44xx.h and use it from there
> " OMAP2_L4_IO_ADDRESS" this should not be used anymore. Rather, use
> "ioremap and readl/writel"
Good points, we'll implement these.
> Why do you have two files ? This IP is not part of OMAP1 and all the code
> can
> go in mach-omap and can be exported from there.
Are you suggesting we use a library approach instead of a driver approach? We want to make this expandable in the future. If something in the underlying hardware changes with OMAP5, OMAP6, etc, we only need to update the device initialization file. The driver can remain unchanged.
Thank you,
Simon
^ permalink raw reply [flat|nested] 12+ messages in thread
* RE: [RFC] omap: hwspinlock: Added hwspinlock driver
2010-06-25 20:31 ` Que, Simon
@ 2010-06-27 6:58 ` Shilimkar, Santosh
2010-06-29 19:27 ` [RFC v.2] " Que, Simon
0 siblings, 1 reply; 12+ messages in thread
From: Shilimkar, Santosh @ 2010-06-27 6:58 UTC (permalink / raw)
To: Que, Simon, linux-omap@vger.kernel.org; +Cc: Kanigeri, Hari, Ohad Ben-Cohen
( Using Web-mail so please excuse me for the email format)
________________________________________
From: Que, Simon
Sent: Friday, June 25, 2010 3:31 PM
To: Shilimkar, Santosh; linux-omap@vger.kernel.org
Cc: Kanigeri, Hari; Ohad Ben-Cohen
Subject: RE: [RFC] omap: hwspinlock: Added hwspinlock driver
>Santosh,
>> This reservation KCONFIG isn't necessary. HWSPINLOCK IP is not really for
>> very generic use
>> but specific usages where you need to protect the data between independent
>> softwares
>>running on different masters.
>>For other Linux only side software even with dual core, kernel spinlock
>>library
>>is sufficient.
>We will consider this.
>> Are you building this for all OMAP's ??
>>Move this base address to plat/omap44xx.h and use it from there
>>" OMAP2_L4_IO_ADDRESS" this should not be used anymore. Rather, use
>>"ioremap and readl/writel"
>Good points, we'll implement these.
>> Why do you have two files ? This IP is not part of OMAP1 and all the code
>> can
>> go in mach-omap and can be exported from there.
>Are you suggesting we use a library approach instead of a driver approach? We want to make this expandable in the future. If something in the underlying hardware changes with OMAP5, OMAP6, etc, we only need to update the device >initialization file. The driver can remain unchanged.
No I am not suggesting the library approach. What you have done is correct except that "you should just make it as early platform driver" using HWMOD. This
isn't so difficult considering you have already created a platform driver. Only the initializations part will change. HW spin-lock doesn't have any direct clock control
so it's even simpler.
Regards
Santosh
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC v.2] omap: hwspinlock: Added hwspinlock driver
2010-06-27 6:58 ` Shilimkar, Santosh
@ 2010-06-29 19:27 ` Que, Simon
2010-06-29 20:54 ` Cousson, Benoit
2010-06-29 21:18 ` Cousson, Benoit
0 siblings, 2 replies; 12+ messages in thread
From: Que, Simon @ 2010-06-29 19:27 UTC (permalink / raw)
To: linux-omap@vger.kernel.org, Kevin Hilman, Cousson, Benoit,
Shilimkar, Santosh
Cc: Kanigeri, Hari, Ohad Ben-Cohen
[-- Attachment #1: Type: text/plain, Size: 19308 bytes --]
Hello,
Thanks for all your comments and feedback. I've updated the patch with changes based on the feedback I've received:
- Switched to using hwmod. I enabled the hwmod entry for spinlock in omap_hwmod_44xx_data.
- Removed reserved/unreserved distinction. Removed CONFIG option for number of reserved locks as well. The reservation will be done at runtime as Kevin and Benoit have suggested. The new API simply provides for reservation of spinlock w/ specific ID or the next available spinlock.
- Simplified #define names -- removed the HWSPINLOCK prefix.
- Added OMAP4 conditional to Makefiles for building hwspinlock.
- Moved base address definition of spinlock register to omap44xx.h
- Base addresses are mapped using ioremap and accessed using readl/writel.
- Removed the irqsave/restore functions. Now there are only lock/trylock/unlock, without the irq and preempt disable/enable. This keeps it simple, and we can add the irq/preempt disable/enable as a future enhancement.
Regarding reserving locks: how do we use the board code to reserve locks? This is not familiar to us.
Please see attached patch, or inline patch contents below.
Thanks,
Simon
============================================================================
>From a1aa1f5a85814bdf924d140b664a3305224392b2 Mon Sep 17 00:00:00 2001
From: Simon Que <sque@ti.com>
Date: Wed, 23 Jun 2010 18:40:30 -0500
Subject: [PATCH] omap: hwspinlock: Added hwspinlock driver
Created driver for OMAP hardware spinlock. This driver supports:
- Reserved spinlocks for internal use
- Dynamic allocation of unreserved locks
- Lock, unlock, and trylock functions, with or without disabling irqs/preempt
- Registered as a platform device driver
The device initialization uses hwmod to configure the devices. One device will
be created for each hardware spinlock. It will pass spinlock register
addresses to the driver. The device initialization file is:
arch/arm/mach-omap2/hwspinlocks.c
The driver takes in data passed in device initialization. The function
hwspinlock_probe() initializes the array of spinlock structures, each
containing a spinlock register address provided by the device initialization.
The device driver file is:
arch/arm/plat-omap/hwspinlock.c
Here's an API summary:
int hwspinlock_lock(struct hwspinlock *);
Attempt to lock a hardware spinlock. If it is busy, the function will
keep trying until it succeeds. This is a blocking function.
int hwspinlock_trylock(struct hwspinlock *);
Attempt to lock a hardware spinlock. If it is busy, the function will
return BUSY. If it succeeds in locking, the function will return
ACQUIRED. This is a non-blocking function
int hwspinlock_unlock(struct hwspinlock *);
Unlock a hardware spinlock.
struct hwspinlock *hwspinlock_request(void);
Provides for "dynamic allocation" of a hardware spinlock. It returns
the handle to the next available (unallocated) spinlock. If no more
locks are available, it returns NULL.
struct hwspinlock *hwspinlock_request_specific(unsigned int);
Provides for "static allocation" of a specific hardware spinlock. This
allows the system to use a specific spinlock, identified by an ID. If
the ID is invalid or if the desired lock is already allocated, this
will return NULL. Otherwise it returns a spinlock handle.
int hwspinlock_free(struct hwspinlock *);
Frees an allocated hardware spinlock (either reserved or unreserved).
Signed-off-by: Simon Que <sque@ti.com>
---
arch/arm/mach-omap2/Makefile | 2 +
arch/arm/mach-omap2/hwspinlocks.c | 122 +++++++++++++
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 2 +-
arch/arm/plat-omap/Makefile | 3 +-
arch/arm/plat-omap/hwspinlock.c | 250 ++++++++++++++++++++++++++
arch/arm/plat-omap/include/plat/hwspinlock.h | 30 +++
arch/arm/plat-omap/include/plat/omap44xx.h | 2 +
7 files changed, 409 insertions(+), 2 deletions(-)
create mode 100644 arch/arm/mach-omap2/hwspinlocks.c
create mode 100644 arch/arm/plat-omap/hwspinlock.c
create mode 100644 arch/arm/plat-omap/include/plat/hwspinlock.h
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 6725b3a..5f5c87b 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-$(CONFIG_ARCH_OMAP4) += hwspinlocks.o
\ 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..868edea
--- /dev/null
+++ b/arch/arm/mach-omap2/hwspinlocks.c
@@ -0,0 +1,122 @@
+/*
+ * OMAP hardware spinlock driver
+ *
+ * Copyright (C) 2010 Texas Instruments. All rights reserved.
+ *
+ * Contact: Simon Que <sque@ti.com>
+ *
+ * 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>
+
+#include <plat/omap_device.h>
+#include <plat/omap_hwmod.h>
+
+#define SPINLOCK_REGADDR(reg) (OMAP44XX_SPINLOCK_BASE + (reg))
+
+/* Spinlock register offsets */
+#define REVISION_OFFSET 0x0000
+#define SYSCONFIG_OFFSET 0x0010
+#define SYSSTATUS_OFFSET 0x0014
+#define LOCK_BASE_OFFSET 0x0800
+
+/* Spinlock register addresses */
+#define SPINLOCK_REVISION_REG \
+ SPINLOCK_REGADDR(REVISION_OFFSET)
+#define SPINLOCK_SYSCONFIG_REG \
+ SPINLOCK_REGADDR(SYSCONFIG_OFFSET)
+#define SPINLOCK_SYSSTATUS_REG \
+ SPINLOCK_REGADDR(SYSSTATUS_OFFSET)
+#define SPINLOCK_LOCK_REG(i) \
+ SPINLOCK_REGADDR(LOCK_BASE_OFFSET + 0x4 * (i))
+
+/* Spinlock count code */
+#define SPINLOCK_32_REGS 1
+#define SPINLOCK_64_REGS 2
+#define SPINLOCK_128_REGS 4
+#define SPINLOCK_256_REGS 8
+#define SPINLOCK_NUMLOCKS_OFFSET 24
+
+/* Initialization function */
+int __init hwspinlocks_init(void)
+{
+ int i;
+ int retval = 0;
+
+ struct hwspinlock_plat_info *pdata;
+ void __iomem *base;
+ int num_locks;
+
+ void __iomem *sysstatus_reg = ioremap(SPINLOCK_SYSSTATUS_REG,
+ sizeof(u32));
+
+ struct omap_hwmod *oh;
+ char *oh_name, *pdev_name;
+
+ /* Determine number of locks */
+ switch (readl(sysstatus_reg) >> SPINLOCK_NUMLOCKS_OFFSET) {
+ case SPINLOCK_32_REGS:
+ num_locks = 32;
+ break;
+ case SPINLOCK_64_REGS:
+ num_locks = 64;
+ break;
+ case SPINLOCK_128_REGS:
+ num_locks = 128;
+ break;
+ case SPINLOCK_256_REGS:
+ num_locks = 256;
+ break;
+ default:
+ return -EINVAL; /* Invalid spinlock count code */
+ }
+
+ iounmap(sysstatus_reg);
+
+ oh_name = "spinlock";
+ oh = omap_hwmod_lookup(oh_name);
+ if (WARN_ON(oh == NULL))
+ return -EINVAL;
+
+ pdev_name = "hwspinlock";
+
+ /* Device drivers */
+ for (i = 0; i < num_locks; i++) {
+ base = ioremap(SPINLOCK_LOCK_REG(i), sizeof(u32));
+
+ /* Pass data to device initialization */
+ pdata = kzalloc(sizeof(struct hwspinlock_plat_info),
+ GFP_KERNEL);
+ pdata->num_locks = num_locks;
+ pdata->io_base = base;
+
+ omap_device_build(pdev_name, i, oh, pdata,
+ sizeof(struct hwspinlock_plat_info),
+ NULL, 0, false);
+ }
+
+ return retval;
+}
+module_init(hwspinlocks_init);
+
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index d8d6d58..ce6c5ff 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -4875,7 +4875,7 @@ static __initdata struct omap_hwmod *omap44xx_hwmods[] = {
/* &omap44xx_smartreflex_iva_hwmod, */
/* &omap44xx_smartreflex_mpu_hwmod, */
/* spinlock class */
-/* &omap44xx_spinlock_hwmod, */
+ &omap44xx_spinlock_hwmod,
/* timer class */
&omap44xx_timer1_hwmod,
&omap44xx_timer2_hwmod,
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile
index a37abf5..f725afc 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-$(CONFIG_ARCH_OMAP4) += hwspinlock.o
\ 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..dbbf543
--- /dev/null
+++ b/arch/arm/plat-omap/hwspinlock.c
@@ -0,0 +1,250 @@
+/*
+ * OMAP hardware spinlock driver
+ *
+ * Copyright (C) 2010 Texas Instruments. All rights reserved.
+ *
+ * Contact: Simon Que <sque@ti.com>
+ *
+ * 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 {
+ bool is_init;
+ void __iomem *io_base;
+ 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 = 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 = 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 */
+ writel(0, handle->io_base);
+
+ return 0;
+}
+EXPORT_SYMBOL(hwspinlock_unlock);
+
+/* 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) {
+ 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_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;
+
+ /* 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;
+
+ for (id = 0; id < pdata->num_locks; id++)
+ hwspinlocks[id].is_init = false;
+ }
+
+ id = pdev->id;
+
+ hwspinlocks[id].pdev = pdev;
+
+ hwspinlocks[id].is_allocated = false;
+ hwspinlocks[id].io_base = pdata->io_base;
+ hwspinlocks[id].is_init = true;
+
+ 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);
+
+ return retval;
+}
+
+/* Cleanup function */
+static void __exit hwspinlock_exit(void)
+{
+ int id;
+
+ platform_driver_unregister(&hwspinlock_driver);
+
+ for (id = 0; id < hwspinlock_module->num_locks; id++) {
+ iounmap(hwspinlocks[id].io_base);
+ hwspinlocks[id].is_init = false;
+ }
+
+ /* 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..6de2cb9
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/hwspinlock.h
@@ -0,0 +1,30 @@
+/* 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);
+
+struct hwspinlock *hwspinlock_request(void);
+struct hwspinlock *hwspinlock_request_specific(unsigned int id);
+int hwspinlock_free(struct hwspinlock *hwspinlock_ptr);
+
+#endif /* HWSPINLOCK_H */
diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h
index 8b3f12f..8016508 100644
--- a/arch/arm/plat-omap/include/plat/omap44xx.h
+++ b/arch/arm/plat-omap/include/plat/omap44xx.h
@@ -52,5 +52,7 @@
#define OMAP4_MMU1_BASE 0x55082000
#define OMAP4_MMU2_BASE 0x4A066000
+#define OMAP44XX_SPINLOCK_BASE (L4_44XX_BASE + 0xF6000)
+
#endif /* __ASM_ARCH_OMAP44XX_H */
--
1.7.0
[-- Attachment #2: 0001-omap-hwspinlock-Added-hwspinlock-driver.patch --]
[-- Type: application/octet-stream, Size: 15430 bytes --]
From a1aa1f5a85814bdf924d140b664a3305224392b2 Mon Sep 17 00:00:00 2001
From: Simon Que <sque@ti.com>
Date: Wed, 23 Jun 2010 18:40:30 -0500
Subject: [PATCH] omap: hwspinlock: Added hwspinlock driver
Created driver for OMAP hardware spinlock. This driver supports:
- Reserved spinlocks for internal use
- Dynamic allocation of unreserved locks
- Lock, unlock, and trylock functions, with or without disabling irqs/preempt
- Registered as a platform device driver
The device initialization uses hwmod to configure the devices. One device will
be created for each hardware spinlock. It will pass spinlock register
addresses to the driver. The device initialization file is:
arch/arm/mach-omap2/hwspinlocks.c
The driver takes in data passed in device initialization. The function
hwspinlock_probe() initializes the array of spinlock structures, each
containing a spinlock register address provided by the device initialization.
The device driver file is:
arch/arm/plat-omap/hwspinlock.c
Here's an API summary:
int hwspinlock_lock(struct hwspinlock *);
Attempt to lock a hardware spinlock. If it is busy, the function will
keep trying until it succeeds. This is a blocking function.
int hwspinlock_trylock(struct hwspinlock *);
Attempt to lock a hardware spinlock. If it is busy, the function will
return BUSY. If it succeeds in locking, the function will return
ACQUIRED. This is a non-blocking function
int hwspinlock_unlock(struct hwspinlock *);
Unlock a hardware spinlock.
struct hwspinlock *hwspinlock_request(void);
Provides for "dynamic allocation" of a hardware spinlock. It returns
the handle to the next available (unallocated) spinlock. If no more
locks are available, it returns NULL.
struct hwspinlock *hwspinlock_request_specific(unsigned int);
Provides for "static allocation" of a specific hardware spinlock. This
allows the system to use a specific spinlock, identified by an ID. If
the ID is invalid or if the desired lock is already allocated, this
will return NULL. Otherwise it returns a spinlock handle.
int hwspinlock_free(struct hwspinlock *);
Frees an allocated hardware spinlock (either reserved or unreserved).
Signed-off-by: Simon Que <sque@ti.com>
---
arch/arm/mach-omap2/Makefile | 2 +
arch/arm/mach-omap2/hwspinlocks.c | 122 +++++++++++++
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 2 +-
arch/arm/plat-omap/Makefile | 3 +-
arch/arm/plat-omap/hwspinlock.c | 250 ++++++++++++++++++++++++++
arch/arm/plat-omap/include/plat/hwspinlock.h | 30 +++
arch/arm/plat-omap/include/plat/omap44xx.h | 2 +
7 files changed, 409 insertions(+), 2 deletions(-)
create mode 100644 arch/arm/mach-omap2/hwspinlocks.c
create mode 100644 arch/arm/plat-omap/hwspinlock.c
create mode 100644 arch/arm/plat-omap/include/plat/hwspinlock.h
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 6725b3a..5f5c87b 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-$(CONFIG_ARCH_OMAP4) += hwspinlocks.o
\ 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..868edea
--- /dev/null
+++ b/arch/arm/mach-omap2/hwspinlocks.c
@@ -0,0 +1,122 @@
+/*
+ * OMAP hardware spinlock driver
+ *
+ * Copyright (C) 2010 Texas Instruments. All rights reserved.
+ *
+ * Contact: Simon Que <sque@ti.com>
+ *
+ * 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>
+
+#include <plat/omap_device.h>
+#include <plat/omap_hwmod.h>
+
+#define SPINLOCK_REGADDR(reg) (OMAP44XX_SPINLOCK_BASE + (reg))
+
+/* Spinlock register offsets */
+#define REVISION_OFFSET 0x0000
+#define SYSCONFIG_OFFSET 0x0010
+#define SYSSTATUS_OFFSET 0x0014
+#define LOCK_BASE_OFFSET 0x0800
+
+/* Spinlock register addresses */
+#define SPINLOCK_REVISION_REG \
+ SPINLOCK_REGADDR(REVISION_OFFSET)
+#define SPINLOCK_SYSCONFIG_REG \
+ SPINLOCK_REGADDR(SYSCONFIG_OFFSET)
+#define SPINLOCK_SYSSTATUS_REG \
+ SPINLOCK_REGADDR(SYSSTATUS_OFFSET)
+#define SPINLOCK_LOCK_REG(i) \
+ SPINLOCK_REGADDR(LOCK_BASE_OFFSET + 0x4 * (i))
+
+/* Spinlock count code */
+#define SPINLOCK_32_REGS 1
+#define SPINLOCK_64_REGS 2
+#define SPINLOCK_128_REGS 4
+#define SPINLOCK_256_REGS 8
+#define SPINLOCK_NUMLOCKS_OFFSET 24
+
+/* Initialization function */
+int __init hwspinlocks_init(void)
+{
+ int i;
+ int retval = 0;
+
+ struct hwspinlock_plat_info *pdata;
+ void __iomem *base;
+ int num_locks;
+
+ void __iomem *sysstatus_reg = ioremap(SPINLOCK_SYSSTATUS_REG,
+ sizeof(u32));
+
+ struct omap_hwmod *oh;
+ char *oh_name, *pdev_name;
+
+ /* Determine number of locks */
+ switch (readl(sysstatus_reg) >> SPINLOCK_NUMLOCKS_OFFSET) {
+ case SPINLOCK_32_REGS:
+ num_locks = 32;
+ break;
+ case SPINLOCK_64_REGS:
+ num_locks = 64;
+ break;
+ case SPINLOCK_128_REGS:
+ num_locks = 128;
+ break;
+ case SPINLOCK_256_REGS:
+ num_locks = 256;
+ break;
+ default:
+ return -EINVAL; /* Invalid spinlock count code */
+ }
+
+ iounmap(sysstatus_reg);
+
+ oh_name = "spinlock";
+ oh = omap_hwmod_lookup(oh_name);
+ if (WARN_ON(oh == NULL))
+ return -EINVAL;
+
+ pdev_name = "hwspinlock";
+
+ /* Device drivers */
+ for (i = 0; i < num_locks; i++) {
+ base = ioremap(SPINLOCK_LOCK_REG(i), sizeof(u32));
+
+ /* Pass data to device initialization */
+ pdata = kzalloc(sizeof(struct hwspinlock_plat_info),
+ GFP_KERNEL);
+ pdata->num_locks = num_locks;
+ pdata->io_base = base;
+
+ omap_device_build(pdev_name, i, oh, pdata,
+ sizeof(struct hwspinlock_plat_info),
+ NULL, 0, false);
+ }
+
+ return retval;
+}
+module_init(hwspinlocks_init);
+
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index d8d6d58..ce6c5ff 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -4875,7 +4875,7 @@ static __initdata struct omap_hwmod *omap44xx_hwmods[] = {
/* &omap44xx_smartreflex_iva_hwmod, */
/* &omap44xx_smartreflex_mpu_hwmod, */
/* spinlock class */
-/* &omap44xx_spinlock_hwmod, */
+ &omap44xx_spinlock_hwmod,
/* timer class */
&omap44xx_timer1_hwmod,
&omap44xx_timer2_hwmod,
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile
index a37abf5..f725afc 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-$(CONFIG_ARCH_OMAP4) += hwspinlock.o
\ 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..dbbf543
--- /dev/null
+++ b/arch/arm/plat-omap/hwspinlock.c
@@ -0,0 +1,250 @@
+/*
+ * OMAP hardware spinlock driver
+ *
+ * Copyright (C) 2010 Texas Instruments. All rights reserved.
+ *
+ * Contact: Simon Que <sque@ti.com>
+ *
+ * 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 {
+ bool is_init;
+ void __iomem *io_base;
+ 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 = 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 = 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 */
+ writel(0, handle->io_base);
+
+ return 0;
+}
+EXPORT_SYMBOL(hwspinlock_unlock);
+
+/* 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) {
+ 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_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;
+
+ /* 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;
+
+ for (id = 0; id < pdata->num_locks; id++)
+ hwspinlocks[id].is_init = false;
+ }
+
+ id = pdev->id;
+
+ hwspinlocks[id].pdev = pdev;
+
+ hwspinlocks[id].is_allocated = false;
+ hwspinlocks[id].io_base = pdata->io_base;
+ hwspinlocks[id].is_init = true;
+
+ 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);
+
+ return retval;
+}
+
+/* Cleanup function */
+static void __exit hwspinlock_exit(void)
+{
+ int id;
+
+ platform_driver_unregister(&hwspinlock_driver);
+
+ for (id = 0; id < hwspinlock_module->num_locks; id++) {
+ iounmap(hwspinlocks[id].io_base);
+ hwspinlocks[id].is_init = false;
+ }
+
+ /* 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..6de2cb9
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/hwspinlock.h
@@ -0,0 +1,30 @@
+/* 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);
+
+struct hwspinlock *hwspinlock_request(void);
+struct hwspinlock *hwspinlock_request_specific(unsigned int id);
+int hwspinlock_free(struct hwspinlock *hwspinlock_ptr);
+
+#endif /* HWSPINLOCK_H */
diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h
index 8b3f12f..8016508 100644
--- a/arch/arm/plat-omap/include/plat/omap44xx.h
+++ b/arch/arm/plat-omap/include/plat/omap44xx.h
@@ -52,5 +52,7 @@
#define OMAP4_MMU1_BASE 0x55082000
#define OMAP4_MMU2_BASE 0x4A066000
+#define OMAP44XX_SPINLOCK_BASE (L4_44XX_BASE + 0xF6000)
+
#endif /* __ASM_ARCH_OMAP44XX_H */
--
1.7.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* RE: [RFC v.2] omap: hwspinlock: Added hwspinlock driver
2010-06-29 19:27 ` [RFC v.2] " Que, Simon
@ 2010-06-29 20:54 ` Cousson, Benoit
2010-06-29 21:18 ` Cousson, Benoit
1 sibling, 0 replies; 12+ messages in thread
From: Cousson, Benoit @ 2010-06-29 20:54 UTC (permalink / raw)
To: Que, Simon, linux-omap@vger.kernel.org, Kevin Hilman,
Shilimkar, Santosh, P
Cc: Kanigeri, Hari, Ohad Ben-Cohen
Hi Simon,
>From: Que, Simon
>Sent: Tuesday, June 29, 2010 2:28 PM
>To: linux-omap@vger.kernel.org; Kevin Hilman; Cousson, Benoit;
>
>Hello,
>
>Thanks for all your comments and feedback. I've updated the
>patch with changes based on the feedback I've received:
>
>- Switched to using hwmod. I enabled the hwmod entry for
>spinlock in omap_hwmod_44xx_data.
>- Removed reserved/unreserved distinction. Removed CONFIG
>option for number of reserved locks as well. The reservation
>will be done at runtime as Kevin and Benoit have suggested.
>The new API simply provides for reservation of spinlock w/
>specific ID or the next available spinlock.
>- Simplified #define names -- removed the HWSPINLOCK prefix.
>- Added OMAP4 conditional to Makefiles for building hwspinlock.
>- Moved base address definition of spinlock register to omap44xx.h
>- Base addresses are mapped using ioremap and accessed using
>readl/writel.
>- Removed the irqsave/restore functions. Now there are only
>lock/trylock/unlock, without the irq and preempt
>disable/enable. This keeps it simple, and we can add the
>irq/preempt disable/enable as a future enhancement.
>
>Regarding reserving locks: how do we use the board code to
>reserve locks? This is not familiar to us.
>
>Please see attached patch, or inline patch contents below.
>
>Thanks,
>Simon
>
>
>===============================================================
>=============
>
>
>From a1aa1f5a85814bdf924d140b664a3305224392b2 Mon Sep 17 00:00:00 2001
>From: Simon Que <sque@ti.com>
>Date: Wed, 23 Jun 2010 18:40:30 -0500
>Subject: [PATCH] omap: hwspinlock: Added hwspinlock driver
>
>Created driver for OMAP hardware spinlock. This driver supports:
>- Reserved spinlocks for internal use
>- Dynamic allocation of unreserved locks
>- Lock, unlock, and trylock functions, with or without
>disabling irqs/preempt
>- Registered as a platform device driver
>
>The device initialization uses hwmod to configure the devices.
> One device will
>be created for each hardware spinlock. It will pass spinlock register
>addresses to the driver. The device initialization file is:
> arch/arm/mach-omap2/hwspinlocks.c
>
>The driver takes in data passed in device initialization. The function
>hwspinlock_probe() initializes the array of spinlock structures, each
>containing a spinlock register address provided by the device
>initialization.
>The device driver file is:
> arch/arm/plat-omap/hwspinlock.c
>
>Here's an API summary:
>int hwspinlock_lock(struct hwspinlock *);
> Attempt to lock a hardware spinlock. If it is busy,
>the function will
> keep trying until it succeeds. This is a blocking function.
>int hwspinlock_trylock(struct hwspinlock *);
> Attempt to lock a hardware spinlock. If it is busy,
>the function will
> return BUSY. If it succeeds in locking, the function
>will return
> ACQUIRED. This is a non-blocking function
>int hwspinlock_unlock(struct hwspinlock *);
> Unlock a hardware spinlock.
>
>struct hwspinlock *hwspinlock_request(void);
> Provides for "dynamic allocation" of a hardware
>spinlock. It returns
> the handle to the next available (unallocated)
>spinlock. If no more
> locks are available, it returns NULL.
>struct hwspinlock *hwspinlock_request_specific(unsigned int);
> Provides for "static allocation" of a specific hardware
>spinlock. This
> allows the system to use a specific spinlock,
>identified by an ID. If
> the ID is invalid or if the desired lock is already
>allocated, this
> will return NULL. Otherwise it returns a spinlock handle.
>int hwspinlock_free(struct hwspinlock *);
> Frees an allocated hardware spinlock (either reserved
>or unreserved).
>
>Signed-off-by: Simon Que <sque@ti.com>
>---
> arch/arm/mach-omap2/Makefile | 2 +
> arch/arm/mach-omap2/hwspinlocks.c | 122 +++++++++++++
> arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 2 +-
> arch/arm/plat-omap/Makefile | 3 +-
> arch/arm/plat-omap/hwspinlock.c | 250
>++++++++++++++++++++++++++
> arch/arm/plat-omap/include/plat/hwspinlock.h | 30 +++
> arch/arm/plat-omap/include/plat/omap44xx.h | 2 +
> 7 files changed, 409 insertions(+), 2 deletions(-)
> create mode 100644 arch/arm/mach-omap2/hwspinlocks.c
> create mode 100644 arch/arm/plat-omap/hwspinlock.c
> create mode 100644 arch/arm/plat-omap/include/plat/hwspinlock.h
>
>diff --git a/arch/arm/mach-omap2/Makefile
>b/arch/arm/mach-omap2/Makefile
>index 6725b3a..5f5c87b 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-$(CONFIG_ARCH_OMAP4) += hwspinlocks.o
>\ 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..868edea
>--- /dev/null
>+++ b/arch/arm/mach-omap2/hwspinlocks.c
>@@ -0,0 +1,122 @@
>+/*
>+ * OMAP hardware spinlock driver
>+ *
>+ * Copyright (C) 2010 Texas Instruments. All rights reserved.
>+ *
>+ * Contact: Simon Que <sque@ti.com>
>+ *
>+ * 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>
>+
>+#include <plat/omap_device.h>
>+#include <plat/omap_hwmod.h>
>+
>+#define SPINLOCK_REGADDR(reg) (OMAP44XX_SPINLOCK_BASE + (reg))
You should not access the physical address directly. The driver should just do a regular platform_get_resource(pdev, IORESOURCE_MEM...) to get the physical address and then do the ioremap.
>+
>+/* Spinlock register offsets */
>+#define REVISION_OFFSET 0x0000
>+#define SYSCONFIG_OFFSET 0x0010
>+#define SYSSTATUS_OFFSET 0x0014
>+#define LOCK_BASE_OFFSET 0x0800
>+
>+/* Spinlock register addresses */
>+#define SPINLOCK_REVISION_REG \
>+ SPINLOCK_REGADDR(REVISION_OFFSET)
>+#define SPINLOCK_SYSCONFIG_REG \
>+ SPINLOCK_REGADDR(SYSCONFIG_OFFSET)
>+#define SPINLOCK_SYSSTATUS_REG \
>+ SPINLOCK_REGADDR(SYSSTATUS_OFFSET)
>+#define SPINLOCK_LOCK_REG(i) \
>+ SPINLOCK_REGADDR(LOCK_BASE_OFFSET + 0x4 * (i))
>+
>+/* Spinlock count code */
>+#define SPINLOCK_32_REGS 1
>+#define SPINLOCK_64_REGS 2
>+#define SPINLOCK_128_REGS 4
>+#define SPINLOCK_256_REGS 8
>+#define SPINLOCK_NUMLOCKS_OFFSET 24
>+
>+/* Initialization function */
>+int __init hwspinlocks_init(void)
>+{
>+ int i;
>+ int retval = 0;
>+
>+ struct hwspinlock_plat_info *pdata;
>+ void __iomem *base;
>+ int num_locks;
>+
>+ void __iomem *sysstatus_reg = ioremap(SPINLOCK_SYSSTATUS_REG,
>+
>sizeof(u32));
>+
>+ struct omap_hwmod *oh;
>+ char *oh_name, *pdev_name;
>+
>+ /* Determine number of locks */
>+ switch (readl(sysstatus_reg) >> SPINLOCK_NUMLOCKS_OFFSET) {
>+ case SPINLOCK_32_REGS:
>+ num_locks = 32;
>+ break;
>+ case SPINLOCK_64_REGS:
>+ num_locks = 64;
>+ break;
>+ case SPINLOCK_128_REGS:
>+ num_locks = 128;
>+ break;
>+ case SPINLOCK_256_REGS:
>+ num_locks = 256;
>+ break;
>+ default:
>+ return -EINVAL; /* Invalid spinlock count code */
>+ }
>+
>+ iounmap(sysstatus_reg);
>+
>+ oh_name = "spinlock";
>+ oh = omap_hwmod_lookup(oh_name);
>+ if (WARN_ON(oh == NULL))
>+ return -EINVAL;
>+
>+ pdev_name = "hwspinlock";
>+
>+ /* Device drivers */
>+ for (i = 0; i < num_locks; i++) {
>+ base = ioremap(SPINLOCK_LOCK_REG(i), sizeof(u32));
That should be done during probe time and not during device init time.
Regards,
Benoit
Texas Instruments France SA, 821 Avenue Jack Kilby, 06270 Villeneuve Loubet. 036 420 040 R.C.S Antibes. Capital de EUR 753.920
^ permalink raw reply [flat|nested] 12+ messages in thread* RE: [RFC v.2] omap: hwspinlock: Added hwspinlock driver
2010-06-29 19:27 ` [RFC v.2] " Que, Simon
2010-06-29 20:54 ` Cousson, Benoit
@ 2010-06-29 21:18 ` Cousson, Benoit
1 sibling, 0 replies; 12+ messages in thread
From: Cousson, Benoit @ 2010-06-29 21:18 UTC (permalink / raw)
To: Que, Simon, linux-omap@vger.kernel.org, Kevin Hilman,
Shilimkar, Santosh, P
Cc: Kanigeri, Hari, Ohad Ben-Cohen
>From: Que, Simon
>Sent: Tuesday, June 29, 2010 2:28 PM
[snip]
>+
>+/* Spinlock count code */
>+#define SPINLOCK_32_REGS 1
>+#define SPINLOCK_64_REGS 2
>+#define SPINLOCK_128_REGS 4
>+#define SPINLOCK_256_REGS 8
>+#define SPINLOCK_NUMLOCKS_OFFSET 24
>+
>+/* Initialization function */
>+int __init hwspinlocks_init(void)
>+{
>+ int i;
>+ int retval = 0;
>+
>+ struct hwspinlock_plat_info *pdata;
>+ void __iomem *base;
>+ int num_locks;
>+
>+ void __iomem *sysstatus_reg = ioremap(SPINLOCK_SYSSTATUS_REG,
>+
>sizeof(u32));
>+
>+ struct omap_hwmod *oh;
>+ char *oh_name, *pdev_name;
>+
>+ /* Determine number of locks */
>+ switch (readl(sysstatus_reg) >> SPINLOCK_NUMLOCKS_OFFSET) {
>+ case SPINLOCK_32_REGS:
>+ num_locks = 32;
>+ break;
>+ case SPINLOCK_64_REGS:
>+ num_locks = 64;
>+ break;
>+ case SPINLOCK_128_REGS:
>+ num_locks = 128;
>+ break;
>+ case SPINLOCK_256_REGS:
>+ num_locks = 256;
>+ break;
>+ default:
>+ return -EINVAL; /* Invalid spinlock count code */
>+ }
>+
>+ iounmap(sysstatus_reg);
>+
>+ oh_name = "spinlock";
>+ oh = omap_hwmod_lookup(oh_name);
>+ if (WARN_ON(oh == NULL))
>+ return -EINVAL;
>+
>+ pdev_name = "hwspinlock";
>+
>+ /* Device drivers */
>+ for (i = 0; i < num_locks; i++) {
Sorry, I missed that part in my previous email...
Are you trying to create one omap_device per lock?
You cannot really do that, because omap_device is supposed to have a one device to many hwmods mapping, but not a many devices to one hwmod.
There is no reference counting done in the framework, so as soon as you will try to enable several locks, that should output a warning saying that you are trying to enable twice the same hwmod. Same thing when you will disable.
Didn't you have that kind of issue during your tests?
BTW, are you enabling the device at some point? I cannot see that either in this patch?
Benoit
>+ base = ioremap(SPINLOCK_LOCK_REG(i), sizeof(u32));
>+
>+ /* Pass data to device initialization */
>+ pdata = kzalloc(sizeof(struct hwspinlock_plat_info),
>+
>GFP_KERNEL);
>+ pdata->num_locks = num_locks;
>+ pdata->io_base = base;
>+
>+ omap_device_build(pdev_name, i, oh, pdata,
>+ sizeof(struct hwspinlock_plat_info),
>+ NULL, 0, false);
>+ }
>+
>+ return retval;
>+}
>+module_init(hwspinlocks_init);
>+
>diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>index d8d6d58..ce6c5ff 100644
>--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>@@ -4875,7 +4875,7 @@ static __initdata struct omap_hwmod
>*omap44xx_hwmods[] = {
> /* &omap44xx_smartreflex_iva_hwmod, */
> /* &omap44xx_smartreflex_mpu_hwmod, */
> /* spinlock class */
>-/* &omap44xx_spinlock_hwmod, */
>+ &omap44xx_spinlock_hwmod,
> /* timer class */
> &omap44xx_timer1_hwmod,
> &omap44xx_timer2_hwmod,
>diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile
>index a37abf5..f725afc 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-$(CONFIG_ARCH_OMAP4) += hwspinlock.o
>\ 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..dbbf543
>--- /dev/null
>+++ b/arch/arm/plat-omap/hwspinlock.c
>@@ -0,0 +1,250 @@
>+/*
>+ * OMAP hardware spinlock driver
>+ *
>+ * Copyright (C) 2010 Texas Instruments. All rights reserved.
>+ *
>+ * Contact: Simon Que <sque@ti.com>
>+ *
>+ * 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 {
>+ bool is_init;
>+ void __iomem *io_base;
>+ 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 = 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 = 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 */
>+ writel(0, handle->io_base);
>+
>+ return 0;
>+}
>+EXPORT_SYMBOL(hwspinlock_unlock);
>+
>+/* 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) {
>+ 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_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;
>+
>+ /* 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;
>+
>+ for (id = 0; id < pdata->num_locks; id++)
>+ hwspinlocks[id].is_init = false;
>+ }
>+
>+ id = pdev->id;
>+
>+ hwspinlocks[id].pdev = pdev;
>+
>+ hwspinlocks[id].is_allocated = false;
>+ hwspinlocks[id].io_base = pdata->io_base;
>+ hwspinlocks[id].is_init = true;
>+
>+ 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);
>+
>+ return retval;
>+}
>+
>+/* Cleanup function */
>+static void __exit hwspinlock_exit(void)
>+{
>+ int id;
>+
>+ platform_driver_unregister(&hwspinlock_driver);
>+
>+ for (id = 0; id < hwspinlock_module->num_locks; id++) {
>+ iounmap(hwspinlocks[id].io_base);
>+ hwspinlocks[id].is_init = false;
>+ }
>+
>+ /* 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..6de2cb9
>--- /dev/null
>+++ b/arch/arm/plat-omap/include/plat/hwspinlock.h
>@@ -0,0 +1,30 @@
>+/* 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);
>+
>+struct hwspinlock *hwspinlock_request(void);
>+struct hwspinlock *hwspinlock_request_specific(unsigned int id);
>+int hwspinlock_free(struct hwspinlock *hwspinlock_ptr);
>+
>+#endif /* HWSPINLOCK_H */
>diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h
>b/arch/arm/plat-omap/include/plat/omap44xx.h
>index 8b3f12f..8016508 100644
>--- a/arch/arm/plat-omap/include/plat/omap44xx.h
>+++ b/arch/arm/plat-omap/include/plat/omap44xx.h
>@@ -52,5 +52,7 @@
> #define OMAP4_MMU1_BASE 0x55082000
> #define OMAP4_MMU2_BASE 0x4A066000
>
>+#define OMAP44XX_SPINLOCK_BASE (L4_44XX_BASE + 0xF6000)
>+
> #endif /* __ASM_ARCH_OMAP44XX_H */
>
>--
>1.7.0
>
>
Texas Instruments France SA, 821 Avenue Jack Kilby, 06270 Villeneuve Loubet. 036 420 040 R.C.S Antibes. Capital de EUR 753.920
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2010-06-29 21:19 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-06-25 0:40 [RFC] omap: hwspinlock: Added hwspinlock driver Que, Simon
2010-06-25 8:11 ` Cousson, Benoit
2010-06-25 18:13 ` Que, Simon
2010-06-25 18:38 ` Kevin Hilman
2010-06-25 21:02 ` Cousson, Benoit
2010-06-25 18:26 ` Pandita, Vikram
2010-06-25 18:33 ` Shilimkar, Santosh
2010-06-25 20:31 ` Que, Simon
2010-06-27 6:58 ` Shilimkar, Santosh
2010-06-29 19:27 ` [RFC v.2] " Que, Simon
2010-06-29 20:54 ` Cousson, Benoit
2010-06-29 21:18 ` Cousson, Benoit
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.