summaryrefslogtreecommitdiffstats
path: root/common/recipes-utils/bitbang/files
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-utils/bitbang/files')
-rw-r--r--common/recipes-utils/bitbang/files/src/Makefile29
-rw-r--r--common/recipes-utils/bitbang/files/src/bitbang.c236
-rw-r--r--common/recipes-utils/bitbang/files/src/bitbang.h66
-rw-r--r--common/recipes-utils/bitbang/files/src/mdio_bb.c335
-rw-r--r--common/recipes-utils/bitbang/files/src/spi_bb.c317
5 files changed, 983 insertions, 0 deletions
diff --git a/common/recipes-utils/bitbang/files/src/Makefile b/common/recipes-utils/bitbang/files/src/Makefile
new file mode 100644
index 0000000..24e4220
--- /dev/null
+++ b/common/recipes-utils/bitbang/files/src/Makefile
@@ -0,0 +1,29 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+
+all: spi-bb mdio-bb
+
+spi-bb: spi_bb.o bitbang.o
+ $(CC) -o $@ $^ $(LDFLAGS) -lgpio
+
+mdio-bb: mdio_bb.o bitbang.o
+ $(CC) -o $@ $^ $(LDFLAGS) -lgpio
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o spi-bb mdio-bb
diff --git a/common/recipes-utils/bitbang/files/src/bitbang.c b/common/recipes-utils/bitbang/files/src/bitbang.c
new file mode 100644
index 0000000..1318f42
--- /dev/null
+++ b/common/recipes-utils/bitbang/files/src/bitbang.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+//#define DEBUG
+//#define VERBOSE
+
+#include "bitbang.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <openbmc/log.h>
+
+#define NANOSEC_IN_SEC (1000 * 1000 * 1000)
+
+#define BITBANG_FREQ_MAX (500 * 1000 * 1000) /* 500M Hz */
+#define BITBANG_FREQ_DEFAULT (1 * 1000 * 1000) /* 1M Hz */
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+struct bitbang_handle {
+ bitbang_init_st bbh_init;
+ uint32_t bbh_half_clk; /* ns per clock cycle */
+};
+
+void bitbang_init_default(bitbang_init_st *init)
+{
+ memset(init, sizeof(*init), 0);
+ init->bbi_clk_start = BITBANG_PIN_HIGH;
+ init->bbi_data_out = BITBANG_CLK_EDGE_FALLING;
+ init->bbi_data_in = BITBANG_CLK_EDGE_RISING;
+ init->bbi_freq = BITBANG_FREQ_DEFAULT;
+}
+
+bitbang_handle_st* bitbang_open(const bitbang_init_st *init)
+{
+ bitbang_handle_st *hdl;
+
+ if (!init || !init->bbi_pin_f
+ || !init->bbi_freq || init->bbi_freq > BITBANG_FREQ_MAX) {
+ LOG_ERR(EINVAL, "Invalid init structure");
+ return NULL;
+ }
+
+ hdl = calloc(1, sizeof(*hdl));
+ if (!hdl) {
+ return NULL;
+ }
+
+ hdl->bbh_init = *init;
+ hdl->bbh_half_clk = NANOSEC_IN_SEC / init->bbi_freq / 2;
+
+ LOG_DBG("Bitbang open with initial %s, data out at %s, data in at %s, "
+ "freq at %uHz, half clk %uns",
+ (init->bbi_clk_start == BITBANG_PIN_LOW) ? "LOW" : "HIGH",
+ (init->bbi_data_out == BITBANG_CLK_EDGE_RISING)
+ ? "RISING" : "FALLING",
+ (init->bbi_data_in == BITBANG_CLK_EDGE_RISING)
+ ? "RISING" : "FALLING",
+ init->bbi_freq, hdl->bbh_half_clk);
+
+ return hdl;
+}
+
+void bitbang_close(bitbang_handle_st *hdl)
+{
+ free(hdl);
+}
+
+/*
+ * The threshold (ns) to use spin instead of nanosleep().
+ * Before adding the high resolution timer support, either spin or nanosleep()
+ * will not bring the process wakeup within 10ms. It turns out the system time
+ * update is also controlled by HZ (100).
+ * After I added the high resolution timer support, the spin works as the
+ * system time is updated more frequently. However, nanosleep() solution is
+ * still noticable slower comparing with spin. There could be some kernel
+ * scheduling tweak missing. Did not get time on that yet.
+ * For now, use 10ms as the threshold to determine if spin or nanosleep()
+ * is used.
+ */
+#define BITBANG_SPIN_THRESHOLD (10 * 1000 * 1000)
+
+static int sleep_ns(uint32_t clk)
+{
+ struct timespec req, rem;
+ int rc = 0;
+ if (clk <= BITBANG_SPIN_THRESHOLD) {
+ struct timespec orig;
+ rc = clock_gettime(CLOCK_MONOTONIC, &req);
+ orig = req;
+ while (!rc && clk) {
+ uint32_t tmp;
+ rc = clock_gettime(CLOCK_MONOTONIC, &rem);
+ tmp = (rem.tv_sec - req.tv_sec) * NANOSEC_IN_SEC;
+ if (rem.tv_nsec >= req.tv_nsec) {
+ tmp += rem.tv_nsec - req.tv_nsec;
+ } else {
+ tmp -= req.tv_nsec - rem.tv_nsec;
+ }
+ if (tmp >= clk) {
+ break;
+ }
+ clk -= tmp;
+ req = rem;
+ }
+ } else {
+ req.tv_sec = 0;
+ req.tv_nsec = clk;
+ while ((rc = nanosleep(&req, &rem)) == -1 && errno == EINTR) {
+ req = rem;
+ }
+ }
+ if (rc == -1) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to sleep %u nanoseconds", clk);
+ }
+ return rc;
+}
+
+int bitbang_io(const bitbang_handle_st *hdl, bitbang_io_st *io)
+{
+ int rc = 0;
+ uint32_t clk = hdl->bbh_half_clk;
+ const struct {
+ bitbang_pin_value_en value;
+ bitbang_clk_edge_en edge;
+ } clks[] = {
+ {BITBANG_PIN_HIGH, BITBANG_CLK_EDGE_FALLING},
+ {BITBANG_PIN_LOW, BITBANG_CLK_EDGE_RISING},
+ };
+ int clk_idx;
+ int n_clk = 0;
+ int n_bits = 0;
+ const uint8_t *dout = io->bbio_dout;
+ uint8_t *din = io->bbio_din;
+ int bit_pos = 7;
+ bitbang_pin_func pin_f = hdl->bbh_init.bbi_pin_f;
+ void *context = hdl->bbh_init.bbi_context;
+
+ if ((io->bbio_in_bits == 0 && io->bbio_din)
+ || (io->bbio_in_bits > 0 && !io->bbio_din)) {
+ rc = EINVAL;
+ LOG_ERR(rc, "Incorrect in bits and in buffer");
+ goto out;
+ }
+
+ if ((io->bbio_out_bits == 0 && io->bbio_dout)
+ || (io->bbio_out_bits > 0 && !io->bbio_dout)) {
+ rc = EINVAL;
+ LOG_ERR(rc, "Incorrect out bits and out buffer");
+ goto out;
+ }
+
+ if (io->bbio_in_bits == 0 && io->bbio_out_bits == 0) {
+ rc = EINVAL;
+ LOG_ERR(rc, "Both in and out bits are 0");
+ goto out;
+ }
+
+ if (hdl->bbh_init.bbi_clk_start == BITBANG_PIN_HIGH) {
+ clk_idx = 0;
+ } else {
+ clk_idx = 1;
+ }
+
+ /* set the CLK pin start position */
+ pin_f(BITBANG_CLK_PIN, clks[clk_idx].value, context);
+
+ /* clear the first byte of din */
+ if (din && io->bbio_in_bits) {
+ memset(din, 0, (io->bbio_in_bits + 7) / 8);
+ }
+
+ do {
+ if ((rc = sleep_ns(clk))) {
+ goto out;
+ }
+
+ /* output first */
+ if (hdl->bbh_init.bbi_data_out == clks[clk_idx].edge) {
+ if (dout && n_bits < io->bbio_out_bits) {
+ pin_f(BITBANG_DATA_OUT, (*dout >> bit_pos) & 0x1, context);
+ }
+ }
+
+ /* then, input */
+ if (hdl->bbh_init.bbi_data_in == clks[clk_idx].edge) {
+ if (din && n_bits < io->bbio_in_bits) {
+ *din |= (pin_f(BITBANG_DATA_IN, 0, context) & 0x1) << bit_pos;
+ }
+ }
+
+ if (++n_clk % 2 == 0) {
+ /* one bit for every 2 half clks */
+ n_bits ++;
+ if (bit_pos == 0) {
+ if (dout) {
+ dout++;
+ }
+ if (din) {
+ din++;
+ }
+ bit_pos = 7;
+ } else {
+ bit_pos --;
+ }
+ }
+ clk_idx = 1 - clk_idx;
+ pin_f(BITBANG_CLK_PIN, clks[clk_idx].value, context);
+ } while (n_bits < MAX(io->bbio_in_bits, io->bbio_out_bits));
+
+ out:
+
+ return -rc;
+}
diff --git a/common/recipes-utils/bitbang/files/src/bitbang.h b/common/recipes-utils/bitbang/files/src/bitbang.h
new file mode 100644
index 0000000..0f21a49
--- /dev/null
+++ b/common/recipes-utils/bitbang/files/src/bitbang.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef BITBANG_H
+#define BITBANG_H
+
+#include <stdint.h>
+
+typedef enum {
+ BITBANG_CLK_PIN,
+ BITBANG_DATA_IN,
+ BITBANG_DATA_OUT,
+} bitbang_pin_type_en;
+
+typedef enum {
+ BITBANG_PIN_LOW = 0,
+ BITBANG_PIN_HIGH = 1,
+} bitbang_pin_value_en;
+
+typedef enum {
+ BITBANG_CLK_EDGE_RISING,
+ BITBANG_CLK_EDGE_FALLING,
+} bitbang_clk_edge_en;
+
+typedef bitbang_pin_value_en (* bitbang_pin_func)(
+ bitbang_pin_type_en pin, bitbang_pin_value_en value, void *context);
+
+typedef struct {
+ bitbang_pin_value_en bbi_clk_start;
+ bitbang_clk_edge_en bbi_data_out;
+ bitbang_clk_edge_en bbi_data_in;
+ uint32_t bbi_freq;
+ bitbang_pin_func bbi_pin_f;
+ void *bbi_context;
+} bitbang_init_st;
+
+typedef struct bitbang_handle bitbang_handle_st;
+
+void bitbang_init_default(bitbang_init_st *init);
+bitbang_handle_st* bitbang_open(const bitbang_init_st *init);
+void bitbang_close(bitbang_handle_st *hdl);
+
+typedef struct {
+ uint32_t bbio_in_bits;
+ uint32_t bbio_out_bits;
+ uint8_t *bbio_dout;
+ uint8_t *bbio_din;
+} bitbang_io_st;
+
+int bitbang_io(const bitbang_handle_st *hdl, bitbang_io_st *io);
+
+#endif
diff --git a/common/recipes-utils/bitbang/files/src/mdio_bb.c b/common/recipes-utils/bitbang/files/src/mdio_bb.c
new file mode 100644
index 0000000..9137e17
--- /dev/null
+++ b/common/recipes-utils/bitbang/files/src/mdio_bb.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+//#define DEBUG
+//#define VERBOSE
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <openbmc/log.h>
+#include <openbmc/gpio.h>
+
+#include "bitbang.h"
+
+typedef struct {
+ gpio_st m_mdc;
+ gpio_st m_mdio;
+} mdio_context_st;
+
+/*
+ * 32b preamble, 2b start of frame, 2b operation code,
+ * 5b phy addr, 5b register addr, 2b turnaround, 16b data
+ */
+#define N_BITS (32 + 2 + 2 + 5 + 5 + 2 + 16)
+#define N_BYTES (N_BITS + 7 / 8)
+
+#define START_OF_FRAME 0x1
+#define OP_READ 0x2
+#define OP_WRITE 0x1
+#define TURNAROUND 0x2 /* TA for write, for read, phy sends out TA */
+
+void usage()
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "mdio-bb: -c <GPIO for MDC> [-C <HIGH|low>]\n"
+ " -d <GPIO for MDIO> [-O <rising|FALLING>]\n"
+ " [-I <RISING|falling>] [-p] [-b]\n"
+ " <read|write> <phy address> <register address>\n"
+ " [value to write]\n");
+}
+
+bitbang_pin_value_en mdio_pin_f(
+ bitbang_pin_type_en pin, bitbang_pin_value_en value, void *context)
+{
+ mdio_context_st *ctx = (mdio_context_st *)context;
+ gpio_st *gpio;
+ bitbang_pin_value_en res;
+
+ switch (pin) {
+ case BITBANG_CLK_PIN:
+ gpio = &ctx->m_mdc;
+ break;
+ case BITBANG_DATA_IN:
+ case BITBANG_DATA_OUT:
+ gpio = &ctx->m_mdio;
+ break;
+ }
+ if (pin == BITBANG_DATA_IN) {
+ res = gpio_read(gpio) ? BITBANG_PIN_HIGH : BITBANG_PIN_LOW;
+ } else {
+ res = value;
+ gpio_write(gpio, ((res == BITBANG_PIN_HIGH)
+ ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW));
+ }
+ return res;
+}
+
+int main(int argc, char* const argv[])
+{
+ int opt;
+ int mdc = -1, mdio = -1;
+ bitbang_pin_value_en mdc_start = BITBANG_PIN_HIGH;
+ bitbang_clk_edge_en out_edge = BITBANG_CLK_EDGE_FALLING;
+ bitbang_clk_edge_en in_edge = BITBANG_CLK_EDGE_RISING;
+ int is_write;
+ uint32_t phy_addr;
+ uint32_t reg_addr;
+ uint32_t data; /* data to write/read*/
+ uint8_t buf[N_BYTES];
+ uint8_t *buf_p;
+ mdio_context_st ctx;
+ bitbang_init_st init;
+ bitbang_handle_st *hdl = NULL;
+ bitbang_io_st io;
+ int n_bits;
+ int i;
+ int rc = 0;
+ int preamble = 0;
+ int binary = 0;
+
+ while ((opt = getopt(argc, argv, "bc:C:d:D:p")) != -1) {
+ switch (opt) {
+ case 'b':
+ binary = 1;
+ break;
+ case 'c':
+ mdc = atoi(optarg);
+ break;
+ case 'C':
+ if (!strcasecmp(optarg, "high")) {
+ mdc_start = BITBANG_PIN_HIGH;
+ } else if (!strcasecmp(optarg, "low")) {
+ mdc_start = BITBANG_PIN_LOW;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'd':
+ mdio = atoi(optarg);
+ break;
+ case 'I':
+ if (!strcasecmp(optarg, "rising")) {
+ in_edge = BITBANG_CLK_EDGE_RISING;
+ } if (!strcasecmp(optarg, "falling")) {
+ in_edge = BITBANG_CLK_EDGE_FALLING;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'O':
+ if (!strcasecmp(optarg, "rising")) {
+ out_edge = BITBANG_CLK_EDGE_RISING;
+ } if (!strcasecmp(optarg, "falling")) {
+ out_edge = BITBANG_CLK_EDGE_FALLING;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'p':
+ preamble = 1;
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+ }
+
+ if (mdc < 0 || mdio < 0) {
+ usage();
+ exit(-1);
+ }
+
+ if (optind + 2 >= argc) {
+ usage();
+ exit(-1);
+ }
+
+ /* read or write */
+ if (!strcasecmp(argv[optind], "read")) {
+ is_write = 0;
+ } else if (!strcasecmp(argv[optind], "write")) {
+ is_write = 1;
+ } else {
+ usage();
+ exit(-1);
+ }
+
+ /* phy address, 5 bits only, so must be <= 0x1f */
+ phy_addr = strtoul(argv[optind + 1], NULL, 0);
+ if (phy_addr > 0x1f) {
+ usage();
+ exit(-1);
+ }
+
+ /* register address, 5 bits only, so must be <= 0x1f */
+ reg_addr = strtoul(argv[optind + 2], NULL, 0);
+ if (reg_addr > 0x1f) {
+ usage();
+ exit(-1);
+ }
+
+ /* data */
+ if (is_write) {
+ if ((!binary && (optind + 4 != argc)) ||
+ (binary && (optind + 3 != argc))) {
+ usage();
+ exit(-1);
+ }
+ if (binary) {
+ uint16_t temp = 0;
+ if (fread(&temp, sizeof(temp), 1, stdin) != 1) {
+ usage();
+ exit(-1);
+ }
+ data = htons(temp);
+ } else {
+ data = strtoul(argv[optind + 3], NULL, 0);
+ }
+ if (data > 0xFFFF) {
+ usage();
+ exit(-1);
+ }
+ } else {
+ if ((!binary && (optind + 3 != argc)) ||
+ (binary && (optind + 2 != argc))) {
+ usage();
+ exit(-1);
+ }
+ }
+
+ /* open all gpio */
+ memset(&ctx, sizeof(ctx), 0);
+ gpio_init_default(&ctx.m_mdc);
+ gpio_init_default(&ctx.m_mdio);
+ if (gpio_open(&ctx.m_mdc, mdc) || gpio_open(&ctx.m_mdio, mdio)) {
+ goto out;
+ }
+
+ if (gpio_change_direction(&ctx.m_mdc, GPIO_DIRECTION_OUT)
+ || gpio_change_direction(&ctx.m_mdio, GPIO_DIRECTION_OUT)) {
+ goto out;
+ }
+
+ bitbang_init_default(&init);
+ init.bbi_clk_start = mdc_start;
+ init.bbi_data_out = out_edge;
+ init.bbi_data_in = in_edge;
+ init.bbi_freq = 1000 * 1000; /* 1M Hz */
+ init.bbi_pin_f = mdio_pin_f;
+ init.bbi_context = &ctx;
+ hdl = bitbang_open(&init);
+ if (!hdl) {
+ goto out;
+ }
+
+ if (is_write) {
+ buf[0] = (data >> 8) & 0xFF;
+ buf[1] = data & 0xFF;
+ io.bbio_out_bits = 16;
+ io.bbio_dout = buf;
+ io.bbio_in_bits = 0;
+ io.bbio_din = NULL;
+ } else {
+ io.bbio_in_bits = 16;
+ io.bbio_din = buf;
+ io.bbio_out_bits = 0;
+ io.bbio_dout = NULL;
+ }
+
+ /* preamble, 32b */
+ buf_p = buf;
+ n_bits = 0;
+ if (preamble) {
+ /* 32 bit of 1 for preamble */
+ for (i = 0; i < 4; i++) {
+ *buf_p++ = 0xFF;
+ }
+ n_bits += 32;
+ }
+
+ /*
+ * MDIO transaction header is:
+ * 2b START, 2b OPER CODE, 5b PHY ADDR, 5b register addr, 2b TURNROUND
+ */
+ *buf_p++ = (START_OF_FRAME << 6) | (((is_write) ? OP_WRITE : OP_READ) << 4)
+ | ((phy_addr >> 1) & 0xF);
+ *buf_p++ = ((phy_addr & 0x1) << 7) | ((reg_addr & 0x1F) << 2) | TURNAROUND;
+ if (is_write) {
+ *buf_p++ = (data >> 8) & 0xFF;
+ *buf_p++ = data & 0xFF;
+ /* total # of bits is transaction header + 2 bytes to write */
+ n_bits += 2 + 2 + 5 + 5 + 2 + 16;
+ } else {
+ /* for read, master does not send TR, so, n_bits should not include TR */
+ n_bits += 2 + 2 + 5 + 5;
+ }
+
+ memset(&io, sizeof(io), 0);
+ io.bbio_out_bits = n_bits;
+ io.bbio_dout = buf;
+ io.bbio_in_bits = 0;
+ io.bbio_din = NULL;
+
+ rc = bitbang_io(hdl, &io);
+ if (rc != 0) {
+ goto out;
+ }
+
+ /* for read, need to do another io for (2b TR + 16b data) reading */
+ if (!is_write) {
+ /* first, change the MDIO to input */
+ gpio_change_direction(&ctx.m_mdio, GPIO_DIRECTION_IN);
+ /* then, run the clock for read */
+ memset(&io, sizeof(io), 0);
+ io.bbio_out_bits = 0;
+ io.bbio_dout = NULL;;
+ io.bbio_in_bits = 18;
+ io.bbio_din = buf;
+
+ rc = bitbang_io(hdl, &io);
+ if (rc != 0) {
+ goto out;
+ }
+
+ data = ((buf[0] << 2) | (buf[1] >> 6)) & 0xFF;
+ data <<= 8;
+ data |= ((buf[1] << 2) | (buf[2] >> 6)) & 0xFF;
+ }
+
+ if (binary) {
+ if (!is_write) {
+ uint16_t temp = ntohs(data);
+ fwrite(&temp, sizeof(temp), 1, stdout);
+ }
+ } else {
+ printf("%s: 0x%02x\n", (is_write) ? "Wrote" : "Read", data);
+ }
+
+ out:
+ if (hdl) {
+ bitbang_close(hdl);
+ }
+ gpio_close(&ctx.m_mdc);
+ gpio_close(&ctx.m_mdio);
+
+ return 0;
+}
diff --git a/common/recipes-utils/bitbang/files/src/spi_bb.c b/common/recipes-utils/bitbang/files/src/spi_bb.c
new file mode 100644
index 0000000..2125877
--- /dev/null
+++ b/common/recipes-utils/bitbang/files/src/spi_bb.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+//#define DEBUG
+//#define VERBOSE
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <openbmc/gpio.h>
+#include <openbmc/log.h>
+
+#include "bitbang.h"
+
+void usage()
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "spi-bb: -s <GPIO for CS> [-S <HIGH|low>]\n"
+ " -c <GPIO for CLK> [-C <HIGH|low>]\n"
+ " -o <GPIO for MOSI> [-O <rising|FALLING>]\n"
+ " -i <GPIO for MISO> [-I <RISING|falling>]\n"
+ " [-b]\n"
+ " < [-r <number of bits to read>]\n"
+ " [-w <number of bits to write> <byte 1> [... byte N]>\n\n"
+ "Note: If both '-r' and '-w' are provided, 'write' will be performed\n"
+ " before 'read'.\n");
+}
+
+typedef struct {
+ gpio_st sc_clk;
+ gpio_st sc_mosi;
+ gpio_st sc_miso;
+} spi_context_st;
+
+bitbang_pin_value_en spi_pin_f(
+ bitbang_pin_type_en pin, bitbang_pin_value_en value, void *context)
+{
+ spi_context_st *ctx = (spi_context_st *)context;
+ gpio_st *gpio;
+ bitbang_pin_value_en res;
+
+ switch (pin) {
+ case BITBANG_CLK_PIN:
+ gpio = &ctx->sc_clk;
+ break;
+ case BITBANG_DATA_IN:
+ gpio = &ctx->sc_miso;
+ break;
+ case BITBANG_DATA_OUT:
+ gpio = &ctx->sc_mosi;
+ break;
+ }
+ if (pin == BITBANG_DATA_IN) {
+ res = gpio_read(gpio) ? BITBANG_PIN_HIGH : BITBANG_PIN_LOW;
+ } else {
+ res = value;
+ gpio_write(gpio, ((res == BITBANG_PIN_HIGH)
+ ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW));
+ }
+ return res;
+}
+
+int main(int argc, char * const argv[])
+{
+ bitbang_init_st init;
+ bitbang_handle_st *hdl = NULL;
+ int cs = -1, clk = -1, in = -1, out = -1;
+ gpio_st cs_gpio;
+ int opt;
+ int is_write = 0;
+ int is_read = 0;
+ int read_bits = 0;
+ int write_bits = 0;
+ int read_bytes = 0;
+ int write_bytes = 0;
+ int i;
+ uint8_t *read_buf = NULL;;
+ uint8_t *write_buf = NULL;;
+ bitbang_clk_edge_en dout_edge = BITBANG_CLK_EDGE_FALLING;
+ bitbang_clk_edge_en din_edge = BITBANG_CLK_EDGE_RISING;
+ bitbang_pin_value_en clk_start = BITBANG_PIN_HIGH;
+ bitbang_pin_value_en cs_value = BITBANG_PIN_HIGH;
+ spi_context_st ctx;
+ bitbang_io_st io;
+ int rc = 0;
+ int binary = 0;
+
+ memset(&ctx, sizeof(ctx), 0);
+ gpio_init_default(&ctx.sc_clk);
+ gpio_init_default(&ctx.sc_mosi);
+ gpio_init_default(&ctx.sc_miso);
+ gpio_init_default(&cs_gpio);
+
+ while ((opt = getopt(argc, argv, "bs:S:c:C:o:O:i:I:w:r:")) != -1) {
+ switch (opt) {
+ case 'b':
+ binary = 1;
+ break;
+ case 's':
+ cs = atoi(optarg);
+ break;
+ case 'S':
+ if (!strcmp(optarg, "high")) {
+ cs_value = BITBANG_PIN_HIGH;
+ } else if (!strcmp(optarg, "low")) {
+ cs_value = BITBANG_PIN_LOW;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'c':
+ clk = atoi(optarg);
+ break;
+ case 'C':
+ if (!strcasecmp(optarg, "high")) {
+ clk_start = BITBANG_PIN_HIGH;
+ } else if (!strcasecmp(optarg, "low")) {
+ clk_start = BITBANG_PIN_LOW;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'o':
+ out = atoi(optarg);
+ break;
+ case 'O':
+ if (!strcasecmp(optarg, "rising")) {
+ dout_edge = BITBANG_CLK_EDGE_RISING;
+ } else if (!strcasecmp(optarg, "falling")) {
+ dout_edge = BITBANG_CLK_EDGE_FALLING;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'i':
+ in = atoi(optarg);
+ break;
+ case 'I':
+ if (!strcasecmp(optarg, "rising")) {
+ din_edge = BITBANG_CLK_EDGE_RISING;
+ } else if (!strcasecmp(optarg, "falling")) {
+ din_edge = BITBANG_CLK_EDGE_FALLING;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'w':
+ is_write = 1;
+ write_bits = atoi(optarg);
+ if (write_bits <= 0) {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'r':
+ is_read = 1;
+ read_bits = atoi(optarg);
+ if (read_bits <= 0) {
+ usage();
+ exit(-1);
+ }
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+ }
+
+ if (clk < 0 || in < 0 || out < 0) {
+ usage();
+ exit(-1);
+ }
+
+ if ((!is_read && !is_write)) {
+ usage();
+ exit(-1);
+ }
+
+ write_bytes = ((write_bits + 7) / 8);
+ if (write_bytes) {
+ write_buf = calloc(write_bytes, sizeof(uint8_t));
+ if (!write_buf) {
+ goto out;
+ }
+ if (binary) {
+ size_t written_bytes;
+ written_bytes = fread(write_buf, sizeof(*write_buf), write_bytes, stdin);
+ if( written_bytes != write_bytes ) {
+ goto out;
+ }
+ } else {
+ for (i = 0; i < write_bytes && i + optind < argc; i++) {
+ write_buf[i] = strtoul(argv[i + optind], NULL, 0);
+ }
+ }
+ }
+
+ read_bytes = ((read_bits + 7) / 8);
+ if (read_bytes) {
+ read_buf = calloc(read_bytes, sizeof(uint8_t));
+ if (!read_buf) {
+ goto out;
+ }
+ }
+
+ if (gpio_open(&ctx.sc_clk, clk) || gpio_open(&ctx.sc_miso, in)
+ || gpio_open(&ctx.sc_mosi, out)) {
+ goto out;
+ }
+
+ /* change GPIO directions, only MISO is input, all others are output */
+ if (gpio_change_direction(&ctx.sc_clk, GPIO_DIRECTION_OUT)
+ || gpio_change_direction(&ctx.sc_miso, GPIO_DIRECTION_IN)
+ || gpio_change_direction(&ctx.sc_mosi, GPIO_DIRECTION_OUT)) {
+ goto out;
+ }
+
+ if (cs != -1) {
+ if (gpio_open(&cs_gpio, cs)) {
+ goto out;
+ }
+ if (gpio_change_direction(&cs_gpio, GPIO_DIRECTION_OUT)) {
+ goto out;
+ }
+ }
+
+ bitbang_init_default(&init);
+ init.bbi_clk_start = clk_start;
+ init.bbi_data_out = dout_edge;
+ init.bbi_data_in = din_edge;
+ init.bbi_freq = 1000 * 1000; /* 1M Hz */
+ init.bbi_pin_f = spi_pin_f;
+ init.bbi_context = &ctx;
+
+ hdl = bitbang_open(&init);
+ if (!hdl) {
+ goto out;
+ }
+
+ if (cs != -1) {
+ /* have chip select */
+ gpio_write(&cs_gpio, ((cs_value == BITBANG_PIN_HIGH)
+ ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW));
+ }
+
+ memset(&io, sizeof(io), 0);
+ io.bbio_in_bits = read_bits;
+ io.bbio_din = read_buf;
+ io.bbio_out_bits = write_bits;
+ io.bbio_dout = write_buf;
+
+ rc = bitbang_io(hdl, &io);
+ if (rc != 0) {
+ goto out;
+ }
+
+ if (binary) {
+ fwrite(read_buf, sizeof(*read_buf), read_bytes, stdout);
+ } else {
+ if (write_bits) {
+ printf("Wrote %u bits:", write_bits);
+ for (i = 0; i < write_bytes; i++) {
+ printf(" %02x", write_buf[i]);
+ }
+ printf("\n");
+ }
+
+ if (read_bits) {
+ printf("Read %u bits:", read_bits);
+ for (i = 0; i < read_bytes; i++) {
+ printf(" %02x", read_buf[i]);
+ }
+ printf("\n");
+ }
+ }
+
+ out:
+ if (hdl) {
+ bitbang_close(hdl);
+ }
+ gpio_close(&ctx.sc_clk);
+ gpio_close(&ctx.sc_miso);
+ gpio_close(&ctx.sc_mosi);
+ if (cs != -1) {
+ /* reset have chip select */
+ gpio_write(&cs_gpio, ((cs_value == BITBANG_PIN_HIGH)
+ ? GPIO_VALUE_LOW : GPIO_VALUE_HIGH));
+ gpio_close(&cs_gpio);
+ }
+
+ if (read_buf) {
+ free(read_buf);
+ }
+ if (write_buf) {
+ free(write_buf);
+ }
+ return rc;
+}
OpenPOWER on IntegriCloud