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

cobalt_lcdfb.c (7026B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Cobalt/SEAD3 LCD frame buffer driver.
      4 *
      5 *  Copyright (C) 2008  Yoichi Yuasa <yuasa@linux-mips.org>
      6 *  Copyright (C) 2012  MIPS Technologies, Inc.
      7 */
      8#include <linux/delay.h>
      9#include <linux/fb.h>
     10#include <linux/init.h>
     11#include <linux/io.h>
     12#include <linux/ioport.h>
     13#include <linux/uaccess.h>
     14#include <linux/platform_device.h>
     15#include <linux/module.h>
     16#include <linux/sched/signal.h>
     17
     18/*
     19 * Cursor position address
     20 * \X  0    1    2  ...  14   15
     21 * Y+----+----+----+---+----+----+
     22 * 0|0x00|0x01|0x02|...|0x0e|0x0f|
     23 *  +----+----+----+---+----+----+
     24 * 1|0x40|0x41|0x42|...|0x4e|0x4f|
     25 *  +----+----+----+---+----+----+
     26 */
     27#define LCD_DATA_REG_OFFSET	0x10
     28#define LCD_XRES_MAX		16
     29#define LCD_YRES_MAX		2
     30#define LCD_CHARS_MAX		32
     31
     32#define LCD_CLEAR		0x01
     33#define LCD_CURSOR_MOVE_HOME	0x02
     34#define LCD_RESET		0x06
     35#define LCD_OFF			0x08
     36#define LCD_CURSOR_OFF		0x0c
     37#define LCD_CURSOR_BLINK_OFF	0x0e
     38#define LCD_CURSOR_ON		0x0f
     39#define LCD_ON			LCD_CURSOR_ON
     40#define LCD_CURSOR_MOVE_LEFT	0x10
     41#define LCD_CURSOR_MOVE_RIGHT	0x14
     42#define LCD_DISPLAY_LEFT	0x18
     43#define LCD_DISPLAY_RIGHT	0x1c
     44#define LCD_PRERESET		0x3f	/* execute 4 times continuously */
     45#define LCD_BUSY		0x80
     46
     47#define LCD_GRAPHIC_MODE	0x40
     48#define LCD_TEXT_MODE		0x80
     49#define LCD_CUR_POS_MASK	0x7f
     50
     51#define LCD_CUR_POS(x)		((x) & LCD_CUR_POS_MASK)
     52#define LCD_TEXT_POS(x)		((x) | LCD_TEXT_MODE)
     53
     54static inline void lcd_write_control(struct fb_info *info, u8 control)
     55{
     56	writel((u32)control << 24, info->screen_base);
     57}
     58
     59static inline u8 lcd_read_control(struct fb_info *info)
     60{
     61	return readl(info->screen_base) >> 24;
     62}
     63
     64static inline void lcd_write_data(struct fb_info *info, u8 data)
     65{
     66	writel((u32)data << 24, info->screen_base + LCD_DATA_REG_OFFSET);
     67}
     68
     69static inline u8 lcd_read_data(struct fb_info *info)
     70{
     71	return readl(info->screen_base + LCD_DATA_REG_OFFSET) >> 24;
     72}
     73
     74static int lcd_busy_wait(struct fb_info *info)
     75{
     76	u8 val = 0;
     77	int timeout = 10, retval = 0;
     78
     79	do {
     80		val = lcd_read_control(info);
     81		val &= LCD_BUSY;
     82		if (val != LCD_BUSY)
     83			break;
     84
     85		if (msleep_interruptible(1))
     86			return -EINTR;
     87
     88		timeout--;
     89	} while (timeout);
     90
     91	if (val == LCD_BUSY)
     92		retval = -EBUSY;
     93
     94	return retval;
     95}
     96
     97static void lcd_clear(struct fb_info *info)
     98{
     99	int i;
    100
    101	for (i = 0; i < 4; i++) {
    102		udelay(150);
    103
    104		lcd_write_control(info, LCD_PRERESET);
    105	}
    106
    107	udelay(150);
    108
    109	lcd_write_control(info, LCD_CLEAR);
    110
    111	udelay(150);
    112
    113	lcd_write_control(info, LCD_RESET);
    114}
    115
    116static const struct fb_fix_screeninfo cobalt_lcdfb_fix = {
    117	.id		= "cobalt-lcd",
    118	.type		= FB_TYPE_TEXT,
    119	.type_aux	= FB_AUX_TEXT_MDA,
    120	.visual		= FB_VISUAL_MONO01,
    121	.line_length	= LCD_XRES_MAX,
    122	.accel		= FB_ACCEL_NONE,
    123};
    124
    125static ssize_t cobalt_lcdfb_read(struct fb_info *info, char __user *buf,
    126				 size_t count, loff_t *ppos)
    127{
    128	char src[LCD_CHARS_MAX];
    129	unsigned long pos;
    130	int len, retval = 0;
    131
    132	pos = *ppos;
    133	if (pos >= LCD_CHARS_MAX || count == 0)
    134		return 0;
    135
    136	if (count > LCD_CHARS_MAX)
    137		count = LCD_CHARS_MAX;
    138
    139	if (pos + count > LCD_CHARS_MAX)
    140		count = LCD_CHARS_MAX - pos;
    141
    142	for (len = 0; len < count; len++) {
    143		retval = lcd_busy_wait(info);
    144		if (retval < 0)
    145			break;
    146
    147		lcd_write_control(info, LCD_TEXT_POS(pos));
    148
    149		retval = lcd_busy_wait(info);
    150		if (retval < 0)
    151			break;
    152
    153		src[len] = lcd_read_data(info);
    154		if (pos == 0x0f)
    155			pos = 0x40;
    156		else
    157			pos++;
    158	}
    159
    160	if (retval < 0 && signal_pending(current))
    161		return -ERESTARTSYS;
    162
    163	if (copy_to_user(buf, src, len))
    164		return -EFAULT;
    165
    166	*ppos += len;
    167
    168	return len;
    169}
    170
    171static ssize_t cobalt_lcdfb_write(struct fb_info *info, const char __user *buf,
    172				  size_t count, loff_t *ppos)
    173{
    174	char dst[LCD_CHARS_MAX];
    175	unsigned long pos;
    176	int len, retval = 0;
    177
    178	pos = *ppos;
    179	if (pos >= LCD_CHARS_MAX || count == 0)
    180		return 0;
    181
    182	if (count > LCD_CHARS_MAX)
    183		count = LCD_CHARS_MAX;
    184
    185	if (pos + count > LCD_CHARS_MAX)
    186		count = LCD_CHARS_MAX - pos;
    187
    188	if (copy_from_user(dst, buf, count))
    189		return -EFAULT;
    190
    191	for (len = 0; len < count; len++) {
    192		retval = lcd_busy_wait(info);
    193		if (retval < 0)
    194			break;
    195
    196		lcd_write_control(info, LCD_TEXT_POS(pos));
    197
    198		retval = lcd_busy_wait(info);
    199		if (retval < 0)
    200			break;
    201
    202		lcd_write_data(info, dst[len]);
    203		if (pos == 0x0f)
    204			pos = 0x40;
    205		else
    206			pos++;
    207	}
    208
    209	if (retval < 0 && signal_pending(current))
    210		return -ERESTARTSYS;
    211
    212	*ppos += len;
    213
    214	return len;
    215}
    216
    217static int cobalt_lcdfb_blank(int blank_mode, struct fb_info *info)
    218{
    219	int retval;
    220
    221	retval = lcd_busy_wait(info);
    222	if (retval < 0)
    223		return retval;
    224
    225	switch (blank_mode) {
    226	case FB_BLANK_UNBLANK:
    227		lcd_write_control(info, LCD_ON);
    228		break;
    229	default:
    230		lcd_write_control(info, LCD_OFF);
    231		break;
    232	}
    233
    234	return 0;
    235}
    236
    237static int cobalt_lcdfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
    238{
    239	u32 x, y;
    240	int retval;
    241
    242	switch (cursor->set) {
    243	case FB_CUR_SETPOS:
    244		x = cursor->image.dx;
    245		y = cursor->image.dy;
    246		if (x >= LCD_XRES_MAX || y >= LCD_YRES_MAX)
    247			return -EINVAL;
    248
    249		retval = lcd_busy_wait(info);
    250		if (retval < 0)
    251			return retval;
    252
    253		lcd_write_control(info,
    254				  LCD_TEXT_POS(info->fix.line_length * y + x));
    255		break;
    256	default:
    257		return -EINVAL;
    258	}
    259
    260	retval = lcd_busy_wait(info);
    261	if (retval < 0)
    262		return retval;
    263
    264	if (cursor->enable)
    265		lcd_write_control(info, LCD_CURSOR_ON);
    266	else
    267		lcd_write_control(info, LCD_CURSOR_OFF);
    268
    269	return 0;
    270}
    271
    272static const struct fb_ops cobalt_lcd_fbops = {
    273	.owner		= THIS_MODULE,
    274	.fb_read	= cobalt_lcdfb_read,
    275	.fb_write	= cobalt_lcdfb_write,
    276	.fb_blank	= cobalt_lcdfb_blank,
    277	.fb_cursor	= cobalt_lcdfb_cursor,
    278};
    279
    280static int cobalt_lcdfb_probe(struct platform_device *dev)
    281{
    282	struct fb_info *info;
    283	struct resource *res;
    284	int retval;
    285
    286	info = framebuffer_alloc(0, &dev->dev);
    287	if (!info)
    288		return -ENOMEM;
    289
    290	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
    291	if (!res) {
    292		framebuffer_release(info);
    293		return -EBUSY;
    294	}
    295
    296	info->screen_size = resource_size(res);
    297	info->screen_base = devm_ioremap(&dev->dev, res->start,
    298					 info->screen_size);
    299	if (!info->screen_base) {
    300		framebuffer_release(info);
    301		return -ENOMEM;
    302	}
    303
    304	info->fbops = &cobalt_lcd_fbops;
    305	info->fix = cobalt_lcdfb_fix;
    306	info->fix.smem_start = res->start;
    307	info->fix.smem_len = info->screen_size;
    308	info->pseudo_palette = NULL;
    309	info->par = NULL;
    310	info->flags = FBINFO_DEFAULT;
    311
    312	retval = register_framebuffer(info);
    313	if (retval < 0) {
    314		framebuffer_release(info);
    315		return retval;
    316	}
    317
    318	platform_set_drvdata(dev, info);
    319
    320	lcd_clear(info);
    321
    322	fb_info(info, "Cobalt server LCD frame buffer device\n");
    323
    324	return 0;
    325}
    326
    327static int cobalt_lcdfb_remove(struct platform_device *dev)
    328{
    329	struct fb_info *info;
    330
    331	info = platform_get_drvdata(dev);
    332	if (info) {
    333		unregister_framebuffer(info);
    334		framebuffer_release(info);
    335	}
    336
    337	return 0;
    338}
    339
    340static struct platform_driver cobalt_lcdfb_driver = {
    341	.probe	= cobalt_lcdfb_probe,
    342	.remove	= cobalt_lcdfb_remove,
    343	.driver	= {
    344		.name	= "cobalt-lcd",
    345	},
    346};
    347module_platform_driver(cobalt_lcdfb_driver);
    348
    349MODULE_LICENSE("GPL v2");
    350MODULE_AUTHOR("Yoichi Yuasa");
    351MODULE_DESCRIPTION("Cobalt server LCD frame buffer driver");