/* * Toumaz Xenif TZ1090 PDC GPIO handling. * * Copyright (C) 2012-2013 Imagination Technologies Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include /* Register offsets from SOC_GPIO_CONTROL0 */ #define REG_SOC_GPIO_CONTROL0 0x00 #define REG_SOC_GPIO_CONTROL1 0x04 #define REG_SOC_GPIO_CONTROL2 0x08 #define REG_SOC_GPIO_CONTROL3 0x0c #define REG_SOC_GPIO_STATUS 0x80 /* PDC GPIOs go after normal GPIOs */ #define GPIO_PDC_BASE 90 #define GPIO_PDC_NGPIO 7 /* Out of PDC gpios, only syswakes have irqs */ #define GPIO_PDC_IRQ_FIRST 2 #define GPIO_PDC_NIRQ 3 /** * struct tz1090_pdc_gpio - GPIO bank private data * @chip: Generic GPIO chip for GPIO bank * @reg: Base of registers, offset for this GPIO bank * @irq: IRQ numbers for Syswake GPIOs * * This is the main private data for the PDC GPIO driver. It encapsulates a * gpio_chip, and the callbacks for the gpio_chip can access the private data * with the to_pdc() macro below. */ struct tz1090_pdc_gpio { struct gpio_chip chip; void __iomem *reg; int irq[GPIO_PDC_NIRQ]; }; /* Register accesses into the PDC MMIO area */ static inline void pdc_write(struct tz1090_pdc_gpio *priv, unsigned int reg_offs, unsigned int data) { writel(data, priv->reg + reg_offs); } static inline unsigned int pdc_read(struct tz1090_pdc_gpio *priv, unsigned int reg_offs) { return readl(priv->reg + reg_offs); } /* Generic GPIO interface */ static int tz1090_pdc_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip); u32 value; int lstat; __global_lock2(lstat); value = pdc_read(priv, REG_SOC_GPIO_CONTROL1); value |= BIT(offset); pdc_write(priv, REG_SOC_GPIO_CONTROL1, value); __global_unlock2(lstat); return 0; } static int tz1090_pdc_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int output_value) { struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip); u32 value; int lstat; __global_lock2(lstat); /* EXT_POWER doesn't seem to have an output value bit */ if (offset < 6) { value = pdc_read(priv, REG_SOC_GPIO_CONTROL0); if (output_value) value |= BIT(offset); else value &= ~BIT(offset); pdc_write(priv, REG_SOC_GPIO_CONTROL0, value); } value = pdc_read(priv, REG_SOC_GPIO_CONTROL1); value &= ~BIT(offset); pdc_write(priv, REG_SOC_GPIO_CONTROL1, value); __global_unlock2(lstat); return 0; } static int tz1090_pdc_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip); return !!(pdc_read(priv, REG_SOC_GPIO_STATUS) & BIT(offset)); } static void tz1090_pdc_gpio_set(struct gpio_chip *chip, unsigned int offset, int output_value) { struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip); u32 value; int lstat; /* EXT_POWER doesn't seem to have an output value bit */ if (offset >= 6) return; __global_lock2(lstat); value = pdc_read(priv, REG_SOC_GPIO_CONTROL0); if (output_value) value |= BIT(offset); else value &= ~BIT(offset); pdc_write(priv, REG_SOC_GPIO_CONTROL0, value); __global_unlock2(lstat); } static int tz1090_pdc_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) { struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip); unsigned int syswake = offset - GPIO_PDC_IRQ_FIRST; int irq; /* only syswakes have irqs */ if (syswake >= GPIO_PDC_NIRQ) return -EINVAL; irq = priv->irq[syswake]; if (!irq) return -EINVAL; return irq; } static int tz1090_pdc_gpio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct resource *res_regs; struct tz1090_pdc_gpio *priv; unsigned int i; if (!np) { dev_err(&pdev->dev, "must be instantiated via devicetree\n"); return -ENOENT; } res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res_regs) { dev_err(&pdev->dev, "cannot find registers resource\n"); return -ENOENT; } priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(&pdev->dev, "unable to allocate driver data\n"); return -ENOMEM; } /* Ioremap the registers */ priv->reg = devm_ioremap(&pdev->dev, res_regs->start, resource_size(res_regs)); if (!priv->reg) { dev_err(&pdev->dev, "unable to ioremap registers\n"); return -ENOMEM; } /* Set up GPIO chip */ priv->chip.label = "tz1090-pdc-gpio"; priv->chip.parent = &pdev->dev; priv->chip.direction_input = tz1090_pdc_gpio_direction_input; priv->chip.direction_output = tz1090_pdc_gpio_direction_output; priv->chip.get = tz1090_pdc_gpio_get; priv->chip.set = tz1090_pdc_gpio_set; priv->chip.free = gpiochip_generic_free; priv->chip.request = gpiochip_generic_request; priv->chip.to_irq = tz1090_pdc_gpio_to_irq; priv->chip.of_node = np; /* GPIO numbering */ priv->chip.base = GPIO_PDC_BASE; priv->chip.ngpio = GPIO_PDC_NGPIO; /* Map the syswake irqs */ for (i = 0; i < GPIO_PDC_NIRQ; ++i) priv->irq[i] = irq_of_parse_and_map(np, i); /* Add the GPIO bank */ gpiochip_add_data(&priv->chip, priv); return 0; } static struct of_device_id tz1090_pdc_gpio_of_match[] = { { .compatible = "img,tz1090-pdc-gpio" }, { }, }; static struct platform_driver tz1090_pdc_gpio_driver = { .driver = { .name = "tz1090-pdc-gpio", .of_match_table = tz1090_pdc_gpio_of_match, }, .probe = tz1090_pdc_gpio_probe, }; static int __init tz1090_pdc_gpio_init(void) { return platform_driver_register(&tz1090_pdc_gpio_driver); } subsys_initcall(tz1090_pdc_gpio_init);