0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/err.h>
0010 #include <linux/init.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/io.h>
0013 #include <linux/irq.h>
0014 #include <linux/irqdomain.h>
0015 #include <linux/of.h>
0016 #include <linux/of_address.h>
0017 #include <linux/of_device.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/slab.h>
0020 #include <linux/gpio/driver.h>
0021 #include <linux/module.h>
0022
0023 #define MXS_SET 0x4
0024 #define MXS_CLR 0x8
0025
0026 #define PINCTRL_DOUT(p) ((is_imx23_gpio(p) ? 0x0500 : 0x0700) + (p->id) * 0x10)
0027 #define PINCTRL_DIN(p) ((is_imx23_gpio(p) ? 0x0600 : 0x0900) + (p->id) * 0x10)
0028 #define PINCTRL_DOE(p) ((is_imx23_gpio(p) ? 0x0700 : 0x0b00) + (p->id) * 0x10)
0029 #define PINCTRL_PIN2IRQ(p) ((is_imx23_gpio(p) ? 0x0800 : 0x1000) + (p->id) * 0x10)
0030 #define PINCTRL_IRQEN(p) ((is_imx23_gpio(p) ? 0x0900 : 0x1100) + (p->id) * 0x10)
0031 #define PINCTRL_IRQLEV(p) ((is_imx23_gpio(p) ? 0x0a00 : 0x1200) + (p->id) * 0x10)
0032 #define PINCTRL_IRQPOL(p) ((is_imx23_gpio(p) ? 0x0b00 : 0x1300) + (p->id) * 0x10)
0033 #define PINCTRL_IRQSTAT(p) ((is_imx23_gpio(p) ? 0x0c00 : 0x1400) + (p->id) * 0x10)
0034
0035 #define GPIO_INT_FALL_EDGE 0x0
0036 #define GPIO_INT_LOW_LEV 0x1
0037 #define GPIO_INT_RISE_EDGE 0x2
0038 #define GPIO_INT_HIGH_LEV 0x3
0039 #define GPIO_INT_LEV_MASK (1 << 0)
0040 #define GPIO_INT_POL_MASK (1 << 1)
0041
0042 enum mxs_gpio_id {
0043 IMX23_GPIO,
0044 IMX28_GPIO,
0045 };
0046
0047 struct mxs_gpio_port {
0048 void __iomem *base;
0049 int id;
0050 int irq;
0051 struct irq_domain *domain;
0052 struct gpio_chip gc;
0053 struct device *dev;
0054 enum mxs_gpio_id devid;
0055 u32 both_edges;
0056 };
0057
0058 static inline int is_imx23_gpio(struct mxs_gpio_port *port)
0059 {
0060 return port->devid == IMX23_GPIO;
0061 }
0062
0063
0064
0065 static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
0066 {
0067 u32 val;
0068 u32 pin_mask = 1 << d->hwirq;
0069 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
0070 struct irq_chip_type *ct = irq_data_get_chip_type(d);
0071 struct mxs_gpio_port *port = gc->private;
0072 void __iomem *pin_addr;
0073 int edge;
0074
0075 if (!(ct->type & type))
0076 if (irq_setup_alt_chip(d, type))
0077 return -EINVAL;
0078
0079 port->both_edges &= ~pin_mask;
0080 switch (type) {
0081 case IRQ_TYPE_EDGE_BOTH:
0082 val = readl(port->base + PINCTRL_DIN(port)) & pin_mask;
0083 if (val)
0084 edge = GPIO_INT_FALL_EDGE;
0085 else
0086 edge = GPIO_INT_RISE_EDGE;
0087 port->both_edges |= pin_mask;
0088 break;
0089 case IRQ_TYPE_EDGE_RISING:
0090 edge = GPIO_INT_RISE_EDGE;
0091 break;
0092 case IRQ_TYPE_EDGE_FALLING:
0093 edge = GPIO_INT_FALL_EDGE;
0094 break;
0095 case IRQ_TYPE_LEVEL_LOW:
0096 edge = GPIO_INT_LOW_LEV;
0097 break;
0098 case IRQ_TYPE_LEVEL_HIGH:
0099 edge = GPIO_INT_HIGH_LEV;
0100 break;
0101 default:
0102 return -EINVAL;
0103 }
0104
0105
0106 pin_addr = port->base + PINCTRL_IRQLEV(port);
0107 if (edge & GPIO_INT_LEV_MASK) {
0108 writel(pin_mask, pin_addr + MXS_SET);
0109 writel(pin_mask, port->base + PINCTRL_IRQEN(port) + MXS_SET);
0110 } else {
0111 writel(pin_mask, pin_addr + MXS_CLR);
0112 writel(pin_mask, port->base + PINCTRL_PIN2IRQ(port) + MXS_SET);
0113 }
0114
0115
0116 pin_addr = port->base + PINCTRL_IRQPOL(port);
0117 if (edge & GPIO_INT_POL_MASK)
0118 writel(pin_mask, pin_addr + MXS_SET);
0119 else
0120 writel(pin_mask, pin_addr + MXS_CLR);
0121
0122 writel(pin_mask, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
0123
0124 return 0;
0125 }
0126
0127 static void mxs_flip_edge(struct mxs_gpio_port *port, u32 gpio)
0128 {
0129 u32 bit, val, edge;
0130 void __iomem *pin_addr;
0131
0132 bit = 1 << gpio;
0133
0134 pin_addr = port->base + PINCTRL_IRQPOL(port);
0135 val = readl(pin_addr);
0136 edge = val & bit;
0137
0138 if (edge)
0139 writel(bit, pin_addr + MXS_CLR);
0140 else
0141 writel(bit, pin_addr + MXS_SET);
0142 }
0143
0144
0145 static void mxs_gpio_irq_handler(struct irq_desc *desc)
0146 {
0147 u32 irq_stat;
0148 struct mxs_gpio_port *port = irq_desc_get_handler_data(desc);
0149
0150 desc->irq_data.chip->irq_ack(&desc->irq_data);
0151
0152 irq_stat = readl(port->base + PINCTRL_IRQSTAT(port)) &
0153 readl(port->base + PINCTRL_IRQEN(port));
0154
0155 while (irq_stat != 0) {
0156 int irqoffset = fls(irq_stat) - 1;
0157 if (port->both_edges & (1 << irqoffset))
0158 mxs_flip_edge(port, irqoffset);
0159
0160 generic_handle_domain_irq(port->domain, irqoffset);
0161 irq_stat &= ~(1 << irqoffset);
0162 }
0163 }
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174 static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable)
0175 {
0176 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
0177 struct mxs_gpio_port *port = gc->private;
0178
0179 if (enable)
0180 enable_irq_wake(port->irq);
0181 else
0182 disable_irq_wake(port->irq);
0183
0184 return 0;
0185 }
0186
0187 static int mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
0188 {
0189 struct irq_chip_generic *gc;
0190 struct irq_chip_type *ct;
0191 int rv;
0192
0193 gc = devm_irq_alloc_generic_chip(port->dev, "gpio-mxs", 2, irq_base,
0194 port->base, handle_level_irq);
0195 if (!gc)
0196 return -ENOMEM;
0197
0198 gc->private = port;
0199
0200 ct = &gc->chip_types[0];
0201 ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
0202 ct->chip.irq_ack = irq_gc_ack_set_bit;
0203 ct->chip.irq_mask = irq_gc_mask_disable_reg;
0204 ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
0205 ct->chip.irq_set_type = mxs_gpio_set_irq_type;
0206 ct->chip.irq_set_wake = mxs_gpio_set_wake_irq;
0207 ct->chip.flags = IRQCHIP_SET_TYPE_MASKED;
0208 ct->regs.ack = PINCTRL_IRQSTAT(port) + MXS_CLR;
0209 ct->regs.enable = PINCTRL_PIN2IRQ(port) + MXS_SET;
0210 ct->regs.disable = PINCTRL_PIN2IRQ(port) + MXS_CLR;
0211
0212 ct = &gc->chip_types[1];
0213 ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
0214 ct->chip.irq_ack = irq_gc_ack_set_bit;
0215 ct->chip.irq_mask = irq_gc_mask_disable_reg;
0216 ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
0217 ct->chip.irq_set_type = mxs_gpio_set_irq_type;
0218 ct->chip.irq_set_wake = mxs_gpio_set_wake_irq;
0219 ct->chip.flags = IRQCHIP_SET_TYPE_MASKED;
0220 ct->regs.ack = PINCTRL_IRQSTAT(port) + MXS_CLR;
0221 ct->regs.enable = PINCTRL_IRQEN(port) + MXS_SET;
0222 ct->regs.disable = PINCTRL_IRQEN(port) + MXS_CLR;
0223 ct->handler = handle_level_irq;
0224
0225 rv = devm_irq_setup_generic_chip(port->dev, gc, IRQ_MSK(32),
0226 IRQ_GC_INIT_NESTED_LOCK,
0227 IRQ_NOREQUEST, 0);
0228
0229 return rv;
0230 }
0231
0232 static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
0233 {
0234 struct mxs_gpio_port *port = gpiochip_get_data(gc);
0235
0236 return irq_find_mapping(port->domain, offset);
0237 }
0238
0239 static int mxs_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
0240 {
0241 struct mxs_gpio_port *port = gpiochip_get_data(gc);
0242 u32 mask = 1 << offset;
0243 u32 dir;
0244
0245 dir = readl(port->base + PINCTRL_DOE(port));
0246 if (dir & mask)
0247 return GPIO_LINE_DIRECTION_OUT;
0248
0249 return GPIO_LINE_DIRECTION_IN;
0250 }
0251
0252 static const struct of_device_id mxs_gpio_dt_ids[] = {
0253 { .compatible = "fsl,imx23-gpio", .data = (void *) IMX23_GPIO, },
0254 { .compatible = "fsl,imx28-gpio", .data = (void *) IMX28_GPIO, },
0255 { }
0256 };
0257 MODULE_DEVICE_TABLE(of, mxs_gpio_dt_ids);
0258
0259 static int mxs_gpio_probe(struct platform_device *pdev)
0260 {
0261 struct device_node *np = pdev->dev.of_node;
0262 struct device_node *parent;
0263 static void __iomem *base;
0264 struct mxs_gpio_port *port;
0265 int irq_base;
0266 int err;
0267
0268 port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
0269 if (!port)
0270 return -ENOMEM;
0271
0272 port->id = of_alias_get_id(np, "gpio");
0273 if (port->id < 0)
0274 return port->id;
0275 port->devid = (enum mxs_gpio_id)of_device_get_match_data(&pdev->dev);
0276 port->dev = &pdev->dev;
0277 port->irq = platform_get_irq(pdev, 0);
0278 if (port->irq < 0)
0279 return port->irq;
0280
0281
0282
0283
0284
0285 if (!base) {
0286 parent = of_get_parent(np);
0287 base = of_iomap(parent, 0);
0288 of_node_put(parent);
0289 if (!base)
0290 return -EADDRNOTAVAIL;
0291 }
0292 port->base = base;
0293
0294
0295 writel(0, port->base + PINCTRL_PIN2IRQ(port));
0296 writel(0, port->base + PINCTRL_IRQEN(port));
0297
0298
0299 writel(~0U, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
0300
0301 irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0, 32, numa_node_id());
0302 if (irq_base < 0) {
0303 err = irq_base;
0304 goto out_iounmap;
0305 }
0306
0307 port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
0308 &irq_domain_simple_ops, NULL);
0309 if (!port->domain) {
0310 err = -ENODEV;
0311 goto out_iounmap;
0312 }
0313
0314
0315 err = mxs_gpio_init_gc(port, irq_base);
0316 if (err < 0)
0317 goto out_irqdomain_remove;
0318
0319
0320 irq_set_chained_handler_and_data(port->irq, mxs_gpio_irq_handler,
0321 port);
0322
0323 err = bgpio_init(&port->gc, &pdev->dev, 4,
0324 port->base + PINCTRL_DIN(port),
0325 port->base + PINCTRL_DOUT(port) + MXS_SET,
0326 port->base + PINCTRL_DOUT(port) + MXS_CLR,
0327 port->base + PINCTRL_DOE(port), NULL, 0);
0328 if (err)
0329 goto out_irqdomain_remove;
0330
0331 port->gc.to_irq = mxs_gpio_to_irq;
0332 port->gc.get_direction = mxs_gpio_get_direction;
0333 port->gc.base = port->id * 32;
0334
0335 err = gpiochip_add_data(&port->gc, port);
0336 if (err)
0337 goto out_irqdomain_remove;
0338
0339 return 0;
0340
0341 out_irqdomain_remove:
0342 irq_domain_remove(port->domain);
0343 out_iounmap:
0344 iounmap(port->base);
0345 return err;
0346 }
0347
0348 static struct platform_driver mxs_gpio_driver = {
0349 .driver = {
0350 .name = "gpio-mxs",
0351 .of_match_table = mxs_gpio_dt_ids,
0352 .suppress_bind_attrs = true,
0353 },
0354 .probe = mxs_gpio_probe,
0355 };
0356
0357 static int __init mxs_gpio_init(void)
0358 {
0359 return platform_driver_register(&mxs_gpio_driver);
0360 }
0361 postcore_initcall(mxs_gpio_init);
0362
0363 MODULE_AUTHOR("Freescale Semiconductor, "
0364 "Daniel Mack <danielncaiaq.de>, "
0365 "Juergen Beisert <kernel@pengutronix.de>");
0366 MODULE_DESCRIPTION("Freescale MXS GPIO");
0367 MODULE_LICENSE("GPL");