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