diff --git a/drivers/gpio/gpio-dw.c b/drivers/gpio/gpio-dw.c index 362f365..258e43b 100644 --- a/drivers/gpio/gpio-dw.c +++ b/drivers/gpio/gpio-dw.c @@ -33,11 +33,15 @@ #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 */ - void __iomem *regs; }; static inline struct dw_gpio_instance *to_dw_gpio(struct gpio_chip *gc) @@ -48,29 +52,32 @@ 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(chip->regs + DW_GPIO_EXT) >> offset) & 1; + 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(chip->regs + DW_GPIO_DR); + data_reg = readl(parent->regs + DW_GPIO_DR); data_reg = (data_reg & ~(1<regs + DW_GPIO_DR); + writel(data_reg, parent->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(chip->regs + DW_GPIO_DDR); + gpio_ddr = readl(parent->regs + DW_GPIO_DDR); gpio_ddr &= ~(1 << offset); - writel(gpio_ddr, chip->regs + DW_GPIO_DDR); + writel(gpio_ddr, parent->regs + DW_GPIO_DDR); return 0; } @@ -79,14 +86,15 @@ 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(chip->regs + DW_GPIO_DDR); + gpio_ddr = readl(parent->regs + DW_GPIO_DDR); gpio_ddr |= (1 << offset); - writel(gpio_ddr, chip->regs + DW_GPIO_DDR); + writel(gpio_ddr, parent->regs + DW_GPIO_DDR); return 0; } @@ -94,8 +102,9 @@ 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(chip->regs + DW_GPIO_DDR) & (1 << offset)) ? + return (readl(parent->regs + DW_GPIO_DDR) & (1 << offset)) ? GPIOF_DIR_OUT : GPIOF_DIR_IN; } @@ -107,47 +116,69 @@ .set = dw_gpio_set, }; -static int dw_gpio_probe(struct device_d *dev) +static int dw_gpio_add_port(struct device_d *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->regs = dev_request_mem_region(dev, 0); - if (IS_ERR(chip->regs)) - return PTR_ERR(chip->regs); chip->chip.ops = &dw_gpio_ops; - if (dev->id < 0) { - chip->chip.base = of_alias_get_id(dev->device_node, "gpio"); - if (chip->chip.base < 0) - return chip->chip.base; - chip->chip.base *= 32; - } else { + if (dev->id < 0) + chip->chip.base = DEVICE_ID_DYNAMIC; + else chip->chip.base = dev->id * 32; - } - config2 = readl(chip->regs + DW_GPIO_CONFIG2); - config1 = readl(chip->regs + DW_GPIO_CONFIG1); + 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 = dev; + 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->device_node = node; ret = gpiochip_add(&chip->chip); if (ret) return ret; - dev_dbg(dev, "probed gpiochip with %d gpios, base %d\n", + 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_d *dev) +{ + struct dw_gpio *gpio; + struct device_node *node; + + gpio = xzalloc(sizeof(*gpio)); + + gpio->regs = dev_request_mem_region(dev, 0); + if (IS_ERR(gpio->regs)) + return PTR_ERR(gpio->regs); + + for_each_child_of_node(dev->device_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",