| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| /* Compile this driver in place of common/spi.c for bitbang testing. |
| NOTE: Also need to adjust board-specific code for GPIO pinmux! */ |
| |
| #include <assert.h> |
| #include <gpio.h> |
| #include <soc/spi.h> |
| #include <spi_bitbang.h> |
| #include <spi_flash.h> |
| #include <stddef.h> |
| |
| struct rockchip_bitbang_slave { |
| struct spi_bitbang_ops ops; |
| gpio_t miso; |
| gpio_t mosi; |
| gpio_t clk; |
| gpio_t cs; |
| }; |
| |
| static int get_miso(const struct spi_bitbang_ops *ops) |
| { |
| const struct rockchip_bitbang_slave *slave = |
| container_of(ops, const struct rockchip_bitbang_slave, ops); |
| return gpio_get(slave->miso); |
| } |
| |
| static void set_mosi(const struct spi_bitbang_ops *ops, int value) |
| { |
| const struct rockchip_bitbang_slave *slave = |
| container_of(ops, const struct rockchip_bitbang_slave, ops); |
| gpio_set(slave->mosi, value); |
| } |
| |
| static void set_clk(const struct spi_bitbang_ops *ops, int value) |
| { |
| const struct rockchip_bitbang_slave *slave = |
| container_of(ops, const struct rockchip_bitbang_slave, ops); |
| gpio_set(slave->clk, value); |
| } |
| |
| static void set_cs(const struct spi_bitbang_ops *ops, int value) |
| { |
| const struct rockchip_bitbang_slave *slave = |
| container_of(ops, const struct rockchip_bitbang_slave, ops); |
| gpio_set(slave->cs, value); |
| } |
| |
| /* Can't use GPIO() here because of bug in GCC version used by Chromium OS. */ |
| static const struct rockchip_bitbang_slave slaves[] = { |
| [0] = { |
| .ops = { get_miso, set_mosi, set_clk, set_cs }, |
| .miso = { .port = 3, .bank = GPIO_A, .idx = 4 }, |
| .mosi = { .port = 3, .bank = GPIO_A, .idx = 5 }, |
| .clk = { .port = 3, .bank = GPIO_A, .idx = 6 }, |
| .cs = { .port = 3, .bank = GPIO_A, .idx = 7 }, |
| }, |
| [1] = { |
| .ops = { get_miso, set_mosi, set_clk, set_cs }, |
| .miso = { .port = 1, .bank = GPIO_A, .idx = 7 }, |
| .mosi = { .port = 1, .bank = GPIO_B, .idx = 0 }, |
| .clk = { .port = 1, .bank = GPIO_B, .idx = 1 }, |
| .cs = { .port = 1, .bank = GPIO_B, .idx = 2 }, |
| }, |
| [2] = { |
| .ops = { get_miso, set_mosi, set_clk, set_cs }, |
| .miso = { .port = 2, .bank = GPIO_B, .idx = 1 }, |
| .mosi = { .port = 2, .bank = GPIO_B, .idx = 2 }, |
| .clk = { .port = 2, .bank = GPIO_B, .idx = 3 }, |
| .cs = { .port = 2, .bank = GPIO_B, .idx = 4 }, |
| }, |
| [3] = { |
| .ops = { get_miso, set_mosi, set_clk, set_cs }, |
| .miso = { .port = 1, .bank = GPIO_B, .idx = 7 }, |
| .mosi = { .port = 1, .bank = GPIO_C, .idx = 0 }, |
| .clk = { .port = 1, .bank = GPIO_C, .idx = 1 }, |
| .cs = { .port = 1, .bank = GPIO_C, .idx = 2 }, |
| }, |
| [4] = { |
| .ops = { get_miso, set_mosi, set_clk, set_cs }, |
| .miso = { .port = 3, .bank = GPIO_A, .idx = 0 }, |
| .mosi = { .port = 3, .bank = GPIO_A, .idx = 1 }, |
| .clk = { .port = 3, .bank = GPIO_A, .idx = 2 }, |
| .cs = { .port = 3, .bank = GPIO_A, .idx = 3 }, |
| }, |
| [5] = { |
| .ops = { get_miso, set_mosi, set_clk, set_cs }, |
| .miso = { .port = 2, .bank = GPIO_C, .idx = 4 }, |
| .mosi = { .port = 2, .bank = GPIO_C, .idx = 5 }, |
| .clk = { .port = 2, .bank = GPIO_C, .idx = 6 }, |
| .cs = { .port = 2, .bank = GPIO_C, .idx = 7 }, |
| }, |
| }; |
| |
| void rockchip_spi_init(unsigned int bus, unsigned int ignored_speed_hz) |
| { |
| assert(bus >= 0 && bus < ARRAY_SIZE(slaves)); |
| |
| gpio_output(slaves[bus].cs, 1); |
| gpio_output(slaves[bus].clk, 0); |
| gpio_input(slaves[bus].miso); |
| gpio_output(slaves[bus].mosi, 0); |
| } |
| |
| void rockchip_spi_set_sample_delay(unsigned int bus, unsigned int delay_ns) |
| { |
| /* not supported, and not necessary for slow bitbang speeds */ |
| } |
| |
| static int spi_ctrlr_claim_bus(const struct spi_slave *slave) |
| { |
| assert(slave->bus >= 0 && slave->bus < ARRAY_SIZE(slaves)); |
| return spi_bitbang_claim_bus(&slaves[slave->bus].ops); |
| } |
| |
| static void spi_ctrlr_release_bus(const struct spi_slave *slave) |
| { |
| assert(slave->bus >= 0 && slave->bus < ARRAY_SIZE(slaves)); |
| spi_bitbang_release_bus(&slaves[slave->bus].ops); |
| } |
| |
| static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout, |
| size_t bytes_out, void *din, size_t bytes_in) |
| { |
| assert(slave->bus >= 0 && slave->bus < ARRAY_SIZE(slaves)); |
| return spi_bitbang_xfer(&slaves[slave->bus].ops, |
| dout, bytes_out, din, bytes_in); |
| } |
| |
| static const struct spi_ctrlr spi_ctrlr = { |
| .claim_bus = spi_ctrlr_claim_bus, |
| .release_bus = spi_ctrlr_release_bus, |
| .xfer = spi_ctrlr_xfer, |
| .max_xfer_size = 65535, |
| }; |
| |
| const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = { |
| { |
| .ctrlr = &spi_ctrlr, |
| .bus_start = 0, |
| .bus_end = ARRAY_SIZE(slaves) - 1, |
| }, |
| }; |
| |
| const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map); |