main.c

来自「linux 内核源代码」· C语言 代码 · 共 1,162 行 · 第 1/2 页

C
1,162
字号
/* * Sonics Silicon Backplane * Subsystem core * * Copyright 2005, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de> * * Licensed under the GNU/GPL. See COPYING for details. */#include "ssb_private.h"#include <linux/delay.h>#include <linux/io.h>#include <linux/ssb/ssb.h>#include <linux/ssb/ssb_regs.h>#include <linux/dma-mapping.h>#include <linux/pci.h>#include <pcmcia/cs_types.h>#include <pcmcia/cs.h>#include <pcmcia/cistpl.h>#include <pcmcia/ds.h>MODULE_DESCRIPTION("Sonics Silicon Backplane driver");MODULE_LICENSE("GPL");/* Temporary list of yet-to-be-attached buses */static LIST_HEAD(attach_queue);/* List if running buses */static LIST_HEAD(buses);/* Software ID counter */static unsigned int next_busnumber;/* buses_mutes locks the two buslists and the next_busnumber. * Don't lock this directly, but use ssb_buses_[un]lock() below. */static DEFINE_MUTEX(buses_mutex);/* There are differences in the codeflow, if the bus is * initialized from early boot, as various needed services * are not available early. This is a mechanism to delay * these initializations to after early boot has finished. * It's also used to avoid mutex locking, as that's not * available and needed early. */static bool ssb_is_early_boot = 1;static void ssb_buses_lock(void);static void ssb_buses_unlock(void);#ifdef CONFIG_SSB_PCIHOSTstruct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev){	struct ssb_bus *bus;	ssb_buses_lock();	list_for_each_entry(bus, &buses, list) {		if (bus->bustype == SSB_BUSTYPE_PCI &&		    bus->host_pci == pdev)			goto found;	}	bus = NULL;found:	ssb_buses_unlock();	return bus;}#endif /* CONFIG_SSB_PCIHOST */static struct ssb_device *ssb_device_get(struct ssb_device *dev){	if (dev)		get_device(dev->dev);	return dev;}static void ssb_device_put(struct ssb_device *dev){	if (dev)		put_device(dev->dev);}static int ssb_bus_resume(struct ssb_bus *bus){	int err;	ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1);	err = ssb_pcmcia_init(bus);	if (err) {		/* No need to disable XTAL, as we don't have one on PCMCIA. */		return err;	}	ssb_chipco_resume(&bus->chipco);	return 0;}static int ssb_device_resume(struct device *dev){	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);	struct ssb_driver *ssb_drv;	struct ssb_bus *bus;	int err = 0;	bus = ssb_dev->bus;	if (bus->suspend_cnt == bus->nr_devices) {		err = ssb_bus_resume(bus);		if (err)			return err;	}	bus->suspend_cnt--;	if (dev->driver) {		ssb_drv = drv_to_ssb_drv(dev->driver);		if (ssb_drv && ssb_drv->resume)			err = ssb_drv->resume(ssb_dev);		if (err)			goto out;	}out:	return err;}static void ssb_bus_suspend(struct ssb_bus *bus, pm_message_t state){	ssb_chipco_suspend(&bus->chipco, state);	ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);	/* Reset HW state information in memory, so that HW is	 * completely reinitialized on resume. */	bus->mapped_device = NULL;#ifdef CONFIG_SSB_DRIVER_PCICORE	bus->pcicore.setup_done = 0;#endif#ifdef CONFIG_SSB_DEBUG	bus->powered_up = 0;#endif}static int ssb_device_suspend(struct device *dev, pm_message_t state){	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);	struct ssb_driver *ssb_drv;	struct ssb_bus *bus;	int err = 0;	if (dev->driver) {		ssb_drv = drv_to_ssb_drv(dev->driver);		if (ssb_drv && ssb_drv->suspend)			err = ssb_drv->suspend(ssb_dev, state);		if (err)			goto out;	}	bus = ssb_dev->bus;	bus->suspend_cnt++;	if (bus->suspend_cnt == bus->nr_devices) {		/* All devices suspended. Shutdown the bus. */		ssb_bus_suspend(bus, state);	}out:	return err;}#ifdef CONFIG_SSB_PCIHOSTint ssb_devices_freeze(struct ssb_bus *bus){	struct ssb_device *dev;	struct ssb_driver *drv;	int err = 0;	int i;	pm_message_t state = PMSG_FREEZE;	/* First check that we are capable to freeze all devices. */	for (i = 0; i < bus->nr_devices; i++) {		dev = &(bus->devices[i]);		if (!dev->dev ||		    !dev->dev->driver ||		    !device_is_registered(dev->dev))			continue;		drv = drv_to_ssb_drv(dev->dev->driver);		if (!drv)			continue;		if (!drv->suspend) {			/* Nope, can't suspend this one. */			return -EOPNOTSUPP;		}	}	/* Now suspend all devices */	for (i = 0; i < bus->nr_devices; i++) {		dev = &(bus->devices[i]);		if (!dev->dev ||		    !dev->dev->driver ||		    !device_is_registered(dev->dev))			continue;		drv = drv_to_ssb_drv(dev->dev->driver);		if (!drv)			continue;		err = drv->suspend(dev, state);		if (err) {			ssb_printk(KERN_ERR PFX "Failed to freeze device %s\n",				   dev->dev->bus_id);			goto err_unwind;		}	}	return 0;err_unwind:	for (i--; i >= 0; i--) {		dev = &(bus->devices[i]);		if (!dev->dev ||		    !dev->dev->driver ||		    !device_is_registered(dev->dev))			continue;		drv = drv_to_ssb_drv(dev->dev->driver);		if (!drv)			continue;		if (drv->resume)			drv->resume(dev);	}	return err;}int ssb_devices_thaw(struct ssb_bus *bus){	struct ssb_device *dev;	struct ssb_driver *drv;	int err;	int i;	for (i = 0; i < bus->nr_devices; i++) {		dev = &(bus->devices[i]);		if (!dev->dev ||		    !dev->dev->driver ||		    !device_is_registered(dev->dev))			continue;		drv = drv_to_ssb_drv(dev->dev->driver);		if (!drv)			continue;		if (SSB_WARN_ON(!drv->resume))			continue;		err = drv->resume(dev);		if (err) {			ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n",				   dev->dev->bus_id);		}	}	return 0;}#endif /* CONFIG_SSB_PCIHOST */static void ssb_device_shutdown(struct device *dev){	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);	struct ssb_driver *ssb_drv;	if (!dev->driver)		return;	ssb_drv = drv_to_ssb_drv(dev->driver);	if (ssb_drv && ssb_drv->shutdown)		ssb_drv->shutdown(ssb_dev);}static int ssb_device_remove(struct device *dev){	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);	struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver);	if (ssb_drv && ssb_drv->remove)		ssb_drv->remove(ssb_dev);	ssb_device_put(ssb_dev);	return 0;}static int ssb_device_probe(struct device *dev){	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);	struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver);	int err = 0;	ssb_device_get(ssb_dev);	if (ssb_drv && ssb_drv->probe)		err = ssb_drv->probe(ssb_dev, &ssb_dev->id);	if (err)		ssb_device_put(ssb_dev);	return err;}static int ssb_match_devid(const struct ssb_device_id *tabid,			   const struct ssb_device_id *devid){	if ((tabid->vendor != devid->vendor) &&	    tabid->vendor != SSB_ANY_VENDOR)		return 0;	if ((tabid->coreid != devid->coreid) &&	    tabid->coreid != SSB_ANY_ID)		return 0;	if ((tabid->revision != devid->revision) &&	    tabid->revision != SSB_ANY_REV)		return 0;	return 1;}static int ssb_bus_match(struct device *dev, struct device_driver *drv){	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);	struct ssb_driver *ssb_drv = drv_to_ssb_drv(drv);	const struct ssb_device_id *id;	for (id = ssb_drv->id_table;	     id->vendor || id->coreid || id->revision;	     id++) {		if (ssb_match_devid(id, &ssb_dev->id))			return 1; /* found */	}	return 0;}static int ssb_device_uevent(struct device *dev, struct kobj_uevent_env *env){	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);	if (!dev)		return -ENODEV;	return add_uevent_var(env,			     "MODALIAS=ssb:v%04Xid%04Xrev%02X",			     ssb_dev->id.vendor, ssb_dev->id.coreid,			     ssb_dev->id.revision);}static struct bus_type ssb_bustype = {	.name		= "ssb",	.match		= ssb_bus_match,	.probe		= ssb_device_probe,	.remove		= ssb_device_remove,	.shutdown	= ssb_device_shutdown,	.suspend	= ssb_device_suspend,	.resume		= ssb_device_resume,	.uevent		= ssb_device_uevent,};static void ssb_buses_lock(void){	/* See the comment at the ssb_is_early_boot definition */	if (!ssb_is_early_boot)		mutex_lock(&buses_mutex);}static void ssb_buses_unlock(void){	/* See the comment at the ssb_is_early_boot definition */	if (!ssb_is_early_boot)		mutex_unlock(&buses_mutex);}static void ssb_devices_unregister(struct ssb_bus *bus){	struct ssb_device *sdev;	int i;	for (i = bus->nr_devices - 1; i >= 0; i--) {		sdev = &(bus->devices[i]);		if (sdev->dev)			device_unregister(sdev->dev);	}}void ssb_bus_unregister(struct ssb_bus *bus){	ssb_buses_lock();	ssb_devices_unregister(bus);	list_del(&bus->list);	ssb_buses_unlock();	/* ssb_pcmcia_exit(bus); */	ssb_pci_exit(bus);	ssb_iounmap(bus);}EXPORT_SYMBOL(ssb_bus_unregister);static void ssb_release_dev(struct device *dev){	struct __ssb_dev_wrapper *devwrap;	devwrap = container_of(dev, struct __ssb_dev_wrapper, dev);	kfree(devwrap);}static int ssb_devices_register(struct ssb_bus *bus){	struct ssb_device *sdev;	struct device *dev;	struct __ssb_dev_wrapper *devwrap;	int i, err = 0;	int dev_idx = 0;	for (i = 0; i < bus->nr_devices; i++) {		sdev = &(bus->devices[i]);		/* We don't register SSB-system devices to the kernel,		 * as the drivers for them are built into SSB. */		switch (sdev->id.coreid) {		case SSB_DEV_CHIPCOMMON:		case SSB_DEV_PCI:		case SSB_DEV_PCIE:		case SSB_DEV_PCMCIA:		case SSB_DEV_MIPS:		case SSB_DEV_MIPS_3302:		case SSB_DEV_EXTIF:			continue;		}		devwrap = kzalloc(sizeof(*devwrap), GFP_KERNEL);		if (!devwrap) {			ssb_printk(KERN_ERR PFX				   "Could not allocate device\n");			err = -ENOMEM;			goto error;		}		dev = &devwrap->dev;		devwrap->sdev = sdev;		dev->release = ssb_release_dev;		dev->bus = &ssb_bustype;		snprintf(dev->bus_id, sizeof(dev->bus_id),			 "ssb%u:%d", bus->busnumber, dev_idx);		switch (bus->bustype) {		case SSB_BUSTYPE_PCI:#ifdef CONFIG_SSB_PCIHOST			sdev->irq = bus->host_pci->irq;			dev->parent = &bus->host_pci->dev;#endif			break;		case SSB_BUSTYPE_PCMCIA:#ifdef CONFIG_SSB_PCMCIAHOST			sdev->irq = bus->host_pcmcia->irq.AssignedIRQ;			dev->parent = &bus->host_pcmcia->dev;#endif			break;		case SSB_BUSTYPE_SSB:			break;		}		sdev->dev = dev;		err = device_register(dev);		if (err) {			ssb_printk(KERN_ERR PFX				   "Could not register %s\n",				   dev->bus_id);			/* Set dev to NULL to not unregister			 * dev on error unwinding. */			sdev->dev = NULL;			kfree(devwrap);			goto error;		}		dev_idx++;	}	return 0;error:	/* Unwind the already registered devices. */	ssb_devices_unregister(bus);	return err;}/* Needs ssb_buses_lock() */static int ssb_attach_queued_buses(void){	struct ssb_bus *bus, *n;	int err = 0;	int drop_them_all = 0;	list_for_each_entry_safe(bus, n, &attach_queue, list) {		if (drop_them_all) {			list_del(&bus->list);			continue;		}		/* Can't init the PCIcore in ssb_bus_register(), as that		 * is too early in boot for embedded systems		 * (no udelay() available). So do it here in attach stage.		 */		err = ssb_bus_powerup(bus, 0);		if (err)			goto error;		ssb_pcicore_init(&bus->pcicore);		ssb_bus_may_powerdown(bus);		err = ssb_devices_register(bus);error:		if (err) {			drop_them_all = 1;			list_del(&bus->list);			continue;		}		list_move_tail(&bus->list, &buses);	}	return err;}static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset){	struct ssb_bus *bus = dev->bus;	offset += dev->core_index * SSB_CORE_SIZE;	return readw(bus->mmio + offset);}static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset){	struct ssb_bus *bus = dev->bus;	offset += dev->core_index * SSB_CORE_SIZE;	return readl(bus->mmio + offset);}static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value){	struct ssb_bus *bus = dev->bus;	offset += dev->core_index * SSB_CORE_SIZE;	writew(value, bus->mmio + offset);}static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value){	struct ssb_bus *bus = dev->bus;	offset += dev->core_index * SSB_CORE_SIZE;	writel(value, bus->mmio + offset);}/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */static const struct ssb_bus_ops ssb_ssb_ops = {	.read16		= ssb_ssb_read16,	.read32		= ssb_ssb_read32,	.write16	= ssb_ssb_write16,	.write32	= ssb_ssb_write32,};static int ssb_fetch_invariants(struct ssb_bus *bus,				ssb_invariants_func_t get_invariants){	struct ssb_init_invariants iv;	int err;	memset(&iv, 0, sizeof(iv));	err = get_invariants(bus, &iv);	if (err)		goto out;	memcpy(&bus->boardinfo, &iv.boardinfo, sizeof(iv.boardinfo));	memcpy(&bus->sprom, &iv.sprom, sizeof(iv.sprom));out:	return err;}static int ssb_bus_register(struct ssb_bus *bus,			    ssb_invariants_func_t get_invariants,			    unsigned long baseaddr){	int err;	spin_lock_init(&bus->bar_lock);	INIT_LIST_HEAD(&bus->list);	/* Powerup the bus */	err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1);	if (err)		goto out;	ssb_buses_lock();	bus->busnumber = next_busnumber;	/* Scan for devices (cores) */	err = ssb_bus_scan(bus, baseaddr);	if (err)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?