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 + -
显示快捷键?