sm501.c

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

C
1,284
字号
{	kfree(to_sm_device(to_platform_device(dev)));}/* sm501_create_subdev * * Create a skeleton platform device with resources for passing to a * sub-driver*/static struct platform_device *sm501_create_subdev(struct sm501_devdata *sm,		    char *name, unsigned int res_count){	struct sm501_device *smdev;	smdev = kzalloc(sizeof(struct sm501_device) +			sizeof(struct resource) * res_count, GFP_KERNEL);	if (!smdev)		return NULL;	smdev->pdev.dev.release = sm501_device_release;	smdev->pdev.name = name;	smdev->pdev.id = sm->pdev_id;	smdev->pdev.resource = (struct resource *)(smdev+1);	smdev->pdev.num_resources = res_count;	smdev->pdev.dev.parent = sm->dev;	return &smdev->pdev;}/* sm501_register_device * * Register a platform device created with sm501_create_subdev()*/static int sm501_register_device(struct sm501_devdata *sm,				 struct platform_device *pdev){	struct sm501_device *smdev = to_sm_device(pdev);	int ptr;	int ret;	for (ptr = 0; ptr < pdev->num_resources; ptr++) {		printk("%s[%d] flags %08lx: %08llx..%08llx\n",		       pdev->name, ptr,		       pdev->resource[ptr].flags,		       (unsigned long long)pdev->resource[ptr].start,		       (unsigned long long)pdev->resource[ptr].end);	}	ret = platform_device_register(pdev);	if (ret >= 0) {		dev_dbg(sm->dev, "registered %s\n", pdev->name);		list_add_tail(&smdev->list, &sm->devices);	} else		dev_err(sm->dev, "error registering %s (%d)\n",			pdev->name, ret);	return ret;}/* sm501_create_subio * * Fill in an IO resource for a sub device*/static void sm501_create_subio(struct sm501_devdata *sm,			       struct resource *res,			       resource_size_t offs,			       resource_size_t size){	res->flags = IORESOURCE_MEM;	res->parent = sm->io_res;	res->start = sm->io_res->start + offs;	res->end = res->start + size - 1;}/* sm501_create_mem * * Fill in an MEM resource for a sub device*/static void sm501_create_mem(struct sm501_devdata *sm,			     struct resource *res,			     resource_size_t *offs,			     resource_size_t size){	*offs -= size;		/* adjust memory size */	res->flags = IORESOURCE_MEM;	res->parent = sm->mem_res;	res->start = sm->mem_res->start + *offs;	res->end = res->start + size - 1;}/* sm501_create_irq * * Fill in an IRQ resource for a sub device*/static void sm501_create_irq(struct sm501_devdata *sm,			     struct resource *res){	res->flags = IORESOURCE_IRQ;	res->parent = NULL;	res->start = res->end = sm->irq;}static int sm501_register_usbhost(struct sm501_devdata *sm,				  resource_size_t *mem_avail){	struct platform_device *pdev;	pdev = sm501_create_subdev(sm, "sm501-usb", 3);	if (!pdev)		return -ENOMEM;	sm501_create_subio(sm, &pdev->resource[0], 0x40000, 0x20000);	sm501_create_mem(sm, &pdev->resource[1], mem_avail, 256*1024);	sm501_create_irq(sm, &pdev->resource[2]);	return sm501_register_device(sm, pdev);}static int sm501_register_display(struct sm501_devdata *sm,				  resource_size_t *mem_avail){	struct platform_device *pdev;	pdev = sm501_create_subdev(sm, "sm501-fb", 4);	if (!pdev)		return -ENOMEM;	sm501_create_subio(sm, &pdev->resource[0], 0x80000, 0x10000);	sm501_create_subio(sm, &pdev->resource[1], 0x100000, 0x50000);	sm501_create_mem(sm, &pdev->resource[2], mem_avail, *mem_avail);	sm501_create_irq(sm, &pdev->resource[3]);	return sm501_register_device(sm, pdev);}/* sm501_dbg_regs * * Debug attribute to attach to parent device to show core registers*/static ssize_t sm501_dbg_regs(struct device *dev,			      struct device_attribute *attr, char *buff){	struct sm501_devdata *sm = dev_get_drvdata(dev)	;	unsigned int reg;	char *ptr = buff;	int ret;	for (reg = 0x00; reg < 0x70; reg += 4) {		ret = sprintf(ptr, "%08x = %08x\n",			      reg, readl(sm->regs + reg));		ptr += ret;	}	return ptr - buff;}static DEVICE_ATTR(dbg_regs, 0666, sm501_dbg_regs, NULL);/* sm501_init_reg * * Helper function for the init code to setup a register * * clear the bits which are set in r->mask, and then set * the bits set in r->set.*/static inline void sm501_init_reg(struct sm501_devdata *sm,				  unsigned long reg,				  struct sm501_reg_init *r){	unsigned long tmp;	tmp = readl(sm->regs + reg);	tmp &= ~r->mask;	tmp |= r->set;	writel(tmp, sm->regs + reg);}/* sm501_init_regs * * Setup core register values*/static void sm501_init_regs(struct sm501_devdata *sm,			    struct sm501_initdata *init){	sm501_misc_control(sm->dev,			   init->misc_control.set,			   init->misc_control.mask);	sm501_init_reg(sm, SM501_MISC_TIMING, &init->misc_timing);	sm501_init_reg(sm, SM501_GPIO31_0_CONTROL, &init->gpio_low);	sm501_init_reg(sm, SM501_GPIO63_32_CONTROL, &init->gpio_high);	if (init->m1xclk) {		dev_info(sm->dev, "setting M1XCLK to %ld\n", init->m1xclk);		sm501_set_clock(sm->dev, SM501_CLOCK_M1XCLK, init->m1xclk);	}	if (init->mclk) {		dev_info(sm->dev, "setting MCLK to %ld\n", init->mclk);		sm501_set_clock(sm->dev, SM501_CLOCK_MCLK, init->mclk);	}}/* Check the PLL sources for the M1CLK and M1XCLK * * If the M1CLK and M1XCLKs are not sourced from the same PLL, then * there is a risk (see errata AB-5) that the SM501 will cease proper * function. If this happens, then it is likely the SM501 will * hang the system.*/static int sm501_check_clocks(struct sm501_devdata *sm){	unsigned long pwrmode = readl(sm->regs + SM501_CURRENT_CLOCK);	unsigned long msrc = (pwrmode & SM501_POWERMODE_M_SRC);	unsigned long m1src = (pwrmode & SM501_POWERMODE_M1_SRC);	return ((msrc == 0 && m1src != 0) || (msrc != 0 && m1src == 0));}static unsigned int sm501_mem_local[] = {	[0]	= 4*1024*1024,	[1]	= 8*1024*1024,	[2]	= 16*1024*1024,	[3]	= 32*1024*1024,	[4]	= 64*1024*1024,	[5]	= 2*1024*1024,};/* sm501_init_dev * * Common init code for an SM501*/static int sm501_init_dev(struct sm501_devdata *sm){	resource_size_t mem_avail;	unsigned long dramctrl;	unsigned long devid;	int ret;	mutex_init(&sm->clock_lock);	spin_lock_init(&sm->reg_lock);	INIT_LIST_HEAD(&sm->devices);	devid = readl(sm->regs + SM501_DEVICEID);	if ((devid & SM501_DEVICEID_IDMASK) != SM501_DEVICEID_SM501) {		dev_err(sm->dev, "incorrect device id %08lx\n", devid);		return -EINVAL;	}	dramctrl = readl(sm->regs + SM501_DRAM_CONTROL);	mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7];	dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n",		 sm->regs, devid, (unsigned long)mem_avail >> 20, sm->irq);	sm501_dump_gate(sm);	ret = device_create_file(sm->dev, &dev_attr_dbg_regs);	if (ret)		dev_err(sm->dev, "failed to create debug regs file\n");	sm501_dump_clk(sm);	/* check to see if we have some device initialisation */	if (sm->platdata) {		struct sm501_platdata *pdata = sm->platdata;		if (pdata->init) {			sm501_init_regs(sm, sm->platdata->init);			if (pdata->init->devices & SM501_USE_USB_HOST)				sm501_register_usbhost(sm, &mem_avail);		}	}	ret = sm501_check_clocks(sm);	if (ret) {		dev_err(sm->dev, "M1X and M clocks sourced from different "					"PLLs\n");		return -EINVAL;	}	/* always create a framebuffer */	sm501_register_display(sm, &mem_avail);	return 0;}static int sm501_plat_probe(struct platform_device *dev){	struct sm501_devdata *sm;	int err;	sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL);	if (sm == NULL) {		dev_err(&dev->dev, "no memory for device data\n");		err = -ENOMEM;		goto err1;	}	sm->dev = &dev->dev;	sm->pdev_id = dev->id;	sm->irq = platform_get_irq(dev, 0);	sm->io_res = platform_get_resource(dev, IORESOURCE_MEM, 1);	sm->mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0);	sm->platdata = dev->dev.platform_data;	if (sm->irq < 0) {		dev_err(&dev->dev, "failed to get irq resource\n");		err = sm->irq;		goto err_res;	}	if (sm->io_res == NULL || sm->mem_res == NULL) {		dev_err(&dev->dev, "failed to get IO resource\n");		err = -ENOENT;		goto err_res;	}	sm->regs_claim = request_mem_region(sm->io_res->start,					    0x100, "sm501");	if (sm->regs_claim == NULL) {		dev_err(&dev->dev, "cannot claim registers\n");		err= -EBUSY;		goto err_res;	}	platform_set_drvdata(dev, sm);	sm->regs = ioremap(sm->io_res->start,			   (sm->io_res->end - sm->io_res->start) - 1);	if (sm->regs == NULL) {		dev_err(&dev->dev, "cannot remap registers\n");		err = -EIO;		goto err_claim;	}	return sm501_init_dev(sm); err_claim:	release_resource(sm->regs_claim);	kfree(sm->regs_claim); err_res:	kfree(sm); err1:	return err;}#ifdef CONFIG_PM/* power management support */static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state){	struct sm501_devdata *sm = platform_get_drvdata(pdev);	sm->in_suspend = 1;	sm->pm_misc = readl(sm->regs + SM501_MISC_CONTROL);	sm501_dump_regs(sm);	return 0;}static int sm501_plat_resume(struct platform_device *pdev){	struct sm501_devdata *sm = platform_get_drvdata(pdev);	sm501_dump_regs(sm);	sm501_dump_gate(sm);	sm501_dump_clk(sm);	/* check to see if we are in the same state as when suspended */	if (readl(sm->regs + SM501_MISC_CONTROL) != sm->pm_misc) {		dev_info(sm->dev, "SM501_MISC_CONTROL changed over sleep\n");		writel(sm->pm_misc, sm->regs + SM501_MISC_CONTROL);		/* our suspend causes the controller state to change,		 * either by something attempting setup, power loss,		 * or an external reset event on power change */		if (sm->platdata && sm->platdata->init) {			sm501_init_regs(sm, sm->platdata->init);		}	}	/* dump our state from resume */	sm501_dump_regs(sm);	sm501_dump_clk(sm);	sm->in_suspend = 0;	return 0;}#else#define sm501_plat_suspend NULL#define sm501_plat_resume NULL#endif/* Initialisation data for PCI devices */static struct sm501_initdata sm501_pci_initdata = {	.gpio_high	= {		.set	= 0x3F000000,		/* 24bit panel */		.mask	= 0x0,	},	.misc_timing	= {		.set	= 0x010100,		/* SDRAM timing */		.mask	= 0x1F1F00,	},	.misc_control	= {		.set	= SM501_MISC_PNL_24BIT,		.mask	= 0,	},	.devices	= SM501_USE_ALL,	/* Errata AB-3 says that 72MHz is the fastest available	 * for 33MHZ PCI with proper bus-mastering operation */	.mclk		= 72 * MHZ,	.m1xclk		= 144 * MHZ,};static struct sm501_platdata_fbsub sm501_pdata_fbsub = {	.flags		= (SM501FB_FLAG_USE_INIT_MODE |			   SM501FB_FLAG_USE_HWCURSOR |			   SM501FB_FLAG_USE_HWACCEL |			   SM501FB_FLAG_DISABLE_AT_EXIT),};static struct sm501_platdata_fb sm501_fb_pdata = {	.fb_route	= SM501_FB_OWN,	.fb_crt		= &sm501_pdata_fbsub,	.fb_pnl		= &sm501_pdata_fbsub,};static struct sm501_platdata sm501_pci_platdata = {	.init		= &sm501_pci_initdata,	.fb		= &sm501_fb_pdata,};static int sm501_pci_probe(struct pci_dev *dev,			   const struct pci_device_id *id){	struct sm501_devdata *sm;	int err;	sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL);	if (sm == NULL) {		dev_err(&dev->dev, "no memory for device data\n");		err = -ENOMEM;		goto err1;	}	/* set a default set of platform data */	dev->dev.platform_data = sm->platdata = &sm501_pci_platdata;	/* set a hopefully unique id for our child platform devices */	sm->pdev_id = 32 + dev->devfn;	pci_set_drvdata(dev, sm);	err = pci_enable_device(dev);	if (err) {		dev_err(&dev->dev, "cannot enable device\n");		goto err2;	}	sm->dev = &dev->dev;	sm->irq = dev->irq;#ifdef __BIG_ENDIAN	/* if the system is big-endian, we most probably have a	 * translation in the IO layer making the PCI bus little endian	 * so make the framebuffer swapped pixels */	sm501_fb_pdata.flags |= SM501_FBPD_SWAP_FB_ENDIAN;#endif	/* check our resources */	if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM)) {		dev_err(&dev->dev, "region #0 is not memory?\n");		err = -EINVAL;		goto err3;	}	if (!(pci_resource_flags(dev, 1) & IORESOURCE_MEM)) {		dev_err(&dev->dev, "region #1 is not memory?\n");		err = -EINVAL;		goto err3;	}	/* make our resources ready for sharing */	sm->io_res = &dev->resource[1];	sm->mem_res = &dev->resource[0];	sm->regs_claim = request_mem_region(sm->io_res->start,					    0x100, "sm501");	if (sm->regs_claim == NULL) {		dev_err(&dev->dev, "cannot claim registers\n");		err= -EBUSY;		goto err3;	}	sm->regs = ioremap(pci_resource_start(dev, 1),			   pci_resource_len(dev, 1));	if (sm->regs == NULL) {		dev_err(&dev->dev, "cannot remap registers\n");		err = -EIO;		goto err4;	}	sm501_init_dev(sm);	return 0; err4:	release_resource(sm->regs_claim);	kfree(sm->regs_claim); err3:	pci_disable_device(dev); err2:	pci_set_drvdata(dev, NULL);	kfree(sm); err1:	return err;}static void sm501_remove_sub(struct sm501_devdata *sm,			     struct sm501_device *smdev){	list_del(&smdev->list);	platform_device_unregister(&smdev->pdev);}static void sm501_dev_remove(struct sm501_devdata *sm){	struct sm501_device *smdev, *tmp;	list_for_each_entry_safe(smdev, tmp, &sm->devices, list)		sm501_remove_sub(sm, smdev);	device_remove_file(sm->dev, &dev_attr_dbg_regs);}static void sm501_pci_remove(struct pci_dev *dev){	struct sm501_devdata *sm = pci_get_drvdata(dev);	sm501_dev_remove(sm);	iounmap(sm->regs);	release_resource(sm->regs_claim);	kfree(sm->regs_claim);	pci_set_drvdata(dev, NULL);	pci_disable_device(dev);}static int sm501_plat_remove(struct platform_device *dev){	struct sm501_devdata *sm = platform_get_drvdata(dev);	sm501_dev_remove(sm);	iounmap(sm->regs);	release_resource(sm->regs_claim);	kfree(sm->regs_claim);	return 0;}static struct pci_device_id sm501_pci_tbl[] = {	{ 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	{ 0, },};MODULE_DEVICE_TABLE(pci, sm501_pci_tbl);static struct pci_driver sm501_pci_drv = {	.name		= "sm501",	.id_table	= sm501_pci_tbl,	.probe		= sm501_pci_probe,	.remove		= sm501_pci_remove,};static struct platform_driver sm501_plat_drv = {	.driver		= {		.name	= "sm501",		.owner	= THIS_MODULE,	},	.probe		= sm501_plat_probe,	.remove		= sm501_plat_remove,	.suspend	= sm501_plat_suspend,	.resume		= sm501_plat_resume,};static int __init sm501_base_init(void){	platform_driver_register(&sm501_plat_drv);	return pci_register_driver(&sm501_pci_drv);}static void __exit sm501_base_exit(void){	platform_driver_unregister(&sm501_plat_drv);	pci_unregister_driver(&sm501_pci_drv);}module_init(sm501_base_init);module_exit(sm501_base_exit);MODULE_DESCRIPTION("SM501 Core Driver");MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Vincent Sanders");MODULE_LICENSE("GPL v2");

⌨️ 快捷键说明

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