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

img-ascii-lcd.c (7189B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2016 Imagination Technologies
      4 * Author: Paul Burton <paul.burton@mips.com>
      5 */
      6
      7#include <linux/kernel.h>
      8#include <linux/io.h>
      9#include <linux/mfd/syscon.h>
     10#include <linux/module.h>
     11#include <linux/of_address.h>
     12#include <linux/of_platform.h>
     13#include <linux/platform_device.h>
     14#include <linux/regmap.h>
     15#include <linux/slab.h>
     16
     17#include "line-display.h"
     18
     19struct img_ascii_lcd_ctx;
     20
     21/**
     22 * struct img_ascii_lcd_config - Configuration information about an LCD model
     23 * @num_chars: the number of characters the LCD can display
     24 * @external_regmap: true if registers are in a system controller, else false
     25 * @update: function called to update the LCD
     26 */
     27struct img_ascii_lcd_config {
     28	unsigned int num_chars;
     29	bool external_regmap;
     30	void (*update)(struct linedisp *linedisp);
     31};
     32
     33/**
     34 * struct img_ascii_lcd_ctx - Private data structure
     35 * @base: the base address of the LCD registers
     36 * @regmap: the regmap through which LCD registers are accessed
     37 * @offset: the offset within regmap to the start of the LCD registers
     38 * @cfg: pointer to the LCD model configuration
     39 * @linedisp: line display structure
     40 * @curr: the string currently displayed on the LCD
     41 */
     42struct img_ascii_lcd_ctx {
     43	union {
     44		void __iomem *base;
     45		struct regmap *regmap;
     46	};
     47	u32 offset;
     48	const struct img_ascii_lcd_config *cfg;
     49	struct linedisp linedisp;
     50	char curr[] __aligned(8);
     51};
     52
     53/*
     54 * MIPS Boston development board
     55 */
     56
     57static void boston_update(struct linedisp *linedisp)
     58{
     59	struct img_ascii_lcd_ctx *ctx =
     60		container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
     61	ulong val;
     62
     63#if BITS_PER_LONG == 64
     64	val = *((u64 *)&ctx->curr[0]);
     65	__raw_writeq(val, ctx->base);
     66#elif BITS_PER_LONG == 32
     67	val = *((u32 *)&ctx->curr[0]);
     68	__raw_writel(val, ctx->base);
     69	val = *((u32 *)&ctx->curr[4]);
     70	__raw_writel(val, ctx->base + 4);
     71#else
     72# error Not 32 or 64 bit
     73#endif
     74}
     75
     76static struct img_ascii_lcd_config boston_config = {
     77	.num_chars = 8,
     78	.update = boston_update,
     79};
     80
     81/*
     82 * MIPS Malta development board
     83 */
     84
     85static void malta_update(struct linedisp *linedisp)
     86{
     87	struct img_ascii_lcd_ctx *ctx =
     88		container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
     89	unsigned int i;
     90	int err = 0;
     91
     92	for (i = 0; i < linedisp->num_chars; i++) {
     93		err = regmap_write(ctx->regmap,
     94				   ctx->offset + (i * 8), ctx->curr[i]);
     95		if (err)
     96			break;
     97	}
     98
     99	if (unlikely(err))
    100		pr_err_ratelimited("Failed to update LCD display: %d\n", err);
    101}
    102
    103static struct img_ascii_lcd_config malta_config = {
    104	.num_chars = 8,
    105	.external_regmap = true,
    106	.update = malta_update,
    107};
    108
    109/*
    110 * MIPS SEAD3 development board
    111 */
    112
    113enum {
    114	SEAD3_REG_LCD_CTRL		= 0x00,
    115#define SEAD3_REG_LCD_CTRL_SETDRAM	BIT(7)
    116	SEAD3_REG_LCD_DATA		= 0x08,
    117	SEAD3_REG_CPLD_STATUS		= 0x10,
    118#define SEAD3_REG_CPLD_STATUS_BUSY	BIT(0)
    119	SEAD3_REG_CPLD_DATA		= 0x18,
    120#define SEAD3_REG_CPLD_DATA_BUSY	BIT(7)
    121};
    122
    123static int sead3_wait_sm_idle(struct img_ascii_lcd_ctx *ctx)
    124{
    125	unsigned int status;
    126	int err;
    127
    128	do {
    129		err = regmap_read(ctx->regmap,
    130				  ctx->offset + SEAD3_REG_CPLD_STATUS,
    131				  &status);
    132		if (err)
    133			return err;
    134	} while (status & SEAD3_REG_CPLD_STATUS_BUSY);
    135
    136	return 0;
    137
    138}
    139
    140static int sead3_wait_lcd_idle(struct img_ascii_lcd_ctx *ctx)
    141{
    142	unsigned int cpld_data;
    143	int err;
    144
    145	err = sead3_wait_sm_idle(ctx);
    146	if (err)
    147		return err;
    148
    149	do {
    150		err = regmap_read(ctx->regmap,
    151				  ctx->offset + SEAD3_REG_LCD_CTRL,
    152				  &cpld_data);
    153		if (err)
    154			return err;
    155
    156		err = sead3_wait_sm_idle(ctx);
    157		if (err)
    158			return err;
    159
    160		err = regmap_read(ctx->regmap,
    161				  ctx->offset + SEAD3_REG_CPLD_DATA,
    162				  &cpld_data);
    163		if (err)
    164			return err;
    165	} while (cpld_data & SEAD3_REG_CPLD_DATA_BUSY);
    166
    167	return 0;
    168}
    169
    170static void sead3_update(struct linedisp *linedisp)
    171{
    172	struct img_ascii_lcd_ctx *ctx =
    173		container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
    174	unsigned int i;
    175	int err = 0;
    176
    177	for (i = 0; i < linedisp->num_chars; i++) {
    178		err = sead3_wait_lcd_idle(ctx);
    179		if (err)
    180			break;
    181
    182		err = regmap_write(ctx->regmap,
    183				   ctx->offset + SEAD3_REG_LCD_CTRL,
    184				   SEAD3_REG_LCD_CTRL_SETDRAM | i);
    185		if (err)
    186			break;
    187
    188		err = sead3_wait_lcd_idle(ctx);
    189		if (err)
    190			break;
    191
    192		err = regmap_write(ctx->regmap,
    193				   ctx->offset + SEAD3_REG_LCD_DATA,
    194				   ctx->curr[i]);
    195		if (err)
    196			break;
    197	}
    198
    199	if (unlikely(err))
    200		pr_err_ratelimited("Failed to update LCD display: %d\n", err);
    201}
    202
    203static struct img_ascii_lcd_config sead3_config = {
    204	.num_chars = 16,
    205	.external_regmap = true,
    206	.update = sead3_update,
    207};
    208
    209static const struct of_device_id img_ascii_lcd_matches[] = {
    210	{ .compatible = "img,boston-lcd", .data = &boston_config },
    211	{ .compatible = "mti,malta-lcd", .data = &malta_config },
    212	{ .compatible = "mti,sead3-lcd", .data = &sead3_config },
    213	{ /* sentinel */ }
    214};
    215MODULE_DEVICE_TABLE(of, img_ascii_lcd_matches);
    216
    217/**
    218 * img_ascii_lcd_probe() - probe an LCD display device
    219 * @pdev: the LCD platform device
    220 *
    221 * Probe an LCD display device, ensuring that we have the required resources in
    222 * order to access the LCD & setting up private data as well as sysfs files.
    223 *
    224 * Return: 0 on success, else -ERRNO
    225 */
    226static int img_ascii_lcd_probe(struct platform_device *pdev)
    227{
    228	const struct of_device_id *match;
    229	const struct img_ascii_lcd_config *cfg;
    230	struct device *dev = &pdev->dev;
    231	struct img_ascii_lcd_ctx *ctx;
    232	int err;
    233
    234	match = of_match_device(img_ascii_lcd_matches, dev);
    235	if (!match)
    236		return -ENODEV;
    237
    238	cfg = match->data;
    239	ctx = devm_kzalloc(dev, sizeof(*ctx) + cfg->num_chars, GFP_KERNEL);
    240	if (!ctx)
    241		return -ENOMEM;
    242
    243	if (cfg->external_regmap) {
    244		ctx->regmap = syscon_node_to_regmap(dev->parent->of_node);
    245		if (IS_ERR(ctx->regmap))
    246			return PTR_ERR(ctx->regmap);
    247
    248		if (of_property_read_u32(dev->of_node, "offset", &ctx->offset))
    249			return -EINVAL;
    250	} else {
    251		ctx->base = devm_platform_ioremap_resource(pdev, 0);
    252		if (IS_ERR(ctx->base))
    253			return PTR_ERR(ctx->base);
    254	}
    255
    256	err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, ctx->curr,
    257				cfg->update);
    258	if (err)
    259		return err;
    260
    261	/* for backwards compatibility */
    262	err = compat_only_sysfs_link_entry_to_kobj(&dev->kobj,
    263						   &ctx->linedisp.dev.kobj,
    264						   "message", NULL);
    265	if (err)
    266		goto err_unregister;
    267
    268	platform_set_drvdata(pdev, ctx);
    269	return 0;
    270
    271err_unregister:
    272	linedisp_unregister(&ctx->linedisp);
    273	return err;
    274}
    275
    276/**
    277 * img_ascii_lcd_remove() - remove an LCD display device
    278 * @pdev: the LCD platform device
    279 *
    280 * Remove an LCD display device, freeing private resources & ensuring that the
    281 * driver stops using the LCD display registers.
    282 *
    283 * Return: 0
    284 */
    285static int img_ascii_lcd_remove(struct platform_device *pdev)
    286{
    287	struct img_ascii_lcd_ctx *ctx = platform_get_drvdata(pdev);
    288
    289	sysfs_remove_link(&pdev->dev.kobj, "message");
    290	linedisp_unregister(&ctx->linedisp);
    291	return 0;
    292}
    293
    294static struct platform_driver img_ascii_lcd_driver = {
    295	.driver = {
    296		.name		= "img-ascii-lcd",
    297		.of_match_table	= img_ascii_lcd_matches,
    298	},
    299	.probe	= img_ascii_lcd_probe,
    300	.remove	= img_ascii_lcd_remove,
    301};
    302module_platform_driver(img_ascii_lcd_driver);
    303
    304MODULE_DESCRIPTION("Imagination Technologies ASCII LCD Display");
    305MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>");
    306MODULE_LICENSE("GPL");