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

fbtft-io.c (5314B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <linux/export.h>
      3#include <linux/errno.h>
      4#include <linux/gpio/consumer.h>
      5#include <linux/spi/spi.h>
      6#include "fbtft.h"
      7
      8int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len)
      9{
     10	struct spi_transfer t = {
     11		.tx_buf = buf,
     12		.len = len,
     13	};
     14	struct spi_message m;
     15
     16	fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
     17			  "%s(len=%zu): ", __func__, len);
     18
     19	if (!par->spi) {
     20		dev_err(par->info->device,
     21			"%s: par->spi is unexpectedly NULL\n", __func__);
     22		return -1;
     23	}
     24
     25	spi_message_init(&m);
     26	spi_message_add_tail(&t, &m);
     27	return spi_sync(par->spi, &m);
     28}
     29EXPORT_SYMBOL(fbtft_write_spi);
     30
     31/**
     32 * fbtft_write_spi_emulate_9() - write SPI emulating 9-bit
     33 * @par: Driver data
     34 * @buf: Buffer to write
     35 * @len: Length of buffer (must be divisible by 8)
     36 *
     37 * When 9-bit SPI is not available, this function can be used to emulate that.
     38 * par->extra must hold a transformation buffer used for transfer.
     39 */
     40int fbtft_write_spi_emulate_9(struct fbtft_par *par, void *buf, size_t len)
     41{
     42	u16 *src = buf;
     43	u8 *dst = par->extra;
     44	size_t size = len / 2;
     45	size_t added = 0;
     46	int bits, i, j;
     47	u64 val, dc, tmp;
     48
     49	fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
     50			  "%s(len=%zu): ", __func__, len);
     51
     52	if (!par->extra) {
     53		dev_err(par->info->device, "%s: error: par->extra is NULL\n",
     54			__func__);
     55		return -EINVAL;
     56	}
     57	if ((len % 8) != 0) {
     58		dev_err(par->info->device,
     59			"error: len=%zu must be divisible by 8\n", len);
     60		return -EINVAL;
     61	}
     62
     63	for (i = 0; i < size; i += 8) {
     64		tmp = 0;
     65		bits = 63;
     66		for (j = 0; j < 7; j++) {
     67			dc = (*src & 0x0100) ? 1 : 0;
     68			val = *src & 0x00FF;
     69			tmp |= dc << bits;
     70			bits -= 8;
     71			tmp |= val << bits--;
     72			src++;
     73		}
     74		tmp |= ((*src & 0x0100) ? 1 : 0);
     75		*(__be64 *)dst = cpu_to_be64(tmp);
     76		dst += 8;
     77		*dst++ = (u8)(*src++ & 0x00FF);
     78		added++;
     79	}
     80
     81	return spi_write(par->spi, par->extra, size + added);
     82}
     83EXPORT_SYMBOL(fbtft_write_spi_emulate_9);
     84
     85int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len)
     86{
     87	int ret;
     88	u8 txbuf[32] = { 0, };
     89	struct spi_transfer	t = {
     90			.speed_hz = 2000000,
     91			.rx_buf		= buf,
     92			.len		= len,
     93		};
     94	struct spi_message	m;
     95
     96	if (!par->spi) {
     97		dev_err(par->info->device,
     98			"%s: par->spi is unexpectedly NULL\n", __func__);
     99		return -ENODEV;
    100	}
    101
    102	if (par->startbyte) {
    103		if (len > 32) {
    104			dev_err(par->info->device,
    105				"len=%zu can't be larger than 32 when using 'startbyte'\n",
    106				len);
    107			return -EINVAL;
    108		}
    109		txbuf[0] = par->startbyte | 0x3;
    110		t.tx_buf = txbuf;
    111		fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8,
    112				  txbuf, len, "%s(len=%zu) txbuf => ",
    113				  __func__, len);
    114	}
    115
    116	spi_message_init(&m);
    117	spi_message_add_tail(&t, &m);
    118	ret = spi_sync(par->spi, &m);
    119	fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8, buf, len,
    120			  "%s(len=%zu) buf <= ", __func__, len);
    121
    122	return ret;
    123}
    124EXPORT_SYMBOL(fbtft_read_spi);
    125
    126/*
    127 * Optimized use of gpiolib is twice as fast as no optimization
    128 * only one driver can use the optimized version at a time
    129 */
    130int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len)
    131{
    132	u8 data;
    133	int i;
    134#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
    135	static u8 prev_data;
    136#endif
    137
    138	fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
    139			  "%s(len=%zu): ", __func__, len);
    140
    141	while (len--) {
    142		data = *(u8 *)buf;
    143
    144		/* Start writing by pulling down /WR */
    145		gpiod_set_value(par->gpio.wr, 1);
    146
    147		/* Set data */
    148#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
    149		if (data == prev_data) {
    150			gpiod_set_value(par->gpio.wr, 1); /* used as delay */
    151		} else {
    152			for (i = 0; i < 8; i++) {
    153				if ((data & 1) != (prev_data & 1))
    154					gpiod_set_value(par->gpio.db[i],
    155							data & 1);
    156				data >>= 1;
    157				prev_data >>= 1;
    158			}
    159		}
    160#else
    161		for (i = 0; i < 8; i++) {
    162			gpiod_set_value(par->gpio.db[i], data & 1);
    163			data >>= 1;
    164		}
    165#endif
    166
    167		/* Pullup /WR */
    168		gpiod_set_value(par->gpio.wr, 0);
    169
    170#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
    171		prev_data = *(u8 *)buf;
    172#endif
    173		buf++;
    174	}
    175
    176	return 0;
    177}
    178EXPORT_SYMBOL(fbtft_write_gpio8_wr);
    179
    180int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len)
    181{
    182	u16 data;
    183	int i;
    184#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
    185	static u16 prev_data;
    186#endif
    187
    188	fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
    189			  "%s(len=%zu): ", __func__, len);
    190
    191	while (len) {
    192		data = *(u16 *)buf;
    193
    194		/* Start writing by pulling down /WR */
    195		gpiod_set_value(par->gpio.wr, 1);
    196
    197		/* Set data */
    198#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
    199		if (data == prev_data) {
    200			gpiod_set_value(par->gpio.wr, 1); /* used as delay */
    201		} else {
    202			for (i = 0; i < 16; i++) {
    203				if ((data & 1) != (prev_data & 1))
    204					gpiod_set_value(par->gpio.db[i],
    205							data & 1);
    206				data >>= 1;
    207				prev_data >>= 1;
    208			}
    209		}
    210#else
    211		for (i = 0; i < 16; i++) {
    212			gpiod_set_value(par->gpio.db[i], data & 1);
    213			data >>= 1;
    214		}
    215#endif
    216
    217		/* Pullup /WR */
    218		gpiod_set_value(par->gpio.wr, 0);
    219
    220#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
    221		prev_data = *(u16 *)buf;
    222#endif
    223		buf += 2;
    224		len -= 2;
    225	}
    226
    227	return 0;
    228}
    229EXPORT_SYMBOL(fbtft_write_gpio16_wr);
    230
    231int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len)
    232{
    233	dev_err(par->info->device, "%s: function not implemented\n", __func__);
    234	return -1;
    235}
    236EXPORT_SYMBOL(fbtft_write_gpio16_wr_latched);