From patchwork Fri Aug 13 19:59:02 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Que, Simon" X-Patchwork-Id: 119500 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 o7DJx86L026928 for ; Fri, 13 Aug 2010 19:59:09 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754440Ab0HMT7H (ORCPT ); Fri, 13 Aug 2010 15:59:07 -0400 Received: from arroyo.ext.ti.com ([192.94.94.40]:36541 "EHLO arroyo.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753411Ab0HMT7F (ORCPT ); Fri, 13 Aug 2010 15:59:05 -0400 Received: from dlep35.itg.ti.com ([157.170.170.118]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id o7DJx4xV004423 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Fri, 13 Aug 2010 14:59:04 -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 o7DJx4NP022472; Fri, 13 Aug 2010 14:59:04 -0500 (CDT) Received: from dlee75.ent.ti.com (localhost [127.0.0.1]) by dlep26.itg.ti.com (8.13.8/8.13.8) with ESMTP id o7DJx4qK002398; Fri, 13 Aug 2010 14:59:04 -0500 (CDT) Received: from dlee03.ent.ti.com ([157.170.170.18]) by dlee75.ent.ti.com ([157.170.170.72]) with mapi; Fri, 13 Aug 2010 14:59:03 -0500 From: "Que, Simon" To: "Que, Simon" , Linux Omap , Tony Lindgren CC: "Cousson, Benoit" , "Shilimkar, Santosh" , "Premi, Sanjeev" , "Kanigeri, Hari" Date: Fri, 13 Aug 2010 14:59:02 -0500 Subject: [PATCH 2/3 v3] omap: hwspinlock: Created driver for OMAP hardware spinlock Thread-Topic: [PATCH 2/3 v3] omap: hwspinlock: Created driver for OMAP hardware spinlock Thread-Index: Acs5tXfBBKuQTFk6TcaZt3XKZmDYgABbBQoA 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, 13 Aug 2010 19:59:09 +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..f4b0185 --- /dev/null +++ b/arch/arm/plat-omap/hwspinlock.c @@ -0,0 +1,347 @@ +/* + * 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 + +#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 { + struct list_head head; + bool is_init; + int id; + void __iomem *lock_reg; + bool is_allocated; + struct platform_device *pdev; +}; + +/* Array of spinlocks */ +static struct hwspinlock *hwspinlock_array; + +/* List for keeping track of free locks */ +static struct list_head free_locks; + +/* API functions */ + +/* Busy loop to acquire a spinlock, with timeout */ +int hwspinlock_lock_timeout(struct hwspinlock *handle, int max_attempts) +{ + 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 && (max_attempts > 0 && + num_attempts < max_attempts)); + + /* If there was a timeout, put the device back */ + if (num_attempts >= max_attempts) + pm_runtime_put_sync(&handle->pdev->dev); + + return retval; +} +EXPORT_SYMBOL(hwspinlock_lock_timeout); + +/* 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) +{ + struct hwspinlock *handle = NULL; + unsigned long flags; + + spin_lock_irqsave(&hwspinlock_state.local_lock, flags); + + if (WARN_ON(!hwspinlock_state.is_init)) + goto exit; + + if (WARN_ON(list_empty(&free_locks))) + goto exit; + + handle = (struct hwspinlock *) free_locks.next; + list_del(free_locks.next); + handle->is_allocated = true; + +exit: + spin_unlock_irqrestore(&hwspinlock_state.local_lock, flags); + 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_state.is_init)) + goto exit; + + if (WARN_ON(id >= hwspinlock_state.num_locks)) + goto exit; + + if (WARN_ON(hwspinlock_array[id].is_allocated)) + goto exit; + + handle = &hwspinlock_array[id]; + handle->is_allocated = true; + + /* Remove from free lock list */ + list_del(&handle->head); + +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) +{ + unsigned long flags; + + spin_lock_irqsave(&hwspinlock_state.local_lock, flags); + + if (WARN_ON(handle == NULL)) + return -EINVAL; + + if (WARN_ON(!handle->is_allocated)) + return -ENOMEM; + + list_add(&free_locks, &handle->head); + handle->is_allocated = false; + + spin_unlock_irqrestore(&hwspinlock_state.local_lock, flags); + 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; + + /* 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; + + /* Determine number of locks */ + switch (readl(io_base + SYSSTATUS_OFFSET) >> + 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 */ + } + + /* 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); + + /* Mark module as initialized */ + hwspinlock_state.is_init = true; + + /* Initialize lock lists */ + INIT_LIST_HEAD(&free_locks); + + /* 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; + + /* Add it to the list of free locks */ + list_add(&hwspinlock_array[id].head, &free_locks); + } + 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); + + INIT_LIST_HEAD(&free_locks); /* Clear free lock list */ +} +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..3a38190 --- /dev/null +++ b/arch/arm/plat-omap/include/plat/hwspinlock.h @@ -0,0 +1,53 @@ +/* + * 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; + +/* Basic operations */ +int hwspinlock_lock_timeout(struct hwspinlock *handle, int max_attempts); +int hwspinlock_unlock(struct hwspinlock *handle); + +/* Try locking once */ +#define hwspinlock_trylock(h) (hwspinlock_lock_timeout(h, 1)) +/* Lock without timeout */ +#define hwspinlock_lock(h) (hwspinlock_lock_timeout(h, -1)) + +/* Allocation functions */ +struct hwspinlock *hwspinlock_request(void); +struct hwspinlock *hwspinlock_request_specific(unsigned int id); +int hwspinlock_free(struct hwspinlock *hwspinlock_ptr); + +#endif /* HWSPINLOCK_H */