18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * uio_aec.c -- simple driver for Adrienne Electronics Corp time code PCI device
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Brandon Philips <brandon@ifup.org>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/pci.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/cdev.h>
148c2ecf20Sopenharmony_ci#include <linux/fs.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
178c2ecf20Sopenharmony_ci#include <linux/uio_driver.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define PCI_VENDOR_ID_AEC 0xaecb
218c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_AEC_VITCLTC 0x6250
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define INT_ENABLE_ADDR		0xFC
248c2ecf20Sopenharmony_ci#define INT_ENABLE		0x10
258c2ecf20Sopenharmony_ci#define INT_DISABLE		0x0
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define INT_MASK_ADDR		0x2E
288c2ecf20Sopenharmony_ci#define INT_MASK_ALL		0x3F
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define INTA_DRVR_ADDR		0xFE
318c2ecf20Sopenharmony_ci#define INTA_ENABLED_FLAG	0x08
328c2ecf20Sopenharmony_ci#define INTA_FLAG		0x01
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define MAILBOX			0x0F
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic struct pci_device_id ids[] = {
378c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AEC, PCI_DEVICE_ID_AEC_VITCLTC), },
388c2ecf20Sopenharmony_ci	{ 0, }
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ids);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic irqreturn_t aectc_irq(int irq, struct uio_info *dev_info)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	void __iomem *int_flag = dev_info->priv + INTA_DRVR_ADDR;
458c2ecf20Sopenharmony_ci	unsigned char status = ioread8(int_flag);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if ((status & INTA_ENABLED_FLAG) && (status & INTA_FLAG)) {
498c2ecf20Sopenharmony_ci		/* application writes 0x00 to 0x2F to get next interrupt */
508c2ecf20Sopenharmony_ci		status = ioread8(dev_info->priv + MAILBOX);
518c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return IRQ_NONE;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void print_board_data(struct pci_dev *pdev, struct uio_info *i)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "PCI-TC board vendor: %x%x number: %x%x"
608c2ecf20Sopenharmony_ci		" revision: %c%c\n",
618c2ecf20Sopenharmony_ci		ioread8(i->priv + 0x01),
628c2ecf20Sopenharmony_ci		ioread8(i->priv + 0x00),
638c2ecf20Sopenharmony_ci		ioread8(i->priv + 0x03),
648c2ecf20Sopenharmony_ci		ioread8(i->priv + 0x02),
658c2ecf20Sopenharmony_ci		ioread8(i->priv + 0x06),
668c2ecf20Sopenharmony_ci		ioread8(i->priv + 0x07));
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int probe(struct pci_dev *pdev, const struct pci_device_id *id)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct uio_info *info;
728c2ecf20Sopenharmony_ci	int ret;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
758c2ecf20Sopenharmony_ci	if (!info)
768c2ecf20Sopenharmony_ci		return -ENOMEM;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (pci_enable_device(pdev))
798c2ecf20Sopenharmony_ci		goto out_free;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (pci_request_regions(pdev, "aectc"))
828c2ecf20Sopenharmony_ci		goto out_disable;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	info->name = "aectc";
858c2ecf20Sopenharmony_ci	info->port[0].start = pci_resource_start(pdev, 0);
868c2ecf20Sopenharmony_ci	if (!info->port[0].start)
878c2ecf20Sopenharmony_ci		goto out_release;
888c2ecf20Sopenharmony_ci	info->priv = pci_iomap(pdev, 0, 0);
898c2ecf20Sopenharmony_ci	if (!info->priv)
908c2ecf20Sopenharmony_ci		goto out_release;
918c2ecf20Sopenharmony_ci	info->port[0].size = pci_resource_len(pdev, 0);
928c2ecf20Sopenharmony_ci	info->port[0].porttype = UIO_PORT_GPIO;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	info->version = "0.0.1";
958c2ecf20Sopenharmony_ci	info->irq = pdev->irq;
968c2ecf20Sopenharmony_ci	info->irq_flags = IRQF_SHARED;
978c2ecf20Sopenharmony_ci	info->handler = aectc_irq;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	print_board_data(pdev, info);
1008c2ecf20Sopenharmony_ci	ret = uio_register_device(&pdev->dev, info);
1018c2ecf20Sopenharmony_ci	if (ret)
1028c2ecf20Sopenharmony_ci		goto out_unmap;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	iowrite32(INT_ENABLE, info->priv + INT_ENABLE_ADDR);
1058c2ecf20Sopenharmony_ci	iowrite8(INT_MASK_ALL, info->priv + INT_MASK_ADDR);
1068c2ecf20Sopenharmony_ci	if (!(ioread8(info->priv + INTA_DRVR_ADDR)
1078c2ecf20Sopenharmony_ci			& INTA_ENABLED_FLAG))
1088c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "aectc: interrupts not enabled\n");
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, info);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return 0;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ciout_unmap:
1158c2ecf20Sopenharmony_ci	pci_iounmap(pdev, info->priv);
1168c2ecf20Sopenharmony_ciout_release:
1178c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
1188c2ecf20Sopenharmony_ciout_disable:
1198c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
1208c2ecf20Sopenharmony_ciout_free:
1218c2ecf20Sopenharmony_ci	kfree(info);
1228c2ecf20Sopenharmony_ci	return -ENODEV;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void remove(struct pci_dev *pdev)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct uio_info *info = pci_get_drvdata(pdev);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* disable interrupts */
1308c2ecf20Sopenharmony_ci	iowrite8(INT_DISABLE, info->priv + INT_MASK_ADDR);
1318c2ecf20Sopenharmony_ci	iowrite32(INT_DISABLE, info->priv + INT_ENABLE_ADDR);
1328c2ecf20Sopenharmony_ci	/* read mailbox to ensure board drops irq */
1338c2ecf20Sopenharmony_ci	ioread8(info->priv + MAILBOX);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	uio_unregister_device(info);
1368c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
1378c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
1388c2ecf20Sopenharmony_ci	iounmap(info->priv);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	kfree(info);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic struct pci_driver pci_driver = {
1448c2ecf20Sopenharmony_ci	.name = "aectc",
1458c2ecf20Sopenharmony_ci	.id_table = ids,
1468c2ecf20Sopenharmony_ci	.probe = probe,
1478c2ecf20Sopenharmony_ci	.remove = remove,
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cimodule_pci_driver(pci_driver);
1518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
152