>-----Original Message----- >From: linux-omap-owner@xxxxxxxxxxxxxxx [mailto:linux-omap- >owner@xxxxxxxxxxxxxxx] On Behalf Of Que, Simon >Sent: Thursday, June 24, 2010 7:40 PM >To: linux-omap@xxxxxxxxxxxxxxx >Cc: Kanigeri, Hari; Ohad Ben-Cohen >Subject: [RFC] omap: hwspinlock: Added hwspinlock driver > > >Hi, > <snip> >diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile >index 6725b3a..14af19a 100644 >--- a/arch/arm/mach-omap2/Makefile >+++ b/arch/arm/mach-omap2/Makefile >@@ -170,3 +170,5 @@ obj-y += $(nand- >m) $(nand-y) > > smc91x-$(CONFIG_SMC91X) := gpmc-smc91x.o > obj-y += $(smc91x-m) $(smc91x-y) >+ >+obj-y += hwspinlocks.o If this block is only on OMAP4, why build it for omap2 and omap3? >\ No newline at end of file >diff --git a/arch/arm/mach-omap2/hwspinlocks.c b/arch/arm/mach- >omap2/hwspinlocks.c >new file mode 100644 >index 0000000..de813a0 >--- /dev/null >+++ b/arch/arm/mach-omap2/hwspinlocks.c >@@ -0,0 +1,126 @@ >+/* >+ * OMAP hardware spinlock driver >+ * >+ * Copyright (C) 2010 Texas Instruments. All rights reserved. >+ * >+ * Contact: Simon Que <sque@xxxxxx> >+ * >+ * This program is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU General Public License >+ * version 2 as published by the Free Software Foundation. >+ * >+ * This program is distributed in the hope that it will be useful, but >+ * WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >+ * General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA >+ * 02110-1301 USA >+ * >+ */ >+ >+#include <linux/module.h> >+#include <linux/interrupt.h> >+#include <linux/device.h> >+#include <linux/delay.h> >+#include <linux/io.h> >+#include <linux/slab.h> >+ >+#include <plat/hwspinlock.h> >+ >+/* Base address of HW spinlock module */ >+#define HWSPINLOCK_BASE (L4_44XX_BASE + 0xF6000) >+#define HWSPINLOCK_REGADDR(reg) \ >+ OMAP2_L4_IO_ADDRESS(HWSPINLOCK_BASE + (reg)) >+ >+/* Spinlock register offsets */ >+#define HWSPINLOCK_REVISION 0x0000 >+#define HWSPINLOCK_SYSCONFIG 0x0010 >+#define HWSPINLOCK_SYSSTATUS 0x0014 >+#define HWSPINLOCK_LOCK_BASE 0x0800 >+ >+/* Spinlock register addresses */ >+#define HWSPINLOCK_REVISION_REG \ >+ HWSPINLOCK_REGADDR(HWSPINLOCK_REVISION) >+#define HWSPINLOCK_SYSCONFIG_REG \ >+ HWSPINLOCK_REGADDR(HWSPINLOCK_SYSCONFIG) >+#define HWSPINLOCK_SYSSTATUS_REG \ >+ HWSPINLOCK_REGADDR(HWSPINLOCK_SYSSTATUS) >+#define HWSPINLOCK_LOCK_REG(i) \ >+ HWSPINLOCK_REGADDR(HWSPINLOCK_LOCK_BASE + 0x4 * (i)) >+ >+/* Spinlock count code */ >+#define HWSPINLOCK_32_REGS 1 >+#define HWSPINLOCK_64_REGS 2 >+#define HWSPINLOCK_128_REGS 4 >+#define HWSPINLOCK_256_REGS 8 >+#define HWSPINLOCK_NUMLOCKS_OFFSET 24 >+ >+ >+/* Initialization function */ >+int __init hwspinlocks_init(void) >+{ >+ int i; >+ int retval = 0; >+ >+ struct platform_device *pdev; >+ struct hwspinlock_plat_info *pdata; >+ void __iomem *base; >+ int num_locks; >+ bool is_reserved; >+ >+ /* Determine number of locks */ >+ switch (__raw_readl(HWSPINLOCK_SYSSTATUS_REG) >> >+ HWSPINLOCK_NUMLOCKS_OFFSET) { >+ case HWSPINLOCK_32_REGS: >+ num_locks = 32; >+ break; >+ case HWSPINLOCK_64_REGS: >+ num_locks = 64; >+ break; >+ case HWSPINLOCK_128_REGS: >+ num_locks = 128; >+ break; >+ case HWSPINLOCK_256_REGS: >+ num_locks = 256; >+ break; >+ default: >+ return -EINVAL; /* Invalid spinlock count code */ >+ } >+ >+ /* Device drivers */ >+ for (i = 0; i < num_locks; i++) { >+ pdev = platform_device_alloc("hwspinlock", i); >+ >+ base = HWSPINLOCK_LOCK_REG(i); /* Get register address */ >+ >+ /* Some locks are reserved for system use */ >+ if (i < CONFIG_OMAP_HWSPINLOCK_NUM_RESERVED) >+ is_reserved = true; >+ else >+ is_reserved = false; >+ >+ /* Pass data to device initialization */ >+ pdata = kzalloc(sizeof(struct hwspinlock_plat_info), >+ >GFP_KERNEL); >+ pdata->num_locks = num_locks; >+ pdata->io_base = base; Any reason not to have the driver ioremap the HWSPINLOCK address and rather pass the physical pointer to driver? >+ pdata->is_reserved = is_reserved; >+ retval = platform_device_add_data(pdev, pdata, >sizeof(*pdata)); >+ if (retval) >+ goto device_add_fail; >+ >+ retval = platform_device_add(pdev); >+ if (retval) >+ goto device_add_fail; >+ continue; >+device_add_fail: >+ platform_device_put(pdev); >+ } >+ >+ return retval; >+} >+module_init(hwspinlocks_init); >+ >diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile >index a37abf5..fb98ff9 100644 >--- a/arch/arm/plat-omap/Makefile >+++ b/arch/arm/plat-omap/Makefile >@@ -32,4 +32,5 @@ obj-y += $(i2c-omap-m) $(i2c-omap-y) > obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox.o > obj-$(CONFIG_OMAP_REMOTE_PROC) += remoteproc.o > >-obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-noop.o >\ No newline at end of file >+obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-noop.o >+obj-y += hwspinlock.o Same comment as above - why build for all platforms? >\ No newline at end of file >diff --git a/arch/arm/plat-omap/hwspinlock.c b/arch/arm/plat- >omap/hwspinlock.c >new file mode 100644 >index 0000000..327a524 >--- /dev/null >+++ b/arch/arm/plat-omap/hwspinlock.c >@@ -0,0 +1,331 @@ >+/* >+ * OMAP hardware spinlock driver >+ * >+ * Copyright (C) 2010 Texas Instruments. All rights reserved. >+ * >+ * Contact: Simon Que <sque@xxxxxx> >+ * >+ * This program is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU General Public License >+ * version 2 as published by the Free Software Foundation. >+ * >+ * This program is distributed in the hope that it will be useful, but >+ * WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >+ * General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA >+ * 02110-1301 USA >+ * >+ */ >+ >+#include <linux/module.h> >+#include <linux/interrupt.h> >+#include <linux/device.h> >+#include <linux/delay.h> >+#include <linux/io.h> >+#include <linux/slab.h> >+#include <linux/spinlock.h> >+ >+#include <plat/hwspinlock.h> >+ >+/* for managing a hardware spinlock module */ >+struct hwspinlock_state { >+ bool is_init; /* For first-time initialization */ >+ int num_locks; /* Total number of locks in system >*/ >+ spinlock_t local_lock; /* Local protection */ >+}; >+ >+/* Points to the hardware spinlock module */ >+static struct hwspinlock_state hwspinlock_state; >+static struct hwspinlock_state *hwspinlock_module = &hwspinlock_state; >+ >+/* Spinlock object */ >+struct hwspinlock { >+ void __iomem *io_base; >+ bool is_reserved; >+ bool is_allocated; >+ struct platform_device *pdev; >+}; >+ >+/* Array of spinlocks */ >+static struct hwspinlock *hwspinlocks; >+ >+/* API functions */ >+ >+/* Busy loop to acquire a spinlock */ >+int hwspinlock_lock(struct hwspinlock *handle) >+{ >+ int retval; >+ >+ if (WARN_ON(handle == NULL)) >+ return -EINVAL; >+ >+ if (WARN_ON(in_atomic() || in_irq())) >+ return -EPERM; >+ >+ /* Attempt to acquire the lock by reading from it */ >+ do { >+ retval = __raw_readl(handle->io_base); >+ } while (retval == HWSPINLOCK_BUSY); >+ >+ return 0; >+} >+EXPORT_SYMBOL(hwspinlock_lock); >+ >+/* Attempt to acquire a spinlock once */ >+int hwspinlock_trylock(struct hwspinlock *handle) >+{ >+ int retval = 0; >+ >+ if (WARN_ON(handle == NULL)) >+ return -EINVAL; >+ >+ if (WARN_ON(in_atomic() || in_irq())) >+ return -EPERM; >+ >+ /* Attempt to acquire the lock by reading from it */ >+ retval = __raw_readl(handle->io_base); >+ >+ return retval; >+} >+EXPORT_SYMBOL(hwspinlock_trylock); >+ >+/* Release a spinlock */ >+int hwspinlock_unlock(struct hwspinlock *handle) >+{ >+ if (WARN_ON(handle == NULL)) >+ return -EINVAL; >+ >+ /* Release it by writing 0 to it */ >+ __raw_writel(0, handle->io_base); >+ >+ return 0; >+} >+EXPORT_SYMBOL(hwspinlock_unlock); >+ >+/* Busy loop to acquire a spinlock, disabling interrupts/preemption */ >+int hwspinlock_lock_irqsave(struct hwspinlock *handle, unsigned long >*flags) >+{ >+ int retval; >+ unsigned long temp_flags; >+ >+ if (WARN_ON(handle == NULL)) >+ return -EINVAL; >+ >+ if (WARN_ON(in_atomic() || in_irq())) >+ return -EPERM; >+ >+ if (WARN_ON(flags == NULL)) >+ return -EINVAL; >+ >+ /* Attempt to acquire the lock by reading from it */ >+ do { >+ preempt_disable(); /* Disable >preemption */ >+ local_irq_save(temp_flags); /* Disable >interrupts */ >+ >+ retval = __raw_readl(handle->io_base); >+ >+ /* Restore interrupts and preemption if not successful */ >+ if (retval == HWSPINLOCK_BUSY) { >+ local_irq_restore(temp_flags); >+ preempt_enable(); >+ } >+ } while (retval == HWSPINLOCK_BUSY); >+ >+ *flags = temp_flags; /* Return IRQ state */ >+ >+ return 0; >+} >+EXPORT_SYMBOL(hwspinlock_lock_irqsave); >+ >+/* Attempt to acquire a spinlock once, disabling interrupts/preemption */ >+int hwspinlock_trylock_irqsave(struct hwspinlock *handle, unsigned long >*flags) >+{ >+ int retval = 0; >+ unsigned long temp_flags; >+ >+ if (WARN_ON(handle == NULL)) >+ return -EINVAL; >+ >+ if (WARN_ON(in_atomic() || in_irq())) >+ return -EPERM; >+ >+ if (WARN_ON(flags == NULL)) >+ return -EINVAL; >+ >+ preempt_disable(); /* Disable preemption */ Any hard need to disable PREEMPTION? >+ local_irq_save(temp_flags); /* Disable interrupts */ >+ >+ /* Attempt to acquire the lock by reading from it */ >+ retval = __raw_readl(handle->io_base); >+ >+ /* Restore interrupts and preemption if not successful */ >+ if (retval == HWSPINLOCK_BUSY) { >+ local_irq_restore(temp_flags); >+ preempt_enable(); >+ } else >+ *flags = temp_flags; /* Return IRQ state */ >+ >+ return retval; >+} >+EXPORT_SYMBOL(hwspinlock_trylock_irqsave); >+ >+/* Unlock a spinlock that was locked with irq/preempt disabled */ >+int hwspinlock_unlock_irqrestore(struct hwspinlock *handle, unsigned long >+ >flags) >+{ >+ if (WARN_ON(handle == NULL)) >+ return -EINVAL; >+ >+ /* Release it by writing 0 to it */ >+ __raw_writel(0, handle->io_base); >+ >+ /* Restore interrupts and preemption */ >+ local_irq_restore(flags); >+ preempt_enable(); >+ >+ return 0; >+} >+EXPORT_SYMBOL(hwspinlock_unlock_irqrestore); >+ >+/* Request an unclaimed spinlock */ >+struct hwspinlock *hwspinlock_request(void) >+{ >+ int i; >+ bool found = false; >+ struct hwspinlock *handle = NULL; >+ unsigned long flags; >+ >+ spin_lock_irqsave(&hwspinlock_module->local_lock, flags); >+ /* Search for an unclaimed, unreserved lock */ >+ for (i = 0; i < hwspinlock_module->num_locks && !found; i++) { >+ if (!hwspinlocks[i].is_allocated && >+ >!hwspinlocks[i].is_reserved) { >+ found = true; >+ handle = &hwspinlocks[i]; >+ } >+ } >+ spin_unlock_irqrestore(&hwspinlock_module->local_lock, flags); >+ >+ /* Return error if no more locks available */ >+ if (!found) >+ return NULL; >+ >+ handle->is_allocated = true; >+ >+ return handle; >+} >+EXPORT_SYMBOL(hwspinlock_request); >+ >+/* Request an unclaimed spinlock by ID */ >+struct hwspinlock *hwspinlock_request_specific(unsigned int id) >+{ >+ struct hwspinlock *handle = NULL; >+ unsigned long flags; >+ >+ spin_lock_irqsave(&hwspinlock_module->local_lock, flags); >+ >+ if (WARN_ON(!hwspinlocks[id].is_reserved)) >+ goto exit; >+ >+ if (WARN_ON(hwspinlocks[id].is_allocated)) >+ goto exit; >+ >+ handle = &hwspinlocks[id]; >+ handle->is_allocated = true; >+ >+exit: >+ spin_unlock_irqrestore(&hwspinlock_module->local_lock, flags); >+ return handle; >+} >+EXPORT_SYMBOL(hwspinlock_request_specific); >+ >+/* Release a claimed spinlock */ >+int hwspinlock_free(struct hwspinlock *handle) >+{ >+ if (WARN_ON(handle == NULL)) >+ return -EINVAL; >+ >+ if (WARN_ON(!handle->is_allocated)) >+ return -ENOMEM; >+ >+ handle->is_allocated = false; >+ >+ return 0; >+} >+EXPORT_SYMBOL(hwspinlock_free); >+ >+/* Probe function */ >+static int __devinit hwspinlock_probe(struct platform_device *pdev) >+{ >+ struct hwspinlock_plat_info *pdata = pdev->dev.platform_data; >+ int id = pdev->id; >+ >+ /* Set up the spinlock count and array */ >+ if (!hwspinlock_module->is_init) { >+ hwspinlock_module->num_locks = pdata->num_locks; >+ >+ /* Allocate spinlock device objects */ >+ hwspinlocks = kmalloc(sizeof(struct hwspinlock) * >+ hwspinlock_module->num_locks, GFP_KERNEL); >+ if (WARN_ON(hwspinlocks == NULL)) >+ return -ENOMEM; >+ >+ /* Initialize local lock */ >+ spin_lock_init(&hwspinlock_module->local_lock); >+ >+ /* Only do initialization once */ >+ hwspinlock_module->is_init = true; >+ } >+ >+ hwspinlocks[id].pdev = pdev; >+ >+ hwspinlocks[id].is_reserved = pdata->is_reserved; >+ hwspinlocks[id].is_allocated = false; >+ hwspinlocks[id].io_base = pdata->io_base; >+ >+ return 0; >+} >+ >+static struct platform_driver hwspinlock_driver = { >+ .probe = hwspinlock_probe, >+ .driver = { >+ .name = "hwspinlock", >+ }, >+}; >+ >+/* Initialization function */ >+static int __init hwspinlock_init(void) >+{ >+ int retval = 0; >+ >+ /* Register spinlock driver */ >+ retval = platform_driver_register(&hwspinlock_driver); >+ >+ /* Make sure the it was properly initialized */ >+ if (WARN_ON(!hwspinlock_module->is_init)) >+ return -EACCES; >+ >+ return retval; >+} >+ >+/* Cleanup function */ >+static void __exit hwspinlock_exit(void) >+{ >+ platform_driver_unregister(&hwspinlock_driver); >+ >+ /* Free spinlock device objects */ >+ if (hwspinlock_module->is_init) >+ kfree(hwspinlocks); >+} >+ >+module_init(hwspinlock_init); >+module_exit(hwspinlock_exit); >+ >+MODULE_LICENSE("GPL v2"); >+MODULE_DESCRIPTION("Hardware spinlock driver"); >+MODULE_AUTHOR("Simon Que"); >+MODULE_AUTHOR("Hari Kanigeri"); >diff --git a/arch/arm/plat-omap/include/plat/hwspinlock.h b/arch/arm/plat- >omap/include/plat/hwspinlock.h >new file mode 100644 >index 0000000..1cdf7a8 >--- /dev/null >+++ b/arch/arm/plat-omap/include/plat/hwspinlock.h >@@ -0,0 +1,35 @@ >+/* hwspinlock.h */ >+ >+#ifndef HWSPINLOCK_H >+#define HWSPINLOCK_H >+ >+#include <linux/platform_device.h> >+#include <plat/omap44xx.h> >+ >+/* Read values from the spinlock register */ >+#define HWSPINLOCK_ACQUIRED 0 >+#define HWSPINLOCK_BUSY 1 >+ >+/* Device data */ >+struct hwspinlock_plat_info { >+ int num_locks; /* Number of locks (initialization) >*/ >+ void __iomem *io_base; /* Address of spinlock register */ >+ bool is_reserved; /* Reserved for system use? */ >+}; >+ >+struct hwspinlock; >+ >+int hwspinlock_lock(struct hwspinlock *handle); >+int hwspinlock_trylock(struct hwspinlock *handle); >+int hwspinlock_unlock(struct hwspinlock *handle); >+ >+int hwspinlock_lock_irqsave(struct hwspinlock *handle, unsigned long >*flags); >+int hwspinlock_trylock_irqsave(struct hwspinlock *handle, unsigned long >*flags); >+int hwspinlock_unlock_irqrestore(struct hwspinlock *handle, unsigned long >+ >flags); >+ >+struct hwspinlock *hwspinlock_request(void); >+struct hwspinlock *hwspinlock_request_specific(unsigned int id); >+int hwspinlock_free(struct hwspinlock *hwspinlock_ptr); >+ >+#endif /* HWSPINLOCK_H */ >-- >1.7.0 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html