Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/interconnect/qcom/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 7 kB image not shown  

Quelle  osm-l3.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
 * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
 */


#include <linux/args.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/interconnect-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>

#include <dt-bindings/interconnect/qcom,osm-l3.h>

#define LUT_MAX_ENTRIES   40U
#define LUT_SRC    GENMASK(31, 30)
#define LUT_L_VAL   GENMASK(7, 0)
#define CLK_HW_DIV   2

/* OSM Register offsets */
#define REG_ENABLE   0x0
#define OSM_LUT_ROW_SIZE  32
#define OSM_REG_FREQ_LUT  0x110
#define OSM_REG_PERF_STATE  0x920

/* EPSS Register offsets */
#define EPSS_LUT_ROW_SIZE  4
#define EPSS_REG_L3_VOTE  0x90
#define EPSS_REG_FREQ_LUT  0x100
#define EPSS_REG_PERF_STATE  0x320

#define to_osm_l3_provider(_provider) \
 container_of(_provider, struct qcom_osm_l3_icc_provider, provider)

struct qcom_osm_l3_icc_provider {
 void __iomem *base;
 unsigned int max_state;
 unsigned int reg_perf_state;
 unsigned long lut_tables[LUT_MAX_ENTRIES];
 struct icc_provider provider;
};

/**
 * struct qcom_osm_l3_node - Qualcomm specific interconnect nodes
 * @name: the node name used in debugfs
 * @buswidth: width of the interconnect between a node and the bus
 */

struct qcom_osm_l3_node {
 const char *name;
 u16 buswidth;
};

struct qcom_osm_l3_desc {
 const struct qcom_osm_l3_node * const *nodes;
 size_t num_nodes;
 unsigned int lut_row_size;
 unsigned int reg_freq_lut;
 unsigned int reg_perf_state;
};

#define DEFINE_QNODE(_name, _buswidth)     \
 static const struct qcom_osm_l3_node _name = {   \
  .name = #_name,      \
  .buswidth = _buswidth,     \
 }

DEFINE_QNODE(osm_l3_slave, 16);
DEFINE_QNODE(osm_l3_master, 16);

static const struct qcom_osm_l3_node * const osm_l3_nodes[] = {
 [MASTER_OSM_L3_APPS] = &osm_l3_master,
 [SLAVE_OSM_L3] = &osm_l3_slave,
};

DEFINE_QNODE(epss_l3_slave, 32);
DEFINE_QNODE(epss_l3_master, 32);

static const struct qcom_osm_l3_node * const epss_l3_nodes[] = {
 [MASTER_EPSS_L3_APPS] = &epss_l3_master,
 [SLAVE_EPSS_L3_SHARED] = &epss_l3_slave,
};

static const struct qcom_osm_l3_desc osm_l3 = {
 .nodes = osm_l3_nodes,
 .num_nodes = ARRAY_SIZE(osm_l3_nodes),
 .lut_row_size = OSM_LUT_ROW_SIZE,
 .reg_freq_lut = OSM_REG_FREQ_LUT,
 .reg_perf_state = OSM_REG_PERF_STATE,
};

static const struct qcom_osm_l3_desc epss_l3_perf_state = {
 .nodes = epss_l3_nodes,
 .num_nodes = ARRAY_SIZE(epss_l3_nodes),
 .lut_row_size = EPSS_LUT_ROW_SIZE,
 .reg_freq_lut = EPSS_REG_FREQ_LUT,
 .reg_perf_state = EPSS_REG_PERF_STATE,
};

static const struct qcom_osm_l3_desc epss_l3_l3_vote = {
 .nodes = epss_l3_nodes,
 .num_nodes = ARRAY_SIZE(epss_l3_nodes),
 .lut_row_size = EPSS_LUT_ROW_SIZE,
 .reg_freq_lut = EPSS_REG_FREQ_LUT,
 .reg_perf_state = EPSS_REG_L3_VOTE,
};

static int qcom_osm_l3_set(struct icc_node *src, struct icc_node *dst)
{
 struct qcom_osm_l3_icc_provider *qp;
 struct icc_provider *provider;
 const struct qcom_osm_l3_node *qn;
 unsigned int index;
 u64 rate;

 qn = src->data;
 provider = src->provider;
 qp = to_osm_l3_provider(provider);

 rate = icc_units_to_bps(dst->peak_bw);
 do_div(rate, qn->buswidth);

 for (index = 0; index < qp->max_state - 1; index++) {
  if (qp->lut_tables[index] >= rate)
   break;
 }

 writel_relaxed(index, qp->base + qp->reg_perf_state);

 return 0;
}

static void qcom_osm_l3_remove(struct platform_device *pdev)
{
 struct qcom_osm_l3_icc_provider *qp = platform_get_drvdata(pdev);

 icc_provider_deregister(&qp->provider);
 icc_nodes_remove(&qp->provider);
}

static int qcom_osm_l3_probe(struct platform_device *pdev)
{
 u32 info, src, lval, i, prev_freq = 0, freq;
 static unsigned long hw_rate, xo_rate;
 struct qcom_osm_l3_icc_provider *qp;
 const struct qcom_osm_l3_desc *desc;
 struct icc_onecell_data *data;
 struct icc_provider *provider;
 const struct qcom_osm_l3_node * const *qnodes;
 struct icc_node *node;
 size_t num_nodes;
 struct clk *clk;
 int ret;

 clk = clk_get(&pdev->dev, "xo");
 if (IS_ERR(clk))
  return PTR_ERR(clk);

 xo_rate = clk_get_rate(clk);
 clk_put(clk);

 clk = clk_get(&pdev->dev, "alternate");
 if (IS_ERR(clk))
  return PTR_ERR(clk);

 hw_rate = clk_get_rate(clk) / CLK_HW_DIV;
 clk_put(clk);

 qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
 if (!qp)
  return -ENOMEM;

 qp->base = devm_platform_ioremap_resource(pdev, 0);
 if (IS_ERR(qp->base))
  return PTR_ERR(qp->base);

 /* HW should be in enabled state to proceed */
 if (!(readl_relaxed(qp->base + REG_ENABLE) & 0x1)) {
  dev_err(&pdev->dev, "error hardware not enabled\n");
  return -ENODEV;
 }

 desc = device_get_match_data(&pdev->dev);
 if (!desc)
  return -EINVAL;

 qp->reg_perf_state = desc->reg_perf_state;

 for (i = 0; i < LUT_MAX_ENTRIES; i++) {
  info = readl_relaxed(qp->base + desc->reg_freq_lut +
         i * desc->lut_row_size);
  src = FIELD_GET(LUT_SRC, info);
  lval = FIELD_GET(LUT_L_VAL, info);
  if (src)
   freq = xo_rate * lval;
  else
   freq = hw_rate;

  /* Two of the same frequencies signify end of table */
  if (i > 0 && prev_freq == freq)
   break;

  dev_dbg(&pdev->dev, "index=%d freq=%d\n", i, freq);

  qp->lut_tables[i] = freq;
  prev_freq = freq;
 }
 qp->max_state = i;

 qnodes = desc->nodes;
 num_nodes = desc->num_nodes;

 data = devm_kzalloc(&pdev->dev, struct_size(data, nodes, num_nodes), GFP_KERNEL);
 if (!data)
  return -ENOMEM;
 data->num_nodes = num_nodes;

 provider = &qp->provider;
 provider->dev = &pdev->dev;
 provider->set = qcom_osm_l3_set;
 provider->aggregate = icc_std_aggregate;
 provider->xlate = of_icc_xlate_onecell;
 provider->data = data;

 icc_provider_init(provider);

 /* Create nodes */
 for (i = 0; i < num_nodes; i++) {
  node = icc_node_create_dyn();

  if (IS_ERR(node)) {
   ret = PTR_ERR(node);
   goto err;
  }

  ret = icc_node_set_name(node, provider, qnodes[i]->name);
  if (ret) {
   icc_node_destroy(node->id);
   goto err;
  }

  /* Cast away const and add it back in qcom_osm_l3_set() */
  node->data = (void *)qnodes[i];
  icc_node_add(node, provider);

  data->nodes[i] = node;
 }

 /* Create link */
 icc_link_nodes(data->nodes[MASTER_OSM_L3_APPS], &data->nodes[SLAVE_OSM_L3]);

 ret = icc_provider_register(provider);
 if (ret)
  goto err;

 platform_set_drvdata(pdev, qp);

 return 0;
err:
 icc_nodes_remove(provider);

 return ret;
}

static const struct of_device_id osm_l3_of_match[] = {
 { .compatible = "qcom,epss-l3", .data = &epss_l3_l3_vote },
 { .compatible = "qcom,osm-l3", .data = &osm_l3 },
 { .compatible = "qcom,sa8775p-epss-l3", .data = &epss_l3_perf_state },
 { .compatible = "qcom,sc7180-osm-l3", .data = &osm_l3 },
 { .compatible = "qcom,sc7280-epss-l3", .data = &epss_l3_perf_state },
 { .compatible = "qcom,sdm845-osm-l3", .data = &osm_l3 },
 { .compatible = "qcom,sm8150-osm-l3", .data = &osm_l3 },
 { .compatible = "qcom,sc8180x-osm-l3", .data = &osm_l3 },
 { .compatible = "qcom,sm8250-epss-l3", .data = &epss_l3_perf_state },
 { }
};
MODULE_DEVICE_TABLE(of, osm_l3_of_match);

static struct platform_driver osm_l3_driver = {
 .probe = qcom_osm_l3_probe,
 .remove = qcom_osm_l3_remove,
 .driver = {
  .name = "osm-l3",
  .of_match_table = osm_l3_of_match,
  .sync_state = icc_sync_state,
 },
};
module_platform_driver(osm_l3_driver);

MODULE_DESCRIPTION("Qualcomm OSM L3 interconnect driver");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=91 H=96 G=93

¤ Dauer der Verarbeitung: 0.3 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.