diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 9c72673..3f1cefb 100644 --- a/arch/sandbox/Kconfig +++ b/arch/sandbox/Kconfig @@ -1,6 +1,7 @@ config SANDBOX bool select OFTREE + select GPIOLIB default y config ARCH_TEXT_BASE diff --git a/arch/sandbox/Makefile b/arch/sandbox/Makefile index 8155a79..85c70b5 100644 --- a/arch/sandbox/Makefile +++ b/arch/sandbox/Makefile @@ -21,7 +21,9 @@ -Dfputs=barebox_fputs -Dsetenv=barebox_setenv \ -Dgetenv=barebox_getenv -Dprintf=barebox_printf \ -Dglob=barebox_glob -Dglobfree=barebox_globfree \ - -Dioctl=barebox_ioctl -Dfstat=barebox_fstat + -Dioctl=barebox_ioctl -Dfstat=barebox_fstat \ + -Dopendir=barebox_opendir -Dreaddir=barebox_readdir \ + -Dclosedir=barebox_closedir machdirs := $(patsubst %,arch/sandbox/mach-%/,$(machine-y)) @@ -39,9 +41,13 @@ SDL_LIBS := $(shell pkg-config sdl --libs) endif +ifeq ($(CONFIG_GPIO_LIBFTDI1),y) +FTDI1_LIBS := $(shell pkg-config libftdi1 --libs) +endif + cmd_barebox__ = $(CC) -o $@ -Wl,-T,$(barebox-lds) \ -Wl,--start-group $(barebox-common) -Wl,--end-group \ - -lrt -lpthread $(SDL_LIBS) + -lrt -lpthread $(SDL_LIBS) $(FTDI1_LIBS) common-y += $(BOARD) arch/sandbox/os/ diff --git a/arch/sandbox/configs/sandbox_defconfig b/arch/sandbox/configs/sandbox_defconfig index 12b2479..68fe40f 100644 --- a/arch/sandbox/configs/sandbox_defconfig +++ b/arch/sandbox/configs/sandbox_defconfig @@ -53,6 +53,11 @@ CONFIG_CMD_DETECT=y CONFIG_CMD_FLASH=y CONFIG_CMD_POWEROFF=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_I2C=y +CONFIG_CMD_LED=y +CONFIG_CMD_SPI=y +CONFIG_CMD_LED_TRIGGER=y CONFIG_CMD_2048=y CONFIG_CMD_OF_NODE=y CONFIG_CMD_OF_PROPERTY=y @@ -66,10 +71,24 @@ CONFIG_OFDEVICE=y CONFIG_OF_BAREBOX_DRIVERS=y CONFIG_DRIVER_NET_TAP=y -# CONFIG_SPI is not set +CONFIG_DRIVER_SPI_GPIO=y +CONFIG_I2C=y +CONFIG_I2C_GPIO=y +CONFIG_MTD=y +CONFIG_MTD_M25P80=y CONFIG_VIDEO=y CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_LED=y +CONFIG_LED_GPIO=y +CONFIG_LED_GPIO_OF=y +CONFIG_LED_GPIO_RGB=y +CONFIG_LED_GPIO_BICOLOR=y +CONFIG_LED_TRIGGERS=y +CONFIG_EEPROM_AT25=y +CONFIG_EEPROM_AT24=y # CONFIG_PINCTRL is not set +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_DS1307=y CONFIG_FS_CRAMFS=y CONFIG_FS_EXT4=y CONFIG_FS_TFTP=y diff --git a/arch/sandbox/dts/sandbox-libftdi-example.dtsi b/arch/sandbox/dts/sandbox-libftdi-example.dtsi new file mode 100644 index 0000000..c1c3074 --- /dev/null +++ b/arch/sandbox/dts/sandbox-libftdi-example.dtsi @@ -0,0 +1,74 @@ +#include + +/ { + gpio0: gpio@0 { + compatible = "barebox,libftdi1-gpio"; + usb,id_vendor = <0x0403>; + usb,id_product = <0x6010>; + + /* + * The serial number can be used to select + * a specific device in case more than + * one is connected to the host. + */ + /* usb,i_serial_number = "20180120"; */ + + /* use ACBUS[7:0] */ + gpio-controller; + #gpio-cells = <2>; + + status = "okay"; + }; + + spi0: spi0 { + compatible = "spi-gpio"; + #address-cells = <1>; + #size-cells = <0>; + + gpio-sck = <&gpio0 0 GPIO_ACTIVE_HIGH>; + gpio-mosi = <&gpio0 1 GPIO_ACTIVE_HIGH>; + gpio-miso = <&gpio0 2 GPIO_ACTIVE_HIGH>; + cs-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>; + + num-chipselects = <1>; + + m25p128@0 { + compatible = "m25p128", "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + spi-max-frequency = <1000000>; + }; + }; + + i2c0: i2c0 { + compatible = "i2c-gpio"; + #address-cells = <1>; + #size-cells = <0>; + + gpios = <&gpio0 4 GPIO_ACTIVE_HIGH /* sda */ + &gpio0 5 GPIO_ACTIVE_HIGH /* scl */ + >; + i2c-gpio,scl-output-only; + + eeprom: at24@50 { + compatible = "atmel,24c32"; + reg = <0x50>; + }; + + rtc: ds1307@68 { + compatible = "dallas,ds1307"; + reg = <0x68>; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + + heartbeat-led { + label = "heartbeat"; + gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; + linux,default-trigger = "heartbeat"; + }; + }; +}; diff --git a/arch/sandbox/mach-sandbox/include/mach/linux.h b/arch/sandbox/mach-sandbox/include/mach/linux.h index 1327a56..52360f8 100644 --- a/arch/sandbox/mach-sandbox/include/mach/linux.h +++ b/arch/sandbox/mach-sandbox/include/mach/linux.h @@ -38,4 +38,16 @@ struct fb_bitfield *b, struct fb_bitfield *a); void sdl_setpixel(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +struct ft2232_bitbang; +struct ft2232_bitbang *barebox_libftdi1_open(int vendor_id, int device_id, + const char *serial); +void barebox_libftdi1_gpio_direction(struct ft2232_bitbang *ftbb, + unsigned off, unsigned dir); +int barebox_libftdi1_gpio_get_value(struct ft2232_bitbang *ftbb, + unsigned off); +void barebox_libftdi1_gpio_set_value(struct ft2232_bitbang *ftbb, + unsigned off, unsigned val); +int barebox_libftdi1_update(struct ft2232_bitbang *ftbb); +void barebox_libftdi1_close(void); + #endif /* __ASM_ARCH_LINUX_H */ diff --git a/arch/sandbox/os/Makefile b/arch/sandbox/os/Makefile index 537f848..75baa34 100644 --- a/arch/sandbox/os/Makefile +++ b/arch/sandbox/os/Makefile @@ -17,3 +17,6 @@ CFLAGS_sdl.o = $(shell pkg-config sdl --cflags) obj-$(CONFIG_DRIVER_VIDEO_SDL) += sdl.o + +CFLAGS_ftdi.o = $(shell pkg-config libftdi1 --cflags) +obj-$(CONFIG_GPIO_LIBFTDI1) += ftdi.o diff --git a/arch/sandbox/os/ftdi.c b/arch/sandbox/os/ftdi.c new file mode 100644 index 0000000..6485d5c --- /dev/null +++ b/arch/sandbox/os/ftdi.c @@ -0,0 +1,154 @@ +/* + * sandbox barebox libftdi1 support + * + * Copyright (C) 2016, 2017 Antony Pavlov + * + * 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. + * + */ + +#include +#include + +#define GPIOF_DIR_OUT (0 << 0) +#define GPIOF_DIR_IN (1 << 0) + +#define BIT(nr) (1UL << (nr)) + +struct ft2232_bitbang { + struct ftdi_context *ftdi; + uint8_t odata; + uint8_t dir; +}; + +static struct ft2232_bitbang ftbb; + +static inline int ftdi_flush(struct ftdi_context *ftdi) +{ + uint8_t buf[1]; + int ret; + + buf[0] = SEND_IMMEDIATE; + + ret = ftdi_write_data(ftdi, buf, 1); + + return ret; +} + +static int ftdi_get_high_byte_data(struct ftdi_context *ftdi, uint8_t *data) +{ + uint8_t obuf; + int ret; + + obuf = GET_BITS_HIGH; + ret = ftdi_write_data(ftdi, &obuf, 1); + + ret = ftdi_read_data(ftdi, data, 1); + + return ret; +} + +static int ftdi_set_high_byte_data_dir(struct ft2232_bitbang *ftbb) +{ + uint8_t buf[3]; + int ret; + + buf[0] = SET_BITS_HIGH; + buf[1] = ftbb->odata; + buf[2] = ftbb->dir; + + ret = ftdi_write_data(ftbb->ftdi, buf, 3); + ftdi_flush(ftbb->ftdi); + + return ret; +} + +int barebox_libftdi1_update(struct ft2232_bitbang *ftbb) +{ + return ftdi_set_high_byte_data_dir(ftbb); +} + +void barebox_libftdi1_gpio_direction(struct ft2232_bitbang *ftbb, + unsigned off, unsigned dir) +{ + switch (dir) { + case GPIOF_DIR_IN: + ftbb->dir &= ~BIT(off); + break; + case GPIOF_DIR_OUT: + ftbb->dir |= BIT(off); + break; + default: + printf("%s:%d: invalid dir argument\n", __FILE__, __LINE__); + } +} + +int barebox_libftdi1_gpio_get_value(struct ft2232_bitbang *ftbb, unsigned off) +{ + uint8_t data; + + ftdi_get_high_byte_data(ftbb->ftdi, &data); + + return !!(data & BIT(off)); +} + +void barebox_libftdi1_gpio_set_value(struct ft2232_bitbang *ftbb, + unsigned off, unsigned val) +{ + if (val) + ftbb->odata |= BIT(off); + else + ftbb->odata &= ~BIT(off); +} + +struct ft2232_bitbang *barebox_libftdi1_open(int id_vendor, int id_product, + const char *serial) +{ + struct ftdi_context *ftdi; + int ret; + + ftdi = ftdi_new(); + if (!ftdi) { + fprintf(stderr, "ftdi_new failed\n"); + goto error; + } + + ret = ftdi_usb_open_desc(ftdi, id_vendor, id_product, NULL, serial); + if (ret < 0 && ret != -5) { + fprintf(stderr, "unable to open ftdi device: %d (%s)\n", + ret, ftdi_get_error_string(ftdi)); + goto error; + } + + ftdi_set_interface(ftdi, INTERFACE_A); + ftdi_set_bitmode(ftdi, 0x00, BITMODE_MPSSE); + + ftbb.ftdi = ftdi; + + /* reset pins to default neutral state */ + ftbb.dir = 0; + ftbb.odata = 0; + ftdi_set_high_byte_data_dir(&ftbb); + + return &ftbb; + +error: + return NULL; +} + +void barebox_libftdi1_close(void) +{ + struct ftdi_context *ftdi = ftbb.ftdi; + + ftdi_set_interface(ftdi, INTERFACE_ANY); + ftdi_usb_close(ftdi); + ftdi_free(ftdi); +} diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 434c568..ed93e86 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -132,6 +132,10 @@ Say Y here to build support for the Semtec Sx150x I2C GPIO expander chip. +config GPIO_LIBFTDI1 + bool "libftdi1 driver" + depends on SANDBOX + endmenu endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index f37dd08..f5ed876 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_GPIO_DIGIC) += gpio-digic.o obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o obj-$(CONFIG_GPIO_IMX) += gpio-imx.o +obj-$(CONFIG_GPIO_LIBFTDI1) += gpio-libftdi1.o obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o obj-$(CONFIG_GPIO_JZ4740) += gpio-jz4740.o obj-$(CONFIG_GPIO_MALTA_FPGA_I2C) += gpio-malta-fpga-i2c.o diff --git a/drivers/gpio/gpio-libftdi1.c b/drivers/gpio/gpio-libftdi1.c new file mode 100644 index 0000000..cd36b08 --- /dev/null +++ b/drivers/gpio/gpio-libftdi1.c @@ -0,0 +1,137 @@ +/* + * libftdi1 sandbox barebox GPIO driver + * + * Copyright (C) 2016, 2017 Antony Pavlov + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +struct libftdi1_gpio_chip { + struct gpio_chip chip; + struct ft2232_bitbang *ftbb; +}; + +static int libftdi1_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct libftdi1_gpio_chip *gpio = + container_of(chip, struct libftdi1_gpio_chip, chip); + + barebox_libftdi1_gpio_direction(gpio->ftbb, off, GPIOF_DIR_IN); + barebox_libftdi1_update(gpio->ftbb); + + return 0; +} + +static int libftdi1_gpio_direction_output( + struct gpio_chip *chip, unsigned off, int value) +{ + struct libftdi1_gpio_chip *gpio = + container_of(chip, struct libftdi1_gpio_chip, chip); + + barebox_libftdi1_gpio_set_value(gpio->ftbb, off, value); + barebox_libftdi1_gpio_direction(gpio->ftbb, off, GPIOF_DIR_OUT); + barebox_libftdi1_update(gpio->ftbb); + + return 0; +} + +static int libftdi1_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct libftdi1_gpio_chip *gpio = + container_of(chip, struct libftdi1_gpio_chip, chip); + + return barebox_libftdi1_gpio_get_value(gpio->ftbb, off); +} + +static void libftdi1_gpio_set_value( + struct gpio_chip *chip, unsigned off, int value) +{ + struct libftdi1_gpio_chip *gpio = + container_of(chip, struct libftdi1_gpio_chip, chip); + + barebox_libftdi1_gpio_set_value(gpio->ftbb, off, value); + barebox_libftdi1_update(gpio->ftbb); +} + +static struct gpio_ops libftdi1_gpio_ops = { + .direction_input = libftdi1_gpio_direction_input, + .direction_output = libftdi1_gpio_direction_output, + .get = libftdi1_gpio_get_value, + .set = libftdi1_gpio_set_value, +}; + +static int libftdi1_gpio_probe(struct device_d *dev) +{ + struct libftdi1_gpio_chip *gpio; + struct ft2232_bitbang *ftbb; + int ret; + uint32_t id_vendor, id_product; + const char *i_serial_number = NULL; + + of_property_read_u32(dev->device_node, "usb,id_vendor", + &id_vendor); + + of_property_read_u32(dev->device_node, "usb,id_product", + &id_product); + + of_property_read_string(dev->device_node, "usb,i_serial_number", + &i_serial_number); + + ftbb = barebox_libftdi1_open(id_vendor, id_product, + i_serial_number); + if (!ftbb) + return -EIO; + + gpio = xzalloc(sizeof(*gpio)); + + gpio->ftbb = ftbb; + + gpio->chip.dev = dev; + gpio->chip.ops = &libftdi1_gpio_ops; + gpio->chip.base = 0; + gpio->chip.ngpio = 8; + + ret = gpiochip_add(&gpio->chip); + + dev_dbg(dev, "%d: probed gpio%d with base %d\n", + ret, dev->id, gpio->chip.base); + + return 0; +} + +static __maybe_unused const struct of_device_id libftdi1_gpio_dt_ids[] = { + { + .compatible = "barebox,libftdi1-gpio", + }, { + /* sentinel */ + }, +}; + +static void libftdi1_gpio_remove(struct device_d *dev) +{ + barebox_libftdi1_close(); +} + +static struct driver_d libftdi1_gpio_driver = { + .name = "libftdi1-gpio", + .probe = libftdi1_gpio_probe, + .remove = libftdi1_gpio_remove, + .of_compatible = DRV_OF_COMPAT(libftdi1_gpio_dt_ids), +}; +device_platform_driver(libftdi1_gpio_driver);