11bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips/* 21bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * uio_aec.c -- simple driver for Adrienne Electronics Corp time code PCI device 31bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * 41bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * Copyright (C) 2008 Brandon Philips <brandon@ifup.org> 51bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * 61bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * This program is free software; you can redistribute it and/or modify it 71bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * under the terms of the GNU General Public License version 2 as published 81bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * by the Free Software Foundation. 91bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * 101bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * This program is distributed in the hope that it will be useful, 111bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * but WITHOUT ANY WARRANTY; without even the implied warranty of 121bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 131bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * GNU General Public License for more details. 141bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * 151bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * You should have received a copy of the GNU General Public License along 161bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * with this program; if not, write to the Free Software Foundation, Inc., 59 171bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips * Temple Place, Suite 330, Boston, MA 02111-1307, USA. 181bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips */ 191bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 201bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#include <linux/kernel.h> 211bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#include <linux/module.h> 221bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#include <linux/pci.h> 231bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#include <linux/init.h> 241bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#include <linux/interrupt.h> 251bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#include <linux/cdev.h> 261bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#include <linux/fs.h> 271bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#include <linux/io.h> 281bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#include <linux/uaccess.h> 291bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#include <linux/uio_driver.h> 305a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 311bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 321bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#define PCI_VENDOR_ID_AEC 0xaecb 331bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#define PCI_DEVICE_ID_AEC_VITCLTC 0x6250 341bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 351bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#define INT_ENABLE_ADDR 0xFC 361bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#define INT_ENABLE 0x10 371bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#define INT_DISABLE 0x0 381bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 391bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#define INT_MASK_ADDR 0x2E 401bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#define INT_MASK_ALL 0x3F 411bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 421bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#define INTA_DRVR_ADDR 0xFE 431bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#define INTA_ENABLED_FLAG 0x08 441bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#define INTA_FLAG 0x01 451bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 461bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips#define MAILBOX 0x0F 471bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 481bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philipsstatic struct pci_device_id ids[] = { 491bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips { PCI_DEVICE(PCI_VENDOR_ID_AEC, PCI_DEVICE_ID_AEC_VITCLTC), }, 501bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips { 0, } 511bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips}; 521bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon PhilipsMODULE_DEVICE_TABLE(pci, ids); 531bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 541bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philipsstatic irqreturn_t aectc_irq(int irq, struct uio_info *dev_info) 551bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips{ 561bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips void __iomem *int_flag = dev_info->priv + INTA_DRVR_ADDR; 571bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips unsigned char status = ioread8(int_flag); 581bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 591bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 601bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips if ((status & INTA_ENABLED_FLAG) && (status & INTA_FLAG)) { 611bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips /* application writes 0x00 to 0x2F to get next interrupt */ 621bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips status = ioread8(dev_info->priv + MAILBOX); 631bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips return IRQ_HANDLED; 641bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips } 651bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 661bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips return IRQ_NONE; 671bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips} 681bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 691bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philipsstatic void print_board_data(struct pci_dev *pdev, struct uio_info *i) 701bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips{ 711bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips dev_info(&pdev->dev, "PCI-TC board vendor: %x%x number: %x%x" 721bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips " revision: %c%c\n", 731bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips ioread8(i->priv + 0x01), 741bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips ioread8(i->priv + 0x00), 751bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips ioread8(i->priv + 0x03), 761bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips ioread8(i->priv + 0x02), 771bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips ioread8(i->priv + 0x06), 781bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips ioread8(i->priv + 0x07)); 791bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips} 801bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 81b17b75bb524c6c0dfa5ee4a33591b8a7dcc034d2Bill Pembertonstatic int probe(struct pci_dev *pdev, const struct pci_device_id *id) 821bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips{ 831bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips struct uio_info *info; 841bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips int ret; 851bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 861bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); 871bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips if (!info) 881bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips return -ENOMEM; 891bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 901bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips if (pci_enable_device(pdev)) 911bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips goto out_free; 921bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 931bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips if (pci_request_regions(pdev, "aectc")) 941bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips goto out_disable; 951bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 961bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips info->name = "aectc"; 971bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips info->port[0].start = pci_resource_start(pdev, 0); 981bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips if (!info->port[0].start) 991bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips goto out_release; 1001bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips info->priv = pci_iomap(pdev, 0, 0); 1011bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips if (!info->priv) 1021bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips goto out_release; 1031bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips info->port[0].size = pci_resource_len(pdev, 0); 1041bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips info->port[0].porttype = UIO_PORT_GPIO; 1051bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 1061bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips info->version = "0.0.1"; 1071bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips info->irq = pdev->irq; 1081bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips info->irq_flags = IRQF_SHARED; 1091bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips info->handler = aectc_irq; 1101bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 1111bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips print_board_data(pdev, info); 1121bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips ret = uio_register_device(&pdev->dev, info); 1131bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips if (ret) 1141bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips goto out_unmap; 1151bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 1161bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips iowrite32(INT_ENABLE, info->priv + INT_ENABLE_ADDR); 1171bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips iowrite8(INT_MASK_ALL, info->priv + INT_MASK_ADDR); 1181bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips if (!(ioread8(info->priv + INTA_DRVR_ADDR) 1191bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips & INTA_ENABLED_FLAG)) 1201bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips dev_err(&pdev->dev, "aectc: interrupts not enabled\n"); 1211bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 1221bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips pci_set_drvdata(pdev, info); 1231bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 1241bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips return 0; 1251bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 1261bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philipsout_unmap: 1271bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips pci_iounmap(pdev, info->priv); 1281bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philipsout_release: 1291bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips pci_release_regions(pdev); 1301bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philipsout_disable: 1311bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips pci_disable_device(pdev); 1321bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philipsout_free: 1331bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips kfree(info); 1341bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips return -ENODEV; 1351bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips} 1361bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 1371bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philipsstatic void remove(struct pci_dev *pdev) 1381bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips{ 1391bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips struct uio_info *info = pci_get_drvdata(pdev); 1401bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 1411bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips /* disable interrupts */ 1421bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips iowrite8(INT_DISABLE, info->priv + INT_MASK_ADDR); 1431bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips iowrite32(INT_DISABLE, info->priv + INT_ENABLE_ADDR); 1441bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips /* read mailbox to ensure board drops irq */ 1451bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips ioread8(info->priv + MAILBOX); 1461bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 1471bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips uio_unregister_device(info); 1481bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips pci_release_regions(pdev); 1491bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips pci_disable_device(pdev); 1501bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips iounmap(info->priv); 1511bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 1521bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips kfree(info); 1531bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips} 1541bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 1551bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philipsstatic struct pci_driver pci_driver = { 1561bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips .name = "aectc", 1571bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips .id_table = ids, 1581bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips .probe = probe, 1591bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips .remove = remove, 1601bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips}; 1611bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon Philips 162aa8c06f7c9471f5722faa14ec8b887989de1226dPeter Huewemodule_pci_driver(pci_driver); 1631bafeb378e915f39b1bf44ee0871823d6f402ea5Brandon PhilipsMODULE_LICENSE("GPL"); 164