17fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz/* 27fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * OMAP hardware spinlock driver 37fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * 47fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com 57fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * 67fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * Contact: Simon Que <sque@ti.com> 77fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * Hari Kanigeri <h-kanigeri2@ti.com> 87fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * Ohad Ben-Cohen <ohad@wizery.com> 97fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * 107fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * This program is free software; you can redistribute it and/or 117fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * modify it under the terms of the GNU General Public License 127fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * version 2 as published by the Free Software Foundation. 137fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * 147fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * This program is distributed in the hope that it will be useful, but 157fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * WITHOUT ANY WARRANTY; without even the implied warranty of 167fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 177fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * General Public License for more details. 187fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz */ 197fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 207fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/kernel.h> 217fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/module.h> 227fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/device.h> 237fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/delay.h> 24f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander#include <linux/io.h> 257fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/bitops.h> 267fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/pm_runtime.h> 277fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/slab.h> 287fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/spinlock.h> 297fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/hwspinlock.h> 30f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander#include <linux/platform_device.h> 317fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 327fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include "hwspinlock_internal.h" 337fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 347fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz/* Spinlock register offsets */ 357fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define SYSSTATUS_OFFSET 0x0014 367fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define LOCK_BASE_OFFSET 0x0800 377fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 387fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define SPINLOCK_NUMLOCKS_BIT_OFFSET (24) 397fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 407fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz/* Possible values of SPINLOCK_LOCK_REG */ 417fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define SPINLOCK_NOTTAKEN (0) /* free */ 427fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define SPINLOCK_TAKEN (1) /* locked */ 437fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 447fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic int omap_hwspinlock_trylock(struct hwspinlock *lock) 457fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 467fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz void __iomem *lock_addr = lock->priv; 477fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 487fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz /* attempt to acquire the lock by reading its value */ 497fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return (SPINLOCK_NOTTAKEN == readl(lock_addr)); 507fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 517fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 527fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic void omap_hwspinlock_unlock(struct hwspinlock *lock) 537fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 547fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz void __iomem *lock_addr = lock->priv; 557fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 567fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz /* release the lock by writing 0 to it */ 577fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz writel(SPINLOCK_NOTTAKEN, lock_addr); 587fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 597fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 607fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz/* 617fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * relax the OMAP interconnect while spinning on it. 627fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * 637fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * The specs recommended that the retry delay time will be 647fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * just over half of the time that a requester would be 657fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * expected to hold the lock. 667fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * 677fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * The number below is taken from an hardware specs example, 687fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * obviously it is somewhat arbitrary. 697fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz */ 707fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic void omap_hwspinlock_relax(struct hwspinlock *lock) 717fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 727fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz ndelay(50); 737fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 747fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 757fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic const struct hwspinlock_ops omap_hwspinlock_ops = { 767fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .trylock = omap_hwspinlock_trylock, 777fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .unlock = omap_hwspinlock_unlock, 787fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .relax = omap_hwspinlock_relax, 797fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz}; 807fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 817fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic int __devinit omap_hwspinlock_probe(struct platform_device *pdev) 827fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 837fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz struct hwspinlock_pdata *pdata = pdev->dev.platform_data; 847fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz struct hwspinlock_device *bank; 857fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz struct hwspinlock *hwlock; 867fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz struct resource *res; 877fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz void __iomem *io_base; 887fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int num_locks, i, ret; 897fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 907fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (!pdata) 917fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return -ENODEV; 927fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 937fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 947fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (!res) 957fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return -ENODEV; 967fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 977fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz io_base = ioremap(res->start, resource_size(res)); 987fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (!io_base) 997fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return -ENOMEM; 1007fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1017fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz /* Determine number of locks */ 1027fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz i = readl(io_base + SYSSTATUS_OFFSET); 1037fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz i >>= SPINLOCK_NUMLOCKS_BIT_OFFSET; 1047fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1057fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz /* one of the four lsb's must be set, and nothing else */ 1067fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (hweight_long(i & 0xf) != 1 || i > 8) { 1077fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz ret = -EINVAL; 1087fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto iounmap_base; 1097fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 1107fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1117fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz num_locks = i * 32; /* actual number of locks in this device */ 1127fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1137fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz bank = kzalloc(sizeof(*bank) + num_locks * sizeof(*hwlock), GFP_KERNEL); 114f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander if (!bank) { 1157fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz ret = -ENOMEM; 116f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander goto iounmap_base; 1177fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 118f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander 1197fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz platform_set_drvdata(pdev, bank); 1207fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1217fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++) 1227fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz hwlock->priv = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i; 1237fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1247fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz /* 1257fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * runtime PM will make sure the clock of this module is 1267fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * enabled iff at least one lock is requested 1277fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz */ 1287fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz pm_runtime_enable(&pdev->dev); 1297fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1307fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops, 1317fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz pdata->base_id, num_locks); 1327fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (ret) 1337fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto reg_fail; 1347fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1357fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return 0; 1367fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1377fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazreg_fail: 1387fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz pm_runtime_disable(&pdev->dev); 1397fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz kfree(bank); 1407fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaziounmap_base: 1417fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz iounmap(io_base); 1427fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return ret; 1437fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 1447fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1457fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic int __devexit omap_hwspinlock_remove(struct platform_device *pdev) 1467fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 1477fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz struct hwspinlock_device *bank = platform_get_drvdata(pdev); 1487fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz void __iomem *io_base = bank->lock[0].priv - LOCK_BASE_OFFSET; 1497fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int ret; 1507fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1517fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz ret = hwspin_lock_unregister(bank); 1527fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (ret) { 1537fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); 1547fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return ret; 1557fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 1567fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1577fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz pm_runtime_disable(&pdev->dev); 1587fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz iounmap(io_base); 1597fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz kfree(bank); 1607fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1617fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return 0; 1627fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 1637fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1647fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic struct platform_driver omap_hwspinlock_driver = { 1657fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .probe = omap_hwspinlock_probe, 1667fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .remove = __devexit_p(omap_hwspinlock_remove), 1677fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .driver = { 1687fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .name = "omap_hwspinlock", 1697fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .owner = THIS_MODULE, 1707fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz }, 1717fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz}; 1727fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1737fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic int __init omap_hwspinlock_init(void) 1747fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 175f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander return platform_driver_register(&omap_hwspinlock_driver); 1767fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 177f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander/* board init code might need to reserve hwspinlocks for predefined purposes */ 1787fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazpostcore_initcall(omap_hwspinlock_init); 1797fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 180f3ca07824f309474b308d859c9a2cc871c6c5ab8David Janderstatic void __exit omap_hwspinlock_exit(void) 1817fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 1827fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz platform_driver_unregister(&omap_hwspinlock_driver); 1837fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 1847fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazmodule_exit(omap_hwspinlock_exit); 1857fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 18652b7ad3a63a42b76f4f07cba876479a3c416f1e8Samuel OrtizMODULE_LICENSE("GPL v2"); 187f3ca07824f309474b308d859c9a2cc871c6c5ab8David JanderMODULE_DESCRIPTION("Hardware spinlock driver for OMAP"); 1887fdcef8a414eaeb367b3696005b25283d62d195dPhilippe RétornazMODULE_AUTHOR("Simon Que <sque@ti.com>"); 1897fdcef8a414eaeb367b3696005b25283d62d195dPhilippe RétornazMODULE_AUTHOR("Hari Kanigeri <h-kanigeri2@ti.com>"); 1907fdcef8a414eaeb367b3696005b25283d62d195dPhilippe RétornazMODULE_AUTHOR("Ohad Ben-Cohen <ohad@wizery.com>"); 191f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander