0001
0002
0003
0004
0005
0006
0007 #include <linux/clk.h>
0008 #include <linux/delay.h>
0009 #include <linux/device.h>
0010 #include <linux/hwspinlock.h>
0011 #include <linux/io.h>
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include <linux/of.h>
0015 #include <linux/of_device.h>
0016 #include <linux/platform_device.h>
0017
0018 #include "hwspinlock_internal.h"
0019
0020
0021 #define HWSPINLOCK_RECCTRL 0x4
0022 #define HWSPINLOCK_MASTERID(_X_) (0x80 + 0x4 * (_X_))
0023 #define HWSPINLOCK_TOKEN(_X_) (0x800 + 0x4 * (_X_))
0024
0025
0026 #define HWSPINLOCK_NOTTAKEN 0x55aa10c5
0027
0028 #define HWSPINLOCK_USER_BITS 0x1
0029
0030
0031 #define SPRD_HWLOCKS_NUM 32
0032
0033 struct sprd_hwspinlock_dev {
0034 void __iomem *base;
0035 struct clk *clk;
0036 struct hwspinlock_device bank;
0037 };
0038
0039
0040 static int sprd_hwspinlock_trylock(struct hwspinlock *lock)
0041 {
0042 struct sprd_hwspinlock_dev *sprd_hwlock =
0043 dev_get_drvdata(lock->bank->dev);
0044 void __iomem *addr = lock->priv;
0045 int user_id, lock_id;
0046
0047 if (!readl(addr))
0048 return 1;
0049
0050 lock_id = hwlock_to_id(lock);
0051
0052 user_id = readl(sprd_hwlock->base + HWSPINLOCK_MASTERID(lock_id));
0053 dev_warn(sprd_hwlock->bank.dev,
0054 "hwspinlock [%d] lock failed and master/user id = %d!\n",
0055 lock_id, user_id);
0056 return 0;
0057 }
0058
0059
0060 static void sprd_hwspinlock_unlock(struct hwspinlock *lock)
0061 {
0062 void __iomem *lock_addr = lock->priv;
0063
0064 writel(HWSPINLOCK_NOTTAKEN, lock_addr);
0065 }
0066
0067
0068 static void sprd_hwspinlock_relax(struct hwspinlock *lock)
0069 {
0070 ndelay(10);
0071 }
0072
0073 static const struct hwspinlock_ops sprd_hwspinlock_ops = {
0074 .trylock = sprd_hwspinlock_trylock,
0075 .unlock = sprd_hwspinlock_unlock,
0076 .relax = sprd_hwspinlock_relax,
0077 };
0078
0079 static void sprd_hwspinlock_disable(void *data)
0080 {
0081 struct sprd_hwspinlock_dev *sprd_hwlock = data;
0082
0083 clk_disable_unprepare(sprd_hwlock->clk);
0084 }
0085
0086 static int sprd_hwspinlock_probe(struct platform_device *pdev)
0087 {
0088 struct sprd_hwspinlock_dev *sprd_hwlock;
0089 struct hwspinlock *lock;
0090 int i, ret;
0091
0092 if (!pdev->dev.of_node)
0093 return -ENODEV;
0094
0095 sprd_hwlock = devm_kzalloc(&pdev->dev,
0096 struct_size(sprd_hwlock, bank.lock, SPRD_HWLOCKS_NUM),
0097 GFP_KERNEL);
0098 if (!sprd_hwlock)
0099 return -ENOMEM;
0100
0101 sprd_hwlock->base = devm_platform_ioremap_resource(pdev, 0);
0102 if (IS_ERR(sprd_hwlock->base))
0103 return PTR_ERR(sprd_hwlock->base);
0104
0105 sprd_hwlock->clk = devm_clk_get(&pdev->dev, "enable");
0106 if (IS_ERR(sprd_hwlock->clk)) {
0107 dev_err(&pdev->dev, "get hwspinlock clock failed!\n");
0108 return PTR_ERR(sprd_hwlock->clk);
0109 }
0110
0111 ret = clk_prepare_enable(sprd_hwlock->clk);
0112 if (ret)
0113 return ret;
0114
0115 ret = devm_add_action_or_reset(&pdev->dev, sprd_hwspinlock_disable,
0116 sprd_hwlock);
0117 if (ret) {
0118 dev_err(&pdev->dev,
0119 "Failed to add hwspinlock disable action\n");
0120 return ret;
0121 }
0122
0123
0124 writel(HWSPINLOCK_USER_BITS, sprd_hwlock->base + HWSPINLOCK_RECCTRL);
0125
0126 for (i = 0; i < SPRD_HWLOCKS_NUM; i++) {
0127 lock = &sprd_hwlock->bank.lock[i];
0128 lock->priv = sprd_hwlock->base + HWSPINLOCK_TOKEN(i);
0129 }
0130
0131 platform_set_drvdata(pdev, sprd_hwlock);
0132
0133 return devm_hwspin_lock_register(&pdev->dev, &sprd_hwlock->bank,
0134 &sprd_hwspinlock_ops, 0,
0135 SPRD_HWLOCKS_NUM);
0136 }
0137
0138 static const struct of_device_id sprd_hwspinlock_of_match[] = {
0139 { .compatible = "sprd,hwspinlock-r3p0", },
0140 { }
0141 };
0142 MODULE_DEVICE_TABLE(of, sprd_hwspinlock_of_match);
0143
0144 static struct platform_driver sprd_hwspinlock_driver = {
0145 .probe = sprd_hwspinlock_probe,
0146 .driver = {
0147 .name = "sprd_hwspinlock",
0148 .of_match_table = sprd_hwspinlock_of_match,
0149 },
0150 };
0151 module_platform_driver(sprd_hwspinlock_driver);
0152
0153 MODULE_LICENSE("GPL v2");
0154 MODULE_DESCRIPTION("Hardware spinlock driver for Spreadtrum");
0155 MODULE_AUTHOR("Baolin Wang <baolin.wang@spreadtrum.com>");
0156 MODULE_AUTHOR("Lanqing Liu <lanqing.liu@spreadtrum.com>");
0157 MODULE_AUTHOR("Long Cheng <aiden.cheng@spreadtrum.com>");