mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-11 15:14:21 +00:00
feat(io): add generic gpio spi bit-bang driver
When using a tpm breakout board with rpi3, we elected to bit-bang gpio pins to emulate a spi interface, this implementation required a driver to interface with the platform specific pins and emulate spi functionality. The generic driver provides the ability to pass in a gpio_spi_data structure that contains the necessary gpio pins in order to simulate spi operations (get_access, start, stop, xfer). Change-Id: I88919e8a294c05e0cabb8224e35ae5c1ba5f2413 Signed-off-by: Tushar Khandelwal <tushar.khandelwal@arm.com> Signed-off-by: Abhi Singh <abhi.singh@arm.com>
This commit is contained in:
parent
6dfcf4e1df
commit
3c54570afc
2 changed files with 162 additions and 0 deletions
130
drivers/gpio/gpio_spi.c
Normal file
130
drivers/gpio/gpio_spi.c
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2025, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <drivers/delay_timer.h>
|
||||
#include <drivers/gpio.h>
|
||||
#include <drivers/gpio_spi.h>
|
||||
#include <platform_def.h>
|
||||
|
||||
static struct spi_plat gpio_spidev;
|
||||
|
||||
static void gpio_spi_delay_us(void)
|
||||
{
|
||||
udelay(gpio_spidev.gpio_data.spi_delay_us);
|
||||
}
|
||||
|
||||
static int gpio_spi_miso(void)
|
||||
{
|
||||
return gpio_get_value(gpio_spidev.gpio_data.miso_gpio);
|
||||
}
|
||||
|
||||
static void gpio_spi_sclk(int bit)
|
||||
{
|
||||
gpio_set_value(gpio_spidev.gpio_data.sclk_gpio, bit);
|
||||
}
|
||||
|
||||
static void gpio_spi_mosi(int bit)
|
||||
{
|
||||
gpio_set_value(gpio_spidev.gpio_data.mosi_gpio, bit);
|
||||
}
|
||||
|
||||
static void gpio_spi_cs(int bit)
|
||||
{
|
||||
gpio_set_value(gpio_spidev.gpio_data.cs_gpio, bit);
|
||||
}
|
||||
|
||||
static void gpio_spi_start(void)
|
||||
{
|
||||
gpio_spi_cs(1);
|
||||
gpio_spi_sclk(0);
|
||||
gpio_spi_cs(0);
|
||||
}
|
||||
|
||||
static void gpio_spi_stop(void)
|
||||
{
|
||||
gpio_spi_cs(1);
|
||||
}
|
||||
|
||||
/* set sclk to a known state (0) before performing any further action */
|
||||
static void gpio_spi_get_access(void)
|
||||
{
|
||||
gpio_spi_sclk(0);
|
||||
}
|
||||
|
||||
static void xfer(unsigned int bytes, const void *out, void *in, int cpol, int cpha)
|
||||
{
|
||||
for (unsigned int j = 0U; j < bytes; j++) {
|
||||
unsigned char in_byte = 0U;
|
||||
unsigned char out_byte = (out != NULL) ? *(const uint8_t *)out++ : 0xFF;
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
if (cpha) {
|
||||
gpio_spi_sclk(!cpol);
|
||||
}
|
||||
|
||||
gpio_spi_mosi(!!(out_byte & (1 << i)));
|
||||
|
||||
gpio_spi_delay_us();
|
||||
gpio_spi_sclk(cpha ? cpol : !cpol);
|
||||
gpio_spi_delay_us();
|
||||
|
||||
in_byte |= gpio_spi_miso() << i;
|
||||
|
||||
if (!cpha) {
|
||||
gpio_spi_sclk(cpol);
|
||||
}
|
||||
}
|
||||
|
||||
if (in != NULL) {
|
||||
*(uint8_t *)in++ = in_byte;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int gpio_spi_xfer(unsigned int bytes, const void *out, void *in)
|
||||
{
|
||||
if ((out == NULL) && (in == NULL)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (gpio_spidev.gpio_data.spi_mode) {
|
||||
case 0:
|
||||
xfer(bytes, out, in, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
xfer(bytes, out, in, 0, 1);
|
||||
break;
|
||||
case 2:
|
||||
xfer(bytes, out, in, 1, 0);
|
||||
break;
|
||||
case 3:
|
||||
xfer(bytes, out, in, 1, 1);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct spi_ops gpio_spidev_ops = {
|
||||
.get_access = gpio_spi_get_access,
|
||||
.start = gpio_spi_start,
|
||||
.stop = gpio_spi_stop,
|
||||
.xfer = gpio_spi_xfer,
|
||||
};
|
||||
|
||||
struct spi_plat *gpio_spi_init(struct gpio_spi_data *gpio_spi_data)
|
||||
{
|
||||
gpio_spidev.gpio_data = *gpio_spi_data;
|
||||
gpio_spidev.ops = &gpio_spidev_ops;
|
||||
|
||||
return &gpio_spidev;
|
||||
}
|
32
include/drivers/gpio_spi.h
Normal file
32
include/drivers/gpio_spi.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef GPIO_SPI_H
|
||||
#define GPIO_SPI_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct gpio_spi_data {
|
||||
uint8_t cs_gpio, sclk_gpio, mosi_gpio, miso_gpio, reset_gpio;
|
||||
uint32_t spi_delay_us;
|
||||
unsigned int spi_mode;
|
||||
};
|
||||
|
||||
struct spi_ops {
|
||||
void (*get_access)(void);
|
||||
void (*start)(void);
|
||||
void (*stop)(void);
|
||||
int (*xfer)(unsigned int bitlen, const void *dout, void *din);
|
||||
};
|
||||
|
||||
struct spi_plat {
|
||||
struct gpio_spi_data gpio_data;
|
||||
const struct spi_ops *ops;
|
||||
};
|
||||
|
||||
struct spi_plat *gpio_spi_init(struct gpio_spi_data *gpio_spi_data);
|
||||
|
||||
#endif /* GPIO_SPI_H */
|
Loading…
Add table
Reference in a new issue