162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sim710.c - Copyright (C) 1999 Richard Hirst <richard@sleepie.demon.co.uk> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci *---------------------------------------------------------------------------- 662306a36Sopenharmony_ci *---------------------------------------------------------------------------- 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * MCA card detection code by Trent McNair. (now deleted) 962306a36Sopenharmony_ci * Fixes to not explicitly nul bss data from Xavier Bestel. 1062306a36Sopenharmony_ci * Some multiboard fixes from Rolf Eike Beer. 1162306a36Sopenharmony_ci * Auto probing of EISA config space from Trevor Hemsley. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Rewritten to use 53c700.c by James.Bottomley@SteelEye.com 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/blkdev.h> 2062306a36Sopenharmony_ci#include <linux/device.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/eisa.h> 2362306a36Sopenharmony_ci#include <linux/interrupt.h> 2462306a36Sopenharmony_ci#include <scsi/scsi_host.h> 2562306a36Sopenharmony_ci#include <scsi/scsi_device.h> 2662306a36Sopenharmony_ci#include <scsi/scsi_transport.h> 2762306a36Sopenharmony_ci#include <scsi/scsi_transport_spi.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "53c700.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Must be enough for EISA */ 3362306a36Sopenharmony_ci#define MAX_SLOTS 8 3462306a36Sopenharmony_cistatic __u8 __initdata id_array[MAX_SLOTS] = { [0 ... MAX_SLOTS-1] = 7 }; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic char *sim710; /* command line passed by insmod */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciMODULE_AUTHOR("Richard Hirst"); 3962306a36Sopenharmony_ciMODULE_DESCRIPTION("Simple NCR53C710 driver"); 4062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cimodule_param(sim710, charp, 0); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#ifdef MODULE 4562306a36Sopenharmony_ci#define ARG_SEP ' ' 4662306a36Sopenharmony_ci#else 4762306a36Sopenharmony_ci#define ARG_SEP ',' 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic __init int 5162306a36Sopenharmony_ciparam_setup(char *str) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci char *pos = str, *next; 5462306a36Sopenharmony_ci int slot = -1; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci while(pos != NULL && (next = strchr(pos, ':')) != NULL) { 5762306a36Sopenharmony_ci int val = (int)simple_strtoul(++next, NULL, 0); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if(!strncmp(pos, "slot:", 5)) 6062306a36Sopenharmony_ci slot = val; 6162306a36Sopenharmony_ci else if(!strncmp(pos, "id:", 3)) { 6262306a36Sopenharmony_ci if(slot == -1) { 6362306a36Sopenharmony_ci printk(KERN_WARNING "sim710: Must specify slot for id parameter\n"); 6462306a36Sopenharmony_ci } else if(slot >= MAX_SLOTS) { 6562306a36Sopenharmony_ci printk(KERN_WARNING "sim710: Illegal slot %d for id %d\n", slot, val); 6662306a36Sopenharmony_ci } else { 6762306a36Sopenharmony_ci id_array[slot] = val; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci if((pos = strchr(pos, ARG_SEP)) != NULL) 7162306a36Sopenharmony_ci pos++; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci return 1; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci__setup("sim710=", param_setup); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct scsi_host_template sim710_driver_template = { 7862306a36Sopenharmony_ci .name = "LSI (Symbios) 710 EISA", 7962306a36Sopenharmony_ci .proc_name = "sim710", 8062306a36Sopenharmony_ci .this_id = 7, 8162306a36Sopenharmony_ci .module = THIS_MODULE, 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int sim710_probe_common(struct device *dev, unsigned long base_addr, 8562306a36Sopenharmony_ci int irq, int clock, int differential, 8662306a36Sopenharmony_ci int scsi_id) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct Scsi_Host * host = NULL; 8962306a36Sopenharmony_ci struct NCR_700_Host_Parameters *hostdata = 9062306a36Sopenharmony_ci kzalloc(sizeof(struct NCR_700_Host_Parameters), GFP_KERNEL); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci printk(KERN_NOTICE "sim710: %s\n", dev_name(dev)); 9362306a36Sopenharmony_ci printk(KERN_NOTICE "sim710: irq = %d, clock = %d, base = 0x%lx, scsi_id = %d\n", 9462306a36Sopenharmony_ci irq, clock, base_addr, scsi_id); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if(hostdata == NULL) { 9762306a36Sopenharmony_ci printk(KERN_ERR "sim710: Failed to allocate host data\n"); 9862306a36Sopenharmony_ci goto out; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if(request_region(base_addr, 64, "sim710") == NULL) { 10262306a36Sopenharmony_ci printk(KERN_ERR "sim710: Failed to reserve IO region 0x%lx\n", 10362306a36Sopenharmony_ci base_addr); 10462306a36Sopenharmony_ci goto out_free; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Fill in the three required pieces of hostdata */ 10862306a36Sopenharmony_ci hostdata->base = ioport_map(base_addr, 64); 10962306a36Sopenharmony_ci hostdata->differential = differential; 11062306a36Sopenharmony_ci hostdata->clock = clock; 11162306a36Sopenharmony_ci hostdata->chip710 = 1; 11262306a36Sopenharmony_ci hostdata->burst_length = 8; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* and register the chip */ 11562306a36Sopenharmony_ci if((host = NCR_700_detect(&sim710_driver_template, hostdata, dev)) 11662306a36Sopenharmony_ci == NULL) { 11762306a36Sopenharmony_ci printk(KERN_ERR "sim710: No host detected; card configuration problem?\n"); 11862306a36Sopenharmony_ci goto out_release; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci host->this_id = scsi_id; 12162306a36Sopenharmony_ci host->base = base_addr; 12262306a36Sopenharmony_ci host->irq = irq; 12362306a36Sopenharmony_ci if (request_irq(irq, NCR_700_intr, IRQF_SHARED, "sim710", host)) { 12462306a36Sopenharmony_ci printk(KERN_ERR "sim710: request_irq failed\n"); 12562306a36Sopenharmony_ci goto out_put_host; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci dev_set_drvdata(dev, host); 12962306a36Sopenharmony_ci scsi_scan_host(host); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci out_put_host: 13462306a36Sopenharmony_ci scsi_host_put(host); 13562306a36Sopenharmony_ci out_release: 13662306a36Sopenharmony_ci release_region(base_addr, 64); 13762306a36Sopenharmony_ci out_free: 13862306a36Sopenharmony_ci kfree(hostdata); 13962306a36Sopenharmony_ci out: 14062306a36Sopenharmony_ci return -ENODEV; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int sim710_device_remove(struct device *dev) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct Scsi_Host *host = dev_get_drvdata(dev); 14662306a36Sopenharmony_ci struct NCR_700_Host_Parameters *hostdata = 14762306a36Sopenharmony_ci (struct NCR_700_Host_Parameters *)host->hostdata[0]; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci scsi_remove_host(host); 15062306a36Sopenharmony_ci NCR_700_release(host); 15162306a36Sopenharmony_ci kfree(hostdata); 15262306a36Sopenharmony_ci free_irq(host->irq, host); 15362306a36Sopenharmony_ci release_region(host->base, 64); 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci#ifdef CONFIG_EISA 15862306a36Sopenharmony_cistatic struct eisa_device_id sim710_eisa_ids[] = { 15962306a36Sopenharmony_ci { "CPQ4410" }, 16062306a36Sopenharmony_ci { "CPQ4411" }, 16162306a36Sopenharmony_ci { "HWP0C80" }, 16262306a36Sopenharmony_ci { "" } 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(eisa, sim710_eisa_ids); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int sim710_eisa_probe(struct device *dev) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct eisa_device *edev = to_eisa_device(dev); 16962306a36Sopenharmony_ci unsigned long io_addr = edev->base_addr; 17062306a36Sopenharmony_ci char eisa_cpq_irqs[] = { 11, 14, 15, 10, 9, 0 }; 17162306a36Sopenharmony_ci char eisa_hwp_irqs[] = { 3, 4, 5, 7, 12, 10, 11, 0}; 17262306a36Sopenharmony_ci char *eisa_irqs; 17362306a36Sopenharmony_ci unsigned char irq_index; 17462306a36Sopenharmony_ci unsigned char irq, differential = 0, scsi_id = 7; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if(strcmp(edev->id.sig, "HWP0C80") == 0) { 17762306a36Sopenharmony_ci __u8 val; 17862306a36Sopenharmony_ci eisa_irqs = eisa_hwp_irqs; 17962306a36Sopenharmony_ci irq_index = (inb(io_addr + 0xc85) & 0x7) - 1; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci val = inb(io_addr + 0x4); 18262306a36Sopenharmony_ci scsi_id = ffs(val) - 1; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if(scsi_id > 7 || (val & ~(1<<scsi_id)) != 0) { 18562306a36Sopenharmony_ci printk(KERN_ERR "sim710.c, EISA card %s has incorrect scsi_id, setting to 7\n", dev_name(dev)); 18662306a36Sopenharmony_ci scsi_id = 7; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci } else { 18962306a36Sopenharmony_ci eisa_irqs = eisa_cpq_irqs; 19062306a36Sopenharmony_ci irq_index = inb(io_addr + 0xc88) & 0x07; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if(irq_index >= strlen(eisa_irqs)) { 19462306a36Sopenharmony_ci printk("sim710.c: irq nasty\n"); 19562306a36Sopenharmony_ci return -ENODEV; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci irq = eisa_irqs[irq_index]; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return sim710_probe_common(dev, io_addr, irq, 50, 20162306a36Sopenharmony_ci differential, scsi_id); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic struct eisa_driver sim710_eisa_driver = { 20562306a36Sopenharmony_ci .id_table = sim710_eisa_ids, 20662306a36Sopenharmony_ci .driver = { 20762306a36Sopenharmony_ci .name = "sim710", 20862306a36Sopenharmony_ci .probe = sim710_eisa_probe, 20962306a36Sopenharmony_ci .remove = sim710_device_remove, 21062306a36Sopenharmony_ci }, 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci#endif /* CONFIG_EISA */ 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int __init sim710_init(void) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci#ifdef MODULE 21762306a36Sopenharmony_ci if (sim710) 21862306a36Sopenharmony_ci param_setup(sim710); 21962306a36Sopenharmony_ci#endif 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci#ifdef CONFIG_EISA 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * FIXME: We'd really like to return -ENODEV if no devices have actually 22462306a36Sopenharmony_ci * been found. However eisa_driver_register() only reports problems 22562306a36Sopenharmony_ci * with kobject_register() so simply return success for now. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci eisa_driver_register(&sim710_eisa_driver); 22862306a36Sopenharmony_ci#endif 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void __exit sim710_exit(void) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci#ifdef CONFIG_EISA 23562306a36Sopenharmony_ci eisa_driver_unregister(&sim710_eisa_driver); 23662306a36Sopenharmony_ci#endif 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cimodule_init(sim710_init); 24062306a36Sopenharmony_cimodule_exit(sim710_exit); 241