brintos

brintos / linux-shallow public Read only

0
0
Text · 6.3 KiB · 0390979 Raw
264 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2013, The Linux Foundation. All rights reserved.4 * Copyright (c) 2015, Sony Mobile Communications AB5 */6 7#include <linux/hwspinlock.h>8#include <linux/io.h>9#include <linux/kernel.h>10#include <linux/mfd/syscon.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/of_device.h>14#include <linux/platform_device.h>15#include <linux/regmap.h>16 17#include "hwspinlock_internal.h"18 19#define QCOM_MUTEX_APPS_PROC_ID	120#define QCOM_MUTEX_NUM_LOCKS	3221 22struct qcom_hwspinlock_of_data {23	u32 offset;24	u32 stride;25	const struct regmap_config *regmap_config;26};27 28static int qcom_hwspinlock_trylock(struct hwspinlock *lock)29{30	struct regmap_field *field = lock->priv;31	u32 lock_owner;32	int ret;33 34	ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID);35	if (ret)36		return ret;37 38	ret = regmap_field_read(field, &lock_owner);39	if (ret)40		return ret;41 42	return lock_owner == QCOM_MUTEX_APPS_PROC_ID;43}44 45static void qcom_hwspinlock_unlock(struct hwspinlock *lock)46{47	struct regmap_field *field = lock->priv;48	u32 lock_owner;49	int ret;50 51	ret = regmap_field_read(field, &lock_owner);52	if (ret) {53		pr_err("%s: unable to query spinlock owner\n", __func__);54		return;55	}56 57	if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) {58		pr_err("%s: spinlock not owned by us (actual owner is %d)\n",59				__func__, lock_owner);60	}61 62	ret = regmap_field_write(field, 0);63	if (ret)64		pr_err("%s: failed to unlock spinlock\n", __func__);65}66 67static int qcom_hwspinlock_bust(struct hwspinlock *lock, unsigned int id)68{69	struct regmap_field *field = lock->priv;70	u32 owner;71	int ret;72 73	ret = regmap_field_read(field, &owner);74	if (ret) {75		dev_err(lock->bank->dev, "unable to query spinlock owner\n");76		return ret;77	}78 79	if (owner != id)80		return 0;81 82	ret = regmap_field_write(field, 0);83	if (ret) {84		dev_err(lock->bank->dev, "failed to bust spinlock\n");85		return ret;86	}87 88	return 0;89}90 91static const struct hwspinlock_ops qcom_hwspinlock_ops = {92	.trylock	= qcom_hwspinlock_trylock,93	.unlock		= qcom_hwspinlock_unlock,94	.bust		= qcom_hwspinlock_bust,95};96 97static const struct regmap_config sfpb_mutex_config = {98	.reg_bits		= 32,99	.reg_stride		= 4,100	.val_bits		= 32,101	.max_register		= 0x100,102	.fast_io		= true,103};104 105static const struct qcom_hwspinlock_of_data of_sfpb_mutex = {106	.offset = 0x4,107	.stride = 0x4,108	.regmap_config = &sfpb_mutex_config,109};110 111static const struct regmap_config tcsr_msm8226_mutex_config = {112	.reg_bits		= 32,113	.reg_stride		= 4,114	.val_bits		= 32,115	.max_register		= 0x1000,116	.fast_io		= true,117};118 119static const struct qcom_hwspinlock_of_data of_msm8226_tcsr_mutex = {120	.offset = 0,121	.stride = 0x80,122	.regmap_config = &tcsr_msm8226_mutex_config,123};124 125static const struct regmap_config tcsr_mutex_config = {126	.reg_bits		= 32,127	.reg_stride		= 4,128	.val_bits		= 32,129	.max_register		= 0x20000,130	.fast_io		= true,131};132 133static const struct qcom_hwspinlock_of_data of_tcsr_mutex = {134	.offset = 0,135	.stride = 0x1000,136	.regmap_config = &tcsr_mutex_config,137};138 139static const struct of_device_id qcom_hwspinlock_of_match[] = {140	{ .compatible = "qcom,sfpb-mutex", .data = &of_sfpb_mutex },141	{ .compatible = "qcom,tcsr-mutex", .data = &of_tcsr_mutex },142	{ .compatible = "qcom,apq8084-tcsr-mutex", .data = &of_msm8226_tcsr_mutex },143	{ .compatible = "qcom,msm8226-tcsr-mutex", .data = &of_msm8226_tcsr_mutex },144	{ .compatible = "qcom,msm8974-tcsr-mutex", .data = &of_msm8226_tcsr_mutex },145	{ .compatible = "qcom,msm8994-tcsr-mutex", .data = &of_msm8226_tcsr_mutex },146	{ }147};148MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match);149 150static struct regmap *qcom_hwspinlock_probe_syscon(struct platform_device *pdev,151						   u32 *base, u32 *stride)152{153	struct device_node *syscon;154	struct regmap *regmap;155	int ret;156 157	syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0);158	if (!syscon)159		return ERR_PTR(-ENODEV);160 161	regmap = syscon_node_to_regmap(syscon);162	of_node_put(syscon);163	if (IS_ERR(regmap))164		return regmap;165 166	ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, base);167	if (ret < 0) {168		dev_err(&pdev->dev, "no offset in syscon\n");169		return ERR_PTR(-EINVAL);170	}171 172	ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, stride);173	if (ret < 0) {174		dev_err(&pdev->dev, "no stride syscon\n");175		return ERR_PTR(-EINVAL);176	}177 178	return regmap;179}180 181static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev,182						 u32 *offset, u32 *stride)183{184	const struct qcom_hwspinlock_of_data *data;185	struct device *dev = &pdev->dev;186	void __iomem *base;187 188	data = of_device_get_match_data(dev);189	if (!data->regmap_config)190		return ERR_PTR(-EINVAL);191 192	*offset = data->offset;193	*stride = data->stride;194 195	base = devm_platform_ioremap_resource(pdev, 0);196	if (IS_ERR(base))197		return ERR_CAST(base);198 199	return devm_regmap_init_mmio(dev, base, data->regmap_config);200}201 202static int qcom_hwspinlock_probe(struct platform_device *pdev)203{204	struct hwspinlock_device *bank;205	struct reg_field field;206	struct regmap *regmap;207	size_t array_size;208	u32 stride;209	u32 base;210	int i;211 212	regmap = qcom_hwspinlock_probe_syscon(pdev, &base, &stride);213	if (IS_ERR(regmap) && PTR_ERR(regmap) == -ENODEV)214		regmap = qcom_hwspinlock_probe_mmio(pdev, &base, &stride);215 216	if (IS_ERR(regmap))217		return PTR_ERR(regmap);218 219	array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);220	bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL);221	if (!bank)222		return -ENOMEM;223 224	platform_set_drvdata(pdev, bank);225 226	for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {227		field.reg = base + i * stride;228		field.lsb = 0;229		field.msb = 31;230 231		bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,232							     regmap, field);233		if (IS_ERR(bank->lock[i].priv))234			return PTR_ERR(bank->lock[i].priv);235	}236 237	return devm_hwspin_lock_register(&pdev->dev, bank, &qcom_hwspinlock_ops,238					 0, QCOM_MUTEX_NUM_LOCKS);239}240 241static struct platform_driver qcom_hwspinlock_driver = {242	.probe		= qcom_hwspinlock_probe,243	.driver		= {244		.name	= "qcom_hwspinlock",245		.of_match_table = qcom_hwspinlock_of_match,246	},247};248 249static int __init qcom_hwspinlock_init(void)250{251	return platform_driver_register(&qcom_hwspinlock_driver);252}253/* board init code might need to reserve hwspinlocks for predefined purposes */254postcore_initcall(qcom_hwspinlock_init);255 256static void __exit qcom_hwspinlock_exit(void)257{258	platform_driver_unregister(&qcom_hwspinlock_driver);259}260module_exit(qcom_hwspinlock_exit);261 262MODULE_LICENSE("GPL v2");263MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs");264