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

meson-canvas.c (5464B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (C) 2018 BayLibre, SAS
      4 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
      5 * Copyright (C) 2014 Endless Mobile
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/mfd/syscon.h>
     10#include <linux/module.h>
     11#include <linux/regmap.h>
     12#include <linux/soc/amlogic/meson-canvas.h>
     13#include <linux/of_address.h>
     14#include <linux/of_platform.h>
     15#include <linux/io.h>
     16
     17#define NUM_CANVAS 256
     18
     19/* DMC Registers */
     20#define DMC_CAV_LUT_DATAL	0x00
     21	#define CANVAS_WIDTH_LBIT	29
     22	#define CANVAS_WIDTH_LWID	3
     23#define DMC_CAV_LUT_DATAH	0x04
     24	#define CANVAS_WIDTH_HBIT	0
     25	#define CANVAS_HEIGHT_BIT	9
     26	#define CANVAS_WRAP_BIT		22
     27	#define CANVAS_BLKMODE_BIT	24
     28	#define CANVAS_ENDIAN_BIT	26
     29#define DMC_CAV_LUT_ADDR	0x08
     30	#define CANVAS_LUT_WR_EN	BIT(9)
     31	#define CANVAS_LUT_RD_EN	BIT(8)
     32
     33struct meson_canvas {
     34	struct device *dev;
     35	void __iomem *reg_base;
     36	spinlock_t lock; /* canvas device lock */
     37	u8 used[NUM_CANVAS];
     38	bool supports_endianness;
     39};
     40
     41static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val)
     42{
     43	writel_relaxed(val, canvas->reg_base + reg);
     44}
     45
     46static u32 canvas_read(struct meson_canvas *canvas, u32 reg)
     47{
     48	return readl_relaxed(canvas->reg_base + reg);
     49}
     50
     51struct meson_canvas *meson_canvas_get(struct device *dev)
     52{
     53	struct device_node *canvas_node;
     54	struct platform_device *canvas_pdev;
     55	struct meson_canvas *canvas;
     56
     57	canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
     58	if (!canvas_node)
     59		return ERR_PTR(-ENODEV);
     60
     61	canvas_pdev = of_find_device_by_node(canvas_node);
     62	if (!canvas_pdev) {
     63		of_node_put(canvas_node);
     64		return ERR_PTR(-EPROBE_DEFER);
     65	}
     66
     67	of_node_put(canvas_node);
     68
     69	/*
     70	 * If priv is NULL, it's probably because the canvas hasn't
     71	 * properly initialized. Bail out with -EINVAL because, in the
     72	 * current state, this driver probe cannot return -EPROBE_DEFER
     73	 */
     74	canvas = dev_get_drvdata(&canvas_pdev->dev);
     75	if (!canvas) {
     76		put_device(&canvas_pdev->dev);
     77		return ERR_PTR(-EINVAL);
     78	}
     79
     80	return canvas;
     81}
     82EXPORT_SYMBOL_GPL(meson_canvas_get);
     83
     84int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index,
     85			u32 addr, u32 stride, u32 height,
     86			unsigned int wrap,
     87			unsigned int blkmode,
     88			unsigned int endian)
     89{
     90	unsigned long flags;
     91
     92	if (endian && !canvas->supports_endianness) {
     93		dev_err(canvas->dev,
     94			"Endianness is not supported on this SoC\n");
     95		return -EINVAL;
     96	}
     97
     98	spin_lock_irqsave(&canvas->lock, flags);
     99	if (!canvas->used[canvas_index]) {
    100		dev_err(canvas->dev,
    101			"Trying to setup non allocated canvas %u\n",
    102			canvas_index);
    103		spin_unlock_irqrestore(&canvas->lock, flags);
    104		return -EINVAL;
    105	}
    106
    107	canvas_write(canvas, DMC_CAV_LUT_DATAL,
    108		     ((addr + 7) >> 3) |
    109		     (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
    110
    111	canvas_write(canvas, DMC_CAV_LUT_DATAH,
    112		     ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
    113						CANVAS_WIDTH_HBIT) |
    114		     (height << CANVAS_HEIGHT_BIT) |
    115		     (wrap << CANVAS_WRAP_BIT) |
    116		     (blkmode << CANVAS_BLKMODE_BIT) |
    117		     (endian << CANVAS_ENDIAN_BIT));
    118
    119	canvas_write(canvas, DMC_CAV_LUT_ADDR,
    120		     CANVAS_LUT_WR_EN | canvas_index);
    121
    122	/* Force a read-back to make sure everything is flushed. */
    123	canvas_read(canvas, DMC_CAV_LUT_DATAH);
    124	spin_unlock_irqrestore(&canvas->lock, flags);
    125
    126	return 0;
    127}
    128EXPORT_SYMBOL_GPL(meson_canvas_config);
    129
    130int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index)
    131{
    132	int i;
    133	unsigned long flags;
    134
    135	spin_lock_irqsave(&canvas->lock, flags);
    136	for (i = 0; i < NUM_CANVAS; ++i) {
    137		if (!canvas->used[i]) {
    138			canvas->used[i] = 1;
    139			spin_unlock_irqrestore(&canvas->lock, flags);
    140			*canvas_index = i;
    141			return 0;
    142		}
    143	}
    144	spin_unlock_irqrestore(&canvas->lock, flags);
    145
    146	dev_err(canvas->dev, "No more canvas available\n");
    147	return -ENODEV;
    148}
    149EXPORT_SYMBOL_GPL(meson_canvas_alloc);
    150
    151int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index)
    152{
    153	unsigned long flags;
    154
    155	spin_lock_irqsave(&canvas->lock, flags);
    156	if (!canvas->used[canvas_index]) {
    157		dev_err(canvas->dev,
    158			"Trying to free unused canvas %u\n", canvas_index);
    159		spin_unlock_irqrestore(&canvas->lock, flags);
    160		return -EINVAL;
    161	}
    162	canvas->used[canvas_index] = 0;
    163	spin_unlock_irqrestore(&canvas->lock, flags);
    164
    165	return 0;
    166}
    167EXPORT_SYMBOL_GPL(meson_canvas_free);
    168
    169static int meson_canvas_probe(struct platform_device *pdev)
    170{
    171	struct meson_canvas *canvas;
    172	struct device *dev = &pdev->dev;
    173
    174	canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL);
    175	if (!canvas)
    176		return -ENOMEM;
    177
    178	canvas->reg_base = devm_platform_ioremap_resource(pdev, 0);
    179	if (IS_ERR(canvas->reg_base))
    180		return PTR_ERR(canvas->reg_base);
    181
    182	canvas->supports_endianness = of_device_get_match_data(dev);
    183
    184	canvas->dev = dev;
    185	spin_lock_init(&canvas->lock);
    186	dev_set_drvdata(dev, canvas);
    187
    188	return 0;
    189}
    190
    191static const struct of_device_id canvas_dt_match[] = {
    192	{ .compatible = "amlogic,meson8-canvas", .data = (void *)false, },
    193	{ .compatible = "amlogic,meson8b-canvas", .data = (void *)false, },
    194	{ .compatible = "amlogic,meson8m2-canvas", .data = (void *)false, },
    195	{ .compatible = "amlogic,canvas", .data = (void *)true, },
    196	{}
    197};
    198MODULE_DEVICE_TABLE(of, canvas_dt_match);
    199
    200static struct platform_driver meson_canvas_driver = {
    201	.probe = meson_canvas_probe,
    202	.driver = {
    203		.name = "amlogic-canvas",
    204		.of_match_table = canvas_dt_match,
    205	},
    206};
    207module_platform_driver(meson_canvas_driver);
    208
    209MODULE_DESCRIPTION("Amlogic Canvas driver");
    210MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>");
    211MODULE_LICENSE("GPL");