cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

xilinx-pr-decoupler.c (4458B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2017, National Instruments Corp.
      4 * Copyright (c) 2017, Xilinx Inc
      5 *
      6 * FPGA Bridge Driver for the Xilinx LogiCORE Partial Reconfiguration
      7 * Decoupler IP Core.
      8 */
      9
     10#include <linux/clk.h>
     11#include <linux/io.h>
     12#include <linux/kernel.h>
     13#include <linux/of_device.h>
     14#include <linux/module.h>
     15#include <linux/fpga/fpga-bridge.h>
     16
     17#define CTRL_CMD_DECOUPLE	BIT(0)
     18#define CTRL_CMD_COUPLE		0
     19#define CTRL_OFFSET		0
     20
     21struct xlnx_config_data {
     22	const char *name;
     23};
     24
     25struct xlnx_pr_decoupler_data {
     26	const struct xlnx_config_data *ipconfig;
     27	void __iomem *io_base;
     28	struct clk *clk;
     29};
     30
     31static inline void xlnx_pr_decoupler_write(struct xlnx_pr_decoupler_data *d,
     32					   u32 offset, u32 val)
     33{
     34	writel(val, d->io_base + offset);
     35}
     36
     37static inline u32 xlnx_pr_decouple_read(const struct xlnx_pr_decoupler_data *d,
     38					u32 offset)
     39{
     40	return readl(d->io_base + offset);
     41}
     42
     43static int xlnx_pr_decoupler_enable_set(struct fpga_bridge *bridge, bool enable)
     44{
     45	int err;
     46	struct xlnx_pr_decoupler_data *priv = bridge->priv;
     47
     48	err = clk_enable(priv->clk);
     49	if (err)
     50		return err;
     51
     52	if (enable)
     53		xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_COUPLE);
     54	else
     55		xlnx_pr_decoupler_write(priv, CTRL_OFFSET, CTRL_CMD_DECOUPLE);
     56
     57	clk_disable(priv->clk);
     58
     59	return 0;
     60}
     61
     62static int xlnx_pr_decoupler_enable_show(struct fpga_bridge *bridge)
     63{
     64	const struct xlnx_pr_decoupler_data *priv = bridge->priv;
     65	u32 status;
     66	int err;
     67
     68	err = clk_enable(priv->clk);
     69	if (err)
     70		return err;
     71
     72	status = readl(priv->io_base);
     73
     74	clk_disable(priv->clk);
     75
     76	return !status;
     77}
     78
     79static const struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = {
     80	.enable_set = xlnx_pr_decoupler_enable_set,
     81	.enable_show = xlnx_pr_decoupler_enable_show,
     82};
     83
     84#ifdef CONFIG_OF
     85static const struct xlnx_config_data decoupler_config = {
     86	.name = "Xilinx PR Decoupler",
     87};
     88
     89static const struct xlnx_config_data shutdown_config = {
     90	.name = "Xilinx DFX AXI Shutdown Manager",
     91};
     92
     93static const struct of_device_id xlnx_pr_decoupler_of_match[] = {
     94	{ .compatible = "xlnx,pr-decoupler-1.00", .data = &decoupler_config },
     95	{ .compatible = "xlnx,pr-decoupler", .data = &decoupler_config },
     96	{ .compatible = "xlnx,dfx-axi-shutdown-manager-1.00",
     97					.data = &shutdown_config },
     98	{ .compatible = "xlnx,dfx-axi-shutdown-manager",
     99					.data = &shutdown_config },
    100	{},
    101};
    102MODULE_DEVICE_TABLE(of, xlnx_pr_decoupler_of_match);
    103#endif
    104
    105static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
    106{
    107	struct device_node *np = pdev->dev.of_node;
    108	struct xlnx_pr_decoupler_data *priv;
    109	struct fpga_bridge *br;
    110	int err;
    111	struct resource *res;
    112
    113	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    114	if (!priv)
    115		return -ENOMEM;
    116
    117	if (np) {
    118		const struct of_device_id *match;
    119
    120		match = of_match_node(xlnx_pr_decoupler_of_match, np);
    121		if (match && match->data)
    122			priv->ipconfig = match->data;
    123	}
    124
    125	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    126	priv->io_base = devm_ioremap_resource(&pdev->dev, res);
    127	if (IS_ERR(priv->io_base))
    128		return PTR_ERR(priv->io_base);
    129
    130	priv->clk = devm_clk_get(&pdev->dev, "aclk");
    131	if (IS_ERR(priv->clk))
    132		return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk),
    133				     "input clock not found\n");
    134
    135	err = clk_prepare_enable(priv->clk);
    136	if (err) {
    137		dev_err(&pdev->dev, "unable to enable clock\n");
    138		return err;
    139	}
    140
    141	clk_disable(priv->clk);
    142
    143	br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
    144				  &xlnx_pr_decoupler_br_ops, priv);
    145	if (IS_ERR(br)) {
    146		err = PTR_ERR(br);
    147		dev_err(&pdev->dev, "unable to register %s",
    148			priv->ipconfig->name);
    149		goto err_clk;
    150	}
    151
    152	platform_set_drvdata(pdev, br);
    153
    154	return 0;
    155
    156err_clk:
    157	clk_unprepare(priv->clk);
    158
    159	return err;
    160}
    161
    162static int xlnx_pr_decoupler_remove(struct platform_device *pdev)
    163{
    164	struct fpga_bridge *bridge = platform_get_drvdata(pdev);
    165	struct xlnx_pr_decoupler_data *p = bridge->priv;
    166
    167	fpga_bridge_unregister(bridge);
    168
    169	clk_unprepare(p->clk);
    170
    171	return 0;
    172}
    173
    174static struct platform_driver xlnx_pr_decoupler_driver = {
    175	.probe = xlnx_pr_decoupler_probe,
    176	.remove = xlnx_pr_decoupler_remove,
    177	.driver = {
    178		.name = "xlnx_pr_decoupler",
    179		.of_match_table = of_match_ptr(xlnx_pr_decoupler_of_match),
    180	},
    181};
    182
    183module_platform_driver(xlnx_pr_decoupler_driver);
    184
    185MODULE_DESCRIPTION("Xilinx Partial Reconfiguration Decoupler");
    186MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
    187MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>");
    188MODULE_LICENSE("GPL v2");