1/* 2 * SIRF hardware spinlock driver 3 * 4 * Copyright (c) 2015 Cambridge Silicon Radio Limited, a CSR plc group company. 5 * 6 * Licensed under GPLv2. 7 */ 8 9#include <linux/kernel.h> 10#include <linux/module.h> 11#include <linux/device.h> 12#include <linux/io.h> 13#include <linux/pm_runtime.h> 14#include <linux/slab.h> 15#include <linux/spinlock.h> 16#include <linux/hwspinlock.h> 17#include <linux/platform_device.h> 18#include <linux/of.h> 19#include <linux/of_address.h> 20 21#include "hwspinlock_internal.h" 22 23struct sirf_hwspinlock { 24 void __iomem *io_base; 25 struct hwspinlock_device bank; 26}; 27 28/* Number of Hardware Spinlocks*/ 29#define HW_SPINLOCK_NUMBER 30 30 31/* Hardware spinlock register offsets */ 32#define HW_SPINLOCK_BASE 0x404 33#define HW_SPINLOCK_OFFSET(x) (HW_SPINLOCK_BASE + 0x4 * (x)) 34 35static int sirf_hwspinlock_trylock(struct hwspinlock *lock) 36{ 37 void __iomem *lock_addr = lock->priv; 38 39 /* attempt to acquire the lock by reading value == 1 from it */ 40 return !!readl(lock_addr); 41} 42 43static void sirf_hwspinlock_unlock(struct hwspinlock *lock) 44{ 45 void __iomem *lock_addr = lock->priv; 46 47 /* release the lock by writing 0 to it */ 48 writel(0, lock_addr); 49} 50 51static const struct hwspinlock_ops sirf_hwspinlock_ops = { 52 .trylock = sirf_hwspinlock_trylock, 53 .unlock = sirf_hwspinlock_unlock, 54}; 55 56static int sirf_hwspinlock_probe(struct platform_device *pdev) 57{ 58 struct sirf_hwspinlock *hwspin; 59 struct hwspinlock *hwlock; 60 int idx, ret; 61 62 if (!pdev->dev.of_node) 63 return -ENODEV; 64 65 hwspin = devm_kzalloc(&pdev->dev, sizeof(*hwspin) + 66 sizeof(*hwlock) * HW_SPINLOCK_NUMBER, GFP_KERNEL); 67 if (!hwspin) 68 return -ENOMEM; 69 70 /* retrieve io base */ 71 hwspin->io_base = of_iomap(pdev->dev.of_node, 0); 72 if (!hwspin->io_base) 73 return -ENOMEM; 74 75 for (idx = 0; idx < HW_SPINLOCK_NUMBER; idx++) { 76 hwlock = &hwspin->bank.lock[idx]; 77 hwlock->priv = hwspin->io_base + HW_SPINLOCK_OFFSET(idx); 78 } 79 80 platform_set_drvdata(pdev, hwspin); 81 82 pm_runtime_enable(&pdev->dev); 83 84 ret = hwspin_lock_register(&hwspin->bank, &pdev->dev, 85 &sirf_hwspinlock_ops, 0, 86 HW_SPINLOCK_NUMBER); 87 if (ret) 88 goto reg_failed; 89 90 return 0; 91 92reg_failed: 93 pm_runtime_disable(&pdev->dev); 94 iounmap(hwspin->io_base); 95 96 return ret; 97} 98 99static int sirf_hwspinlock_remove(struct platform_device *pdev) 100{ 101 struct sirf_hwspinlock *hwspin = platform_get_drvdata(pdev); 102 int ret; 103 104 ret = hwspin_lock_unregister(&hwspin->bank); 105 if (ret) { 106 dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); 107 return ret; 108 } 109 110 pm_runtime_disable(&pdev->dev); 111 112 iounmap(hwspin->io_base); 113 114 return 0; 115} 116 117static const struct of_device_id sirf_hwpinlock_ids[] = { 118 { .compatible = "sirf,hwspinlock", }, 119 {}, 120}; 121MODULE_DEVICE_TABLE(of, sirf_hwpinlock_ids); 122 123static struct platform_driver sirf_hwspinlock_driver = { 124 .probe = sirf_hwspinlock_probe, 125 .remove = sirf_hwspinlock_remove, 126 .driver = { 127 .name = "atlas7_hwspinlock", 128 .of_match_table = of_match_ptr(sirf_hwpinlock_ids), 129 }, 130}; 131 132module_platform_driver(sirf_hwspinlock_driver); 133 134MODULE_LICENSE("GPL v2"); 135MODULE_DESCRIPTION("SIRF Hardware spinlock driver"); 136MODULE_AUTHOR("Wei Chen <wei.chen@csr.com>"); 137