xref: /OK3568_Linux_fs/kernel/drivers/uio/uio_aec.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * uio_aec.c -- simple driver for Adrienne Electronics Corp time code PCI device
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2008 Brandon Philips <brandon@ifup.org>
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/kernel.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/pci.h>
11*4882a593Smuzhiyun #include <linux/init.h>
12*4882a593Smuzhiyun #include <linux/interrupt.h>
13*4882a593Smuzhiyun #include <linux/cdev.h>
14*4882a593Smuzhiyun #include <linux/fs.h>
15*4882a593Smuzhiyun #include <linux/io.h>
16*4882a593Smuzhiyun #include <linux/uaccess.h>
17*4882a593Smuzhiyun #include <linux/uio_driver.h>
18*4882a593Smuzhiyun #include <linux/slab.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #define PCI_VENDOR_ID_AEC 0xaecb
21*4882a593Smuzhiyun #define PCI_DEVICE_ID_AEC_VITCLTC 0x6250
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #define INT_ENABLE_ADDR		0xFC
24*4882a593Smuzhiyun #define INT_ENABLE		0x10
25*4882a593Smuzhiyun #define INT_DISABLE		0x0
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #define INT_MASK_ADDR		0x2E
28*4882a593Smuzhiyun #define INT_MASK_ALL		0x3F
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #define INTA_DRVR_ADDR		0xFE
31*4882a593Smuzhiyun #define INTA_ENABLED_FLAG	0x08
32*4882a593Smuzhiyun #define INTA_FLAG		0x01
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun #define MAILBOX			0x0F
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun static struct pci_device_id ids[] = {
37*4882a593Smuzhiyun 	{ PCI_DEVICE(PCI_VENDOR_ID_AEC, PCI_DEVICE_ID_AEC_VITCLTC), },
38*4882a593Smuzhiyun 	{ 0, }
39*4882a593Smuzhiyun };
40*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, ids);
41*4882a593Smuzhiyun 
aectc_irq(int irq,struct uio_info * dev_info)42*4882a593Smuzhiyun static irqreturn_t aectc_irq(int irq, struct uio_info *dev_info)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	void __iomem *int_flag = dev_info->priv + INTA_DRVR_ADDR;
45*4882a593Smuzhiyun 	unsigned char status = ioread8(int_flag);
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun 	if ((status & INTA_ENABLED_FLAG) && (status & INTA_FLAG)) {
49*4882a593Smuzhiyun 		/* application writes 0x00 to 0x2F to get next interrupt */
50*4882a593Smuzhiyun 		status = ioread8(dev_info->priv + MAILBOX);
51*4882a593Smuzhiyun 		return IRQ_HANDLED;
52*4882a593Smuzhiyun 	}
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	return IRQ_NONE;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun 
print_board_data(struct pci_dev * pdev,struct uio_info * i)57*4882a593Smuzhiyun static void print_board_data(struct pci_dev *pdev, struct uio_info *i)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun 	dev_info(&pdev->dev, "PCI-TC board vendor: %x%x number: %x%x"
60*4882a593Smuzhiyun 		" revision: %c%c\n",
61*4882a593Smuzhiyun 		ioread8(i->priv + 0x01),
62*4882a593Smuzhiyun 		ioread8(i->priv + 0x00),
63*4882a593Smuzhiyun 		ioread8(i->priv + 0x03),
64*4882a593Smuzhiyun 		ioread8(i->priv + 0x02),
65*4882a593Smuzhiyun 		ioread8(i->priv + 0x06),
66*4882a593Smuzhiyun 		ioread8(i->priv + 0x07));
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun 
probe(struct pci_dev * pdev,const struct pci_device_id * id)69*4882a593Smuzhiyun static int probe(struct pci_dev *pdev, const struct pci_device_id *id)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun 	struct uio_info *info;
72*4882a593Smuzhiyun 	int ret;
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
75*4882a593Smuzhiyun 	if (!info)
76*4882a593Smuzhiyun 		return -ENOMEM;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	if (pci_enable_device(pdev))
79*4882a593Smuzhiyun 		goto out_free;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	if (pci_request_regions(pdev, "aectc"))
82*4882a593Smuzhiyun 		goto out_disable;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	info->name = "aectc";
85*4882a593Smuzhiyun 	info->port[0].start = pci_resource_start(pdev, 0);
86*4882a593Smuzhiyun 	if (!info->port[0].start)
87*4882a593Smuzhiyun 		goto out_release;
88*4882a593Smuzhiyun 	info->priv = pci_iomap(pdev, 0, 0);
89*4882a593Smuzhiyun 	if (!info->priv)
90*4882a593Smuzhiyun 		goto out_release;
91*4882a593Smuzhiyun 	info->port[0].size = pci_resource_len(pdev, 0);
92*4882a593Smuzhiyun 	info->port[0].porttype = UIO_PORT_GPIO;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	info->version = "0.0.1";
95*4882a593Smuzhiyun 	info->irq = pdev->irq;
96*4882a593Smuzhiyun 	info->irq_flags = IRQF_SHARED;
97*4882a593Smuzhiyun 	info->handler = aectc_irq;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	print_board_data(pdev, info);
100*4882a593Smuzhiyun 	ret = uio_register_device(&pdev->dev, info);
101*4882a593Smuzhiyun 	if (ret)
102*4882a593Smuzhiyun 		goto out_unmap;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	iowrite32(INT_ENABLE, info->priv + INT_ENABLE_ADDR);
105*4882a593Smuzhiyun 	iowrite8(INT_MASK_ALL, info->priv + INT_MASK_ADDR);
106*4882a593Smuzhiyun 	if (!(ioread8(info->priv + INTA_DRVR_ADDR)
107*4882a593Smuzhiyun 			& INTA_ENABLED_FLAG))
108*4882a593Smuzhiyun 		dev_err(&pdev->dev, "aectc: interrupts not enabled\n");
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	pci_set_drvdata(pdev, info);
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	return 0;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun out_unmap:
115*4882a593Smuzhiyun 	pci_iounmap(pdev, info->priv);
116*4882a593Smuzhiyun out_release:
117*4882a593Smuzhiyun 	pci_release_regions(pdev);
118*4882a593Smuzhiyun out_disable:
119*4882a593Smuzhiyun 	pci_disable_device(pdev);
120*4882a593Smuzhiyun out_free:
121*4882a593Smuzhiyun 	kfree(info);
122*4882a593Smuzhiyun 	return -ENODEV;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
remove(struct pci_dev * pdev)125*4882a593Smuzhiyun static void remove(struct pci_dev *pdev)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	struct uio_info *info = pci_get_drvdata(pdev);
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	/* disable interrupts */
130*4882a593Smuzhiyun 	iowrite8(INT_DISABLE, info->priv + INT_MASK_ADDR);
131*4882a593Smuzhiyun 	iowrite32(INT_DISABLE, info->priv + INT_ENABLE_ADDR);
132*4882a593Smuzhiyun 	/* read mailbox to ensure board drops irq */
133*4882a593Smuzhiyun 	ioread8(info->priv + MAILBOX);
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	uio_unregister_device(info);
136*4882a593Smuzhiyun 	pci_release_regions(pdev);
137*4882a593Smuzhiyun 	pci_disable_device(pdev);
138*4882a593Smuzhiyun 	iounmap(info->priv);
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	kfree(info);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun static struct pci_driver pci_driver = {
144*4882a593Smuzhiyun 	.name = "aectc",
145*4882a593Smuzhiyun 	.id_table = ids,
146*4882a593Smuzhiyun 	.probe = probe,
147*4882a593Smuzhiyun 	.remove = remove,
148*4882a593Smuzhiyun };
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun module_pci_driver(pci_driver);
151*4882a593Smuzhiyun MODULE_LICENSE("GPL");
152