/* * Copyright (c) 2025, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include 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; }