13d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov/*
23d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov * Simple Memory-Mapped GPIOs
33d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov *
43d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov * Copyright (c) MontaVista Software, Inc. 2008.
53d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov *
63d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
73d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov *
83d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov * This program is free software; you can redistribute  it and/or modify it
93d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov * under  the terms of  the GNU General  Public License as published by the
103d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov * Free Software Foundation;  either version 2 of the  License, or (at your
113d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov * option) any later version.
123d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov */
133d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
143d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov#include <linux/init.h>
153d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov#include <linux/kernel.h>
163d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov#include <linux/spinlock.h>
173d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov#include <linux/types.h>
183d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov#include <linux/ioport.h>
193d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov#include <linux/io.h>
203d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov#include <linux/of.h>
213d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov#include <linux/of_gpio.h>
223d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov#include <linux/gpio.h>
235a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
243d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov#include <asm/prom.h>
253d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov#include "simple_gpio.h"
263d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
273d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsovstruct u8_gpio_chip {
283d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	struct of_mm_gpio_chip mm_gc;
293d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	spinlock_t lock;
303d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
313d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	/* shadowed data register to clear/set bits safely */
323d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	u8 data;
333d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov};
343d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
353d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsovstatic struct u8_gpio_chip *to_u8_gpio_chip(struct of_mm_gpio_chip *mm_gc)
363d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov{
373d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	return container_of(mm_gc, struct u8_gpio_chip, mm_gc);
383d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov}
393d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
403d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsovstatic u8 u8_pin2mask(unsigned int pin)
413d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov{
423d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	return 1 << (8 - 1 - pin);
433d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov}
443d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
453d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsovstatic int u8_gpio_get(struct gpio_chip *gc, unsigned int gpio)
463d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov{
473d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
483d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
493d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	return in_8(mm_gc->regs) & u8_pin2mask(gpio);
503d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov}
513d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
523d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsovstatic void u8_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
533d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov{
543d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
553d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	struct u8_gpio_chip *u8_gc = to_u8_gpio_chip(mm_gc);
563d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	unsigned long flags;
573d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
583d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	spin_lock_irqsave(&u8_gc->lock, flags);
593d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
603d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	if (val)
613d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		u8_gc->data |= u8_pin2mask(gpio);
623d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	else
633d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		u8_gc->data &= ~u8_pin2mask(gpio);
643d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
653d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	out_8(mm_gc->regs, u8_gc->data);
663d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
673d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	spin_unlock_irqrestore(&u8_gc->lock, flags);
683d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov}
693d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
703d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsovstatic int u8_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
713d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov{
723d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	return 0;
733d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov}
743d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
753d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsovstatic int u8_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
763d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov{
773d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	u8_gpio_set(gc, gpio, val);
783d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	return 0;
793d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov}
803d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
813d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsovstatic void u8_gpio_save_regs(struct of_mm_gpio_chip *mm_gc)
823d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov{
833d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	struct u8_gpio_chip *u8_gc = to_u8_gpio_chip(mm_gc);
843d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
853d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	u8_gc->data = in_8(mm_gc->regs);
863d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov}
873d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
883d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsovstatic int __init u8_simple_gpiochip_add(struct device_node *np)
893d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov{
903d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	int ret;
913d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	struct u8_gpio_chip *u8_gc;
923d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	struct of_mm_gpio_chip *mm_gc;
933d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	struct gpio_chip *gc;
943d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
953d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	u8_gc = kzalloc(sizeof(*u8_gc), GFP_KERNEL);
963d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	if (!u8_gc)
973d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		return -ENOMEM;
983d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
993d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	spin_lock_init(&u8_gc->lock);
1003d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
1013d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	mm_gc = &u8_gc->mm_gc;
102a19e3da5bc5fc6c10ab73f310bea80f3845b4531Anton Vorontsov	gc = &mm_gc->gc;
1033d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
1043d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	mm_gc->save_regs = u8_gpio_save_regs;
1053d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	gc->ngpio = 8;
1063d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	gc->direction_input = u8_gpio_dir_in;
1073d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	gc->direction_output = u8_gpio_dir_out;
1083d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	gc->get = u8_gpio_get;
1093d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	gc->set = u8_gpio_set;
1103d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
1113d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	ret = of_mm_gpiochip_add(np, mm_gc);
1123d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	if (ret)
1133d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		goto err;
1143d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	return 0;
1153d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsoverr:
1163d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	kfree(u8_gc);
1173d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	return ret;
1183d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov}
1193d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
1203d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsovvoid __init simple_gpiochip_init(const char *compatible)
1213d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov{
1223d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	struct device_node *np;
1233d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
1243d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	for_each_compatible_node(np, NULL, compatible) {
1253d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		int ret;
1263d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		struct resource r;
1273d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
1283d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		ret = of_address_to_resource(np, 0, &r);
1293d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		if (ret)
1303d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov			goto err;
1313d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov
1323d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		switch (resource_size(&r)) {
1333d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		case 1:
1343d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov			ret = u8_simple_gpiochip_add(np);
1353d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov			if (ret)
1363d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov				goto err;
1373d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov			break;
1383d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		default:
1393d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov			/*
1403d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov			 * Whenever you need support for GPIO bank width > 1,
1413d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov			 * please just turn u8_ code into huge macros, and
1423d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov			 * construct needed uX_ code with it.
1433d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov			 */
1443d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov			ret = -ENOSYS;
1453d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov			goto err;
1463d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		}
1473d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		continue;
1483d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsoverr:
1493d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		pr_err("%s: registration failed, status %d\n",
1503d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov		       np->full_name, ret);
1513d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov	}
1523d64de9c50619d32eb71d993d23a50b98d12d3c0Anton Vorontsov}
153