xref: /openbmc/linux/drivers/uio/uio_mf624.c (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
206849faaSRostislav Lisovy /*
306849faaSRostislav Lisovy  * UIO driver fo Humusoft MF624 DAQ card.
406849faaSRostislav Lisovy  * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>,
506849faaSRostislav Lisovy  *                    Czech Technical University in Prague
606849faaSRostislav Lisovy  */
706849faaSRostislav Lisovy 
806849faaSRostislav Lisovy #include <linux/init.h>
906849faaSRostislav Lisovy #include <linux/module.h>
1006849faaSRostislav Lisovy #include <linux/device.h>
1106849faaSRostislav Lisovy #include <linux/pci.h>
1206849faaSRostislav Lisovy #include <linux/slab.h>
1306849faaSRostislav Lisovy #include <linux/io.h>
1406849faaSRostislav Lisovy #include <linux/kernel.h>
1506849faaSRostislav Lisovy #include <linux/uio_driver.h>
1606849faaSRostislav Lisovy 
1706849faaSRostislav Lisovy #define PCI_VENDOR_ID_HUMUSOFT		0x186c
1806849faaSRostislav Lisovy #define PCI_DEVICE_ID_MF624		0x0624
1906849faaSRostislav Lisovy #define PCI_SUBVENDOR_ID_HUMUSOFT	0x186c
2006849faaSRostislav Lisovy #define PCI_SUBDEVICE_DEVICE		0x0624
2106849faaSRostislav Lisovy 
2206849faaSRostislav Lisovy /* BAR0 Interrupt control/status register */
2306849faaSRostislav Lisovy #define INTCSR				0x4C
2406849faaSRostislav Lisovy #define INTCSR_ADINT_ENABLE		(1 << 0)
2506849faaSRostislav Lisovy #define INTCSR_CTR4INT_ENABLE		(1 << 3)
2606849faaSRostislav Lisovy #define INTCSR_PCIINT_ENABLE		(1 << 6)
2706849faaSRostislav Lisovy #define INTCSR_ADINT_STATUS		(1 << 2)
2806849faaSRostislav Lisovy #define INTCSR_CTR4INT_STATUS		(1 << 5)
2906849faaSRostislav Lisovy 
3006849faaSRostislav Lisovy enum mf624_interrupt_source {ADC, CTR4, ALL};
3106849faaSRostislav Lisovy 
mf624_disable_interrupt(enum mf624_interrupt_source source,struct uio_info * info)32497b46dbSFengguang Wu static void mf624_disable_interrupt(enum mf624_interrupt_source source,
3306849faaSRostislav Lisovy 			     struct uio_info *info)
3406849faaSRostislav Lisovy {
3506849faaSRostislav Lisovy 	void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
3606849faaSRostislav Lisovy 
3706849faaSRostislav Lisovy 	switch (source) {
3806849faaSRostislav Lisovy 	case ADC:
3906849faaSRostislav Lisovy 		iowrite32(ioread32(INTCSR_reg)
4006849faaSRostislav Lisovy 			& ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE),
4106849faaSRostislav Lisovy 			INTCSR_reg);
4206849faaSRostislav Lisovy 		break;
4306849faaSRostislav Lisovy 
4406849faaSRostislav Lisovy 	case CTR4:
4506849faaSRostislav Lisovy 		iowrite32(ioread32(INTCSR_reg)
4606849faaSRostislav Lisovy 			& ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE),
4706849faaSRostislav Lisovy 			INTCSR_reg);
4806849faaSRostislav Lisovy 		break;
4906849faaSRostislav Lisovy 
5006849faaSRostislav Lisovy 	case ALL:
5106849faaSRostislav Lisovy 	default:
5206849faaSRostislav Lisovy 		iowrite32(ioread32(INTCSR_reg)
5306849faaSRostislav Lisovy 			& ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
5406849faaSRostislav Lisovy 			    | INTCSR_PCIINT_ENABLE),
5506849faaSRostislav Lisovy 			INTCSR_reg);
5606849faaSRostislav Lisovy 		break;
5706849faaSRostislav Lisovy 	}
5806849faaSRostislav Lisovy }
5906849faaSRostislav Lisovy 
mf624_enable_interrupt(enum mf624_interrupt_source source,struct uio_info * info)60497b46dbSFengguang Wu static void mf624_enable_interrupt(enum mf624_interrupt_source source,
6106849faaSRostislav Lisovy 			    struct uio_info *info)
6206849faaSRostislav Lisovy {
6306849faaSRostislav Lisovy 	void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
6406849faaSRostislav Lisovy 
6506849faaSRostislav Lisovy 	switch (source) {
6606849faaSRostislav Lisovy 	case ADC:
6706849faaSRostislav Lisovy 		iowrite32(ioread32(INTCSR_reg)
6806849faaSRostislav Lisovy 			| INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE,
6906849faaSRostislav Lisovy 			INTCSR_reg);
7006849faaSRostislav Lisovy 		break;
7106849faaSRostislav Lisovy 
7206849faaSRostislav Lisovy 	case CTR4:
7306849faaSRostislav Lisovy 		iowrite32(ioread32(INTCSR_reg)
7406849faaSRostislav Lisovy 			| INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE,
7506849faaSRostislav Lisovy 			INTCSR_reg);
7606849faaSRostislav Lisovy 		break;
7706849faaSRostislav Lisovy 
7806849faaSRostislav Lisovy 	case ALL:
7906849faaSRostislav Lisovy 	default:
8006849faaSRostislav Lisovy 		iowrite32(ioread32(INTCSR_reg)
8106849faaSRostislav Lisovy 			| INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
8206849faaSRostislav Lisovy 			| INTCSR_PCIINT_ENABLE,
8306849faaSRostislav Lisovy 			INTCSR_reg);
8406849faaSRostislav Lisovy 		break;
8506849faaSRostislav Lisovy 	}
8606849faaSRostislav Lisovy }
8706849faaSRostislav Lisovy 
mf624_irq_handler(int irq,struct uio_info * info)8806849faaSRostislav Lisovy static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info)
8906849faaSRostislav Lisovy {
9006849faaSRostislav Lisovy 	void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
9106849faaSRostislav Lisovy 
9206849faaSRostislav Lisovy 	if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE)
9306849faaSRostislav Lisovy 	    && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) {
9406849faaSRostislav Lisovy 		mf624_disable_interrupt(ADC, info);
9506849faaSRostislav Lisovy 		return IRQ_HANDLED;
9606849faaSRostislav Lisovy 	}
9706849faaSRostislav Lisovy 
9806849faaSRostislav Lisovy 	if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE)
9906849faaSRostislav Lisovy 	    && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) {
10006849faaSRostislav Lisovy 		mf624_disable_interrupt(CTR4, info);
10106849faaSRostislav Lisovy 		return IRQ_HANDLED;
10206849faaSRostislav Lisovy 	}
10306849faaSRostislav Lisovy 
10406849faaSRostislav Lisovy 	return IRQ_NONE;
10506849faaSRostislav Lisovy }
10606849faaSRostislav Lisovy 
mf624_irqcontrol(struct uio_info * info,s32 irq_on)10706849faaSRostislav Lisovy static int mf624_irqcontrol(struct uio_info *info, s32 irq_on)
10806849faaSRostislav Lisovy {
10906849faaSRostislav Lisovy 	if (irq_on == 0)
11006849faaSRostislav Lisovy 		mf624_disable_interrupt(ALL, info);
11106849faaSRostislav Lisovy 	else if (irq_on == 1)
11206849faaSRostislav Lisovy 		mf624_enable_interrupt(ALL, info);
11306849faaSRostislav Lisovy 
11406849faaSRostislav Lisovy 	return 0;
11506849faaSRostislav Lisovy }
11606849faaSRostislav Lisovy 
mf624_setup_mem(struct pci_dev * dev,int bar,struct uio_mem * mem,const char * name)117a15d2ddbSMichal Sojka static int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name)
118a15d2ddbSMichal Sojka {
119270579d9SMichal Sojka 	resource_size_t start = pci_resource_start(dev, bar);
120270579d9SMichal Sojka 	resource_size_t len = pci_resource_len(dev, bar);
121270579d9SMichal Sojka 
122a15d2ddbSMichal Sojka 	mem->name = name;
123270579d9SMichal Sojka 	mem->addr = start & PAGE_MASK;
124270579d9SMichal Sojka 	mem->offs = start & ~PAGE_MASK;
125a15d2ddbSMichal Sojka 	if (!mem->addr)
126a15d2ddbSMichal Sojka 		return -ENODEV;
127270579d9SMichal Sojka 	mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK;
128a15d2ddbSMichal Sojka 	mem->memtype = UIO_MEM_PHYS;
129a15d2ddbSMichal Sojka 	mem->internal_addr = pci_ioremap_bar(dev, bar);
130a15d2ddbSMichal Sojka 	if (!mem->internal_addr)
131a15d2ddbSMichal Sojka 		return -ENODEV;
132a15d2ddbSMichal Sojka 	return 0;
133a15d2ddbSMichal Sojka }
134a15d2ddbSMichal Sojka 
mf624_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)13506849faaSRostislav Lisovy static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
13606849faaSRostislav Lisovy {
13706849faaSRostislav Lisovy 	struct uio_info *info;
13806849faaSRostislav Lisovy 
139*6b76c98bSAlexandru Ardelean 	info = devm_kzalloc(&dev->dev, sizeof(struct uio_info), GFP_KERNEL);
14006849faaSRostislav Lisovy 	if (!info)
14106849faaSRostislav Lisovy 		return -ENOMEM;
14206849faaSRostislav Lisovy 
14306849faaSRostislav Lisovy 	if (pci_enable_device(dev))
144*6b76c98bSAlexandru Ardelean 		return -ENODEV;
14506849faaSRostislav Lisovy 
14606849faaSRostislav Lisovy 	if (pci_request_regions(dev, "mf624"))
14706849faaSRostislav Lisovy 		goto out_disable;
14806849faaSRostislav Lisovy 
14906849faaSRostislav Lisovy 	info->name = "mf624";
15006849faaSRostislav Lisovy 	info->version = "0.0.1";
15106849faaSRostislav Lisovy 
15206849faaSRostislav Lisovy 	/* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */
15306849faaSRostislav Lisovy 
15406849faaSRostislav Lisovy 	/* BAR0 */
155a15d2ddbSMichal Sojka 	if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status "
156a15d2ddbSMichal Sojka 			    "bits, special functions"))
15706849faaSRostislav Lisovy 		goto out_release;
15806849faaSRostislav Lisovy 	/* BAR2 */
159a15d2ddbSMichal Sojka 	if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO"))
16006849faaSRostislav Lisovy 		goto out_unmap0;
16106849faaSRostislav Lisovy 
16206849faaSRostislav Lisovy 	/* BAR4 */
163a15d2ddbSMichal Sojka 	if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip"))
16406849faaSRostislav Lisovy 		goto out_unmap1;
16506849faaSRostislav Lisovy 
16606849faaSRostislav Lisovy 	info->irq = dev->irq;
16706849faaSRostislav Lisovy 	info->irq_flags = IRQF_SHARED;
16806849faaSRostislav Lisovy 	info->handler = mf624_irq_handler;
16906849faaSRostislav Lisovy 
17006849faaSRostislav Lisovy 	info->irqcontrol = mf624_irqcontrol;
17106849faaSRostislav Lisovy 
17206849faaSRostislav Lisovy 	if (uio_register_device(&dev->dev, info))
17306849faaSRostislav Lisovy 		goto out_unmap2;
17406849faaSRostislav Lisovy 
17506849faaSRostislav Lisovy 	pci_set_drvdata(dev, info);
17606849faaSRostislav Lisovy 
17706849faaSRostislav Lisovy 	return 0;
17806849faaSRostislav Lisovy 
17906849faaSRostislav Lisovy out_unmap2:
18006849faaSRostislav Lisovy 	iounmap(info->mem[2].internal_addr);
18106849faaSRostislav Lisovy out_unmap1:
18206849faaSRostislav Lisovy 	iounmap(info->mem[1].internal_addr);
18306849faaSRostislav Lisovy out_unmap0:
18406849faaSRostislav Lisovy 	iounmap(info->mem[0].internal_addr);
18506849faaSRostislav Lisovy 
18606849faaSRostislav Lisovy out_release:
18706849faaSRostislav Lisovy 	pci_release_regions(dev);
18806849faaSRostislav Lisovy 
18906849faaSRostislav Lisovy out_disable:
19006849faaSRostislav Lisovy 	pci_disable_device(dev);
19106849faaSRostislav Lisovy 
19206849faaSRostislav Lisovy 	return -ENODEV;
19306849faaSRostislav Lisovy }
19406849faaSRostislav Lisovy 
mf624_pci_remove(struct pci_dev * dev)19506849faaSRostislav Lisovy static void mf624_pci_remove(struct pci_dev *dev)
19606849faaSRostislav Lisovy {
19706849faaSRostislav Lisovy 	struct uio_info *info = pci_get_drvdata(dev);
19806849faaSRostislav Lisovy 
19906849faaSRostislav Lisovy 	mf624_disable_interrupt(ALL, info);
20006849faaSRostislav Lisovy 
20106849faaSRostislav Lisovy 	uio_unregister_device(info);
20206849faaSRostislav Lisovy 	pci_release_regions(dev);
20306849faaSRostislav Lisovy 	pci_disable_device(dev);
20406849faaSRostislav Lisovy 
20506849faaSRostislav Lisovy 	iounmap(info->mem[0].internal_addr);
20606849faaSRostislav Lisovy 	iounmap(info->mem[1].internal_addr);
20706849faaSRostislav Lisovy 	iounmap(info->mem[2].internal_addr);
20806849faaSRostislav Lisovy }
20906849faaSRostislav Lisovy 
2100f4054dfSJingoo Han static const struct pci_device_id mf624_pci_id[] = {
21106849faaSRostislav Lisovy 	{ PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
21206849faaSRostislav Lisovy 	{ 0, }
21306849faaSRostislav Lisovy };
21406849faaSRostislav Lisovy 
21506849faaSRostislav Lisovy static struct pci_driver mf624_pci_driver = {
21606849faaSRostislav Lisovy 	.name = "mf624",
21706849faaSRostislav Lisovy 	.id_table = mf624_pci_id,
21806849faaSRostislav Lisovy 	.probe = mf624_pci_probe,
21906849faaSRostislav Lisovy 	.remove = mf624_pci_remove,
22006849faaSRostislav Lisovy };
22106849faaSRostislav Lisovy MODULE_DEVICE_TABLE(pci, mf624_pci_id);
22206849faaSRostislav Lisovy 
22306849faaSRostislav Lisovy module_pci_driver(mf624_pci_driver);
22406849faaSRostislav Lisovy MODULE_LICENSE("GPL v2");
22506849faaSRostislav Lisovy MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
226