18ba01caeSSuman Anna // SPDX-License-Identifier: GPL-2.0 219a0f612SBjorn Andersson /* 319a0f612SBjorn Andersson * Copyright (c) 2013, The Linux Foundation. All rights reserved. 419a0f612SBjorn Andersson * Copyright (c) 2015, Sony Mobile Communications AB 519a0f612SBjorn Andersson */ 619a0f612SBjorn Andersson 719a0f612SBjorn Andersson #include <linux/hwspinlock.h> 819a0f612SBjorn Andersson #include <linux/io.h> 919a0f612SBjorn Andersson #include <linux/kernel.h> 1019a0f612SBjorn Andersson #include <linux/mfd/syscon.h> 1119a0f612SBjorn Andersson #include <linux/module.h> 1219a0f612SBjorn Andersson #include <linux/of.h> 1319a0f612SBjorn Andersson #include <linux/of_device.h> 1419a0f612SBjorn Andersson #include <linux/platform_device.h> 1519a0f612SBjorn Andersson #include <linux/regmap.h> 1619a0f612SBjorn Andersson 1719a0f612SBjorn Andersson #include "hwspinlock_internal.h" 1819a0f612SBjorn Andersson 1919a0f612SBjorn Andersson #define QCOM_MUTEX_APPS_PROC_ID 1 2019a0f612SBjorn Andersson #define QCOM_MUTEX_NUM_LOCKS 32 2119a0f612SBjorn Andersson 22cdab30b4SChristian Marangi struct qcom_hwspinlock_of_data { 23cdab30b4SChristian Marangi u32 offset; 24cdab30b4SChristian Marangi u32 stride; 255d4753f7SKrzysztof Kozlowski const struct regmap_config *regmap_config; 26cdab30b4SChristian Marangi }; 27cdab30b4SChristian Marangi 2819a0f612SBjorn Andersson static int qcom_hwspinlock_trylock(struct hwspinlock *lock) 2919a0f612SBjorn Andersson { 3019a0f612SBjorn Andersson struct regmap_field *field = lock->priv; 3119a0f612SBjorn Andersson u32 lock_owner; 3219a0f612SBjorn Andersson int ret; 3319a0f612SBjorn Andersson 3419a0f612SBjorn Andersson ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID); 3519a0f612SBjorn Andersson if (ret) 3619a0f612SBjorn Andersson return ret; 3719a0f612SBjorn Andersson 3819a0f612SBjorn Andersson ret = regmap_field_read(field, &lock_owner); 3919a0f612SBjorn Andersson if (ret) 4019a0f612SBjorn Andersson return ret; 4119a0f612SBjorn Andersson 4219a0f612SBjorn Andersson return lock_owner == QCOM_MUTEX_APPS_PROC_ID; 4319a0f612SBjorn Andersson } 4419a0f612SBjorn Andersson 4519a0f612SBjorn Andersson static void qcom_hwspinlock_unlock(struct hwspinlock *lock) 4619a0f612SBjorn Andersson { 4719a0f612SBjorn Andersson struct regmap_field *field = lock->priv; 4819a0f612SBjorn Andersson u32 lock_owner; 4919a0f612SBjorn Andersson int ret; 5019a0f612SBjorn Andersson 5119a0f612SBjorn Andersson ret = regmap_field_read(field, &lock_owner); 5219a0f612SBjorn Andersson if (ret) { 5319a0f612SBjorn Andersson pr_err("%s: unable to query spinlock owner\n", __func__); 5419a0f612SBjorn Andersson return; 5519a0f612SBjorn Andersson } 5619a0f612SBjorn Andersson 5719a0f612SBjorn Andersson if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) { 5819a0f612SBjorn Andersson pr_err("%s: spinlock not owned by us (actual owner is %d)\n", 5919a0f612SBjorn Andersson __func__, lock_owner); 6019a0f612SBjorn Andersson } 6119a0f612SBjorn Andersson 6219a0f612SBjorn Andersson ret = regmap_field_write(field, 0); 6319a0f612SBjorn Andersson if (ret) 6419a0f612SBjorn Andersson pr_err("%s: failed to unlock spinlock\n", __func__); 6519a0f612SBjorn Andersson } 6619a0f612SBjorn Andersson 6719a0f612SBjorn Andersson static const struct hwspinlock_ops qcom_hwspinlock_ops = { 6819a0f612SBjorn Andersson .trylock = qcom_hwspinlock_trylock, 6919a0f612SBjorn Andersson .unlock = qcom_hwspinlock_unlock, 7019a0f612SBjorn Andersson }; 7119a0f612SBjorn Andersson 72*23316be8SChristian Marangi static const struct regmap_config sfpb_mutex_config = { 73*23316be8SChristian Marangi .reg_bits = 32, 74*23316be8SChristian Marangi .reg_stride = 4, 75*23316be8SChristian Marangi .val_bits = 32, 76*23316be8SChristian Marangi .max_register = 0x100, 77*23316be8SChristian Marangi .fast_io = true, 78*23316be8SChristian Marangi }; 79*23316be8SChristian Marangi 80cdab30b4SChristian Marangi static const struct qcom_hwspinlock_of_data of_sfpb_mutex = { 81cdab30b4SChristian Marangi .offset = 0x4, 82cdab30b4SChristian Marangi .stride = 0x4, 83*23316be8SChristian Marangi .regmap_config = &sfpb_mutex_config, 84cdab30b4SChristian Marangi }; 85cdab30b4SChristian Marangi 865d4753f7SKrzysztof Kozlowski static const struct regmap_config tcsr_msm8226_mutex_config = { 875d4753f7SKrzysztof Kozlowski .reg_bits = 32, 885d4753f7SKrzysztof Kozlowski .reg_stride = 4, 895d4753f7SKrzysztof Kozlowski .val_bits = 32, 905d4753f7SKrzysztof Kozlowski .max_register = 0x1000, 915d4753f7SKrzysztof Kozlowski .fast_io = true, 925d4753f7SKrzysztof Kozlowski }; 935d4753f7SKrzysztof Kozlowski 945d4753f7SKrzysztof Kozlowski static const struct qcom_hwspinlock_of_data of_msm8226_tcsr_mutex = { 955d4753f7SKrzysztof Kozlowski .offset = 0, 965d4753f7SKrzysztof Kozlowski .stride = 0x80, 975d4753f7SKrzysztof Kozlowski .regmap_config = &tcsr_msm8226_mutex_config, 985d4753f7SKrzysztof Kozlowski }; 995d4753f7SKrzysztof Kozlowski 1005d4753f7SKrzysztof Kozlowski static const struct regmap_config tcsr_mutex_config = { 1015d4753f7SKrzysztof Kozlowski .reg_bits = 32, 1025d4753f7SKrzysztof Kozlowski .reg_stride = 4, 1035d4753f7SKrzysztof Kozlowski .val_bits = 32, 1045d4753f7SKrzysztof Kozlowski .max_register = 0x20000, 1055d4753f7SKrzysztof Kozlowski .fast_io = true, 1065d4753f7SKrzysztof Kozlowski }; 1075d4753f7SKrzysztof Kozlowski 108cdab30b4SChristian Marangi static const struct qcom_hwspinlock_of_data of_tcsr_mutex = { 109cdab30b4SChristian Marangi .offset = 0, 110cdab30b4SChristian Marangi .stride = 0x1000, 1115d4753f7SKrzysztof Kozlowski .regmap_config = &tcsr_mutex_config, 112cdab30b4SChristian Marangi }; 113cdab30b4SChristian Marangi 11419a0f612SBjorn Andersson static const struct of_device_id qcom_hwspinlock_of_match[] = { 115cdab30b4SChristian Marangi { .compatible = "qcom,sfpb-mutex", .data = &of_sfpb_mutex }, 116cdab30b4SChristian Marangi { .compatible = "qcom,tcsr-mutex", .data = &of_tcsr_mutex }, 1175d4753f7SKrzysztof Kozlowski { .compatible = "qcom,apq8084-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, 1185d4753f7SKrzysztof Kozlowski { .compatible = "qcom,ipq6018-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, 1195d4753f7SKrzysztof Kozlowski { .compatible = "qcom,msm8226-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, 1205d4753f7SKrzysztof Kozlowski { .compatible = "qcom,msm8974-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, 1215d4753f7SKrzysztof Kozlowski { .compatible = "qcom,msm8994-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, 12219a0f612SBjorn Andersson { } 12319a0f612SBjorn Andersson }; 12419a0f612SBjorn Andersson MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match); 12519a0f612SBjorn Andersson 1267a1e6fb1SBjorn Andersson static struct regmap *qcom_hwspinlock_probe_syscon(struct platform_device *pdev, 1277a1e6fb1SBjorn Andersson u32 *base, u32 *stride) 1287a1e6fb1SBjorn Andersson { 1297a1e6fb1SBjorn Andersson struct device_node *syscon; 1307a1e6fb1SBjorn Andersson struct regmap *regmap; 1317a1e6fb1SBjorn Andersson int ret; 1327a1e6fb1SBjorn Andersson 1337a1e6fb1SBjorn Andersson syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0); 1347a1e6fb1SBjorn Andersson if (!syscon) 1357a1e6fb1SBjorn Andersson return ERR_PTR(-ENODEV); 1367a1e6fb1SBjorn Andersson 1377a1e6fb1SBjorn Andersson regmap = syscon_node_to_regmap(syscon); 1387a1e6fb1SBjorn Andersson of_node_put(syscon); 1397a1e6fb1SBjorn Andersson if (IS_ERR(regmap)) 1407a1e6fb1SBjorn Andersson return regmap; 1417a1e6fb1SBjorn Andersson 1427a1e6fb1SBjorn Andersson ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, base); 1437a1e6fb1SBjorn Andersson if (ret < 0) { 1447a1e6fb1SBjorn Andersson dev_err(&pdev->dev, "no offset in syscon\n"); 1457a1e6fb1SBjorn Andersson return ERR_PTR(-EINVAL); 1467a1e6fb1SBjorn Andersson } 1477a1e6fb1SBjorn Andersson 1487a1e6fb1SBjorn Andersson ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, stride); 1497a1e6fb1SBjorn Andersson if (ret < 0) { 1507a1e6fb1SBjorn Andersson dev_err(&pdev->dev, "no stride syscon\n"); 1517a1e6fb1SBjorn Andersson return ERR_PTR(-EINVAL); 1527a1e6fb1SBjorn Andersson } 1537a1e6fb1SBjorn Andersson 1547a1e6fb1SBjorn Andersson return regmap; 1557a1e6fb1SBjorn Andersson } 1567a1e6fb1SBjorn Andersson 1577a1e6fb1SBjorn Andersson static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev, 1587a1e6fb1SBjorn Andersson u32 *offset, u32 *stride) 1597a1e6fb1SBjorn Andersson { 160cdab30b4SChristian Marangi const struct qcom_hwspinlock_of_data *data; 1617a1e6fb1SBjorn Andersson struct device *dev = &pdev->dev; 1627a1e6fb1SBjorn Andersson void __iomem *base; 1637a1e6fb1SBjorn Andersson 164cdab30b4SChristian Marangi data = of_device_get_match_data(dev); 1655d4753f7SKrzysztof Kozlowski if (!data->regmap_config) 1665d4753f7SKrzysztof Kozlowski return ERR_PTR(-EINVAL); 167cdab30b4SChristian Marangi 168cdab30b4SChristian Marangi *offset = data->offset; 169cdab30b4SChristian Marangi *stride = data->stride; 1707a1e6fb1SBjorn Andersson 1717a1e6fb1SBjorn Andersson base = devm_platform_ioremap_resource(pdev, 0); 1727a1e6fb1SBjorn Andersson if (IS_ERR(base)) 1737a1e6fb1SBjorn Andersson return ERR_CAST(base); 1747a1e6fb1SBjorn Andersson 1755d4753f7SKrzysztof Kozlowski return devm_regmap_init_mmio(dev, base, data->regmap_config); 1767a1e6fb1SBjorn Andersson } 1777a1e6fb1SBjorn Andersson 17819a0f612SBjorn Andersson static int qcom_hwspinlock_probe(struct platform_device *pdev) 17919a0f612SBjorn Andersson { 18019a0f612SBjorn Andersson struct hwspinlock_device *bank; 18119a0f612SBjorn Andersson struct reg_field field; 18219a0f612SBjorn Andersson struct regmap *regmap; 18319a0f612SBjorn Andersson size_t array_size; 18419a0f612SBjorn Andersson u32 stride; 18519a0f612SBjorn Andersson u32 base; 18619a0f612SBjorn Andersson int i; 18719a0f612SBjorn Andersson 1887a1e6fb1SBjorn Andersson regmap = qcom_hwspinlock_probe_syscon(pdev, &base, &stride); 1897a1e6fb1SBjorn Andersson if (IS_ERR(regmap) && PTR_ERR(regmap) == -ENODEV) 1907a1e6fb1SBjorn Andersson regmap = qcom_hwspinlock_probe_mmio(pdev, &base, &stride); 19119a0f612SBjorn Andersson 19219a0f612SBjorn Andersson if (IS_ERR(regmap)) 19319a0f612SBjorn Andersson return PTR_ERR(regmap); 19419a0f612SBjorn Andersson 19519a0f612SBjorn Andersson array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock); 19619a0f612SBjorn Andersson bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL); 19719a0f612SBjorn Andersson if (!bank) 19819a0f612SBjorn Andersson return -ENOMEM; 19919a0f612SBjorn Andersson 20019a0f612SBjorn Andersson platform_set_drvdata(pdev, bank); 20119a0f612SBjorn Andersson 20219a0f612SBjorn Andersson for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { 20319a0f612SBjorn Andersson field.reg = base + i * stride; 20419a0f612SBjorn Andersson field.lsb = 0; 205bd5717a4SBjorn Andersson field.msb = 31; 20619a0f612SBjorn Andersson 20719a0f612SBjorn Andersson bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, 20819a0f612SBjorn Andersson regmap, field); 2093c81195aSKang Chen if (IS_ERR(bank->lock[i].priv)) 2103c81195aSKang Chen return PTR_ERR(bank->lock[i].priv); 21119a0f612SBjorn Andersson } 21219a0f612SBjorn Andersson 213ed0611a6SBaolin Wang return devm_hwspin_lock_register(&pdev->dev, bank, &qcom_hwspinlock_ops, 21419a0f612SBjorn Andersson 0, QCOM_MUTEX_NUM_LOCKS); 21519a0f612SBjorn Andersson } 21619a0f612SBjorn Andersson 21719a0f612SBjorn Andersson static struct platform_driver qcom_hwspinlock_driver = { 21819a0f612SBjorn Andersson .probe = qcom_hwspinlock_probe, 21919a0f612SBjorn Andersson .driver = { 22019a0f612SBjorn Andersson .name = "qcom_hwspinlock", 22119a0f612SBjorn Andersson .of_match_table = qcom_hwspinlock_of_match, 22219a0f612SBjorn Andersson }, 22319a0f612SBjorn Andersson }; 22419a0f612SBjorn Andersson 22519a0f612SBjorn Andersson static int __init qcom_hwspinlock_init(void) 22619a0f612SBjorn Andersson { 22719a0f612SBjorn Andersson return platform_driver_register(&qcom_hwspinlock_driver); 22819a0f612SBjorn Andersson } 22919a0f612SBjorn Andersson /* board init code might need to reserve hwspinlocks for predefined purposes */ 23019a0f612SBjorn Andersson postcore_initcall(qcom_hwspinlock_init); 23119a0f612SBjorn Andersson 23219a0f612SBjorn Andersson static void __exit qcom_hwspinlock_exit(void) 23319a0f612SBjorn Andersson { 23419a0f612SBjorn Andersson platform_driver_unregister(&qcom_hwspinlock_driver); 23519a0f612SBjorn Andersson } 23619a0f612SBjorn Andersson module_exit(qcom_hwspinlock_exit); 23719a0f612SBjorn Andersson 23819a0f612SBjorn Andersson MODULE_LICENSE("GPL v2"); 23919a0f612SBjorn Andersson MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs"); 240