diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7302955d8..45b8c53fd 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -37,6 +37,16 @@ config GPIO_GENERIC_PLATFORM config GPIO_IMX def_bool ARCH_IMX +config GPIO_MALTA_FPGA_I2C + bool "Malta CBUS FPGA I2C GPIO" + depends on MACH_MIPS_MALTA + help + Support access to the CBUS FPGA I2C lines through the gpio library. + + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + config GPIO_OMAP def_bool ARCH_OMAP diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 68a76a374..add6ffc1d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o obj-$(CONFIG_GPIO_IMX) += gpio-imx.o +obj-$(CONFIG_GPIO_MALTA_FPGA_I2C) += gpio-malta-fpga-i2c.o obj-$(CONFIG_GPIO_ORION) += gpio-orion.o obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o diff --git a/drivers/gpio/gpio-malta-fpga-i2c.c b/drivers/gpio/gpio-malta-fpga-i2c.c new file mode 100644 index 000000000..d6995aad8 --- /dev/null +++ b/drivers/gpio/gpio-malta-fpga-i2c.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2014 Antony Pavlov + * + * This file is part of barebox. + * See file CREDITS for list of people who contributed to this project. + * + * 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 malta_i2c_gpio { + void __iomem *base; + struct gpio_chip chip; +}; + +#define MALTA_I2CINP 0 +#define MALTA_I2COE 0x8 +#define MALTA_I2COUT 0x10 +#define MALTA_I2CSEL 0x18 + +static inline struct malta_i2c_gpio *chip_to_malta_i2c_gpio(struct gpio_chip *c) +{ + return container_of(c, struct malta_i2c_gpio, chip); +} + +static inline void malta_i2c_gpio_write(struct malta_i2c_gpio *sc, + u32 v, int reg) +{ + __raw_writel(v, sc->base + reg); +} + +static inline u32 malta_i2c_gpio_read(struct malta_i2c_gpio *sc, int reg) +{ + return __raw_readl(sc->base + reg); +} + +static inline int malta_i2c_gpio_get_bit(struct malta_i2c_gpio *sc, + int reg, int bit) +{ + return !!(malta_i2c_gpio_read(sc, reg) & BIT(bit)); +} + +static inline void malta_i2c_gpio_set_bit(struct malta_i2c_gpio *sc, + int reg, int bit, int v) +{ + u32 t; + + t = malta_i2c_gpio_read(sc, reg); + if (v) + t |= BIT(bit); + else + t &= ~BIT(bit); + + malta_i2c_gpio_write(sc, t, reg); +} + +static int malta_i2c_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + struct malta_i2c_gpio *sc = chip_to_malta_i2c_gpio(chip); + + malta_i2c_gpio_set_bit(sc, MALTA_I2COE, gpio, 0); + + return 0; +} + +static int malta_i2c_gpio_direction_output(struct gpio_chip *chip, + unsigned gpio, int v) +{ + struct malta_i2c_gpio *sc = chip_to_malta_i2c_gpio(chip); + + malta_i2c_gpio_set_bit(sc, MALTA_I2COUT, gpio, v); + malta_i2c_gpio_set_bit(sc, MALTA_I2COE, gpio, 1); + + return 0; +} + +static int malta_i2c_gpio_get_direction(struct gpio_chip *chip, unsigned gpio) +{ + struct malta_i2c_gpio *sc = chip_to_malta_i2c_gpio(chip); + + if (malta_i2c_gpio_get_bit(sc, MALTA_I2COE, gpio)) + return GPIOF_DIR_OUT; + + return GPIOF_DIR_IN; +} + +static int malta_i2c_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct malta_i2c_gpio *sc = chip_to_malta_i2c_gpio(chip); + int v; + + v = malta_i2c_gpio_get_bit(sc, MALTA_I2CINP, gpio); + + pr_debug("%s: gpio_chip=%p gpio=%d value=%d\n", + __func__, chip, gpio, v); + + return v; +} + +static void malta_i2c_gpio_set_value(struct gpio_chip *chip, + unsigned gpio, int v) +{ + struct malta_i2c_gpio *sc = chip_to_malta_i2c_gpio(chip); + + pr_debug("%s: gpio_chip=%p gpio=%d value=%d\n", + __func__, chip, gpio, v); + + malta_i2c_gpio_set_bit(sc, MALTA_I2COUT, gpio, v); +} + +static struct gpio_ops malta_i2c_gpio_ops = { + .direction_input = malta_i2c_gpio_direction_input, + .direction_output = malta_i2c_gpio_direction_output, + .get_direction = malta_i2c_gpio_get_direction, + .get = malta_i2c_gpio_get_value, + .set = malta_i2c_gpio_set_value, +}; + +static int malta_i2c_gpio_probe(struct device_d *dev) +{ + void __iomem *gpio_base; + struct malta_i2c_gpio *sc; + int ret; + + gpio_base = dev_request_mem_region(dev, 0); + if (!gpio_base) { + dev_err(dev, "could not get memory region\n"); + return -ENODEV; + } + + sc = xzalloc(sizeof(*sc)); + sc->base = gpio_base; + sc->chip.ops = &malta_i2c_gpio_ops; + sc->chip.base = -1; + sc->chip.ngpio = 2; + sc->chip.dev = dev; + + ret = gpiochip_add(&sc->chip); + if (ret) { + dev_err(dev, "couldn't add gpiochip\n"); + free(sc); + return ret; + } + + malta_i2c_gpio_write(sc, 1, MALTA_I2CSEL); + + dev_info(dev, "probed gpiochip%d with base %d\n", + dev->id, sc->chip.base); + + return 0; +} + +static __maybe_unused struct of_device_id malta_i2c_gpio_dt_ids[] = { + { + .compatible = "mti,malta-fpga-i2c-gpio", + }, { + /* sentinel */ + }, +}; + +static struct driver_d malta_i2c_gpio_driver = { + .name = "malta-fpga-i2c-gpio", + .probe = malta_i2c_gpio_probe, + .of_compatible = DRV_OF_COMPAT(malta_i2c_gpio_dt_ids), +}; + +static int malta_i2c_gpio_driver_init(void) +{ + return platform_driver_register(&malta_i2c_gpio_driver); +} +coredevice_initcall(malta_i2c_gpio_driver_init);