Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Spreadtrum hardware spinlock driver
0004  * Copyright (C) 2017 Spreadtrum  - http://www.spreadtrum.com
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 /* hwspinlock registers definition */
0021 #define HWSPINLOCK_RECCTRL      0x4
0022 #define HWSPINLOCK_MASTERID(_X_)    (0x80 + 0x4 * (_X_))
0023 #define HWSPINLOCK_TOKEN(_X_)       (0x800 + 0x4 * (_X_))
0024 
0025 /* unlocked value */
0026 #define HWSPINLOCK_NOTTAKEN     0x55aa10c5
0027 /* bits definition of RECCTRL reg */
0028 #define HWSPINLOCK_USER_BITS        0x1
0029 
0030 /* hwspinlock number */
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 /* try to lock the hardware spinlock */
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     /* get the hardware spinlock master/user id */
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 /* unlock the hardware spinlock */
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 /* The specs recommended below number as the retry delay time */
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     /* set the hwspinlock to record user id to identify subsystems */
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     { /* sentinel */ }
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>");