From patchwork Thu Aug 12 00:29:50 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Que, Simon" X-Patchwork-Id: 119109 X-Patchwork-Delegate: tony@atomide.com 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 o7C0UJuq003724 for ; Thu, 12 Aug 2010 00:30:20 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759507Ab0HLA35 (ORCPT ); Wed, 11 Aug 2010 20:29:57 -0400 Received: from comal.ext.ti.com ([198.47.26.152]:56810 "EHLO comal.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1759483Ab0HLA3x (ORCPT ); Wed, 11 Aug 2010 20:29:53 -0400 Received: from dlep34.itg.ti.com ([157.170.170.115]) by comal.ext.ti.com (8.13.7/8.13.7) with ESMTP id o7C0Tqkc002179 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Wed, 11 Aug 2010 19:29:52 -0500 Received: from dlep26.itg.ti.com (localhost [127.0.0.1]) by dlep34.itg.ti.com (8.13.7/8.13.7) with ESMTP id o7C0TqBK001724; Wed, 11 Aug 2010 19:29:52 -0500 (CDT) Received: from dsbe71.ent.ti.com (localhost [127.0.0.1]) by dlep26.itg.ti.com (8.13.8/8.13.8) with ESMTP id o7C0Tq6E012119; Wed, 11 Aug 2010 19:29:52 -0500 (CDT) Received: from dlee03.ent.ti.com ([157.170.170.18]) by dsbe71.ent.ti.com ([156.117.232.23]) with mapi; Wed, 11 Aug 2010 19:29:52 -0500 From: "Que, Simon" To: Linux Omap , Tony Lindgren CC: "Cousson, Benoit" , "Shilimkar, Santosh" , "Premi, Sanjeev" , "Kanigeri, Hari" Date: Wed, 11 Aug 2010 19:29:50 -0500 Subject: [PATCH 3/4 v2] omap: hwspinlock: Created driver for OMAP hardware spinlock. Thread-Topic: [PATCH 3/4 v2] omap: hwspinlock: Created driver for OMAP hardware spinlock. Thread-Index: Acs5tXlUTJl8bQImR9Ot+0CqESWA+w== 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]); Thu, 12 Aug 2010 00:30:20 +0000 (UTC) diff --git a/arch/arm/mach-omap2/hwspinlocks.c b/arch/arm/mach-omap2/hwspinlocks.c new file mode 100644 index 0000000..154fc40 --- /dev/null +++ b/arch/arm/mach-omap2/hwspinlocks.c @@ -0,0 +1,65 @@ +/* + * OMAP hardware spinlock device initialization + * + * 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 +#include + + +struct omap_device_pm_latency omap_spinlock_latency[] = { + { + .deactivate_func = omap_device_idle_hwmods, + .activate_func = omap_device_enable_hwmods, + .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST, + } +}; + +/* Initialization function */ +int __init hwspinlocks_init(void) +{ + int retval = 0; + + struct omap_hwmod *oh; + const char *oh_name, *pdev_name; + + oh_name = "spinlock"; + oh = omap_hwmod_lookup(oh_name); + if (WARN_ON(oh == NULL)) + return -EINVAL; + + pdev_name = "hwspinlock"; + + /* Pass data to device initialization */ + omap_device_build(pdev_name, 0, oh, NULL, 0, omap_spinlock_latency, + ARRAY_SIZE(omap_spinlock_latency), false); + + return retval; +} +postcore_initcall(hwspinlocks_init); diff --git a/arch/arm/plat-omap/hwspinlock.c b/arch/arm/plat-omap/hwspinlock.c new file mode 100644 index 0000000..6f78658 --- /dev/null +++ b/arch/arm/plat-omap/hwspinlock.c @@ -0,0 +1,353 @@ +/* + * 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 + * + * 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 IP. It will pass spinlock register offset info to + * the driver. The device initialization file is: + * arch/arm/mach-omap2/hwspinlock_array.c + * + * The driver takes in register offset info passed in device initialization. + * It uses hwmod to obtain the base address of the hardware spinlock module. + * Then it reads info from the registers. The function hwspinlock_probe() + * initializes the array of spinlock structures, each containing a spinlock + * register address calculated from the base address and lock offsets. + * + * 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). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Spinlock register offsets */ +#define SYSSTATUS_OFFSET 0x0014 +#define LOCK_BASE_OFFSET 0x0800 + +/* 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_BIT_OFFSET 24 + +/* Number of attempts to try locking */ +#define MAX_LOCK_ATTEMPTS 1000 + +/* for managing a hardware spinlock module */ +struct hwspinlock_module_state { + bool is_init; /* For first-time initialization */ + int num_locks; /* Total number of locks in system */ + spinlock_t local_lock; /* Local protection */ + void __iomem *io_base; /* Mapped base address */ +}; + +/* Points to the hardware spinlock module */ +static struct hwspinlock_module_state hwspinlock_state; + +/* Spinlock object */ +struct hwspinlock { + bool is_init; + int id; + void __iomem *lock_reg; + bool is_allocated; + struct platform_device *pdev; +}; + +/* Array of spinlocks */ +static struct hwspinlock *hwspinlock_array; + +/* API functions */ + +/* Busy loop to acquire a spinlock */ +int hwspinlock_lock(struct hwspinlock *handle) +{ + int retval; + int num_attempts = 0; + + if (WARN_ON(handle == NULL)) + return -EINVAL; + + /* This is not permitted in IRQ because we do not want a context */ + /* switch while the lock is being held */ + if (WARN_ON(in_irq())) + return -EPERM; + + if (pm_runtime_get_sync(&handle->pdev->dev) < 0) + return -ENODEV; + + /* Attempt to acquire the lock by reading from it */ + do { + retval = readl(handle->lock_reg); + num_attempts++; /* Increment the timeout counter */ + } while (retval == HWSPINLOCK_BUSY && + num_attempts < MAX_LOCK_ATTEMPTS); + + /* If there was a timeout, put the device back */ + if (num_attempts >= MAX_LOCK_ATTEMPTS) + pm_runtime_put_sync(&handle->pdev->dev); + + return retval; +} +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; + + /* This is not permitted in IRQ because we do not want a context */ + /* switch while the lock is being held */ + if (WARN_ON(in_irq())) + return -EPERM; + + if ((retval = pm_runtime_get_sync(&handle->pdev->dev)) < 0) + return -ENODEV; + + /* Attempt to acquire the lock by reading from it */ + retval = readl(handle->lock_reg); + + if (retval == HWSPINLOCK_BUSY) + pm_runtime_put_sync(&handle->pdev->dev); + + 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->lock_reg); + + pm_runtime_put_sync(&handle->pdev->dev); + + 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_state.local_lock, flags); + /* Search for an unclaimed, unreserved lock */ + for (i = 0; i < hwspinlock_state.num_locks && !found; i++) { + if (!hwspinlock_array[i].is_allocated) { + found = true; + handle = &hwspinlock_array[i]; + } + } + spin_unlock_irqrestore(&hwspinlock_state.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_state.local_lock, flags); + + if (WARN_ON(hwspinlock_array[id].is_allocated)) + goto exit; + + handle = &hwspinlock_array[id]; + handle->is_allocated = true; + +exit: + spin_unlock_irqrestore(&hwspinlock_state.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 resource *res; + void __iomem *io_base; + int id; + + void __iomem *sysstatus_reg; + + /* Determine number of locks */ + sysstatus_reg = ioremap(OMAP44XX_SPINLOCK_BASE + SYSSTATUS_OFFSET, + sizeof(u32)); + if (sysstatus_reg == NULL) + return -EFAULT; + + switch (readl(sysstatus_reg) >> SPINLOCK_NUMLOCKS_BIT_OFFSET) { + case SPINLOCK_32_REGS: + hwspinlock_state.num_locks = 32; + break; + case SPINLOCK_64_REGS: + hwspinlock_state.num_locks = 64; + break; + case SPINLOCK_128_REGS: + hwspinlock_state.num_locks = 128; + break; + case SPINLOCK_256_REGS: + hwspinlock_state.num_locks = 256; + break; + default: + return -EINVAL; /* Invalid spinlock count code */ + } + iounmap(sysstatus_reg); + + /* Allocate spinlock device objects */ + hwspinlock_array = kmalloc(sizeof(struct hwspinlock) * + hwspinlock_state.num_locks, GFP_KERNEL); + if (WARN_ON(hwspinlock_array == NULL)) + return -ENOMEM; + + /* Initialize local lock */ + spin_lock_init(&hwspinlock_state.local_lock); + + /* Get address info */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + /* Map spinlock module address space */ + io_base = ioremap(res->start, resource_size(res)); + hwspinlock_state.io_base = io_base; + + /* Set up each individual lock handle */ + for (id = 0; id < hwspinlock_state.num_locks; id++) { + hwspinlock_array[id].id = id; + hwspinlock_array[id].pdev = pdev; + hwspinlock_array[id].is_init = true; + hwspinlock_array[id].is_allocated = false; + + hwspinlock_array[id].lock_reg = io_base + LOCK_BASE_OFFSET + + sizeof(u32) * id; + } + pm_runtime_enable(&pdev->dev); + + 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; +} +postcore_initcall(hwspinlock_init); + +/* Cleanup function */ +static void __exit hwspinlock_exit(void) +{ + int id; + + platform_driver_unregister(&hwspinlock_driver); + + for (id = 0; id < hwspinlock_state.num_locks; id++) + hwspinlock_array[id].is_init = false; + iounmap(hwspinlock_state.io_base); + + /* Free spinlock device objects */ + if (hwspinlock_state.is_init) + kfree(hwspinlock_array); +} +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..ea64c48 --- /dev/null +++ b/arch/arm/plat-omap/include/plat/hwspinlock.h @@ -0,0 +1,47 @@ +/* + * OMAP hardware spinlock driver header + * + * 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 + * + */ + +#ifndef HWSPINLOCK_H +#define HWSPINLOCK_H + +#include +#include + +/* Values that are read from the spinlock register */ +/* ACQUIRED means the lock was successfully acquired by the read operation */ +/* BUSY means the lock was already acquired before the read operation, and */ +/* thus the read operation was not successful */ +#define HWSPINLOCK_ACQUIRED 0 +#define HWSPINLOCK_BUSY 1 + +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 */