📄 sa1111.c
字号:
* @dev: device to restore * * Restore the general state of the SA1111; clock control and * interrupt controller. Other parts of the SA1111 must be * restored by their respective drivers, and must be called * via LDM after this function. */static int sa1111_resume(struct platform_device *dev){ struct sa1111 *sachip = platform_get_drvdata(dev); struct sa1111_save_data *save; unsigned long flags, id; void __iomem *base; save = (struct sa1111_save_data *)dev->dev.power.saved_state; if (!save) return 0; spin_lock_irqsave(&sachip->lock, flags); /* * Ensure that the SA1111 is still here. * FIXME: shouldn't do this here. */ id = sa1111_readl(sachip->base + SA1111_SKID); if ((id & SKID_ID_MASK) != SKID_SA1111_ID) { __sa1111_remove(sachip); platform_set_drvdata(dev, NULL); kfree(save); return 0; } /* * First of all, wake up the chip. */ sa1111_wake(sachip); sa1111_writel(0, sachip->base + SA1111_INTC + SA1111_INTEN0); sa1111_writel(0, sachip->base + SA1111_INTC + SA1111_INTEN1); base = sachip->base; sa1111_writel(save->skcr, base + SA1111_SKCR); sa1111_writel(save->skpcr, base + SA1111_SKPCR); sa1111_writel(save->skcdr, base + SA1111_SKCDR); sa1111_writel(save->skaud, base + SA1111_SKAUD); sa1111_writel(save->skpwm0, base + SA1111_SKPWM0); sa1111_writel(save->skpwm1, base + SA1111_SKPWM1); base = sachip->base + SA1111_INTC; sa1111_writel(save->intpol0, base + SA1111_INTPOL0); sa1111_writel(save->intpol1, base + SA1111_INTPOL1); sa1111_writel(save->inten0, base + SA1111_INTEN0); sa1111_writel(save->inten1, base + SA1111_INTEN1); sa1111_writel(save->wakepol0, base + SA1111_WAKEPOL0); sa1111_writel(save->wakepol1, base + SA1111_WAKEPOL1); sa1111_writel(save->wakeen0, base + SA1111_WAKEEN0); sa1111_writel(save->wakeen1, base + SA1111_WAKEEN1); spin_unlock_irqrestore(&sachip->lock, flags); dev->dev.power.saved_state = NULL; kfree(save); return 0;}#else#define sa1111_suspend NULL#define sa1111_resume NULL#endifstatic int sa1111_probe(struct platform_device *pdev){ struct resource *mem; int irq; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) return -EINVAL; irq = platform_get_irq(pdev, 0); return __sa1111_probe(&pdev->dev, mem, irq);}static int sa1111_remove(struct platform_device *pdev){ struct sa1111 *sachip = platform_get_drvdata(pdev); if (sachip) { __sa1111_remove(sachip); platform_set_drvdata(pdev, NULL);#ifdef CONFIG_PM kfree(pdev->dev.power.saved_state); pdev->dev.power.saved_state = NULL;#endif } return 0;}/* * Not sure if this should be on the system bus or not yet. * We really want some way to register a system device at * the per-machine level, and then have this driver pick * up the registered devices. * * We also need to handle the SDRAM configuration for * PXA250/SA1110 machine classes. */static struct platform_driver sa1111_device_driver = { .probe = sa1111_probe, .remove = sa1111_remove, .suspend = sa1111_suspend, .resume = sa1111_resume, .driver = { .name = "sa1111", },};/* * Get the parent device driver (us) structure * from a child function device */static inline struct sa1111 *sa1111_chip_driver(struct sa1111_dev *sadev){ return (struct sa1111 *)dev_get_drvdata(sadev->dev.parent);}/* * The bits in the opdiv field are non-linear. */static unsigned char opdiv_table[] = { 1, 4, 2, 8 };static unsigned int __sa1111_pll_clock(struct sa1111 *sachip){ unsigned int skcdr, fbdiv, ipdiv, opdiv; skcdr = sa1111_readl(sachip->base + SA1111_SKCDR); fbdiv = (skcdr & 0x007f) + 2; ipdiv = ((skcdr & 0x0f80) >> 7) + 2; opdiv = opdiv_table[(skcdr & 0x3000) >> 12]; return 3686400 * fbdiv / (ipdiv * opdiv);}/** * sa1111_pll_clock - return the current PLL clock frequency. * @sadev: SA1111 function block * * BUG: we should look at SKCR. We also blindly believe that * the chip is being fed with the 3.6864MHz clock. * * Returns the PLL clock in Hz. */unsigned int sa1111_pll_clock(struct sa1111_dev *sadev){ struct sa1111 *sachip = sa1111_chip_driver(sadev); return __sa1111_pll_clock(sachip);}/** * sa1111_select_audio_mode - select I2S or AC link mode * @sadev: SA1111 function block * @mode: One of %SA1111_AUDIO_ACLINK or %SA1111_AUDIO_I2S * * Frob the SKCR to select AC Link mode or I2S mode for * the audio block. */void sa1111_select_audio_mode(struct sa1111_dev *sadev, int mode){ struct sa1111 *sachip = sa1111_chip_driver(sadev); unsigned long flags; unsigned int val; spin_lock_irqsave(&sachip->lock, flags); val = sa1111_readl(sachip->base + SA1111_SKCR); if (mode == SA1111_AUDIO_I2S) { val &= ~SKCR_SELAC; } else { val |= SKCR_SELAC; } sa1111_writel(val, sachip->base + SA1111_SKCR); spin_unlock_irqrestore(&sachip->lock, flags);}/** * sa1111_set_audio_rate - set the audio sample rate * @sadev: SA1111 SAC function block * @rate: sample rate to select */int sa1111_set_audio_rate(struct sa1111_dev *sadev, int rate){ struct sa1111 *sachip = sa1111_chip_driver(sadev); unsigned int div; if (sadev->devid != SA1111_DEVID_SAC) return -EINVAL; div = (__sa1111_pll_clock(sachip) / 256 + rate / 2) / rate; if (div == 0) div = 1; if (div > 128) div = 128; sa1111_writel(div - 1, sachip->base + SA1111_SKAUD); return 0;}/** * sa1111_get_audio_rate - get the audio sample rate * @sadev: SA1111 SAC function block device */int sa1111_get_audio_rate(struct sa1111_dev *sadev){ struct sa1111 *sachip = sa1111_chip_driver(sadev); unsigned long div; if (sadev->devid != SA1111_DEVID_SAC) return -EINVAL; div = sa1111_readl(sachip->base + SA1111_SKAUD) + 1; return __sa1111_pll_clock(sachip) / (256 * div);}void sa1111_set_io_dir(struct sa1111_dev *sadev, unsigned int bits, unsigned int dir, unsigned int sleep_dir){ struct sa1111 *sachip = sa1111_chip_driver(sadev); unsigned long flags; unsigned int val; void __iomem *gpio = sachip->base + SA1111_GPIO;#define MODIFY_BITS(port, mask, dir) \ if (mask) { \ val = sa1111_readl(port); \ val &= ~(mask); \ val |= (dir) & (mask); \ sa1111_writel(val, port); \ } spin_lock_irqsave(&sachip->lock, flags); MODIFY_BITS(gpio + SA1111_GPIO_PADDR, bits & 15, dir); MODIFY_BITS(gpio + SA1111_GPIO_PBDDR, (bits >> 8) & 255, dir >> 8); MODIFY_BITS(gpio + SA1111_GPIO_PCDDR, (bits >> 16) & 255, dir >> 16); MODIFY_BITS(gpio + SA1111_GPIO_PASDR, bits & 15, sleep_dir); MODIFY_BITS(gpio + SA1111_GPIO_PBSDR, (bits >> 8) & 255, sleep_dir >> 8); MODIFY_BITS(gpio + SA1111_GPIO_PCSDR, (bits >> 16) & 255, sleep_dir >> 16); spin_unlock_irqrestore(&sachip->lock, flags);}void sa1111_set_io(struct sa1111_dev *sadev, unsigned int bits, unsigned int v){ struct sa1111 *sachip = sa1111_chip_driver(sadev); unsigned long flags; unsigned int val; void __iomem *gpio = sachip->base + SA1111_GPIO; spin_lock_irqsave(&sachip->lock, flags); MODIFY_BITS(gpio + SA1111_GPIO_PADWR, bits & 15, v); MODIFY_BITS(gpio + SA1111_GPIO_PBDWR, (bits >> 8) & 255, v >> 8); MODIFY_BITS(gpio + SA1111_GPIO_PCDWR, (bits >> 16) & 255, v >> 16); spin_unlock_irqrestore(&sachip->lock, flags);}void sa1111_set_sleep_io(struct sa1111_dev *sadev, unsigned int bits, unsigned int v){ struct sa1111 *sachip = sa1111_chip_driver(sadev); unsigned long flags; unsigned int val; void __iomem *gpio = sachip->base + SA1111_GPIO; spin_lock_irqsave(&sachip->lock, flags); MODIFY_BITS(gpio + SA1111_GPIO_PASSR, bits & 15, v); MODIFY_BITS(gpio + SA1111_GPIO_PBSSR, (bits >> 8) & 255, v >> 8); MODIFY_BITS(gpio + SA1111_GPIO_PCSSR, (bits >> 16) & 255, v >> 16); spin_unlock_irqrestore(&sachip->lock, flags);}/* * Individual device operations. *//** * sa1111_enable_device - enable an on-chip SA1111 function block * @sadev: SA1111 function block device to enable */void sa1111_enable_device(struct sa1111_dev *sadev){ struct sa1111 *sachip = sa1111_chip_driver(sadev); unsigned long flags; unsigned int val; spin_lock_irqsave(&sachip->lock, flags); val = sa1111_readl(sachip->base + SA1111_SKPCR); sa1111_writel(val | sadev->skpcr_mask, sachip->base + SA1111_SKPCR); spin_unlock_irqrestore(&sachip->lock, flags);}/** * sa1111_disable_device - disable an on-chip SA1111 function block * @sadev: SA1111 function block device to disable */void sa1111_disable_device(struct sa1111_dev *sadev){ struct sa1111 *sachip = sa1111_chip_driver(sadev); unsigned long flags; unsigned int val; spin_lock_irqsave(&sachip->lock, flags); val = sa1111_readl(sachip->base + SA1111_SKPCR); sa1111_writel(val & ~sadev->skpcr_mask, sachip->base + SA1111_SKPCR); spin_unlock_irqrestore(&sachip->lock, flags);}/* * SA1111 "Register Access Bus." * * We model this as a regular bus type, and hang devices directly * off this. */static int sa1111_match(struct device *_dev, struct device_driver *_drv){ struct sa1111_dev *dev = SA1111_DEV(_dev); struct sa1111_driver *drv = SA1111_DRV(_drv); return dev->devid == drv->devid;}static int sa1111_bus_suspend(struct device *dev, pm_message_t state){ struct sa1111_dev *sadev = SA1111_DEV(dev); struct sa1111_driver *drv = SA1111_DRV(dev->driver); int ret = 0; if (drv && drv->suspend) ret = drv->suspend(sadev, state); return ret;}static int sa1111_bus_resume(struct device *dev){ struct sa1111_dev *sadev = SA1111_DEV(dev); struct sa1111_driver *drv = SA1111_DRV(dev->driver); int ret = 0; if (drv && drv->resume) ret = drv->resume(sadev); return ret;}static int sa1111_bus_probe(struct device *dev){ struct sa1111_dev *sadev = SA1111_DEV(dev); struct sa1111_driver *drv = SA1111_DRV(dev->driver); int ret = -ENODEV; if (drv->probe) ret = drv->probe(sadev); return ret;}static int sa1111_bus_remove(struct device *dev){ struct sa1111_dev *sadev = SA1111_DEV(dev); struct sa1111_driver *drv = SA1111_DRV(dev->driver); int ret = 0; if (drv->remove) ret = drv->remove(sadev); return ret;}struct bus_type sa1111_bus_type = { .name = "sa1111-rab", .match = sa1111_match, .suspend = sa1111_bus_suspend, .resume = sa1111_bus_resume,};int sa1111_driver_register(struct sa1111_driver *driver){ driver->drv.probe = sa1111_bus_probe; driver->drv.remove = sa1111_bus_remove; driver->drv.bus = &sa1111_bus_type; return driver_register(&driver->drv);}void sa1111_driver_unregister(struct sa1111_driver *driver){ driver_unregister(&driver->drv);}static int __init sa1111_init(void){ int ret = bus_register(&sa1111_bus_type); if (ret == 0) platform_driver_register(&sa1111_device_driver); return ret;}static void __exit sa1111_exit(void){ platform_driver_unregister(&sa1111_device_driver); bus_unregister(&sa1111_bus_type);}subsys_initcall(sa1111_init);module_exit(sa1111_exit);MODULE_DESCRIPTION("Intel Corporation SA1111 core driver");MODULE_LICENSE("GPL");EXPORT_SYMBOL(sa1111_select_audio_mode);EXPORT_SYMBOL(sa1111_set_audio_rate);EXPORT_SYMBOL(sa1111_get_audio_rate);EXPORT_SYMBOL(sa1111_set_io_dir);EXPORT_SYMBOL(sa1111_set_io);EXPORT_SYMBOL(sa1111_set_sleep_io);EXPORT_SYMBOL(sa1111_enable_device);EXPORT_SYMBOL(sa1111_disable_device);EXPORT_SYMBOL(sa1111_pll_clock);EXPORT_SYMBOL(sa1111_bus_type);EXPORT_SYMBOL(sa1111_driver_register);EXPORT_SYMBOL(sa1111_driver_unregister);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -