1*19a0f612SBjorn Andersson /* 2*19a0f612SBjorn Andersson * Copyright (c) 2013, The Linux Foundation. All rights reserved. 3*19a0f612SBjorn Andersson * Copyright (c) 2015, Sony Mobile Communications AB 4*19a0f612SBjorn Andersson * 5*19a0f612SBjorn Andersson * This software is licensed under the terms of the GNU General Public 6*19a0f612SBjorn Andersson * License version 2, as published by the Free Software Foundation, and 7*19a0f612SBjorn Andersson * may be copied, distributed, and modified under those terms. 8*19a0f612SBjorn Andersson * 9*19a0f612SBjorn Andersson * This program is distributed in the hope that it will be useful, 10*19a0f612SBjorn Andersson * but WITHOUT ANY WARRANTY; without even the implied warranty of 11*19a0f612SBjorn Andersson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12*19a0f612SBjorn Andersson * GNU General Public License for more details. 13*19a0f612SBjorn Andersson */ 14*19a0f612SBjorn Andersson 15*19a0f612SBjorn Andersson #include <linux/hwspinlock.h> 16*19a0f612SBjorn Andersson #include <linux/io.h> 17*19a0f612SBjorn Andersson #include <linux/kernel.h> 18*19a0f612SBjorn Andersson #include <linux/mfd/syscon.h> 19*19a0f612SBjorn Andersson #include <linux/module.h> 20*19a0f612SBjorn Andersson #include <linux/of.h> 21*19a0f612SBjorn Andersson #include <linux/of_device.h> 22*19a0f612SBjorn Andersson #include <linux/platform_device.h> 23*19a0f612SBjorn Andersson #include <linux/pm_runtime.h> 24*19a0f612SBjorn Andersson #include <linux/regmap.h> 25*19a0f612SBjorn Andersson 26*19a0f612SBjorn Andersson #include "hwspinlock_internal.h" 27*19a0f612SBjorn Andersson 28*19a0f612SBjorn Andersson #define QCOM_MUTEX_APPS_PROC_ID 1 29*19a0f612SBjorn Andersson #define QCOM_MUTEX_NUM_LOCKS 32 30*19a0f612SBjorn Andersson 31*19a0f612SBjorn Andersson static int qcom_hwspinlock_trylock(struct hwspinlock *lock) 32*19a0f612SBjorn Andersson { 33*19a0f612SBjorn Andersson struct regmap_field *field = lock->priv; 34*19a0f612SBjorn Andersson u32 lock_owner; 35*19a0f612SBjorn Andersson int ret; 36*19a0f612SBjorn Andersson 37*19a0f612SBjorn Andersson ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID); 38*19a0f612SBjorn Andersson if (ret) 39*19a0f612SBjorn Andersson return ret; 40*19a0f612SBjorn Andersson 41*19a0f612SBjorn Andersson ret = regmap_field_read(field, &lock_owner); 42*19a0f612SBjorn Andersson if (ret) 43*19a0f612SBjorn Andersson return ret; 44*19a0f612SBjorn Andersson 45*19a0f612SBjorn Andersson return lock_owner == QCOM_MUTEX_APPS_PROC_ID; 46*19a0f612SBjorn Andersson } 47*19a0f612SBjorn Andersson 48*19a0f612SBjorn Andersson static void qcom_hwspinlock_unlock(struct hwspinlock *lock) 49*19a0f612SBjorn Andersson { 50*19a0f612SBjorn Andersson struct regmap_field *field = lock->priv; 51*19a0f612SBjorn Andersson u32 lock_owner; 52*19a0f612SBjorn Andersson int ret; 53*19a0f612SBjorn Andersson 54*19a0f612SBjorn Andersson ret = regmap_field_read(field, &lock_owner); 55*19a0f612SBjorn Andersson if (ret) { 56*19a0f612SBjorn Andersson pr_err("%s: unable to query spinlock owner\n", __func__); 57*19a0f612SBjorn Andersson return; 58*19a0f612SBjorn Andersson } 59*19a0f612SBjorn Andersson 60*19a0f612SBjorn Andersson if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) { 61*19a0f612SBjorn Andersson pr_err("%s: spinlock not owned by us (actual owner is %d)\n", 62*19a0f612SBjorn Andersson __func__, lock_owner); 63*19a0f612SBjorn Andersson } 64*19a0f612SBjorn Andersson 65*19a0f612SBjorn Andersson ret = regmap_field_write(field, 0); 66*19a0f612SBjorn Andersson if (ret) 67*19a0f612SBjorn Andersson pr_err("%s: failed to unlock spinlock\n", __func__); 68*19a0f612SBjorn Andersson } 69*19a0f612SBjorn Andersson 70*19a0f612SBjorn Andersson static const struct hwspinlock_ops qcom_hwspinlock_ops = { 71*19a0f612SBjorn Andersson .trylock = qcom_hwspinlock_trylock, 72*19a0f612SBjorn Andersson .unlock = qcom_hwspinlock_unlock, 73*19a0f612SBjorn Andersson }; 74*19a0f612SBjorn Andersson 75*19a0f612SBjorn Andersson static const struct of_device_id qcom_hwspinlock_of_match[] = { 76*19a0f612SBjorn Andersson { .compatible = "qcom,sfpb-mutex" }, 77*19a0f612SBjorn Andersson { .compatible = "qcom,tcsr-mutex" }, 78*19a0f612SBjorn Andersson { } 79*19a0f612SBjorn Andersson }; 80*19a0f612SBjorn Andersson MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match); 81*19a0f612SBjorn Andersson 82*19a0f612SBjorn Andersson static int qcom_hwspinlock_probe(struct platform_device *pdev) 83*19a0f612SBjorn Andersson { 84*19a0f612SBjorn Andersson struct hwspinlock_device *bank; 85*19a0f612SBjorn Andersson struct device_node *syscon; 86*19a0f612SBjorn Andersson struct reg_field field; 87*19a0f612SBjorn Andersson struct regmap *regmap; 88*19a0f612SBjorn Andersson size_t array_size; 89*19a0f612SBjorn Andersson u32 stride; 90*19a0f612SBjorn Andersson u32 base; 91*19a0f612SBjorn Andersson int ret; 92*19a0f612SBjorn Andersson int i; 93*19a0f612SBjorn Andersson 94*19a0f612SBjorn Andersson syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0); 95*19a0f612SBjorn Andersson if (!syscon) { 96*19a0f612SBjorn Andersson dev_err(&pdev->dev, "no syscon property\n"); 97*19a0f612SBjorn Andersson return -ENODEV; 98*19a0f612SBjorn Andersson } 99*19a0f612SBjorn Andersson 100*19a0f612SBjorn Andersson regmap = syscon_node_to_regmap(syscon); 101*19a0f612SBjorn Andersson if (IS_ERR(regmap)) 102*19a0f612SBjorn Andersson return PTR_ERR(regmap); 103*19a0f612SBjorn Andersson 104*19a0f612SBjorn Andersson ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base); 105*19a0f612SBjorn Andersson if (ret < 0) { 106*19a0f612SBjorn Andersson dev_err(&pdev->dev, "no offset in syscon\n"); 107*19a0f612SBjorn Andersson return -EINVAL; 108*19a0f612SBjorn Andersson } 109*19a0f612SBjorn Andersson 110*19a0f612SBjorn Andersson ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride); 111*19a0f612SBjorn Andersson if (ret < 0) { 112*19a0f612SBjorn Andersson dev_err(&pdev->dev, "no stride syscon\n"); 113*19a0f612SBjorn Andersson return -EINVAL; 114*19a0f612SBjorn Andersson } 115*19a0f612SBjorn Andersson 116*19a0f612SBjorn Andersson array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock); 117*19a0f612SBjorn Andersson bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL); 118*19a0f612SBjorn Andersson if (!bank) 119*19a0f612SBjorn Andersson return -ENOMEM; 120*19a0f612SBjorn Andersson 121*19a0f612SBjorn Andersson platform_set_drvdata(pdev, bank); 122*19a0f612SBjorn Andersson 123*19a0f612SBjorn Andersson for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { 124*19a0f612SBjorn Andersson field.reg = base + i * stride; 125*19a0f612SBjorn Andersson field.lsb = 0; 126*19a0f612SBjorn Andersson field.msb = 32; 127*19a0f612SBjorn Andersson 128*19a0f612SBjorn Andersson bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, 129*19a0f612SBjorn Andersson regmap, field); 130*19a0f612SBjorn Andersson } 131*19a0f612SBjorn Andersson 132*19a0f612SBjorn Andersson pm_runtime_enable(&pdev->dev); 133*19a0f612SBjorn Andersson 134*19a0f612SBjorn Andersson ret = hwspin_lock_register(bank, &pdev->dev, &qcom_hwspinlock_ops, 135*19a0f612SBjorn Andersson 0, QCOM_MUTEX_NUM_LOCKS); 136*19a0f612SBjorn Andersson if (ret) 137*19a0f612SBjorn Andersson pm_runtime_disable(&pdev->dev); 138*19a0f612SBjorn Andersson 139*19a0f612SBjorn Andersson return ret; 140*19a0f612SBjorn Andersson } 141*19a0f612SBjorn Andersson 142*19a0f612SBjorn Andersson static int qcom_hwspinlock_remove(struct platform_device *pdev) 143*19a0f612SBjorn Andersson { 144*19a0f612SBjorn Andersson struct hwspinlock_device *bank = platform_get_drvdata(pdev); 145*19a0f612SBjorn Andersson int ret; 146*19a0f612SBjorn Andersson 147*19a0f612SBjorn Andersson ret = hwspin_lock_unregister(bank); 148*19a0f612SBjorn Andersson if (ret) { 149*19a0f612SBjorn Andersson dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); 150*19a0f612SBjorn Andersson return ret; 151*19a0f612SBjorn Andersson } 152*19a0f612SBjorn Andersson 153*19a0f612SBjorn Andersson pm_runtime_disable(&pdev->dev); 154*19a0f612SBjorn Andersson 155*19a0f612SBjorn Andersson return 0; 156*19a0f612SBjorn Andersson } 157*19a0f612SBjorn Andersson 158*19a0f612SBjorn Andersson static struct platform_driver qcom_hwspinlock_driver = { 159*19a0f612SBjorn Andersson .probe = qcom_hwspinlock_probe, 160*19a0f612SBjorn Andersson .remove = qcom_hwspinlock_remove, 161*19a0f612SBjorn Andersson .driver = { 162*19a0f612SBjorn Andersson .name = "qcom_hwspinlock", 163*19a0f612SBjorn Andersson .of_match_table = qcom_hwspinlock_of_match, 164*19a0f612SBjorn Andersson }, 165*19a0f612SBjorn Andersson }; 166*19a0f612SBjorn Andersson 167*19a0f612SBjorn Andersson static int __init qcom_hwspinlock_init(void) 168*19a0f612SBjorn Andersson { 169*19a0f612SBjorn Andersson return platform_driver_register(&qcom_hwspinlock_driver); 170*19a0f612SBjorn Andersson } 171*19a0f612SBjorn Andersson /* board init code might need to reserve hwspinlocks for predefined purposes */ 172*19a0f612SBjorn Andersson postcore_initcall(qcom_hwspinlock_init); 173*19a0f612SBjorn Andersson 174*19a0f612SBjorn Andersson static void __exit qcom_hwspinlock_exit(void) 175*19a0f612SBjorn Andersson { 176*19a0f612SBjorn Andersson platform_driver_unregister(&qcom_hwspinlock_driver); 177*19a0f612SBjorn Andersson } 178*19a0f612SBjorn Andersson module_exit(qcom_hwspinlock_exit); 179*19a0f612SBjorn Andersson 180*19a0f612SBjorn Andersson MODULE_LICENSE("GPL v2"); 181*19a0f612SBjorn Andersson MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs"); 182