/* | |
* arch/arm/mach-zx297520v2/zx297520v2-clock.c | |
* | |
* Copyright (C) 2015 ZTE-TSP | |
* | |
* 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. | |
* | |
*/ | |
#include <linux/kernel.h> | |
#include <mach/board.h> | |
#include <mach/iomap.h> | |
#include <mach/debug.h> | |
#include <linux/io.h> | |
#include <mach/spinlock.h> | |
#include <mach/pcu.h> | |
//#include <mach/rpmsg.h> | |
#include <linux/mutex.h> | |
#include <linux/device.h> | |
#include <linux/module.h> | |
#include <linux/cdev.h> | |
#include <linux/fs.h> | |
#include <linux/uaccess.h> | |
#define USE_HW_SPINLOCK 1 | |
#if USE_HW_SPINLOCK | |
#define MACH_NR_SFLOCKS SFLOCK_NUM | |
#define MACH_NR_HWLOCKS HWLOCK_NUM | |
#ifndef CONFIG_ARCH_ZX297520V3_CAP | |
#define SELF_CORE_ID CORE_ID_PS | |
#else | |
#define SELF_CORE_ID CORE_ID_AP | |
#endif | |
/* now use 8*MACH_NR_SFLOCKS bytes */ | |
#define SOFTLOCK_DESC_BASE (SPINLOCK_SOFTLOCK_BASE) | |
#define SPINLOCK_DEBUG 1 | |
#if SPINLOCK_DEBUG | |
#define zspinlock_debug(fmt, ...) \ | |
printk(KERN_INFO fmt, ##__VA_ARGS__) | |
#else | |
#define zspinlock_debug(fmt, ...) | |
#endif | |
#define zspinlock_assert(_EXP) ZDRV_ASSERT(_EXP) | |
static DEFINE_MUTEX(zspinlock_mutex); | |
static unsigned long s_hwSpinlockMsr; | |
/**************************************************************************** | |
* Types | |
****************************************************************************/ | |
struct zte_softlock_desc { | |
unsigned long used; | |
unsigned long owner; | |
}; | |
/************************************************************************** | |
* Global Variables * | |
**************************************************************************/ | |
static volatile struct zte_softlock_desc *softlock_desc[MACH_NR_SFLOCKS]; | |
static const void __iomem __force *hwlock_regs[MACH_NR_HWLOCKS] = | |
{ | |
SHARED_DEVICE_REG1, | |
SHARED_DEVICE_REG2, | |
SHARED_DEVICE_REG3, | |
SHARED_DEVICE_REG4 | |
}; | |
extern void msleep(unsigned int msecs); | |
/******************************************************************************* | |
* Function: _hw_spin_lock | |
* Description:»ñȡӲ¼þËø£¬id 0~3 | |
* Parameters: | |
* Input: | |
* | |
* Output: | |
* | |
* Returns: | |
* | |
* | |
* Others: | |
********************************************************************************/ | |
static void _hw_spin_lock(unsigned long hwid) | |
{ | |
unsigned long tmp; | |
unsigned long msr; | |
local_irq_save(msr); | |
s_hwSpinlockMsr = msr; | |
while(ioread32(hwlock_regs[hwid])&0x1); | |
tmp = ioread32(hwlock_regs[hwid]); | |
tmp &= 0x00ffffff; | |
tmp |= (SELF_CORE_ID&0xff)<<24; | |
iowrite32(tmp, hwlock_regs[hwid]); | |
} | |
/******************************************************************************* | |
* Function: _hw_spin_unlock | |
* Description:ÊÍ·ÅÓ²¼þËø£¬id 0~3 | |
* Parameters: | |
* Input: | |
* | |
* Output: | |
* | |
* Returns: | |
* | |
* | |
* Others: | |
********************************************************************************/ | |
static void _hw_spin_unlock(unsigned long hwid) | |
{ | |
unsigned long tmp; | |
if(SELF_CORE_ID != (ioread32(hwlock_regs[hwid])&0xff000000)>>24){ | |
zspinlock_assert(0); | |
} | |
tmp = ioread32(hwlock_regs[hwid]); | |
tmp &= 0x00fffffe; | |
iowrite32(tmp, hwlock_regs[hwid]); | |
local_irq_restore(s_hwSpinlockMsr); | |
} | |
/******************************************************************************* | |
* Function: hw_spin_lock | |
* Description:»ñȡӲ¼þËø£¬id 0~2£¬ | |
* id 3±£Áô¸øÈí¼þËøÊ¹Óã¬ÍⲿÇý¶¯²»¿ÉÓᣠ| |
* Parameters: | |
* Input: | |
* | |
* Output: | |
* | |
* Returns: | |
* | |
* | |
* Others: | |
********************************************************************************/ | |
void hw_spin_lock(emhw_lock_id hwid) | |
{ | |
_hw_spin_lock(hwid); | |
// zspinlock_debug("cpu %d gets %d hardware lock!/n",SELF_CORE_ID,hwid); | |
} | |
/******************************************************************************* | |
* Function: hw_spin_unlock | |
* Description:Çý¶¯ÊÍ·ÅÓ²¼þËø£¬id 0~2 | |
* Parameters: | |
* Input: | |
* | |
* Output: | |
* | |
* Returns: | |
* | |
* | |
* Others: | |
********************************************************************************/ | |
void hw_spin_unlock(emhw_lock_id hwid) | |
{ | |
_hw_spin_unlock(hwid); | |
// zspinlock_debug("cpu %d releases %d hardware lock!/n",SELF_CORE_ID,hwid); | |
} | |
/******************************************************************************* | |
* Function: soft_spin_lock | |
* Description:Çý¶¯»ñµÃÈí¼þËø½Ó¿Ú | |
* Parameters: | |
* Input: sfid: Èí¼þËøid¡£ | |
* coreid: ±£³ÖidºÅΪsfidÈí¼þËøµÄcpuid¡£ | |
* Output: | |
* | |
* Returns: | |
* | |
* | |
* Others: | |
********************************************************************************/ | |
void soft_spin_lock(emsf_lock_id sfid) | |
{ | |
static unsigned long lock_count = 0; | |
softlock_loop: | |
while(softlock_desc[sfid]->owner != SELF_CORE_ID && softlock_desc[sfid]->used) | |
{ | |
lock_count++; | |
if(lock_count == 1000) | |
{ | |
lock_count = 0; | |
msleep(5); | |
} | |
} | |
_hw_spin_lock(SOFTLOCK_HWLOCK); | |
if(softlock_desc[sfid]->owner != SELF_CORE_ID && softlock_desc[sfid]->used) | |
{ | |
_hw_spin_unlock(SOFTLOCK_HWLOCK); | |
goto softlock_loop; | |
} | |
softlock_desc[sfid]->used ++; | |
softlock_desc[sfid]->owner = SELF_CORE_ID; | |
_hw_spin_unlock(SOFTLOCK_HWLOCK); | |
//zspinlock_debug("cpu %d releases %d software lock!/n",SELF_CORE_ID,sfid); | |
} | |
/******************************************************************************* | |
* Function: soft_spin_unlock | |
* Description:Óësoft_spin_lock¶ÔÓ¦µÄÊÍ·ÅÈí¼þËø½Ó¿Ú¡£ | |
* Parameters: | |
* Input: | |
* | |
* Output: | |
* | |
* Returns: | |
* | |
* | |
* Others: | |
********************************************************************************/ | |
void soft_spin_unlock(emsf_lock_id sfid) | |
{ | |
if(softlock_desc[sfid]->used){ | |
if(SELF_CORE_ID != softlock_desc[sfid]->owner){ | |
zspinlock_assert(0); | |
} | |
_hw_spin_lock(SOFTLOCK_HWLOCK); | |
softlock_desc[sfid]->used --; | |
if(softlock_desc[sfid]->used == 0) { | |
softlock_desc[sfid]->owner = 0x0; | |
} | |
_hw_spin_unlock(SOFTLOCK_HWLOCK); | |
//zspinlock_debug("cpu %d releases %d software lock!/n",SELF_CORE_ID,sfid); | |
} | |
} | |
/******************************************************************************* | |
* Function: soft_spin_lock_psm | |
* Description:Çý¶¯»ñµÃÈí¼þËø½Ó¿Ú | |
* Parameters: | |
* Input: sfid: Èí¼þËøid¡£ | |
* coreid: ±£³ÖidºÅΪsfidÈí¼þËøµÄcpuid¡£ | |
* Output: | |
* | |
* Returns: | |
* | |
* | |
* Others: | |
********************************************************************************/ | |
void soft_spin_lock_psm(emsf_lock_id sfid) | |
{ | |
softlock_loop: | |
while(softlock_desc[sfid]->owner != SELF_CORE_ID && softlock_desc[sfid]->used) | |
{ | |
} | |
_hw_spin_lock(SOFTLOCK_HWLOCK); | |
if(softlock_desc[sfid]->owner != SELF_CORE_ID && softlock_desc[sfid]->used) | |
{ | |
_hw_spin_unlock(SOFTLOCK_HWLOCK); | |
goto softlock_loop; | |
} | |
softlock_desc[sfid]->used ++; | |
softlock_desc[sfid]->owner = SELF_CORE_ID; | |
_hw_spin_unlock(SOFTLOCK_HWLOCK); | |
//zspinlock_debug("cpu %d releases %d software lock!/n",SELF_CORE_ID,sfid); | |
} | |
/******************************************************************************* | |
* Function: soft_spin_unlock_psm | |
* Description:Óësoft_spin_lock_psm¶ÔÓ¦µÄÊÍ·ÅÈí¼þËø½Ó¿Ú¡£ | |
* Parameters: | |
* Input: | |
* | |
* Output: | |
* | |
* Returns: | |
* | |
* | |
* Others: | |
********************************************************************************/ | |
void soft_spin_unlock_psm(emsf_lock_id sfid) | |
{ | |
soft_spin_unlock(sfid); | |
} | |
/******************************************************************************* | |
* Function: reg_spin_lock | |
* Description:Çý¶¯»ñµÃ¼Ä´æÆ÷Ëø½Ó¿Ú | |
* Parameters: | |
* Input: | |
* Output: | |
* | |
* Returns: | |
* | |
* | |
* Others: | |
********************************************************************************/ | |
void reg_spin_lock(void) | |
{ | |
_hw_spin_lock(REGLOCK_HWLOCK); | |
softlock_desc[REG_SFLOCK]->owner = SELF_CORE_ID; | |
} | |
EXPORT_SYMBOL(reg_spin_lock); | |
/******************************************************************************* | |
* Function: reg_spin_unlock | |
* Description:Óëreg_spin_lock¶ÔÓ¦µÄÊͷżĴæÆ÷Ëø½Ó¿Ú¡£ | |
* Parameters: | |
* Input: | |
* | |
* Output: | |
* | |
* Returns: | |
* | |
* | |
* Others: | |
********************************************************************************/ | |
void reg_spin_unlock(void) | |
{ | |
softlock_desc[REG_SFLOCK]->owner = 0x0; | |
_hw_spin_unlock(REGLOCK_HWLOCK); | |
} | |
EXPORT_SYMBOL(reg_spin_unlock); | |
/******************************************************************************* | |
* Function: softspinlock_init | |
* Description:Èí¼þËø³õʼ»¯¡£ | |
* Parameters: | |
* Input: | |
* | |
* Output: | |
* | |
* Returns: | |
* | |
* | |
* Others: | |
********************************************************************************/ | |
int softspinlock_init(void) | |
{ | |
int i; | |
for(i = 0; i<MACH_NR_SFLOCKS; i++){ | |
softlock_desc[i] = | |
(struct zte_softlock_desc *)(SOFTLOCK_DESC_BASE +i*sizeof(struct zte_softlock_desc)); | |
//softlock_desc[i]->used = 0; | |
//softlock_desc[i]->owner= CORE_ID_NUM; | |
} | |
zspinlock_debug("softspinlock init success base=0x%x!",(int)SOFTLOCK_DESC_BASE); | |
return 0; | |
} | |
typedef struct _zx29_softspinlock_ser | |
{ | |
struct cdev cdev; | |
struct module *owner; | |
struct class *classes; | |
const struct file_operations *ops; | |
}zx29_softspinlock_ser; | |
static zx29_softspinlock_ser softspinlock_zx29 = { | |
.owner = THIS_MODULE, | |
}; | |
int soft_spin_lock_get(emsf_lock_id sfid) | |
{ | |
if(sfid>=SFLOCK_NUM) | |
return -EFAULT; | |
if(softlock_desc[sfid]->owner != SELF_CORE_ID && softlock_desc[sfid]->used) | |
return 1; | |
else | |
return 0; | |
} | |
static long softspinlock_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |
{ | |
int ret = 0; | |
char k_arg; | |
switch(cmd) | |
{ | |
case SPINLOCK_GET_STATUS: | |
ret = copy_from_user(&k_arg, arg, sizeof(char)); | |
if (ret) | |
return -EFAULT; | |
if(k_arg>= SFLOCK_NUM) | |
return -EFAULT; | |
k_arg = (char)soft_spin_lock_get(k_arg); | |
ret = copy_to_user(arg,&k_arg, sizeof(char)); | |
if (ret) | |
return -EFAULT; | |
break; | |
default: | |
return -EPERM; | |
} | |
return ret; | |
} | |
static const struct file_operations softspinlock_ops = { | |
.owner = THIS_MODULE, | |
.unlocked_ioctl = softspinlock_ioctl, | |
}; | |
static int __init softspinlock_dev_init(void) | |
{ | |
int ret = 0; | |
dev_t dev; | |
softspinlock_zx29.ops = &softspinlock_ops; | |
ret = alloc_chrdev_region(&dev, 0, 1, "softspinlock"); | |
if (ret) | |
{ | |
printk(KERN_ERR "%s: softspinlock failed to allocate char dev region\n", | |
__FILE__); | |
return ret; | |
} | |
cdev_init(&softspinlock_zx29.cdev, &softspinlock_ops); | |
softspinlock_zx29.cdev.owner = softspinlock_zx29.owner; | |
ret = cdev_add(&softspinlock_zx29.cdev, dev, 1); | |
if (ret) | |
{ | |
unregister_chrdev_region(dev, 1); | |
printk(KERN_ERR "%s: softspinlock failed to add cdev\n", | |
__FILE__); | |
return ret; | |
} | |
softspinlock_zx29.classes = class_create(THIS_MODULE, "softspinlock"); | |
if (IS_ERR(softspinlock_zx29.classes)) | |
return PTR_ERR(softspinlock_zx29.classes); | |
device_create(softspinlock_zx29.classes, NULL, dev, NULL, "softspinlock"); | |
printk("[xxx] softspinlock dev inited! \n"); | |
return ret; | |
} | |
//arch_initcall(softspinlock_init); | |
#else | |
int softspinlock_init(void){return 0;} | |
void reg_spin_lock(void){} | |
void reg_spin_unlock(void){} | |
void soft_spin_lock(emsf_lock_id sfid){} | |
void soft_spin_unlock(emsf_lock_id sfid){} | |
void soft_spin_lock_psm(emsf_lock_id sfid){} | |
void soft_spin_unlock_psm(emsf_lock_id sfid){} | |
void hw_spin_lock(emhw_lock_id hwid){} | |
void hw_spin_unlock(emhw_lock_id hwid){} | |
static int __init softspinlock_dev_init(void){return 0;} | |
#endif | |
module_init(softspinlock_dev_init); | |