xref: /openbmc/linux/arch/x86/platform/scx200/scx200_32.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*09c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23b3da9d2SThomas Gleixner /*
33b3da9d2SThomas Gleixner  *  Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
43b3da9d2SThomas Gleixner  *
53b3da9d2SThomas Gleixner  *  National Semiconductor SCx200 support.
63b3da9d2SThomas Gleixner  */
73b3da9d2SThomas Gleixner 
83b3da9d2SThomas Gleixner #include <linux/module.h>
93b3da9d2SThomas Gleixner #include <linux/errno.h>
103b3da9d2SThomas Gleixner #include <linux/kernel.h>
113b3da9d2SThomas Gleixner #include <linux/init.h>
123b3da9d2SThomas Gleixner #include <linux/mutex.h>
133b3da9d2SThomas Gleixner #include <linux/pci.h>
143b3da9d2SThomas Gleixner 
153b3da9d2SThomas Gleixner #include <linux/scx200.h>
163b3da9d2SThomas Gleixner #include <linux/scx200_gpio.h>
173b3da9d2SThomas Gleixner 
183b3da9d2SThomas Gleixner /* Verify that the configuration block really is there */
193b3da9d2SThomas Gleixner #define scx200_cb_probe(base) (inw((base) + SCx200_CBA) == (base))
203b3da9d2SThomas Gleixner 
213b3da9d2SThomas Gleixner MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
223b3da9d2SThomas Gleixner MODULE_DESCRIPTION("NatSemi SCx200 Driver");
233b3da9d2SThomas Gleixner MODULE_LICENSE("GPL");
243b3da9d2SThomas Gleixner 
253b3da9d2SThomas Gleixner unsigned scx200_gpio_base = 0;
263b3da9d2SThomas Gleixner unsigned long scx200_gpio_shadow[2];
273b3da9d2SThomas Gleixner 
283b3da9d2SThomas Gleixner unsigned scx200_cb_base = 0;
293b3da9d2SThomas Gleixner 
303b3da9d2SThomas Gleixner static struct pci_device_id scx200_tbl[] = {
310ac25260SJim Cromie 	{ PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) },
320ac25260SJim Cromie 	{ PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) },
330ac25260SJim Cromie 	{ PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_SCx200_XBUS)   },
340ac25260SJim Cromie 	{ PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_SC1100_XBUS)   },
353b3da9d2SThomas Gleixner 	{ },
363b3da9d2SThomas Gleixner };
373b3da9d2SThomas Gleixner MODULE_DEVICE_TABLE(pci,scx200_tbl);
383b3da9d2SThomas Gleixner 
39a18e3690SGreg Kroah-Hartman static int scx200_probe(struct pci_dev *, const struct pci_device_id *);
403b3da9d2SThomas Gleixner 
413b3da9d2SThomas Gleixner static struct pci_driver scx200_pci_driver = {
423b3da9d2SThomas Gleixner 	.name = "scx200",
433b3da9d2SThomas Gleixner 	.id_table = scx200_tbl,
443b3da9d2SThomas Gleixner 	.probe = scx200_probe,
453b3da9d2SThomas Gleixner };
463b3da9d2SThomas Gleixner 
473b3da9d2SThomas Gleixner static DEFINE_MUTEX(scx200_gpio_config_lock);
483b3da9d2SThomas Gleixner 
scx200_init_shadow(void)49a18e3690SGreg Kroah-Hartman static void scx200_init_shadow(void)
503b3da9d2SThomas Gleixner {
513b3da9d2SThomas Gleixner 	int bank;
523b3da9d2SThomas Gleixner 
533b3da9d2SThomas Gleixner 	/* read the current values driven on the GPIO signals */
543b3da9d2SThomas Gleixner 	for (bank = 0; bank < 2; ++bank)
553b3da9d2SThomas Gleixner 		scx200_gpio_shadow[bank] = inl(scx200_gpio_base + 0x10 * bank);
563b3da9d2SThomas Gleixner }
573b3da9d2SThomas Gleixner 
scx200_probe(struct pci_dev * pdev,const struct pci_device_id * ent)58a18e3690SGreg Kroah-Hartman static int scx200_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
593b3da9d2SThomas Gleixner {
603b3da9d2SThomas Gleixner 	unsigned base;
613b3da9d2SThomas Gleixner 
623b3da9d2SThomas Gleixner 	if (pdev->device == PCI_DEVICE_ID_NS_SCx200_BRIDGE ||
633b3da9d2SThomas Gleixner 	    pdev->device == PCI_DEVICE_ID_NS_SC1100_BRIDGE) {
643b3da9d2SThomas Gleixner 		base = pci_resource_start(pdev, 0);
658ad95f09SJim Cromie 		pr_info("GPIO base 0x%x\n", base);
663b3da9d2SThomas Gleixner 
678ad95f09SJim Cromie 		if (!request_region(base, SCx200_GPIO_SIZE,
688ad95f09SJim Cromie 				    "NatSemi SCx200 GPIO")) {
698ad95f09SJim Cromie 			pr_err("can't allocate I/O for GPIOs\n");
703b3da9d2SThomas Gleixner 			return -EBUSY;
713b3da9d2SThomas Gleixner 		}
723b3da9d2SThomas Gleixner 
733b3da9d2SThomas Gleixner 		scx200_gpio_base = base;
743b3da9d2SThomas Gleixner 		scx200_init_shadow();
753b3da9d2SThomas Gleixner 
763b3da9d2SThomas Gleixner 	} else {
773b3da9d2SThomas Gleixner 		/* find the base of the Configuration Block */
783b3da9d2SThomas Gleixner 		if (scx200_cb_probe(SCx200_CB_BASE_FIXED)) {
793b3da9d2SThomas Gleixner 			scx200_cb_base = SCx200_CB_BASE_FIXED;
803b3da9d2SThomas Gleixner 		} else {
813b3da9d2SThomas Gleixner 			pci_read_config_dword(pdev, SCx200_CBA_SCRATCH, &base);
823b3da9d2SThomas Gleixner 			if (scx200_cb_probe(base)) {
833b3da9d2SThomas Gleixner 				scx200_cb_base = base;
843b3da9d2SThomas Gleixner 			} else {
858ad95f09SJim Cromie 				pr_warn("Configuration Block not found\n");
863b3da9d2SThomas Gleixner 				return -ENODEV;
873b3da9d2SThomas Gleixner 			}
883b3da9d2SThomas Gleixner 		}
898ad95f09SJim Cromie 		pr_info("Configuration Block base 0x%x\n", scx200_cb_base);
903b3da9d2SThomas Gleixner 	}
913b3da9d2SThomas Gleixner 
923b3da9d2SThomas Gleixner 	return 0;
933b3da9d2SThomas Gleixner }
943b3da9d2SThomas Gleixner 
scx200_gpio_configure(unsigned index,u32 mask,u32 bits)953b3da9d2SThomas Gleixner u32 scx200_gpio_configure(unsigned index, u32 mask, u32 bits)
963b3da9d2SThomas Gleixner {
973b3da9d2SThomas Gleixner 	u32 config, new_config;
983b3da9d2SThomas Gleixner 
993b3da9d2SThomas Gleixner 	mutex_lock(&scx200_gpio_config_lock);
1003b3da9d2SThomas Gleixner 
1013b3da9d2SThomas Gleixner 	outl(index, scx200_gpio_base + 0x20);
1023b3da9d2SThomas Gleixner 	config = inl(scx200_gpio_base + 0x24);
1033b3da9d2SThomas Gleixner 
1043b3da9d2SThomas Gleixner 	new_config = (config & mask) | bits;
1053b3da9d2SThomas Gleixner 	outl(new_config, scx200_gpio_base + 0x24);
1063b3da9d2SThomas Gleixner 
1073b3da9d2SThomas Gleixner 	mutex_unlock(&scx200_gpio_config_lock);
1083b3da9d2SThomas Gleixner 
1093b3da9d2SThomas Gleixner 	return config;
1103b3da9d2SThomas Gleixner }
1113b3da9d2SThomas Gleixner 
scx200_init(void)1123b3da9d2SThomas Gleixner static int __init scx200_init(void)
1133b3da9d2SThomas Gleixner {
1148ad95f09SJim Cromie 	pr_info("NatSemi SCx200 Driver\n");
1153b3da9d2SThomas Gleixner 	return pci_register_driver(&scx200_pci_driver);
1163b3da9d2SThomas Gleixner }
1173b3da9d2SThomas Gleixner 
scx200_cleanup(void)1183b3da9d2SThomas Gleixner static void __exit scx200_cleanup(void)
1193b3da9d2SThomas Gleixner {
1203b3da9d2SThomas Gleixner 	pci_unregister_driver(&scx200_pci_driver);
1213b3da9d2SThomas Gleixner 	release_region(scx200_gpio_base, SCx200_GPIO_SIZE);
1223b3da9d2SThomas Gleixner }
1233b3da9d2SThomas Gleixner 
1243b3da9d2SThomas Gleixner module_init(scx200_init);
1253b3da9d2SThomas Gleixner module_exit(scx200_cleanup);
1263b3da9d2SThomas Gleixner 
1273b3da9d2SThomas Gleixner EXPORT_SYMBOL(scx200_gpio_base);
1283b3da9d2SThomas Gleixner EXPORT_SYMBOL(scx200_gpio_shadow);
1293b3da9d2SThomas Gleixner EXPORT_SYMBOL(scx200_gpio_configure);
1303b3da9d2SThomas Gleixner EXPORT_SYMBOL(scx200_cb_base);
131