brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · 521cf8a Raw
268 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* display7seg.c - Driver implementation for the 7-segment display3 *                 present on Sun Microsystems CP1400 and CP15004 *5 * Copyright (c) 2000 Eric Brower (ebrower@usa.net)6 */7 8#include <linux/device.h>9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/fs.h>12#include <linux/errno.h>13#include <linux/major.h>14#include <linux/miscdevice.h>15#include <linux/ioport.h>		/* request_region */16#include <linux/slab.h>17#include <linux/mutex.h>18#include <linux/of.h>19#include <linux/platform_device.h>20#include <linux/atomic.h>21#include <linux/uaccess.h>		/* put_/get_user			*/22#include <asm/io.h>23 24#include <asm/display7seg.h>25 26#define DRIVER_NAME	"d7s"27#define PFX		DRIVER_NAME ": "28 29static DEFINE_MUTEX(d7s_mutex);30static int sol_compat = 0;		/* Solaris compatibility mode	*/31 32/* Solaris compatibility flag -33 * The Solaris implementation omits support for several34 * documented driver features (ref Sun doc 806-0180-03).  35 * By default, this module supports the documented driver 36 * abilities, rather than the Solaris implementation:37 *38 * 	1) Device ALWAYS reverts to OBP-specified FLIPPED mode39 * 	   upon closure of device or module unload.40 * 	2) Device ioctls D7SIOCRD/D7SIOCWR honor toggling of41 * 	   FLIP bit42 *43 * If you wish the device to operate as under Solaris,44 * omitting above features, set this parameter to non-zero.45 */46module_param(sol_compat, int, 0);47MODULE_PARM_DESC(sol_compat, 48		 "Disables documented functionality omitted from Solaris driver");49 50MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");51MODULE_DESCRIPTION("7-Segment Display driver for Sun Microsystems CP1400/1500");52MODULE_LICENSE("GPL");53 54struct d7s {55	void __iomem	*regs;56	bool		flipped;57};58struct d7s *d7s_device;59 60/*61 * Register block address- see header for details62 * -----------------------------------------63 * | DP | ALARM | FLIP | 4 | 3 | 2 | 1 | 0 |64 * -----------------------------------------65 *66 * DP 		- Toggles decimal point on/off 67 * ALARM	- Toggles "Alarm" LED green/red68 * FLIP		- Inverts display for upside-down mounted board69 * bits 0-4	- 7-segment display contents70 */71static atomic_t d7s_users = ATOMIC_INIT(0);72 73static int d7s_open(struct inode *inode, struct file *f)74{75	if (D7S_MINOR != iminor(inode))76		return -ENODEV;77	atomic_inc(&d7s_users);78	return 0;79}80 81static int d7s_release(struct inode *inode, struct file *f)82{83	/* Reset flipped state to OBP default only if84	 * no other users have the device open and we85	 * are not operating in solaris-compat mode86	 */87	if (atomic_dec_and_test(&d7s_users) && !sol_compat) {88		struct d7s *p = d7s_device;89		u8 regval = 0;90 91		regval = readb(p->regs);92		if (p->flipped)93			regval |= D7S_FLIP;94		else95			regval &= ~D7S_FLIP;96		writeb(regval, p->regs);97	}98 99	return 0;100}101 102static long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg)103{104	struct d7s *p = d7s_device;105	u8 regs = readb(p->regs);106	int error = 0;107	u8 ireg = 0;108 109	if (D7S_MINOR != iminor(file_inode(file)))110		return -ENODEV;111 112	mutex_lock(&d7s_mutex);113	switch (cmd) {114	case D7SIOCWR:115		/* assign device register values we mask-out D7S_FLIP116		 * if in sol_compat mode117		 */118		if (get_user(ireg, (int __user *) arg)) {119			error = -EFAULT;120			break;121		}122		if (sol_compat) {123			if (regs & D7S_FLIP)124				ireg |= D7S_FLIP;125			else126				ireg &= ~D7S_FLIP;127		}128		writeb(ireg, p->regs);129		break;130 131	case D7SIOCRD:132		/* retrieve device register values133		 * NOTE: Solaris implementation returns D7S_FLIP bit134		 * as toggled by user, even though it does not honor it.135		 * This driver will not misinform you about the state136		 * of your hardware while in sol_compat mode137		 */138		if (put_user(regs, (int __user *) arg)) {139			error = -EFAULT;140			break;141		}142		break;143 144	case D7SIOCTM:145		/* toggle device mode-- flip display orientation */146		regs ^= D7S_FLIP;147		writeb(regs, p->regs);148		break;149	}150	mutex_unlock(&d7s_mutex);151 152	return error;153}154 155static const struct file_operations d7s_fops = {156	.owner =		THIS_MODULE,157	.unlocked_ioctl =	d7s_ioctl,158	.compat_ioctl =		compat_ptr_ioctl,159	.open =			d7s_open,160	.release =		d7s_release,161	.llseek = noop_llseek,162};163 164static struct miscdevice d7s_miscdev = {165	.minor		= D7S_MINOR,166	.name		= DRIVER_NAME,167	.fops		= &d7s_fops168};169 170static int d7s_probe(struct platform_device *op)171{172	struct device_node *opts;173	int err = -EINVAL;174	struct d7s *p;175	u8 regs;176 177	if (d7s_device)178		goto out;179 180	p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL);181	err = -ENOMEM;182	if (!p)183		goto out;184 185	p->regs = of_ioremap(&op->resource[0], 0, sizeof(u8), "d7s");186	if (!p->regs) {187		printk(KERN_ERR PFX "Cannot map chip registers\n");188		goto out;189	}190 191	err = misc_register(&d7s_miscdev);192	if (err) {193		printk(KERN_ERR PFX "Unable to acquire miscdevice minor %i\n",194		       D7S_MINOR);195		goto out_iounmap;196	}197 198	/* OBP option "d7s-flipped?" is honored as default for the199	 * device, and reset default when detached200	 */201	regs = readb(p->regs);202	opts = of_find_node_by_path("/options");203	if (opts)204	    p->flipped = of_property_read_bool(opts, "d7s-flipped?");205 206	if (p->flipped)207		regs |= D7S_FLIP;208	else209		regs &= ~D7S_FLIP;210 211	writeb(regs,  p->regs);212 213	printk(KERN_INFO PFX "7-Segment Display%pOF at [%s:0x%llx] %s\n",214	       op->dev.of_node,215	       (regs & D7S_FLIP) ? " (FLIPPED)" : "",216	       op->resource[0].start,217	       sol_compat ? "in sol_compat mode" : "");218 219	dev_set_drvdata(&op->dev, p);220	d7s_device = p;221	err = 0;222	of_node_put(opts);223 224out:225	return err;226 227out_iounmap:228	of_iounmap(&op->resource[0], p->regs, sizeof(u8));229	goto out;230}231 232static void d7s_remove(struct platform_device *op)233{234	struct d7s *p = dev_get_drvdata(&op->dev);235	u8 regs = readb(p->regs);236 237	/* Honor OBP d7s-flipped? unless operating in solaris-compat mode */238	if (sol_compat) {239		if (p->flipped)240			regs |= D7S_FLIP;241		else242			regs &= ~D7S_FLIP;243		writeb(regs, p->regs);244	}245 246	misc_deregister(&d7s_miscdev);247	of_iounmap(&op->resource[0], p->regs, sizeof(u8));248}249 250static const struct of_device_id d7s_match[] = {251	{252		.name = "display7seg",253	},254	{},255};256MODULE_DEVICE_TABLE(of, d7s_match);257 258static struct platform_driver d7s_driver = {259	.driver = {260		.name = DRIVER_NAME,261		.of_match_table = d7s_match,262	},263	.probe		= d7s_probe,264	.remove_new	= d7s_remove,265};266 267module_platform_driver(d7s_driver);268