/****************************************************************************** ** ** FILE NAME : amazon_s_pmu.c ** PROJECT : amazon_s ** MODULES : PMU ** ** DATE : 1 Jan 2006 ** AUTHOR : Huang Xiaogang ** DESCRIPTION : amazon_s Power Management Unit driver ** COPYRIGHT : Copyright (c) 2006 ** Infineon Technologies AG ** Am Campeon 1-12, 85579 Neubiberg, Germany ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** HISTORY ** $Date $Author $Comment ** 01 Jan 2006 Huang Xiaogang modification & verification on amazon_s chip ** 31 Jan 2008 Lei Chuanhua Clean up source code and verified new modules *******************************************************************************/ #include #include #include #include #include #include /* Project header */ #include #include #include #define IFX_PMU_MAJOR 253 #define IFX_PMU_DRV_VERSION "0.0.1" //#define IFX_PMU_DBG #if defined(IFX_PMU_DBG) #define DBG_PMU(format, arg...) \ printk("%s: " format, __func__, ##arg) #define INLINE #else #define DBG_PMU(format, arg...) \ do {} while(0) #define INLINE inline #endif #define PMU_MAX_DELAY_COUNTER 1000000 static pmu_dev *amazon_s_pmu_dev; #ifdef IFX_PMU_DBG static char module_str[PMU_MODULE_MAX][16] = { "USB0PHY", "FPI1", "DFEV0", "DFEV1", "PCI", "DMA", "USB0", "UART0", "SPI", "DSL", "EBU", "LEDC", "GPTU", "VLYNQ", "FPI1", "AHB", "SDIO", "UART1", "RES1", "RES2", "DEU", "PPETC", "PPEEMA", "PPEDPLUS", "DDRMEM", "TDM", "USB1PHY", "USB1", "SWITCH", "DDRDPD", }; #endif /* IFX_PMU_DBG */ int pmu_set(int module, int enable) { u32 cr = 0; int counter = PMU_MAX_DELAY_COUNTER; if (module < 0 || module > PMU_MODULE_MAX) return -EINVAL; cr = *AMAZON_S_PMU_PWDCR; if(enable) cr &= ((~(1 << module)) & 0xFFF3FFFF); else cr |= (1 << module); *AMAZON_S_PMU_PWDCR = cr; DBG_PMU("AMAZON_S_PMU_PWDCR = 0x%08x\n", cr); /* Make sure it is enabled/disabled */ if (enable) { while ( --counter ){ /* Zero means enable */ if (!((*(unsigned long *)AMAZON_S_PMU_SR) & (1 << module))){ break; } } } else { while ( --counter ){ /* One means disable */ if (((*(unsigned long *)AMAZON_S_PMU_SR) & (1 << module))){ break; } } } if ( !counter ) panic("Failed to activate or disactivate module!\n"); DBG_PMU("Module %s %s!!!\n", module_str[module], enable ? "activated" : "disactivated"); return 0; } EXPORT_SYMBOL(pmu_set); int pmu_ddr_time_set(int module, int value) { u32 cr = 0; if ( module != PMU_MODULE_DDR_TIME) return -EINVAL; if (value < AMAZON_S_DDR_TIME_MIN || value > AMAZON_S_DDR_TIME_MAX) return -EINVAL; cr = *AMAZON_S_PMU_PWDCR; cr |= value << module; *AMAZON_S_PMU_PWDCR = cr; return 0; } EXPORT_SYMBOL(pmu_ddr_time_set); static INLINE int pmu_open(struct inode *inode, struct file *filp) { return 0; } static INLINE int pmu_release(struct inode *inode, struct file *filp) { return 0; } static int pmu_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int value, en; DBG_PMU("cmd=0x%x\n", cmd); switch(cmd) { case AMAZON_S_PMU_IOC_GET_STATUS: if (__put_user((int)(*AMAZON_S_PMU_SR), (int*)arg)) return -EFAULT; break; case AMAZON_S_PMU_IOC_SET_USB0_PHY: case AMAZON_S_PMU_IOC_SET_FPI1: case AMAZON_S_PMU_IOC_SET_DFEV0: case AMAZON_S_PMU_IOC_SET_DFEV1: case AMAZON_S_PMU_IOC_SET_PCI: case AMAZON_S_PMU_IOC_SET_DMA: case AMAZON_S_PMU_IOC_SET_USB0: case AMAZON_S_PMU_IOC_SET_UART0: case AMAZON_S_PMU_IOC_SET_SPI: case AMAZON_S_PMU_IOC_SET_DSL: case AMAZON_S_PMU_IOC_SET_EBU: case AMAZON_S_PMU_IOC_SET_LEDC: case AMAZON_S_PMU_IOC_SET_GPTC: case AMAZON_S_PMU_IOC_SET_VLYNQ: case AMAZON_S_PMU_IOC_SET_FPI0: case AMAZON_S_PMU_IOC_SET_AHB: case AMAZON_S_PMU_IOC_SET_SDIO: case AMAZON_S_PMU_IOC_SET_UART1: case AMAZON_S_PMU_IOC_SET_DEU: case AMAZON_S_PMU_IOC_SET_PPE_TC: case AMAZON_S_PMU_IOC_SET_PPE_EMA: case AMAZON_S_PMU_IOC_SET_PPE_DPLUS: case AMAZON_S_PMU_IOC_SET_DDR_MEM: case AMAZON_S_PMU_IOC_SET_TDM: case AMAZON_S_PMU_IOC_SET_USB1_PHY: case AMAZON_S_PMU_IOC_SET_USB1: case AMAZON_S_PMU_IOC_SET_SWITCH: case AMAZON_S_PMU_IOC_SET_DDR_DPD: if (__get_user(en, (int*)arg)) return -EFAULT; pmu_set((cmd & 0xff), en); break; case AMAZON_S_PMU_IOC_SET_DDR_TIME: if (__get_user(value, (int *)arg)) return -EFAULT; pmu_ddr_time_set((cmd & 0xff), value); break; default: return -ENOIOCTLCMD; } return 0; } static struct file_operations pmu_fops = { .owner = THIS_MODULE, .open = pmu_open, .release = pmu_release, .ioctl = pmu_ioctl, }; static int amazon_s_pmu_proc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int len=0; len += sprintf(buf+len,"AMAZON_S_PMU_PROC_READ\n"); len += sprintf(buf+len,"AMAZON_S_PMU_PWDCR(%p) : 0x%08x\n", AMAZON_S_PMU_PWDCR, *AMAZON_S_PMU_PWDCR); len += sprintf(buf+len,"AMAZON_S_PMU_SR(%p) : 0x%08x\n", AMAZON_S_PMU_SR, *AMAZON_S_PMU_SR); *eof = 1; return len; } static inline void amazon_s_pmu_version(void) { printk(KERN_INFO "Infineon Technologies PMU driver version %s \n", IFX_PMU_DRV_VERSION); } static int __init amazon_s_pmu_init(void) { int result; amazon_s_pmu_dev = (pmu_dev*)kmalloc(sizeof(pmu_dev), GFP_KERNEL); if(amazon_s_pmu_dev == NULL) return -ENOMEM; memset(amazon_s_pmu_dev, 0, sizeof(pmu_dev)); strcpy(amazon_s_pmu_dev->name, "amazon_s_pmu"); amazon_s_pmu_dev->minor = 0; result = register_chrdev(0, amazon_s_pmu_dev->name, &pmu_fops); if (result > 0) { amazon_s_pmu_dev->major = result; } else { printk("%s failed to register\n", __func__); kfree(amazon_s_pmu_dev); return result; } DBG_PMU("Major %d\n", result); amazon_s_pmu_version(); create_proc_read_entry("amazon_s_pmu", 0, NULL, amazon_s_pmu_proc_read, NULL); return 0; } static void __exit amazon_s_pmu_exit(void) { unregister_chrdev(amazon_s_pmu_dev->major, amazon_s_pmu_dev->name); remove_proc_entry("amazon_s_pmu", NULL); kfree(amazon_s_pmu_dev); } MODULE_AUTHOR("Huang Xiaogang "); MODULE_DESCRIPTION("amazon_s Power Management Unit driver"); MODULE_LICENSE("GPL"); module_init(amazon_s_pmu_init); module_exit(amazon_s_pmu_exit);