From patchwork Fri Jun 25 00:39:38 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Que, Simon" X-Patchwork-Id: 107979 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o5P0djVi009584 for ; Fri, 25 Jun 2010 00:39:46 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753436Ab0FYAjm (ORCPT ); Thu, 24 Jun 2010 20:39:42 -0400 Received: from comal.ext.ti.com ([198.47.26.152]:44131 "EHLO comal.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753091Ab0FYAjl (ORCPT ); Thu, 24 Jun 2010 20:39:41 -0400 Received: from dlep35.itg.ti.com ([157.170.170.118]) by comal.ext.ti.com (8.13.7/8.13.7) with ESMTP id o5P0dfJa005743 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Thu, 24 Jun 2010 19:39:41 -0500 Received: from dlep26.itg.ti.com (localhost [127.0.0.1]) by dlep35.itg.ti.com (8.13.7/8.13.7) with ESMTP id o5P0de6x003988; Thu, 24 Jun 2010 19:39:40 -0500 (CDT) Received: from dlee74.ent.ti.com (localhost [127.0.0.1]) by dlep26.itg.ti.com (8.13.8/8.13.8) with ESMTP id o5P0deht010767; Thu, 24 Jun 2010 19:39:40 -0500 (CDT) Received: from dlee03.ent.ti.com ([157.170.170.18]) by dlee74.ent.ti.com ([157.170.170.8]) with mapi; Thu, 24 Jun 2010 19:39:40 -0500 From: "Que, Simon" To: "linux-omap@vger.kernel.org" CC: "Kanigeri, Hari" , Ohad Ben-Cohen Date: Thu, 24 Jun 2010 19:39:38 -0500 Subject: [RFC] omap: Thread-Topic: [RFC] omap: Thread-Index: AcsT/uSyE+vCHOyESneG/KF0kTr/Fg== Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: yes X-MS-TNEF-Correlator: acceptlanguage: en-US MIME-Version: 1.0 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Fri, 25 Jun 2010 00:39:46 +0000 (UTC) ===================================================================== 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 + * + * 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 +#include +#include +#include +#include +#include + +#include + +/* 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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +/* 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 +#include + +/* 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 */