// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-FileCopyrightText: 2012 Altera /* * Designware GPIO support functions */ #include #include #include #include #include #include #define DW_GPIO_DR 0x0 #define DW_GPIO_DDR 0x4 #define DW_GPIO_EXT 0x50 #define DW_GPIO_CONFIG2 0x70 #define DW_GPIO_CONFIG1 0x74 #define DW_GPIO_CONFIG2_WIDTH(val, port) (((val) >> ((port) * 4) & 0x1f) + 1) #define DW_GPIO_CONFIG1_NPORTS(val) (((val) >> 2 & 0x3) + 1) struct dw_gpio { void __iomem *regs; }; struct dw_gpio_instance { struct dw_gpio *parent; struct gpio_chip chip; u32 gpio_state; /* GPIO state shadow register */ u32 gpio_dir; /* GPIO direction shadow register */ }; static inline struct dw_gpio_instance *to_dw_gpio(struct gpio_chip *gc) { return container_of(gc, struct dw_gpio_instance, chip); } static int dw_gpio_get(struct gpio_chip *gc, unsigned offset) { struct dw_gpio_instance *chip = to_dw_gpio(gc); struct dw_gpio *parent = chip->parent; return (readl(parent->regs + DW_GPIO_EXT) >> offset) & 1; } static void dw_gpio_set(struct gpio_chip *gc, unsigned offset, int value) { struct dw_gpio_instance *chip = to_dw_gpio(gc); struct dw_gpio *parent = chip->parent; u32 data_reg; data_reg = readl(parent->regs + DW_GPIO_DR); data_reg = (data_reg & ~(1<regs + DW_GPIO_DR); } static int dw_gpio_direction_input(struct gpio_chip *gc, unsigned offset) { struct dw_gpio_instance *chip = to_dw_gpio(gc); struct dw_gpio *parent = chip->parent; u32 gpio_ddr; /* Set pin as input, assumes software controlled IP */ gpio_ddr = readl(parent->regs + DW_GPIO_DDR); gpio_ddr &= ~(1 << offset); writel(gpio_ddr, parent->regs + DW_GPIO_DDR); return 0; } static int dw_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int value) { struct dw_gpio_instance *chip = to_dw_gpio(gc); struct dw_gpio *parent = chip->parent; u32 gpio_ddr; dw_gpio_set(gc, offset, value); /* Set pin as output, assumes software controlled IP */ gpio_ddr = readl(parent->regs + DW_GPIO_DDR); gpio_ddr |= (1 << offset); writel(gpio_ddr, parent->regs + DW_GPIO_DDR); return 0; } static int dw_gpio_get_direction(struct gpio_chip *gc, unsigned offset) { struct dw_gpio_instance *chip = to_dw_gpio(gc); struct dw_gpio *parent = chip->parent; return (readl(parent->regs + DW_GPIO_DDR) & (1 << offset)) ? GPIOF_DIR_OUT : GPIOF_DIR_IN; } static struct gpio_ops dw_gpio_ops = { .direction_input = dw_gpio_direction_input, .direction_output = dw_gpio_direction_output, .get_direction = dw_gpio_get_direction, .get = dw_gpio_get, .set = dw_gpio_set, }; static int dw_gpio_add_port(struct device *dev, struct device_node *node, struct dw_gpio *parent) { struct dw_gpio_instance *chip; uint32_t config1, config2; int ngpio, ret; chip = xzalloc(sizeof(*chip)); chip->chip.ops = &dw_gpio_ops; if (dev->id < 0) chip->chip.base = DEVICE_ID_DYNAMIC; else chip->chip.base = dev->id * 32; config2 = readl(parent->regs + DW_GPIO_CONFIG2); config1 = readl(parent->regs + DW_GPIO_CONFIG1); ngpio = DW_GPIO_CONFIG2_WIDTH(config2, 0); if (DW_GPIO_CONFIG1_NPORTS(config1) > 1) dev_info(dev, "ignoring ports B-D\n"); chip->parent = parent; chip->chip.ngpio = ngpio; chip->chip.dev = add_generic_device("dw-port", DEVICE_ID_DYNAMIC, NULL, dev->resource[0].start, resource_size(&dev->resource[0]), IORESOURCE_MEM, NULL); if (!chip->chip.dev) { dev_err(dev, "unable to add device\n"); return -ENODEV; } chip->chip.dev->of_node = node; ret = gpiochip_add(&chip->chip); if (ret) return ret; dev_dbg(chip->chip.dev, "probed gpiochip with %d gpios, base %d\n", chip->chip.ngpio, chip->chip.base); return 0; } static int dw_gpio_probe(struct device *dev) { struct resource *iores; struct dw_gpio *gpio; struct device_node *node; gpio = xzalloc(sizeof(*gpio)); iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); gpio->regs = IOMEM(iores->start); for_each_child_of_node(dev->of_node, node) dw_gpio_add_port(dev, node, gpio); return 0; } static __maybe_unused struct of_device_id dwgpio_match[] = { { .compatible = "snps,dw-apb-gpio", }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, dwgpio_match); static struct driver dwgpio_driver = { .name = "dw-apb-gpio", .probe = dw_gpio_probe, .of_compatible = DRV_OF_COMPAT(dwgpio_match), }; postcore_platform_driver(dwgpio_driver);