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