195d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller/* display7seg.c - Driver implementation for the 7-segment display
295d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller *                 present on Sun Microsystems CP1400 and CP1500
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2000 Eric Brower (ebrower@usa.net)
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/major.h>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/miscdevice.h>
14fb911ee849756fc6c609dddded92d9207ff3fb29Peter Osterlund#include <linux/ioport.h>		/* request_region */
155a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
16a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmann#include <linux/mutex.h>
1795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller#include <linux/of.h>
1895d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller#include <linux/of_device.h>
1960063497a95e716c9a689af3be2687d261f115b4Arun Sharma#include <linux/atomic.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>		/* put_/get_user			*/
213049f89df13bb8f5ded8737631a6c2dd6209b5caDavid S. Miller#include <asm/io.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/display7seg.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define D7S_MINOR	193
2695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller#define DRIVER_NAME	"d7s"
2795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller#define PFX		DRIVER_NAME ": "
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmannstatic DEFINE_MUTEX(d7s_mutex);
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sol_compat = 0;		/* Solaris compatibility mode	*/
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Solaris compatibility flag -
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The Solaris implementation omits support for several
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * documented driver features (ref Sun doc 806-0180-03).
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * By default, this module supports the documented driver
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * abilities, rather than the Solaris implementation:
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 	1) Device ALWAYS reverts to OBP-specified FLIPPED mode
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 	   upon closure of device or module unload.
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 	2) Device ioctls D7SIOCRD/D7SIOCWR honor toggling of
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 	   FLIP bit
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If you wish the device to operate as under Solaris,
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * omitting above features, set this parameter to non-zero.
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4695d4390579e02e168a14f19506fbadd30daffcedDavid S. Millermodule_param(sol_compat, int, 0);
4795d4390579e02e168a14f19506fbadd30daffcedDavid S. MillerMODULE_PARM_DESC(sol_compat,
4895d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		 "Disables documented functionality omitted from Solaris driver");
4995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
5095d4390579e02e168a14f19506fbadd30daffcedDavid S. MillerMODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
5195d4390579e02e168a14f19506fbadd30daffcedDavid S. MillerMODULE_DESCRIPTION("7-Segment Display driver for Sun Microsystems CP1400/1500");
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
5395d4390579e02e168a14f19506fbadd30daffcedDavid S. MillerMODULE_SUPPORTED_DEVICE("d7s");
5495d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
5595d4390579e02e168a14f19506fbadd30daffcedDavid S. Millerstruct d7s {
5695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	void __iomem	*regs;
5795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	bool		flipped;
5895d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller};
5995d4390579e02e168a14f19506fbadd30daffcedDavid S. Millerstruct d7s *d7s_device;
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Register block address- see header for details
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * -----------------------------------------
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * | DP | ALARM | FLIP | 4 | 3 | 2 | 1 | 0 |
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * -----------------------------------------
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * DP 		- Toggles decimal point on/off
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ALARM	- Toggles "Alarm" LED green/red
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * FLIP		- Inverts display for upside-down mounted board
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * bits 0-4	- 7-segment display contents
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic atomic_t d7s_users = ATOMIC_INIT(0);
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int d7s_open(struct inode *inode, struct file *f)
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (D7S_MINOR != iminor(inode))
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_inc(&d7s_users);
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int d7s_release(struct inode *inode, struct file *f)
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Reset flipped state to OBP default only if
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * no other users have the device open and we
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * are not operating in solaris-compat mode
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (atomic_dec_and_test(&d7s_users) && !sol_compat) {
8995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		struct d7s *p = d7s_device;
9095d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		u8 regval = 0;
9195d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
9295d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		regval = readb(p->regs);
9395d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		if (p->flipped)
9495d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller			regval |= D7S_FLIP;
9595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		else
9695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller			regval &= ~D7S_FLIP;
9795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		writeb(regval, p->regs);
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwigstatic long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	struct d7s *p = d7s_device;
10695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	u8 regs = readb(p->regs);
107a5aac37f1cdbbd1e587fc618e778ddae124e5ac3Andrew Morton	int error = 0;
10895d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	u8 ireg = 0;
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1107fa95f726aba6033d7baffcfda484c31b8cfe153Josef Sipek	if (D7S_MINOR != iminor(file->f_path.dentry->d_inode))
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
113a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmann	mutex_lock(&d7s_mutex);
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case D7SIOCWR:
11695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		/* assign device register values we mask-out D7S_FLIP
11795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		 * if in sol_compat mode
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
1191d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig		if (get_user(ireg, (int __user *) arg)) {
1201d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig			error = -EFAULT;
1211d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig			break;
1221d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig		}
12395d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		if (sol_compat) {
12495d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller			if (regs & D7S_FLIP)
12595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller				ireg |= D7S_FLIP;
12695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller			else
12795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller				ireg &= ~D7S_FLIP;
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
12995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		writeb(ireg, p->regs);
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case D7SIOCRD:
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* retrieve device register values
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * NOTE: Solaris implementation returns D7S_FLIP bit
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * as toggled by user, even though it does not honor it.
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * This driver will not misinform you about the state
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * of your hardware while in sol_compat mode
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
1391d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig		if (put_user(regs, (int __user *) arg)) {
1401d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig			error = -EFAULT;
1411d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig			break;
1421d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig		}
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case D7SIOCTM:
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* toggle device mode-- flip display orientation */
14795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		if (regs & D7S_FLIP)
14895d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller			regs &= ~D7S_FLIP;
14995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		else
15095d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller			regs |= D7S_FLIP;
15195d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		writeb(regs, p->regs);
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
154a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmann	mutex_unlock(&d7s_mutex);
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1561d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig	return error;
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15900977a59b951207d38380c75f03a36829950265cArjan van de Venstatic const struct file_operations d7s_fops = {
1601d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig	.owner =		THIS_MODULE,
1611d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig	.unlocked_ioctl =	d7s_ioctl,
1621d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig	.compat_ioctl =		d7s_ioctl,
1631d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig	.open =			d7s_open,
1641d5d00bd9c44ab4730d353ee6ba0c8ebbff295c7Christoph Hellwig	.release =		d7s_release,
1656038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann	.llseek = noop_llseek,
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16895d4390579e02e168a14f19506fbadd30daffcedDavid S. Millerstatic struct miscdevice d7s_miscdev = {
16995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	.minor		= D7S_MINOR,
17095d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	.name		= DRIVER_NAME,
17195d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	.fops		= &d7s_fops
17295d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller};
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1744ebb24f707187196937607c60810d42f7112d7aaGrant Likelystatic int __devinit d7s_probe(struct platform_device *op)
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	struct device_node *opts;
17795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	int err = -EINVAL;
17895d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	struct d7s *p;
17995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	u8 regs;
18095d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
18195d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	if (d7s_device)
18295d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		goto out;
18395d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
18495d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	p = kzalloc(sizeof(*p), GFP_KERNEL);
18595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	err = -ENOMEM;
18695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	if (!p)
18795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		goto out;
18895d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
18995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	p->regs = of_ioremap(&op->resource[0], 0, sizeof(u8), "d7s");
19095d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	if (!p->regs) {
19195d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		printk(KERN_ERR PFX "Cannot map chip registers\n");
19295d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		goto out_free;
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	err = misc_register(&d7s_miscdev);
19695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	if (err) {
19795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		printk(KERN_ERR PFX "Unable to acquire miscdevice minor %i\n",
19895d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		       D7S_MINOR);
19995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		goto out_iounmap;
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20295d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	/* OBP option "d7s-flipped?" is honored as default for the
20395d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	 * device, and reset default when detached
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
20595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	regs = readb(p->regs);
20695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	opts = of_find_node_by_path("/options");
20795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	if (opts &&
20895d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	    of_get_property(opts, "d7s-flipped?", NULL))
20995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		p->flipped = true;
21095d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
21195d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	if (p->flipped)
21295d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		regs |= D7S_FLIP;
21395d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	else
21495d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		regs &= ~D7S_FLIP;
21595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
21695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	writeb(regs,  p->regs);
21795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
2183f4528d6e91cffde49894f5252e6657d420d3d74Sam Ravnborg	printk(KERN_INFO PFX "7-Segment Display%s at [%s:0x%llx] %s\n",
21961c7a080a5a061c976988fd4b844dfb468dda255Grant Likely	       op->dev.of_node->full_name,
22095d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	       (regs & D7S_FLIP) ? " (FLIPPED)" : "",
22195d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	       op->resource[0].start,
22295d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	       sol_compat ? "in sol_compat mode" : "");
22395d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
22495d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	dev_set_drvdata(&op->dev, p);
22595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	d7s_device = p;
22695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	err = 0;
22795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
22895d4390579e02e168a14f19506fbadd30daffcedDavid S. Millerout:
22995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	return err;
23095d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
23195d4390579e02e168a14f19506fbadd30daffcedDavid S. Millerout_iounmap:
23295d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	of_iounmap(&op->resource[0], p->regs, sizeof(u8));
23395d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
23495d4390579e02e168a14f19506fbadd30daffcedDavid S. Millerout_free:
23595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	kfree(p);
23695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	goto out;
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2392dc11581376829303b98eadb2de253bee065a56aGrant Likelystatic int __devexit d7s_remove(struct platform_device *op)
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
24195d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	struct d7s *p = dev_get_drvdata(&op->dev);
24295d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	u8 regs = readb(p->regs);
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Honor OBP d7s-flipped? unless operating in solaris-compat mode */
24595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	if (sol_compat) {
24695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		if (p->flipped)
24795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller			regs |= D7S_FLIP;
24895d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		else
24995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller			regs &= ~D7S_FLIP;
25095d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		writeb(regs, p->regs);
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	misc_deregister(&d7s_miscdev);
25495d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	of_iounmap(&op->resource[0], p->regs, sizeof(u8));
25595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	kfree(p);
25695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
25795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	return 0;
25895d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller}
25995d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
260fd098316ef533e8441576f020ead4beab93154ceDavid S. Millerstatic const struct of_device_id d7s_match[] = {
26195d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	{
26295d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller		.name = "display7seg",
26395d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	},
26495d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	{},
26595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller};
26695d4390579e02e168a14f19506fbadd30daffcedDavid S. MillerMODULE_DEVICE_TABLE(of, d7s_match);
26795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
2684ebb24f707187196937607c60810d42f7112d7aaGrant Likelystatic struct platform_driver d7s_driver = {
2694018294b53d1dae026880e45f174c1cc63b5d435Grant Likely	.driver = {
2704018294b53d1dae026880e45f174c1cc63b5d435Grant Likely		.name = DRIVER_NAME,
2714018294b53d1dae026880e45f174c1cc63b5d435Grant Likely		.owner = THIS_MODULE,
2724018294b53d1dae026880e45f174c1cc63b5d435Grant Likely		.of_match_table = d7s_match,
2734018294b53d1dae026880e45f174c1cc63b5d435Grant Likely	},
27495d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	.probe		= d7s_probe,
27595d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller	.remove		= __devexit_p(d7s_remove),
27695d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller};
27795d4390579e02e168a14f19506fbadd30daffcedDavid S. Miller
278dbf2b92d54e73e4a2524b90d29bd498ecc4aa593Axel Linmodule_platform_driver(d7s_driver);
279