123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- // SPDX-License-Identifier: GPL-2.0-only
- /* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
- #include <linux/interrupt.h>
- #include <linux/mfd/syscon.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/of_irq.h>
- #include <linux/platform_device.h>
- #include <linux/regmap.h>
- #include "hgsl_tcsr.h"
- /* Sender registers */
- #define TCSR_GLB_CFG_COMPUTE_SIGNALING_REG 0x000
- #define TCSR_COMPUTE_SIGNALING_REG 0x000
- /* Receiver registers */
- #define TCSR_COMPUTE_SIGNAL_STATUS_REG 0x000
- #define TCSR_COMPUTE_SIGNAL_CLEAR_REG 0x400
- #define TCSR_COMPUTE_SIGNAL_MASK_REG 0x800
- struct hgsl_tcsr {
- struct platform_device *pdev;
- struct device *client_dev;
- struct regmap *regmap;
- struct regmap *glb_regmap;
- enum hgsl_tcsr_role role;
- unsigned int enable_count;
- struct mutex dev_mutex;
- unsigned int irq_num;
- irqreturn_t (*isr)(struct device *dev, u32 status);
- };
- static irqreturn_t hgsl_tcsr_isr(int irq, void *ptr)
- {
- struct hgsl_tcsr *tcsr = ptr;
- u32 status;
- regmap_read(tcsr->regmap, TCSR_COMPUTE_SIGNAL_STATUS_REG, &status);
- regmap_write(tcsr->regmap, TCSR_COMPUTE_SIGNAL_CLEAR_REG, status);
- if (tcsr->isr)
- return tcsr->isr(tcsr->client_dev, status);
- else
- return IRQ_HANDLED;
- }
- static int hgsl_tcsr_init_sender(struct hgsl_tcsr *tcsr)
- {
- struct device *dev = &tcsr->pdev->dev;
- struct device_node *np = dev->of_node;
- if (tcsr->glb_regmap != NULL)
- return 0;
- tcsr->regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
- if (IS_ERR_OR_NULL(tcsr->regmap)) {
- dev_err(dev, "failed to map sender register\n");
- return -ENODEV;
- }
- tcsr->glb_regmap = syscon_regmap_lookup_by_phandle(np, "syscon-glb");
- if (IS_ERR_OR_NULL(tcsr->glb_regmap)) {
- dev_err(dev, "failed to map sender global register\n");
- tcsr->glb_regmap = NULL;
- return -ENODEV;
- }
- return 0;
- }
- static int hgsl_tcsr_init_receiver(struct hgsl_tcsr *tcsr)
- {
- struct device *dev = &tcsr->pdev->dev;
- struct device_node *np = dev->of_node;
- int ret;
- if (tcsr->irq_num != 0)
- return 0;
- tcsr->regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
- if (IS_ERR_OR_NULL(tcsr->regmap)) {
- dev_err(dev, "failed to map receiver register\n");
- return -ENODEV;
- }
- tcsr->irq_num = irq_of_parse_and_map(np, 0);
- if (tcsr->irq_num == 0) {
- dev_err(dev, "failed to get irq\n");
- return -ENODEV;
- }
- ret = request_irq(tcsr->irq_num, hgsl_tcsr_isr,
- IRQF_TRIGGER_HIGH, "hgsl-tcsr", tcsr);
- if (ret < 0) {
- dev_err(dev, "failed to request IRQ%u: %d\n",
- tcsr->irq_num, ret);
- tcsr->irq_num = 0;
- return ret;
- }
- disable_irq(tcsr->irq_num);
- return 0;
- }
- #if IS_ENABLED(CONFIG_QCOM_HGSL_TCSR_SIGNAL)
- struct hgsl_tcsr *hgsl_tcsr_request(struct platform_device *pdev,
- enum hgsl_tcsr_role role,
- struct device *client,
- irqreturn_t (*isr)(struct device *, u32))
- {
- struct hgsl_tcsr *tcsr = platform_get_drvdata(pdev);
- int ret = -EINVAL;
- if (!tcsr)
- return ERR_PTR(-ENODEV);
- else if (tcsr->role != role)
- return ERR_PTR(-EINVAL);
- else if (tcsr->client_dev)
- return ERR_PTR(-EBUSY);
- if (role == HGSL_TCSR_ROLE_RECEIVER) {
- if (tcsr->isr)
- return ERR_PTR(-EBUSY);
- else if (!isr)
- return ERR_PTR(-EINVAL);
- tcsr->client_dev = client;
- tcsr->isr = isr;
- ret = hgsl_tcsr_init_receiver(tcsr);
- } else { /* HGSL_TCSR_ROLE_SENDER */
- if (isr)
- return ERR_PTR(-EINVAL);
- tcsr->client_dev = client;
- ret = hgsl_tcsr_init_sender(tcsr);
- }
- if (ret) {
- tcsr = ERR_PTR(ret);
- tcsr->client_dev = NULL;
- tcsr->isr = NULL;
- }
- return tcsr;
- }
- void hgsl_tcsr_free(struct hgsl_tcsr *tcsr)
- {
- if ((tcsr->role == HGSL_TCSR_ROLE_RECEIVER) &&
- (tcsr->irq_num != 0) && (tcsr->isr != NULL))
- free_irq(tcsr->irq_num, tcsr);
- tcsr->client_dev = NULL;
- tcsr->isr = NULL;
- }
- int hgsl_tcsr_enable(struct hgsl_tcsr *tcsr)
- {
- mutex_lock(&tcsr->dev_mutex);
- if (tcsr->enable_count > 0)
- goto done;
- if (tcsr->irq_num)
- enable_irq(tcsr->irq_num);
- done:
- tcsr->enable_count++;
- mutex_unlock(&tcsr->dev_mutex);
- return 0;
- }
- void hgsl_tcsr_disable(struct hgsl_tcsr *tcsr)
- {
- mutex_lock(&tcsr->dev_mutex);
- if (--tcsr->enable_count > 0)
- goto done;
- if (tcsr->irq_num)
- disable_irq(tcsr->irq_num);
- done:
- mutex_unlock(&tcsr->dev_mutex);
- }
- bool hgsl_tcsr_is_enabled(struct hgsl_tcsr *tcsr)
- {
- return (tcsr->enable_count > 0);
- }
- void hgsl_tcsr_irq_trigger(struct hgsl_tcsr *tcsr, int irq_id)
- {
- u32 reg;
- /*
- * Read back this global config register in case
- * it has been modified by others.
- */
- regmap_read(tcsr->glb_regmap,
- TCSR_GLB_CFG_COMPUTE_SIGNALING_REG, ®);
- reg = irq_id << reg;
- regmap_write(tcsr->regmap, TCSR_COMPUTE_SIGNALING_REG, reg);
- }
- void hgsl_tcsr_irq_enable(struct hgsl_tcsr *tcsr, u32 mask, bool enable)
- {
- u32 reg;
- regmap_read(tcsr->regmap, TCSR_COMPUTE_SIGNAL_MASK_REG, ®);
- reg = enable ? (reg | mask) : (reg & ~mask);
- regmap_write(tcsr->regmap, TCSR_COMPUTE_SIGNAL_MASK_REG, reg);
- }
- #endif
- static const struct of_device_id hgsl_tcsr_match_table[] = {
- { .compatible = "qcom,hgsl-tcsr-sender" },
- { .compatible = "qcom,hgsl-tcsr-receiver" },
- {}
- };
- static int hgsl_tcsr_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct hgsl_tcsr *tcsr = devm_kzalloc(dev, sizeof(*tcsr),
- GFP_KERNEL);
- if (!tcsr)
- return -ENOMEM;
- if (of_device_is_compatible(np, "qcom,hgsl-tcsr-receiver")) {
- tcsr->role = HGSL_TCSR_ROLE_RECEIVER;
- } else if (of_device_is_compatible(np, "qcom,hgsl-tcsr-sender")) {
- tcsr->role = HGSL_TCSR_ROLE_SENDER;
- } else {
- dev_err(dev, "Not compatible device\n");
- return -ENODEV;
- }
- mutex_init(&tcsr->dev_mutex);
- tcsr->pdev = pdev;
- platform_set_drvdata(pdev, tcsr);
- return 0;
- }
- static int hgsl_tcsr_remove(struct platform_device *pdev)
- {
- struct hgsl_tcsr *tcsr = platform_get_drvdata(pdev);
- mutex_destroy(&tcsr->dev_mutex);
- return 0;
- }
- struct platform_driver hgsl_tcsr_driver = {
- .probe = hgsl_tcsr_probe,
- .remove = hgsl_tcsr_remove,
- .driver = {
- .name = "hgsl-tcsr",
- .of_match_table = hgsl_tcsr_match_table,
- }
- };
|