diff options
Diffstat (limited to 'drivers/staging')
199 files changed, 45792 insertions, 80 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 9569b2366d8..63b640c3c1e 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -24,6 +24,16 @@ menuconfig STAGING if STAGING +config AB5500_SIM + bool "ST-Ericsson AB5500 SIM Interface driver" + depends on AB5500_CORE + help + SIM Interface driver provides interface to configure + various parameters of AB5550 SIM Level Shifter.Support provided are: + Configure Pull up on sim lines + Configure Operation Mode + Notify Sim Insert/Extract Interrupt + source "drivers/staging/et131x/Kconfig" source "drivers/staging/slicoss/Kconfig" @@ -152,4 +162,12 @@ source "drivers/staging/mei/Kconfig" source "drivers/staging/nvec/Kconfig" +source "drivers/staging/cw1200/Kconfig" + +source "drivers/staging/mmio/Kconfig" + +source "drivers/staging/nmf-cm/Kconfig" + +source "drivers/staging/camera_flash/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index adac8a21754..ec84d500350 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -67,3 +67,8 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ obj-$(CONFIG_DRM_PSB) += gma500/ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_MFD_NVEC) += nvec/ +obj-$(CONFIG_CW1200) += cw1200/ +obj-$(CONFIG_U8500_MMIO) += mmio/ +obj-$(CONFIG_U8500_FLASH) += camera_flash/ +obj-$(CONFIG_U8500_CM) += nmf-cm/ +obj-$(CONFIG_AB5500_SIM) += ab5500_sim/ diff --git a/drivers/staging/ab5500_sim/Makefile b/drivers/staging/ab5500_sim/Makefile new file mode 100644 index 00000000000..520717e4dd7 --- /dev/null +++ b/drivers/staging/ab5500_sim/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AB5500_SIM) += ab5500-sim.o diff --git a/drivers/staging/ab5500_sim/ab5500-sim.c b/drivers/staging/ab5500_sim/ab5500-sim.c new file mode 100644 index 00000000000..d222a22ed24 --- /dev/null +++ b/drivers/staging/ab5500_sim/ab5500-sim.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) ST Ericsson SA 2010 + * + * Sim Interface driver for AB5500 + * + * License Terms: GNU General Public License v2 + * Author: Bibek Basu <bibek.basu@stericsson.com> + */ +#include <linux/init.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <linux/kobject.h> +#include <linux/mfd/abx500.h> +#include <linux/mfd/abx500/ab5500.h> +#include <linux/io.h> +#include <linux/err.h> + +#define USIM_SUP2_REG 0x13 +#define USIM_SUP_REG 0x14 +#define USIM_SIMCTRL_REG 0x17 +#define USIM_SIMCTRL2_REG 0x18 +#define USIM_USBUICC_REG 0x19 +#define USIM_USBUICC2_REG 0x20 +#define SIM_DAT_PULLUP_10K 0x0F +#define SIM_LDO_1_8V 1875000 +#define SIM_LDO_2_8V 2800000 +#define SIM_LDO_2_9V 2900000 + +enum shift { + SHIFT0, + SHIFT1, + SHIFT2, + SHIFT3, + SHIFT4, + SHIFT5, + SHIFT6, + SHIFT7, +}; + +enum mask { + MASK1 = 1, + MASK3 = 3, + MASK7 = 7, +}; + +enum sim_mode { + OFF_MODE, + LOW_PWR, + PWRCTRL, + FULL_PWR, +}; +/** + * struct ab5500_sim - ab5500 Sim Interface device information + * @dev: pointer to the structure device + * @lock: mutex lock + * @sim_int_status: Sim presence status + * @irq_base: Base of the two irqs + */ +struct ab5500_sim { + struct device *dev; + struct mutex lock; + bool sim_int_status; + u8 irq_base; +}; + +/* Exposure to the sysfs interface */ +int ab5500_sim_weak_pulldforce(struct device *dev, + struct device_attribute *attr, + const char *user_buf, size_t count) +{ + unsigned long user_val; + int err; + bool enable; + + err = strict_strtoul(user_buf, 0, &user_val); + if (err) + return -EINVAL; + enable = user_val ? true : false; + err = abx500_mask_and_set(dev, AB5500_BANK_SIM_USBSIM, + USIM_USBUICC2_REG, MASK1 << SHIFT5, user_val << SHIFT5); + if (err) + return -EINVAL; + return count; +} + +int ab5500_sim_load_sel(struct device *dev, + struct device_attribute *attr, + const char *user_buf, size_t count) +{ + unsigned long user_val; + int err; + bool enable; + + err = strict_strtoul(user_buf, 0, &user_val); + if (err) + return -EINVAL; + enable = user_val ? true : false; + err = abx500_mask_and_set(dev, AB5500_BANK_SIM_USBSIM, + USIM_USBUICC_REG, MASK1 << SHIFT1, user_val << SHIFT1); + if (err) + return -EINVAL; + return count; +} + +int ab5500_sim_mode_sel(struct device *dev, + struct device_attribute *attr, + const char *user_buf, size_t count) +{ + unsigned long user_val; + int err; + + err = strict_strtoul(user_buf, 0, &user_val); + if (err) + return -EINVAL; + err = abx500_mask_and_set(dev, AB5500_BANK_SIM_USBSIM, + USIM_SIMCTRL2_REG, MASK3 << SHIFT4, user_val << SHIFT4); + if (err) + return -EINVAL; + return count; +} + +int ab5500_sim_dat_pullup(struct device *dev, + struct device_attribute *attr, + const char *user_buf, size_t count) +{ + unsigned long user_val; + int err; + + err = strict_strtoul(user_buf, 0, &user_val); + if (err) + return -EINVAL; + err = abx500_mask_and_set(dev, AB5500_BANK_SIM_USBSIM, + USIM_SIMCTRL_REG, MASK7, user_val); + if (err) + return -EINVAL; + return count; +} + +int ab5500_sim_enable_pullup(struct device *dev, + struct device_attribute *attr, + const char *user_buf, size_t count) +{ + unsigned long user_val; + int err; + bool enable; + + err = strict_strtoul(user_buf, 0, &user_val); + if (err) + return -EINVAL; + enable = user_val ? true : false; + err = abx500_mask_and_set(dev, AB5500_BANK_SIM_USBSIM, + USIM_SIMCTRL_REG, MASK1 << SHIFT3, enable << SHIFT3); + if (err) + return -EINVAL; + return count; +} + +static ssize_t ab5500_simoff_int(struct device *dev, + struct device_attribute *devattr, char *user_buf) +{ + struct ab5500_sim *di = dev_get_drvdata(dev); + int len; + + mutex_lock(&di->lock); + len = sprintf(user_buf, "%d\n", di->sim_int_status); + mutex_unlock(&di->lock); + return len; +} + +static DEVICE_ATTR(enable_pullup, S_IWUSR, NULL, ab5500_sim_enable_pullup); +static DEVICE_ATTR(dat_pullup, S_IWUSR, NULL, ab5500_sim_dat_pullup); +static DEVICE_ATTR(mode_sel, S_IWUSR, NULL, ab5500_sim_mode_sel); +static DEVICE_ATTR(load_sel, S_IWUSR, NULL, ab5500_sim_load_sel); +static DEVICE_ATTR(weak_pulldforce, S_IWUSR, NULL, ab5500_sim_weak_pulldforce); +static DEVICE_ATTR(simoff_int, S_IRUGO, ab5500_simoff_int, NULL); + +static struct attribute *ab5500_sim_attributes[] = { + &dev_attr_enable_pullup.attr, + &dev_attr_dat_pullup.attr, + &dev_attr_mode_sel.attr, + &dev_attr_load_sel.attr, + &dev_attr_weak_pulldforce.attr, + &dev_attr_simoff_int.attr, + NULL +}; + +static const struct attribute_group ab5500sim_attr_grp = { + .attrs = ab5500_sim_attributes, +}; + +static irqreturn_t ab5500_sim_irq_handler(int irq, void *irq_data) +{ + struct platform_device *pdev = irq_data; + struct ab5500_sim *data = platform_get_drvdata(pdev); + + if (irq == data->irq_base) + data->sim_int_status = true; + else + data->sim_int_status = false; + sysfs_notify(&pdev->dev.kobj, NULL, "simoff_int"); + + return IRQ_HANDLED; +} + +static int __devexit ab5500_sim_remove(struct platform_device *pdev) +{ + struct ab5500_sim *di = platform_get_drvdata(pdev); + int irq = platform_get_irq_byname(pdev, "SIMOFF"); + + if (irq >= 0) { + free_irq(irq, di); + irq++; + free_irq(irq, di); + } + sysfs_remove_group(&pdev->dev.kobj, &ab5500sim_attr_grp); + platform_set_drvdata(pdev, NULL); + kfree(di); + + return 0; +} + +static int __devinit ab5500_sim_probe(struct platform_device *pdev) +{ + int ret = 0; + int irq; + struct ab5500_sim *di = + kzalloc(sizeof(struct ab5500_sim), GFP_KERNEL); + if (!di) { + ret = -ENOMEM; + goto error_alloc; + } + dev_info(&pdev->dev, "ab5500_sim_driver PROBE\n"); + irq = platform_get_irq_byname(pdev, "SIMOFF"); + if (irq < 0) { + dev_err(&pdev->dev, "Get irq by name failed\n"); + ret = irq; + goto exit; + } + di->irq_base = irq; + di->dev = &pdev->dev; + mutex_init(&di->lock); + platform_set_drvdata(pdev, di); + /* sysfs interface to configure sim reg from user space */ + if (sysfs_create_group(&pdev->dev.kobj, &ab5500sim_attr_grp) < 0) { + dev_err(&pdev->dev, " Failed creating sysfs group\n"); + ret = -ENOMEM; + goto error_sysfs; + } + ret = request_threaded_irq(irq, NULL, ab5500_sim_irq_handler, + IRQF_NO_SUSPEND , "ab5500-sim", pdev); + if (ret < 0) { + dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret); + goto error_irq; + } + /* this is the contiguous irq for sim removal,falling edge */ + irq = irq + 1; + ret = request_threaded_irq(irq, NULL, ab5500_sim_irq_handler, + IRQF_NO_SUSPEND , "ab5500-sim", pdev); + if (ret < 0) { + dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret); + free_irq(--irq, di); + goto error_irq; + } + return ret; +error_irq: + sysfs_remove_group(&pdev->dev.kobj, &ab5500sim_attr_grp); +error_sysfs: + platform_set_drvdata(pdev, NULL); +exit: + kfree(di); +error_alloc: + return ret; +} + +static struct platform_driver ab5500_sim_driver = { + .probe = ab5500_sim_probe, + .remove = __devexit_p(ab5500_sim_remove), + .driver = { + .name = "ab5500-sim", + .owner = THIS_MODULE, + }, +}; + +static int __init ab5500_sim_init(void) +{ + return platform_driver_register(&ab5500_sim_driver); +} + +static void __exit ab5500_sim_exit(void) +{ + platform_driver_unregister(&ab5500_sim_driver); +} + +module_init(ab5500_sim_init); +module_exit(ab5500_sim_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Bibek Basu"); +MODULE_ALIAS("platform:ab5500-sim"); +MODULE_DESCRIPTION("AB5500 sim interface driver"); diff --git a/drivers/staging/ab5500_sim/sysfs-sim b/drivers/staging/ab5500_sim/sysfs-sim new file mode 100644 index 00000000000..b809b21e39e --- /dev/null +++ b/drivers/staging/ab5500_sim/sysfs-sim @@ -0,0 +1,83 @@ +What: /sys/devices/platform/ab5500-core.0/ab5500-sim.4/ +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu <bibek.basu@stericsson.com> +Description: + The /sys/devices/.../ab5500-sim.4 directory contains attributes + allowing the user space to check and configure ab5500 sim level + shifter interface caracteristics for communication to SIM card + +What: /sys/devices/.../enable_pullup +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu <bibek.basu@stericsson.com> +Description: + The /sys/devices/.../ab5500-sim.4/enable_pullup attribute allows + the user space to configure if internal pull up in SIMIO lines + has to be enabled or disabled. For enabling write 1 to the file + and 0 for disabling + + +What: /sys/devices/.../dat_pullup +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu <bibek.basu@stericsson.com> +Description: + The /sys/devices/.../ab5500-sim.4/dat_pullup attribute allows + the user space to configure the resistance value for internal + pull up in SIMIO lines. Following value can be written on the file + 0 SIM_DAT pull-up disabled + 1 SIM_DAT pull-up 4kOhm + 2 SIM_DAT pull-up 5kOhm + 3 SIM_DAT pull-up 6kOhm + 4 SIM_DAT pull-up 7kOhm + 5 SIM_DAT pull-up 8kOhm + 6 SIM_DAT pull-up 9kOhm + 7 SIM_DAT pull-up 10kOhm + +What: /sys/devices/.../mode_sel +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu <bibek.basu@stericsson.com> +Description: + The /sys/devices/.../ab5500-sim.4/mode_sel attribute allows + the user space to configure the mode at which the level shifter + will work. Following value can be written on the file + 0 TG mode and LI mode off + 1 TG mode on + 2 LI mode on + 3 TG mode and LI mode off + +What: /sys/devices/.../load_sel +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu <bibek.basu@stericsson.com> +Description: + The /sys/devices/.../ab5500-sim.4/load_sel attribute allows + the user space to configure the load on the USBUICC lines. + Following value can be written on the file. + 0 Data line load < 21pF + 1 Data line load 21-30pF + +What: /sys/devices/.../weak_pulldforce +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu <bibek.basu@stericsson.com> +Description: + The /sys/devices/.../ab5500-sim.4/weak_pulldforce attribute allows + the user space to configure the weak pull down on the USBUICC lines. + Following value can be written on the file. + 0 USB-UICC data lines weak pull down active + 1 USB-UICC data lines weak pull down not active + +What: /sys/devices/.../simoff_int +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu <bibek.basu@stericsson.com> +Description: + The /sys/devices/.../ab5500-sim.4/simoff_int attribute allows + the user space to poll this file and get notified in case a sim + hot swap has happened. a zero means sim extracetd and a one means + inserted. + + diff --git a/drivers/staging/camera_flash/Kconfig b/drivers/staging/camera_flash/Kconfig new file mode 100644 index 00000000000..187217d763f --- /dev/null +++ b/drivers/staging/camera_flash/Kconfig @@ -0,0 +1,7 @@ + +config U8500_FLASH + bool "ST-Ericsson Flash (Camera) Driver" + depends on ARCH_U8500 + help + Adds ST-Ericsson Flash (Camera) Driver + diff --git a/drivers/staging/camera_flash/Makefile b/drivers/staging/camera_flash/Makefile new file mode 100644 index 00000000000..bf2f5aa2dd3 --- /dev/null +++ b/drivers/staging/camera_flash/Makefile @@ -0,0 +1,5 @@ +export ADP1653_SUPPORT +EXTRA_CFLAGS += -DADP1653_SUPPORT +obj-$(CONFIG_U8500_FLASH) := camera_flash.o +camera_flash-y := flash_common.o +camera_flash-y += adp1653.o diff --git a/drivers/staging/camera_flash/adp1653.c b/drivers/staging/camera_flash/adp1653.c new file mode 100644 index 00000000000..f7483eac11f --- /dev/null +++ b/drivers/staging/camera_flash/adp1653.c @@ -0,0 +1,537 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * adp1653: Driver Adp1653 HPLED flash driver chip. This driver + * currently support I2C interface, 2bit interface is not supported. + * Author: Pankaj Chauhan/pankaj.chauhan@stericsson.com for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/version.h> +#include <asm/mach-types.h> +#include "flash_common.h" +#include "adp1653.h" +#include "camera_flash.h" +#include "adp1653_plat.h" + +/* This data is platform specific for 8500 href-v1 platform, + * Ideally this should be supplied from platform code + */ + +static int adapter_i2c2 = 2; +static int flash_position = 0; +module_param(adapter_i2c2, int, S_IRUGO); +MODULE_PARM_DESC(adapter_i2c2, "use the given I2C adaptater to communicate with the chip"); +module_param(flash_position, int, S_IRUGO); +MODULE_PARM_DESC(flash_position, "the position of the flash chip (0=PRIMARY, 1=SECONDARY)"); + + +int __flash_gpio_to_irq(int gpio) +{ + + return NOMADIK_GPIO_TO_IRQ(gpio); +} + +#define DEBUG_LOG(...) printk(KERN_DEBUG "Adp1653 flash driver: " __VA_ARGS__) + +#define ADP1653_SUPPORTED_MODES (FLASH_MODE_VIDEO_LED | FLASH_MODE_STILL_LED | \ + FLASH_MODE_STILL_LED_EXTERNAL_STROBE | \ + FLASH_MODE_AF_ASSISTANT | FLASH_MODE_INDICATOR) + +#define ADP1653_SELFTEST_SUPPORTED_MODES (FLASH_SELFTEST_CONNECTION | FLASH_SELFTEST_FLASH_WITH_STROBE | \ + FLASH_SELFTEST_VIDEO_LIGHT | FLASH_SELFTEST_AF_LIGHT | FLASH_SELFTEST_INDICATOR | FLASH_SELFTEST_TORCH_LIGHT) + +static int adp1653_trigger_strobe(void *priv_data, int enable); + +static int adp1653_get_modes(void *priv_data,unsigned long *modes) +{ + int err; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data; + err = i2c_smbus_read_byte_data(priv_p->i2c_client, FAULT_STATUS_REG); + if (err) + *modes = 0x0; + else + *modes = ADP1653_SUPPORTED_MODES; + return 0; +} + +static int adp1653_get_mode_details(void *priv_data, unsigned long mode, +struct flash_mode_details *details_p) +{ + int err = 0; + memset(details_p,0,sizeof(struct flash_mode_details)); + + details_p->led_type = 2; + + /* Still LED settings*/ + details_p->nbFaultRegisters = 1; + if(mode & (FLASH_MODE_STILL_LED | FLASH_MODE_STILL_LED_EXTERNAL_STROBE)){ + details_p->max_intensity_uAmp = FLASH_MAX_INTENSITY; + details_p->min_intensity_uAmp = FLASH_MIN_INTENSITY; + details_p->max_strobe_duration_uSecs = FLASH_MAX_STROBE_DURATION; + details_p->feature_bitmap = INTENSITY_PROGRAMMABLE | DURATION_PROGRAMMABLE; + goto out; + } + /*Video LED settings*/ + if(mode & FLASH_MODE_VIDEO_LED){ + details_p->max_intensity_uAmp = TORCH_MAX_INTENSITY; + details_p->min_intensity_uAmp = TORCH_MIN_INTENSITY; + details_p->max_strobe_duration_uSecs = 0; + details_p->feature_bitmap = INTENSITY_PROGRAMMABLE; + goto out; + } + /*Privacy Indicator settings */ + if(mode & FLASH_MODE_INDICATOR){ + details_p->max_intensity_uAmp = ILED_MAX_INTENSITY; + details_p->min_intensity_uAmp = ILED_MIN_INTENSITY; + details_p->max_strobe_duration_uSecs = 0; + details_p->feature_bitmap = INTENSITY_PROGRAMMABLE; + goto out; + } + DEBUG_LOG("Mode %lx, not supported\n",mode); + err = EINVAL; +out: + return err; +} + +static int adp1653_enable_flash_mode(void *priv_data, + unsigned long mode, int enable) +{ + int err = 0; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data; + + if(enable){ + + if((!(mode & ADP1653_SUPPORTED_MODES)) && + (mode != FLASH_MODE_NONE)) { + DEBUG_LOG("Unsupported mode %lx\n",mode); + err = -EINVAL; + goto out; + } + /*Nothing to be done in enabling, just set current mode and return*/ + /*May be enable disable can be done here but why not enable in + *probe and keep it on always + */ + adp1653_trigger_strobe(priv_p,0); + priv_p->curr_mode = mode; + }else{ + adp1653_trigger_strobe(priv_p,0); + priv_p->curr_mode =0; + } +out: + return err; +} + +static int adp1653_configure_flash_mode(void *priv,unsigned long mode, +struct flash_mode_params *params_p) +{ + int err = 0; + unsigned char intensity_code; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv; + + if(!(mode & ADP1653_SUPPORTED_MODES)){ + DEBUG_LOG("Mode %lx not supported\n",mode); + err = -EINVAL; + goto out; + } + switch(mode){ + case FLASH_MODE_STILL_LED: + case FLASH_MODE_STILL_LED_EXTERNAL_STROBE: + { + FLASH_UAMP_TO_CODE(intensity_code,params_p->intensity_uAmp); + if(params_p->duration_uSecs){ + DURATION_USEC_TO_CODE(priv_p->flash_duration, + params_p->duration_uSecs); + DEBUG_LOG("Duration %lu, code 0x%x\n",params_p->duration_uSecs, + priv_p->flash_duration); + priv_p->flash_duration |= TIMER_ENABLE; + }else{ + priv_p->flash_duration = 0; + } + priv_p->flash_intensity = intensity_code << 3; + } + break; + case FLASH_MODE_VIDEO_LED: + { + TORCH_UAMP_TO_CODE(intensity_code,params_p->intensity_uAmp); + DEBUG_LOG("Torch mode setting intensity 0x%x, current(uA) %lu\n", + intensity_code,params_p->intensity_uAmp); + priv_p->torch_intensity = intensity_code << 3; + } + break; + case FLASH_MODE_INDICATOR: + { + ILED_UAMP_TO_CODE(intensity_code,params_p->intensity_uAmp); + DEBUG_LOG("ILED setting intensity 0x%x, current(uA) %lu\n", + intensity_code,params_p->intensity_uAmp); + priv_p->indicator_intensity = intensity_code; + } + break; + default: + err = -EINVAL; + DEBUG_LOG("Unsupported mode %lx\n",mode); + break; + } + + if((mode == FLASH_MODE_STILL_LED_EXTERNAL_STROBE) || (mode == FLASH_MODE_STILL_LED)) + { + adp1653_trigger_strobe(priv_p,0); + DEBUG_LOG("CONFIG_TIMER_REG : 0x%x\n",priv_p->flash_duration); + DEBUG_LOG("OUTPUT_SEL_REG : 0x%x\n",priv_p->flash_intensity); + + /*TimeOut Must be programmed before Intensity*/ + err = i2c_smbus_write_byte_data(priv_p->i2c_client,CONFIG_TIMER_REG, + priv_p->flash_duration); + if(err){ + DEBUG_LOG("I2C: Unsable to write timer config, err %d\n",err); + goto out; + } + err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG, + priv_p->flash_intensity); + if(err){ + DEBUG_LOG("I2C: Unable to write OUTPUT_SEL_REG , err %d\n",err); + goto out; + } + } +out: + return err; +} + +static int adp1653_set_intensity(struct adp1653_priv_data *priv_p, uint8_t intensity) +{ + return i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,intensity); +} + +static int adp1653_strobe_still_led(struct adp1653_priv_data *priv_p,int enable) +{ + int err=0,gpio_val; + uint8_t intensity,duration; + + if(enable){ + intensity = priv_p->flash_intensity; + duration = priv_p->flash_duration; + gpio_val = 1; + }else{ + intensity = 0; + duration = 0; + gpio_val = 0; + } + + err = adp1653_set_intensity(priv_p,intensity); + if(err){ + DEBUG_LOG("I2C: Unable to write OUTPUT_SEL_REG reg, err %d\n",err); + goto out; + } + + /*TimeOut Must be programmed before Intensity*/ + err = i2c_smbus_write_byte_data(priv_p->i2c_client,CONFIG_TIMER_REG, + priv_p->flash_duration); + if(err){ + DEBUG_LOG("I2C: Unsable to write timer config, err %d\n",err); + goto out; + } + err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,intensity); + if(err){ + DEBUG_LOG("I2C: Unable to write OUTPUT_SEL_REG, err %d\n",err); + goto out; + } + +out: + return err; +} + +static int adp1653_trigger_strobe(void *priv, int enable) +{ + int err = 0; + uint8_t intensity; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv; + + switch(priv_p->curr_mode){ + case FLASH_MODE_STILL_LED: + case FLASH_MODE_STILL_LED_EXTERNAL_STROBE: + err = adp1653_strobe_still_led(priv_p,enable); + break; + case FLASH_MODE_VIDEO_LED: + { + if(enable) + intensity = priv_p->torch_intensity; + else + intensity = 0; + err = adp1653_set_intensity(priv_p,intensity); + } + break; + case FLASH_MODE_INDICATOR: + { + if(enable) + intensity = priv_p->indicator_intensity; + else + intensity =0; + err = adp1653_set_intensity(priv_p,intensity); + } + break; + default: + DEBUG_LOG("Unsupported mode %lx\n",priv_p->curr_mode); + goto out; + } + if(err){ + DEBUG_LOG("Unable to enable/disable %d, strobe. Mode %lx, err %d\n",enable, + priv_p->curr_mode,err); + goto out; + } + disable_irq(priv_p->i2c_client->irq); + if(enable) + SET_FLASH_STATUS(priv_p->status,FLASH_STATUS_LIT); + else + CLR_FLASH_STATUS(priv_p->status,FLASH_STATUS_LIT); + + enable_irq(priv_p->i2c_client->irq); + +out: + return err; +} +#define FLASH_ERR_ALL (FLASH_ERR_OVER_CHARGE |FLASH_ERR_OVER_HEAT | \ + FLASH_ERR_SHORT_CIRCUIT | FLASH_ERR_TIMEOUT | \ + FLASH_ERR_OVER_VOLTAGE) +int adp1653_get_status(void *priv_data,unsigned long *status) +{ + struct adp1653_priv_data *priv_p= (struct adp1653_priv_data *)priv_data; + disable_irq(priv_p->i2c_client->irq); + if(priv_p->fault){ + if(priv_p->fault & OVER_VOLTAGE_FAULT) + SET_FLASH_ERROR(priv_p->status,FLASH_ERR_OVER_VOLTAGE); + if(priv_p->fault & TIMEOUT_FAULT) + SET_FLASH_ERROR(priv_p->status,FLASH_ERR_TIMEOUT); + if(priv_p->fault & OVER_TEMPERATURE_FAULT) + SET_FLASH_ERROR(priv_p->status,FLASH_ERR_OVER_HEAT); + if(priv_p->fault & SHORT_CIRCUIT_FAULT){ + CLR_FLASH_STATUS(priv_p->status,FLASH_STATUS_READY); + SET_FLASH_STATUS(priv_p->status,FLASH_STATUS_BROKEN); + SET_FLASH_ERROR(priv_p->status,FLASH_ERR_SHORT_CIRCUIT); + } + priv_p->fault =0; + }else{ + CLR_FLASH_ERROR(priv_p->status,FLASH_ERR_ALL); + } + enable_irq(priv_p->i2c_client->irq); + *status = priv_p->status; + return 0; +} + +int adp1653_get_selftest_modes(void *priv_data, unsigned long *modes) +{ + int err; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data; + err = i2c_smbus_read_byte_data(priv_p->i2c_client, FAULT_STATUS_REG); + if (err) *modes = 0x0; + else *modes = ADP1653_SELFTEST_SUPPORTED_MODES; + return 0; +} + +int adp1653_get_fault_registers(void *priv_data, unsigned long mode, unsigned long *status) +{ + int err = 0; + struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data; + + *status = i2c_smbus_read_byte_data(priv_p->i2c_client, FAULT_STATUS_REG); + + /* clear fault register */ + err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,0); + if(0 != err) + { + DEBUG_LOG("Unable to write OUTPUT_SEL_REG, err %d\n",err); + } + return err; +} + +struct flash_chip_ops adp1653_ops = { + .get_modes = adp1653_get_modes, + .get_mode_details = adp1653_get_mode_details, + .get_status = adp1653_get_status, + .enable_flash_mode = adp1653_enable_flash_mode, + .configure_flash_mode = adp1653_configure_flash_mode, + .trigger_strobe = adp1653_trigger_strobe, + .get_selftest_modes = adp1653_get_selftest_modes, + .get_fault_registers = adp1653_get_fault_registers +}; + +static irqreturn_t adp1653_irq_hdlr(int irq_no,void *data) +{ + int err; + struct adp1653_priv_data *priv_p= (struct adp1653_priv_data *)data; + + priv_p->fault = i2c_smbus_read_byte_data(priv_p->i2c_client, + FAULT_STATUS_REG); + DEBUG_LOG("Got Fault, status 0x%x\n",priv_p->fault); + /*Writing 0 to OUTPUT_SEL_REG clears the interrtup + *and FAULT_STATUS_REG register + */ + err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,0); + if(err) + DEBUG_LOG("Unable to write OUTPUT_SEL_REG to clr intr, err %d\n",err); + /*TBD: send even to user process*/ + return IRQ_HANDLED; +} +static int __devinit adp1653_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err = 0; + struct flash_chip *flash_chip_p=NULL; + struct adp1653_priv_data *priv_p=NULL; + struct adp1653_platform_data *pdata = client->dev.platform_data; + + DEBUG_LOG("> adp1653_probe\n"); + + priv_p = kzalloc(sizeof(struct adp1653_priv_data),GFP_KERNEL); + if(!priv_p){ + DEBUG_LOG("Kmalloc failed for priv data\n"); + err = ENOMEM; + goto err_priv; + } + priv_p->i2c_client = client; + flash_chip_p = kzalloc(sizeof(struct flash_chip),GFP_KERNEL); + if(!flash_chip_p){ + DEBUG_LOG("Kmalloc failed for flash_chip_p"); + err = ENOMEM; + goto err_flash_chip_alloc; + } + + if (!pdata) { + dev_err(&client->dev, + "%s: No platform data supplied.\n", __func__); + err = -EINVAL; + goto err_pdata; + } + + flash_chip_p->priv_data = priv_p; + flash_chip_p->ops = &adp1653_ops; + SET_FLASHCHIP_TYPE(flash_chip_p,FLASH_TYPE_HPLED); + SET_FLASHCHIP_ID(flash_chip_p,ADP1653_ID); + + strncpy(flash_chip_p->name,"Adp1653",FLASH_NAME_SIZE); + + i2c_set_clientdata(client,priv_p); + /*Request GPIO and Register IRQ if supported by platform and flash chip*/ + + err = gpio_request(pdata->enable_gpio,"Camera LED flash Enable"); + if(err){ + DEBUG_LOG("Unable to get GPIO %d, for enable\n",pdata->enable_gpio); + goto err_pdata; + } + + err = gpio_direction_output(pdata->enable_gpio, 1); + if(err){ + DEBUG_LOG("Unable to set GPIO %u in output mode, err %d\n",pdata->enable_gpio,err); + gpio_free(pdata->enable_gpio); + goto err_gpio_set; + } + gpio_set_value_cansleep(pdata->enable_gpio, 1); + + err = request_threaded_irq(gpio_to_irq(pdata->irq_no),NULL,adp1653_irq_hdlr, + IRQF_ONESHOT|IRQF_TRIGGER_FALLING, + "Adp1653 flash",priv_p); + if(err){ + DEBUG_LOG("Unable to register flash IRQ handler, irq %d, err %d\n", + pdata->irq_no,err); + goto err_irq; + } + + err = register_flash_chip(flash_position,flash_chip_p); + if(err){ + DEBUG_LOG("Failed to register Adp1653 as flash for %s camera\n", + (flash_position?"Primary":"Secondary")); + goto err_register; + } + SET_FLASH_STATUS(priv_p->status,FLASH_STATUS_READY); + DEBUG_LOG("< adp1653_probe ok\n"); + return err; +err_register: + if(pdata->irq_no) + free_irq(pdata->irq_no,NULL); +err_irq: + gpio_set_value_cansleep(pdata->enable_gpio, 0); +err_gpio_set: + if(pdata->enable_gpio) + gpio_free(pdata->enable_gpio); +err_pdata: + if(flash_chip_p) + kfree(flash_chip_p); +err_flash_chip_alloc: + if(priv_p) + kfree(priv_p); +err_priv: + DEBUG_LOG("< adp1653_probe (%d)\n", err); + return err; +} + +static int __devexit adp1653_remove(struct i2c_client *client) +{ + int err=0; + /*Nothing here yet, implement it later.*/ + return err; +} +static const struct i2c_device_id adp1653_id[] = { + { "adp1653", 0}, + {} +}; +static struct i2c_driver adp1653_i2c_driver = { + .driver = { + .name = "adp1653", + .owner = THIS_MODULE, + }, + .probe = adp1653_probe, + .remove = __devexit_p(adp1653_remove), + .id_table = adp1653_id, +}; + +int adp1653_init(void){ + int err = 0; + struct i2c_adapter *adap_p; + struct i2c_board_info info; + + /* Registration of I2C flash device is platform specific code + * Ideally it should be done from kernel (arch/arm/mach-XXX). + * Do it locally till the time it gets into platform code + * OR This portion (registration of device) and flash chip init + * Routine can be moved to Flash chip module init. */ + DEBUG_LOG("getting I2C adaptor %d\n",adapter_i2c2); + adap_p = i2c_get_adapter(adapter_i2c2); + if(!adap_p){ + DEBUG_LOG("Unable to get I2C adaptor\n"); + goto out; + } + memset(&info,0,sizeof( struct i2c_board_info)); + + strcpy(&info.type[0],"adp1653"); + DEBUG_LOG("trying to register %s at position %d\n", + info.type, + flash_position); + + /* I2C framework expects least significant 7 bits as address, not complete + * 8 bits with bit 0 (read/write bit) + */ + info.addr = 0x60 >> 1; + + err = i2c_add_driver(&adp1653_i2c_driver); + if(err) + { + DEBUG_LOG("Failed to register i2c driver\n"); + goto out; + } + + DEBUG_LOG("Initialized adp1653\n"); + if(!i2c_new_device(adap_p,&info)){ + DEBUG_LOG("Unable to add i2c dev: %s (err=%d)\n",info.type, err); + goto out; + } +out: + return err; +} + +/* +MODULE_DEPEND +*/ diff --git a/drivers/staging/camera_flash/adp1653.h b/drivers/staging/camera_flash/adp1653.h new file mode 100755 index 00000000000..3035ab56d99 --- /dev/null +++ b/drivers/staging/camera_flash/adp1653.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * License terms: GNU General Public License (GPL), version 2. + */ +#ifndef __ADP1653_H__ +#define __ADP1653_H__ + +#include <linux/types.h> +#define ADP1653_ID (0) /*chip does not give any id :) so be it zero!*/ + +#define OUTPUT_SEL_REG (0x00) +#define CONFIG_TIMER_REG (0x01) +#define SW_STROBE_REG (0x02) +#define FAULT_STATUS_REG (0x03) + +/* Fault codes, FALUT_STATUS_REG bits */ +#define OVER_VOLTAGE_FAULT (0x01) +#define TIMEOUT_FAULT (0x02) +#define OVER_TEMPERATURE_FAULT (0x04) +#define SHORT_CIRCUIT_FAULT (0x08) + +/*CONFIG_TIMER_REG bits*/ +#define TIMER_ENABLE (0x10) + +struct adp1653_priv_data{ + struct i2c_client *i2c_client; + unsigned long curr_mode; + unsigned long enable_gpio; + unsigned long strobe_gpio; + unsigned long irq_no; + unsigned long status; + uint8_t fault; + uint8_t flash_intensity; + uint8_t flash_duration; + uint8_t torch_intensity; + uint8_t indicator_intensity; +}; + +/*Intensity current limits in Micro Amps*/ +/* over 250mA flash current is reduced */ +/* do not know why, neither really care about */ +//#define FLASH_MAX_INTENSITY (500000) /*code - 31*/ +#define FLASH_MAX_INTENSITY (250000) +#define FLASH_MIN_INTENSITY (215000) /*code - 12*/ +#define TORCH_MAX_INTENSITY (200000) /*code - 11*/ +#define TORCH_MIN_INTENSITY (50000) /*code - 1*/ +#define ILED_MAX_INTENSITY (17500) /*Code - 7*/ +#define ILED_MIN_INTENSITY (2500) /*code - 1*/ + +#define FLASH_MAX_STROBE_DURATION (820000) /*820 uSec*/ + +#define DURATION_USEC_TO_CODE(_code,_duration) do{ \ + if(_duration > FLASH_MAX_STROBE_DURATION) \ + _duration = FLASH_MAX_STROBE_DURATION; \ + _code = (FLASH_MAX_STROBE_DURATION - _duration) / 54600;\ +}while(0); + +#define HPLED_UAMP_TO_CODE(_current) ((_current - 35000) / 15000) + +#define FLASH_UAMP_TO_CODE(_code,_current){ \ + if(_current > FLASH_MAX_INTENSITY) \ + _current = FLASH_MAX_INTENSITY; \ + if(_current < FLASH_MIN_INTENSITY) \ + _current = FLASH_MIN_INTENSITY; \ + _code = HPLED_UAMP_TO_CODE(_current); \ +}while(0) + +#define TORCH_UAMP_TO_CODE(_code,_current){ \ + if(_current > TORCH_MAX_INTENSITY) \ + _current = TORCH_MAX_INTENSITY; \ + if(_current < TORCH_MIN_INTENSITY) \ + _current = TORCH_MIN_INTENSITY; \ + _code = HPLED_UAMP_TO_CODE(_current); \ +}while(0) + +#define ILED_UAMP_TO_CODE(_code,_current) do { \ + if(_current > ILED_MAX_INTENSITY) \ + _current = ILED_MAX_INTENSITY; \ + _code = _current / ILED_MIN_INTENSITY; /* Min current: 2.5mA/2500uA*/ \ +}while(0) + +#endif diff --git a/drivers/staging/camera_flash/adp1653_plat.h b/drivers/staging/camera_flash/adp1653_plat.h new file mode 100755 index 00000000000..325097aa2a8 --- /dev/null +++ b/drivers/staging/camera_flash/adp1653_plat.h @@ -0,0 +1,24 @@ +/* + * adp1653_plat.h + * ADP1653 Led Flash Driver platform specific structures + * + * Copyright (C) ST-Ericsson SA 2011 + * Author: Rajat Verma <rajat.verma@stericsson.com> + * + * License Terms: GNU General Public License v2 + */ + +#ifndef __LINUX_I2C_ADP1653_PLAT_H__ +#define __LINUX_I2C_ADP1653_PLAT_H__ + +/** + * struct adp1653_platform_data - platform data structure for adp1653 + * @enable_gpio: gpio for chip enable/disable + * @irq_no: interrupt line for flash ic + */ +struct adp1653_platform_data { + u32 enable_gpio; + u32 irq_no; +}; + +#endif //__LINUX_I2C_ADP1653_PLAT_H__ diff --git a/drivers/staging/camera_flash/camera_flash.h b/drivers/staging/camera_flash/camera_flash.h new file mode 100644 index 00000000000..15faf706dc9 --- /dev/null +++ b/drivers/staging/camera_flash/camera_flash.h @@ -0,0 +1,74 @@ +#ifndef __CAMERA_FLASH_H__ +#define __CAMERA_FLASH_H__ + +#define FLASH_NAME_SIZE (20) + +struct flash_mode_details { + unsigned long led_type; + unsigned long max_intensity_uAmp; + unsigned long min_intensity_uAmp; + unsigned long max_strobe_duration_uSecs; + unsigned long feature_bitmap; + unsigned char nbFaultRegisters; +}; + +/*feature_bitmap (in struct flash_mode_details) bit values*/ +#define INTENSITY_PROGRAMMABLE (0x01) +#define DURATION_PROGRAMMABLE (0x02) +#define TIMEOUT_PROGRAMMABLE (0x04) + +/*Status word returned by driver has status in lower 16 bits + *and Error in higher 16 bits. definition of status and error + *bits are there in flash_bitfields.h + */ +#define SET_FLASH_STATUS(_bitmap, _status) (_bitmap |= (_status & 0xffff)) +#define CLR_FLASH_STATUS(_bitmap, _status) (_bitmap &= ~(_status & 0xffff)) +#define SET_FLASH_ERROR(_bitmap, _status) (_bitmap |= (_status << 16)) +#define CLR_FLASH_ERROR(_bitmap, _status) (_bitmap &= ~(_status << 16)) +#define GET_FLASH_STATUS(_bitmap) (_bitmap & 0xffff) +#define GET_FLASH_ERROR(_bitmap) (_bitmap >> 16) + +struct flash_mode_params { + unsigned long duration_uSecs; + unsigned long intensity_uAmp; + unsigned long timeout_uSecs; +}; + +struct flash_ioctl_args_t { + unsigned long flash_mode; + unsigned long cam; + unsigned long status; + union mode_arg{ + struct flash_mode_details details; + struct flash_mode_params params; + unsigned long strobe_enable; + } mode_arg; +}; + +#define FLASH_MAGIC_NUMBER 0x17 +#define FLASH_GET_MODES _IOR(FLASH_MAGIC_NUMBER, 1,\ +struct flash_ioctl_args_t *) +#define FLASH_GET_MODE_DETAILS _IOWR(FLASH_MAGIC_NUMBER, 2,\ +struct flash_ioctl_args_t *) +#define FLASH_ENABLE_MODE _IOW(FLASH_MAGIC_NUMBER, 3,\ +struct flash_ioctl_args_t *) +#define FLASH_DISABLE_MODE _IOW(FLASH_MAGIC_NUMBER, 4,\ +struct flash_ioctl_args_t *) +#define FLASH_CONFIGURE_MODE _IOW(FLASH_MAGIC_NUMBER, 5,\ +struct flash_ioctl_args_t *) +#define FLASH_TRIGGER_STROBE _IOW(FLASH_MAGIC_NUMBER, 6,\ +struct flash_ioctl_args_t *) +#define FLASH_GET_STATUS _IOW(FLASH_MAGIC_NUMBER, 7,\ +struct flash_ioctl_args_t *) +#define FLASH_GET_LIFE_COUNTER _IOW(FLASH_MAGIC_NUMBER, 8,\ +struct flash_ioctl_args_t *) +#define FLASH_GET_SELF_TEST_MODES _IOR(FLASH_MAGIC_NUMBER, 9,\ +struct flash_ioctl_args_t *) +#define FLASH_SELF_TEST _IOW(FLASH_MAGIC_NUMBER, 10,\ +struct flash_ioctl_args_t *) +#define FLASH_GET_FAULT_REGISTERS _IOR(FLASH_MAGIC_NUMBER, 11,\ +struct flash_ioctl_args_t *) +#define FLASH_GET_SELF_TEST_RESULT _IOR(FLASH_MAGIC_NUMBER, 12,\ +struct flash_ioctl_args_t *) + +#endif diff --git a/drivers/staging/camera_flash/camera_flash_bitfields.h b/drivers/staging/camera_flash/camera_flash_bitfields.h new file mode 100644 index 00000000000..05da9c5ef58 --- /dev/null +++ b/drivers/staging/camera_flash/camera_flash_bitfields.h @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + */ +/** +* \file camera_flash_bitfields.h +* \brief Define some constants for the flash drivers API. +* \author ST-Ericsson +*/ +#ifndef __CAMERA_FLASH_BITFIELDS_H__ +#define __CAMERA_FLASH_BITFIELDS_H__ + +/* Flash Mode definitions */ +/* All Operating Modes are off (shutdown low power state)*/ +#define FLASH_MODE_NONE (0x000) +/* Enables the xenon driver. Strobe is managed by the flash driver itself. +Charges the xenon. Automatic periodic recharge is abstracted by the driver */ +#define FLASH_MODE_XENON (0x001) +/* Enables the xenon driver. Strobe is managed externally to the driver */ +#define FLASH_MODE_XENON_EXTERNAL_STROBE (0x002) +/* Enables the video led driver. Strobing is managed by the driver */ +#define FLASH_MODE_VIDEO_LED (0x004) +/* Enables the video led driver. Strobing is managed externally to driver */ +#define FLASH_MODE_VIDEO_LED_EXTERNAL_STROBE (0x008) +/* Enables the still LED driver. Strobing is managed by the driver itself */ +#define FLASH_MODE_STILL_LED (0x010) +/* Enables the still LED driver. Strobe is managed externally to the driver */ +#define FLASH_MODE_STILL_LED_EXTERNAL_STROBE (0x020) +/* Enables the AF assistant driver. Strobe is managed by the driver */ +#define FLASH_MODE_AF_ASSISTANT (0x040) +/* Enable the driver. Strobe is managed by the driver */ +#define FLASH_MODE_INDICATOR (0x080) +/* Enables the still HP LED driver. Strobing is managed by the driver itself */ +#define FLASH_MODE_STILL_HPLED (0x100) +/* Enables the still HP LED driver. Strobe is managed externally to the +driver */ +#define FLASH_MODE_STILL_HPLED_EXTERNAL_STROBE (0x200) + + +/* The flash is not usable anymore */ +#define FLASH_STATUS_BROKEN (0x00) +/* The flash is ready to be fired and unlit */ +#define FLASH_STATUS_READY (0x01) +/* The flash is discharged and by construction, charging; usually an +application shall not try to fire it in that state (although possible +typically in sport mode flash) */ +#define FLASH_STATUS_NOT_READY (0x02) +/* The flash is in shutdown state */ +#define FLASH_STATUS_SHUTDOWN (0x04) +/* Intermediate state that may exist where I2C registers can be programmed */ +#define FLASH_STATUS_STANDBY (0x08) +/* The flash is already strobing */ +#define FLASH_STATUS_LIT (0x10) + +#define FLASH_SELFTEST_NONE 0x000 +/* tests connections to flash driver ICs */ +#define FLASH_SELFTEST_CONNECTION 0x001 +/* tests capture flash without using strobe signal from camera */ +#define FLASH_SELFTEST_FLASH 0x002 +/* tests capture flash using strobe signal from camera: ONLY this one needs to +be done in idle state from flash tests cases */ +#define FLASH_SELFTEST_FLASH_WITH_STROBE 0x004 +/* tests video light */ +#define FLASH_SELFTEST_VIDEO_LIGHT 0x008 +/* tests AF assistance light */ +#define FLASH_SELFTEST_AF_LIGHT 0x010 +/* tests capture indicator light */ +#define FLASH_SELFTEST_INDICATOR 0x020 +/* tests flash in torch mode */ +#define FLASH_SELFTEST_TORCH_LIGHT 0x040 + +/** \brief Flash Error */ +enum TFlashError { + FLASH_ERR_NONE , /* None */ + FLASH_ERR_OVER_CHARGE , /* Error happened during the charge */ + FLASH_ERR_OVER_HEAT , /* Over temperature */ + FLASH_ERR_SHORT_CIRCUIT , /* Short circuit */ + FLASH_ERR_TIMEOUT , /* Timeout */ + FLASH_ERR_OVER_VOLTAGE /* Over voltage */ +} ; + +#endif diff --git a/drivers/staging/camera_flash/flash_common.c b/drivers/staging/camera_flash/flash_common.c new file mode 100644 index 00000000000..fc59879a170 --- /dev/null +++ b/drivers/staging/camera_flash/flash_common.c @@ -0,0 +1,460 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * camera flash: Flash driver to export camera flash to user space application. + * It supports two flashes, one for primary and one for secondary camera + * Author: Pankaj Chauhan/pankaj.chauhan@stericsson.com for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/ioctl.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/kthread.h> +#include <linux/jiffies.h> +#include <linux/miscdevice.h> +#include "camera_flash.h" +#include "flash_common.h" + +#define DEBUG_LOG(...) printk(KERN_DEBUG "Camera Flash driver: " __VA_ARGS__) + +#define PRIMARY_CAMERA (0) +#define SECONDARY_CAMERA (1) +static struct miscdevice misc_dev; +struct flash_chip *flash_chips[2]; +struct fasync_struct * async_queue; +struct task_struct* ptaskStruct; +wait_queue_head_t waitQueue; +int waitCondition = 0; +struct flash_ioctl_args_t flashArg; + +#define COPY_ARG_FROM_USER(_to,_from_usr) do{ \ + memset((_to),0,sizeof(struct flash_ioctl_args_t)); \ + if (copy_from_user((_to), (struct flash_ioctl_args_t*) (_from_usr), sizeof(struct flash_ioctl_args_t))) { \ + DEBUG_LOG("Could not copy data from userspace successfully\n"); \ + break; \ + } \ +}while(0) + +#define COPY_ARG_TO_USER(_to_usr,_from) do{ \ + if (copy_to_user((struct flash_ioctl_args_t *)(_to_usr), (_from), sizeof(struct flash_ioctl_args_t))) { \ + DEBUG_LOG("Could not copy data from userspace successfully\n"); \ + break; \ + } \ +}while(0) + + +static long flash_ioctl(struct file *file_p, unsigned int cmd, unsigned long arg) +{ + int err=0; + struct flash_chip *flash_p = NULL; + struct flash_chip_ops *ops = NULL; + char *my_name=NULL; + struct flash_ioctl_args_t flash_arg; + + if (_IOC_TYPE(cmd) != FLASH_MAGIC_NUMBER) { + printk(KERN_ALERT "Flash driver: Not an ioctl for this module\n"); + err = -EINVAL; + } + + COPY_ARG_FROM_USER(&flash_arg,arg); + + if(flash_arg.cam == SECONDARY_CAMERA || flash_arg.cam == PRIMARY_CAMERA) + flash_p = flash_chips[flash_arg.cam]; + else{ + DEBUG_LOG("unsupported cam %lu\n",flash_arg.cam); + err = -ENODEV; + goto out; + } + my_name = flash_arg.cam ?"Secondary":"Primary"; + + if (flash_arg.cam == PRIMARY_CAMERA) + { + ops = flash_p->ops; + } + + switch(cmd){ + case FLASH_GET_MODES: + { + if (flash_arg.cam == PRIMARY_CAMERA) + { + err = ops->get_modes(flash_p->priv_data,&flash_arg.flash_mode); + if(!err){ + DEBUG_LOG("Supported flash modes for %s camera: %lx\n", + my_name,flash_arg.flash_mode); + COPY_ARG_TO_USER(arg,&flash_arg); + }else{ + DEBUG_LOG("unable to get supported modes for %s camera\n",my_name); + } + } + else + { + flash_arg.flash_mode = FLASH_MODE_NONE; + COPY_ARG_TO_USER(arg,&flash_arg); + } + } + break; + case FLASH_GET_MODE_DETAILS: + { + err = ops->get_mode_details(flash_p->priv_data,flash_arg.flash_mode, + &flash_arg.mode_arg.details); + if(!err){ + COPY_ARG_TO_USER(arg,&flash_arg); + }else{ + DEBUG_LOG("Unable to get mode details for %s camera, flash mode %lx\n", + my_name,flash_arg.flash_mode); + } + } + break; + case FLASH_ENABLE_MODE: + case FLASH_DISABLE_MODE: + { + int enable=0; + if(cmd == FLASH_ENABLE_MODE){ + enable = 1; + } + err = ops->enable_flash_mode(flash_p->priv_data,flash_arg.flash_mode,enable); + if(err){ + DEBUG_LOG("Unable to %s: %s camera, flash mode %lx\n", + (enable ?"Enable":"Disable"), my_name,flash_arg.flash_mode); + } + } + break; + case FLASH_CONFIGURE_MODE: + err = ops->configure_flash_mode(flash_p->priv_data,flash_arg.flash_mode, + &flash_arg.mode_arg.params); + if(err){ + DEBUG_LOG("Unable to configure %s camera, flash mode %lx\n", + my_name,flash_arg.flash_mode); + } + break; + case FLASH_TRIGGER_STROBE: + err = ops->trigger_strobe(flash_p->priv_data,flash_arg.mode_arg.strobe_enable); + if(err){ + DEBUG_LOG("Unable to %s: %s camera strobe trigger, mode %lx\n", + (arg ?"Enable":"Disable"), my_name,flash_arg.flash_mode); + } + break; + case FLASH_GET_STATUS: + err = ops->get_status(flash_p->priv_data,&flash_arg.status); + if(!err){ + COPY_ARG_TO_USER(arg,&flash_arg); + }else{ + DEBUG_LOG("Unable to get status details for %s camera, flash mode %lx\n", + my_name,flash_arg.flash_mode); + } + break; + case FLASH_GET_LIFE_COUNTER: + DEBUG_LOG("Not Implemented\n"); + break; + case FLASH_SELF_TEST: + flashArg = flash_arg; + if (0 != (flashArg.cam & (FLASH_SELFTEST_FLASH | FLASH_SELFTEST_FLASH_WITH_STROBE))) + { + err = ENODEV; + } + else + { + /* wake up worker thread */ + waitCondition = 1; + wake_up_interruptible(&waitQueue); + } + break; + case FLASH_GET_SELF_TEST_MODES: + { + if (flash_arg.cam == PRIMARY_CAMERA) + { + err = ops->get_selftest_modes(flash_p->priv_data,&flash_arg.flash_mode); + if(!err){ + COPY_ARG_TO_USER(arg,&flash_arg); + }else{ + DEBUG_LOG("unable to get supported modes for %s camera\n",my_name); + } + } + else + { + flash_arg.flash_mode = FLASH_SELFTEST_NONE; + COPY_ARG_TO_USER(arg,&flash_arg); + } + break; + } + case FLASH_GET_FAULT_REGISTERS: + { + err = ops->get_fault_registers(flash_p->priv_data,flash_arg.flash_mode,&flash_arg.status); + if(!err){ + COPY_ARG_TO_USER(arg,&flash_arg); + }else{ + DEBUG_LOG("unable to get supported modes for %s camera\n",my_name); + } + + break; + } + case FLASH_GET_SELF_TEST_RESULT: + { + COPY_ARG_TO_USER(arg,&flashArg); + DEBUG_LOG("FLASH_GET_SELF_TEST_RESULT arg : 0x%lx\n", flashArg.status); + break; + } + default: + DEBUG_LOG("Unknown command %x\n",cmd); + + } +out: + return err; +} + +int worker_thread (void* data) +{ + int err = 0; + struct flash_chip *flash_p=NULL; + struct flash_chip_ops *ops=NULL; + struct flash_mode_params params; + struct flash_mode_details details; + + while (1) + { + /* waiting for some job to do */ + wait_event_interruptible(waitQueue, (waitCondition != 0)); + waitCondition = 0; + + DEBUG_LOG("worker_thread wakes up\n"); + /* do we need to stop ? */ + err = kthread_should_stop(); + if (0 != err) + { + DEBUG_LOG("worker_thread stops\n"); + break; + } + + /* do the job */ + flash_p = flash_chips[flashArg.cam]; + ops = flash_p->ops; + + /* clear fault registers */ + err = ops->get_fault_registers(flash_p->priv_data, FLASH_MODE_INDICATOR, &flashArg.status); + if (0 != err) + { + flashArg.status = flashArg.flash_mode; + flashArg.flash_mode = 0; + } + flashArg.status = 0; + + /* do all selftests */ + while (flashArg.flash_mode != FLASH_SELFTEST_NONE) + { + if (0 != (flashArg.flash_mode & FLASH_SELFTEST_CONNECTION)) + { + err = ops->get_mode_details(flash_p->priv_data, FLASH_MODE_INDICATOR, &details); + if (0 != err) + { + DEBUG_LOG("not able to get mode FLASH_MODE_INDICATOR details\n"); + flashArg.status |= FLASH_SELFTEST_CONNECTION; + } + flashArg.flash_mode &= ~FLASH_SELFTEST_CONNECTION; + } + else if (0 != (flashArg.flash_mode & (FLASH_SELFTEST_FLASH | FLASH_SELFTEST_FLASH_WITH_STROBE))) + { + if (0 != (flashArg.flash_mode & FLASH_SELFTEST_FLASH)) + { + flashArg.status |= FLASH_SELFTEST_FLASH; + flashArg.flash_mode &= ~FLASH_SELFTEST_FLASH; + } + else + { + flashArg.status |= FLASH_SELFTEST_FLASH_WITH_STROBE; + flashArg.flash_mode &= ~FLASH_SELFTEST_FLASH_WITH_STROBE; + } + } + /* FLASH_SELFTEST_VIDEO_LIGHT | FLASH_SELFTEST_AF_LIGHT | FLASH_SELFTEST_INDICATOR | FLASH_SELFTEST_TORCH_LIGHT */ + else + { + unsigned long currentSelftest = FLASH_SELFTEST_NONE; + unsigned long currentFlashMode = FLASH_MODE_NONE; + + if (0 != (flashArg.flash_mode & FLASH_SELFTEST_VIDEO_LIGHT)) + { + currentSelftest = FLASH_SELFTEST_VIDEO_LIGHT; + currentFlashMode = FLASH_MODE_VIDEO_LED; + } + else if (0 != (flashArg.flash_mode & FLASH_SELFTEST_AF_LIGHT)) + { + currentSelftest = FLASH_SELFTEST_AF_LIGHT; + currentFlashMode = FLASH_MODE_AF_ASSISTANT; + } + else if (0 != (flashArg.flash_mode & FLASH_SELFTEST_INDICATOR)) + { + currentSelftest = FLASH_SELFTEST_INDICATOR; + currentFlashMode = FLASH_MODE_INDICATOR; + } + else + { + currentSelftest = FLASH_SELFTEST_TORCH_LIGHT; + currentFlashMode = FLASH_MODE_VIDEO_LED; + } + + err = ops->get_mode_details(flash_p->priv_data, currentFlashMode, &details); + if (0 != err) + { + DEBUG_LOG("not able to get mode 0x%lx details\n",currentFlashMode); + flashArg.status |= currentSelftest; + flashArg.flash_mode &= ~currentSelftest; + continue; + } + + err = ops->enable_flash_mode(flash_p->priv_data, currentFlashMode, 1); + if (0 != err) + { + DEBUG_LOG("not able to enable flash mode 0x%lx\n",currentFlashMode); + flashArg.status |= currentSelftest; + flashArg.flash_mode &= ~currentSelftest; + continue; + } + + params.duration_uSecs = 0; + params.intensity_uAmp = details.max_intensity_uAmp; + params.timeout_uSecs = 0; + err = ops->configure_flash_mode(flash_p->priv_data, currentFlashMode, ¶ms); + if (0 != err) + { + DEBUG_LOG("not able to configure flash mode 0x%lx\n",currentFlashMode); + flashArg.status |= currentSelftest; + flashArg.flash_mode &= ~currentSelftest; + continue; + } + + err = ops->trigger_strobe(flash_p->priv_data,1); + if (0 != err) + { + DEBUG_LOG("not able to strobe, mode : 0x%lx\n",currentFlashMode); + flashArg.status |= currentSelftest; + flashArg.flash_mode &= ~currentSelftest; + continue; + } + + wait_event_timeout(waitQueue, 0, msecs_to_jiffies(1000)); + + err = ops->trigger_strobe(flash_p->priv_data,0); + if (0 != err) + { + DEBUG_LOG("not able to strobe, mode : 0x%lx\n",currentFlashMode); + flashArg.status |= currentSelftest; + flashArg.flash_mode &= ~currentSelftest; + continue; + } + flashArg.flash_mode &= ~currentSelftest; + } + } + + /* job's done ! */ + flash_async_notify(); + } + return 0; +} + +int flash_open(struct inode *node, struct file *file_p) +{ + // init sleep queue + init_waitqueue_head(&waitQueue); + + // start worker thread + ptaskStruct = kthread_run (&worker_thread, NULL, "flashDriverWorker"); + + return 0; +} + +int register_flash_chip(unsigned int cam, struct flash_chip *flash_p) +{ + int err =0; + DEBUG_LOG("Registering cam %d\n", cam); + DEBUG_LOG("flash_p: name=%s\n", flash_p->name); + if(cam > 1 || !flash_p){ + DEBUG_LOG("Registration: something is wrong! cam %d, flash_p %x \n",cam,(int)flash_p); + err = EINVAL; + goto out; + } + if(!flash_chips[cam]){ + flash_chips[cam] = flash_p; + DEBUG_LOG("Registered flash: id %lx, %s for camera %d\n", + flash_p->id,flash_p->name,cam); + }else{ + DEBUG_LOG("%s flash already registered for camera %d, ignore flash %s\n", + flash_chips[cam]->name,cam, flash_p->name); + } +out: + return err; +} + +int flash_async_notify () +{ + kill_fasync(&async_queue, SIGIO, POLL_IN); + return 0; +} + +static int flash_fasync(int fd, struct file *filp, int mode) +{ + DEBUG_LOG("registered async notification on %d fd\n",fd); + return fasync_helper(fd, filp, mode, &async_queue); +} + +static int flash_release(struct inode *node, struct file *file_p) +{ + int err = 0; + + fasync_helper(-1, file_p, 0, &async_queue); + + // stop worker thread + waitCondition = 1; + err = kthread_stop(ptaskStruct); + return err; +} + +static struct file_operations flash_fops = { + owner:THIS_MODULE, + unlocked_ioctl:flash_ioctl, + open:flash_open, + release:flash_release, + fasync:flash_fasync, +}; + +int major_device_number; + +/*Temporary here (adp_init)*/ +extern int adp1653_init(void); +static int __init flash_init(void) +{ + int err = 0; + err = adp1653_init(); + if(err){ + DEBUG_LOG("Unable to initialize adp1653, err %d\n",err); + goto out; + } + /* Register misc device */ + misc_dev.minor = MISC_DYNAMIC_MINOR; + misc_dev.name = "camera_flash"; + misc_dev.fops = &flash_fops; + err = misc_register(&misc_dev); + if (err < 0) { + printk(KERN_INFO "camera_flash driver misc_register failed (%d)\n", err); + return err; + } else { + major_device_number = err; + printk(KERN_INFO "camera_flash driver initialized with minor=%d\n", misc_dev.minor); + } +out: + return err; +} + +static void __exit flash_exit(void) +{ + misc_deregister(&misc_dev); + printk(KERN_INFO"Camera flash driver unregistered\n"); +} + +module_init(flash_init); +module_exit(flash_exit); +MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(register_flash_chip); +EXPORT_SYMBOL(flash_async_notify); diff --git a/drivers/staging/camera_flash/flash_common.h b/drivers/staging/camera_flash/flash_common.h new file mode 100755 index 00000000000..d1f63631e82 --- /dev/null +++ b/drivers/staging/camera_flash/flash_common.h @@ -0,0 +1,57 @@ +#ifndef __FLASH_COMMON_H__ +#define __FLASH_COMMON_H__ + +#include "camera_flash_bitfields.h" +#include "camera_flash.h" + +struct flash_chip_ops{ + int (*get_modes)( void *priv_data, unsigned long *modes); + int (*get_mode_details)(void *priv_data,unsigned long mode, + struct flash_mode_details *details_p); + int (*enable_flash_mode) (void *priv_data,unsigned long mode, + int enable); + int (*configure_flash_mode) (void *priv_data, unsigned long mode, + struct flash_mode_params *params_p); + int (*trigger_strobe) (void *priv_data, int enable); + int (*get_life_counter) (void *priv_data); + int (*get_status) (void *priv_data, unsigned long *status); + int (*get_selftest_modes) (void *priv_data, + unsigned long *modes); + int (*get_fault_registers) (void *priv_data, unsigned long mode, + unsigned long *status); +}; + +#define FLASH_TYPE_XENON (0x1) +#define FLASH_TYPE_HPLED (0x2) + +#define SET_FLASHCHIP_TYPE(flash_chip_p,_TYPE) ((flash_chip_p)->id |= _TYPE) +#define GET_FLASHHIP_TYPE(flash_chip_p) ((flash_chip_p)->id & 0xffff) +#define GET_FLASHCHIP_ID(flash_chip_p) ((flash_chip_p)->id >> 16) +#define SET_FLASHCHIP_ID(flash_chip_p,_ID) ((flash_chip_p)->id |= (_ID << 16)) + +struct flash_chip { + unsigned long id; + struct flash_chip_ops *ops; + void *priv_data; + unsigned char name[FLASH_NAME_SIZE]; +}; + +/** + * struct flash_platform_data: + * platform specific data For flash chip driver + * @cam : 0 - primary, 1 - secondary + * @strobe_gpio: GPIO used as strobe + * @enable_gpio: GPIO used for enable/reset input + */ +struct flash_platform_data{ + unsigned long cam; + unsigned long strobe_gpio; + unsigned long strobe_gpio_alt_func; + unsigned long enable_gpio; + unsigned long enable_gpio_alt_func; +}; + +extern int register_flash_chip(unsigned int cam, struct flash_chip *flash_p); +extern int flash_async_notify (void ); + +#endif diff --git a/drivers/staging/cw1200/.gitignore b/drivers/staging/cw1200/.gitignore new file mode 100644 index 00000000000..6ad0d1ec58e --- /dev/null +++ b/drivers/staging/cw1200/.gitignore @@ -0,0 +1,10 @@ +*.o +*.ko +*.ko.cmd +.tmp_versions +modules.order +Module.symvers +Module.markers +*.o.cmd +*.mod.c +*.swp diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig new file mode 100644 index 00000000000..946d366cbfb --- /dev/null +++ b/drivers/staging/cw1200/Kconfig @@ -0,0 +1,71 @@ +config CW1200 + tristate "CW1200 WLAN support" + select MAC80211 + select CFG80211 + help + + This is an experimental driver for the cw1200 chip-set. + Enabling this option enables the generic driver without + any platform support. + + Please select the appropriate platform below. + +if CW1200 + +config CW1200_NON_POWER_OF_TWO_BLOCKSIZES + bool "Platform supports non-power-of-two SDIO transfer" + depends on CW1200 + help + Say N here only if you are running the driver on a platform + which does not have support for non-power-of-two SDIO transfer. + If unsure, say Y. + +config CW1200_USE_GPIO_IRQ + bool "Use GPIO interrupt" + depends on CW1200 + help + Say Y here if you want to include GPIO IRQ support instead of SDIO IRQ. + If unsure, say N. + +config CW1200_5GHZ_SUPPORT + bool "5GHz band support" + depends on CW1200 + help + Say Y if your device supports 5GHz band. Should be disabled for + CW1100 silicon. + If unsure, say N. + +config CW1200_WAPI_SUPPORT + bool "WAPI support" + depends on CW1200 + help + Say Y if your compat-wireless support WAPI. + If unsure, say N. + +menu "Driver debug features" + depends on CW1200 + +config CW1200_DEBUGFS + bool "Expose driver internals to DebugFS (DEVELOPMENT)" + +config CW1200_BH_DEBUG + bool "Enable low-level device communication logs (DEVELOPMENT)" + +config CW1200_WSM_DEBUG + bool "Enable WSM API debug messages (DEVELOPMENT)" + +config CW1200_WSM_DUMPS + bool "Verbose WSM API logging (DEVELOPMENT)" + +config CW1200_TXRX_DEBUG + bool "Enable TX/RX debug messages (DEVELOPMENT)" + +config CW1200_TX_POLICY_DEBUG + bool "Enable TX policy debug (DEVELOPMENT)" + +config CW1200_STA_DEBUG + bool "Enable STA/AP debug (DEVELOPMENT)" + +endmenu + +endif diff --git a/drivers/staging/cw1200/Makefile b/drivers/staging/cw1200/Makefile new file mode 100644 index 00000000000..c0e88bd0f11 --- /dev/null +++ b/drivers/staging/cw1200/Makefile @@ -0,0 +1,19 @@ +cw1200_core-y := \ + fwio.o \ + txrx.o \ + main.o \ + queue.o \ + hwio.o \ + bh.o \ + wsm.o \ + sta.o \ + ap.o \ + scan.o +cw1200_core-$(CONFIG_CW1200_DEBUGFS) += debug.o +cw1200_core-$(CONFIG_PM) += pm.o + +cw1200_wlan-y := cw1200_sdio.o + +obj-$(CONFIG_CW1200) += cw1200_core.o +obj-$(CONFIG_CW1200) += cw1200_wlan.o + diff --git a/drivers/staging/cw1200/TODO b/drivers/staging/cw1200/TODO new file mode 100644 index 00000000000..0d2be40e1f4 --- /dev/null +++ b/drivers/staging/cw1200/TODO @@ -0,0 +1,10 @@ +TODO: + - IBSS: Not implemented (3-10 m*d). + - 11n: Almost done. WSM API upgrade is required fo finish implementation. (2-3 m*d). + - 11n: verification (??? m*d Resources? WLAN RF lab? 11n sniffers + availability? Bring up of the test equipment?). + - memory leakage verification and proper cleanup: not done (1-3 m*d). + - AP (hot-spot) mode: Implemented, some problems with WEP104/WPA/WPA2 security. + FW bug? To be investigated. + - U-APSD configuration (0.5-1 m*d). + - Cleanup of debug printouts (1 m*d). diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c new file mode 100755 index 00000000000..cd74143e47e --- /dev/null +++ b/drivers/staging/cw1200/ap.c @@ -0,0 +1,995 @@ +/* + * mac80211 STA and AP API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "cw1200.h" +#include "sta.h" +#include "ap.h" +#include "bh.h" + +#if defined(CONFIG_CW1200_STA_DEBUG) +#define ap_printk(...) printk(__VA_ARGS__) +#else +#define ap_printk(...) +#endif + +#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) + +static int cw1200_upload_beacon(struct cw1200_common *priv); +static int cw1200_upload_pspoll(struct cw1200_common *priv); +static int cw1200_upload_null(struct cw1200_common *priv); +static int cw1200_start_ap(struct cw1200_common *priv); +static int cw1200_update_beaconing(struct cw1200_common *priv); +static int cw1200_enable_beaconing(struct cw1200_common *priv, + bool enable); +static void __cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta); + +/* ******************************************************************** */ +/* AP API */ + +int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; + struct sk_buff *skb; + + if (priv->mode != NL80211_IFTYPE_AP) + return 0; + + sta_priv->link_id = cw1200_find_link_id(priv, sta->addr); + if (WARN_ON(!sta_priv->link_id)) { + /* Impossible error */ + wiphy_info(priv->hw->wiphy, + "[AP] No more link IDs available.\n"); + return -ENOENT; + } + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + entry->status = CW1200_LINK_HARD; + while ((skb = skb_dequeue(&entry->rx_queue))) + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + return 0; +} + +int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; + + if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) + return 0; + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + entry->status = CW1200_LINK_SOFT; + entry->timestamp = jiffies; + if (!delayed_work_pending(&priv->link_id_gc_work)) + queue_delayed_work(priv->workqueue, + &priv->link_id_gc_work, + CW1200_LINK_ID_GC_TIMEOUT); + spin_unlock_bh(&priv->ps_state_lock); + return 0; +} + +static void __cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = dev->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + u32 bit = BIT(sta_priv->link_id); + u32 prev = priv->sta_asleep_mask & bit; + + switch (notify_cmd) { + case STA_NOTIFY_SLEEP: + if (!prev) { + if (priv->buffered_multicasts && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_start_work); + priv->sta_asleep_mask |= bit; + } + break; + case STA_NOTIFY_AWAKE: + if (prev) { + priv->sta_asleep_mask &= ~bit; + priv->pspoll_mask &= ~bit; + if (priv->tx_multicast && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_stop_work); + cw1200_bh_wakeup(priv); + } + break; + } +} + +void cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = dev->priv; + spin_lock_bh(&priv->ps_state_lock); + __cw1200_sta_notify(dev, vif, notify_cmd, sta); + spin_unlock_bh(&priv->ps_state_lock); +} + +static void cw1200_ps_notify(struct cw1200_common *priv, + int link_id, bool ps) +{ + struct ieee80211_sta *sta; + + if (!link_id || link_id > CW1200_MAX_STA_IN_AP_MODE) + return; + + txrx_printk(KERN_DEBUG "%s for LinkId: %d. STAs asleep: %.8X\n", + ps ? "Stop" : "Start", + link_id, priv->sta_asleep_mask); + + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, + priv->link_id_db[link_id - 1].mac); + if (sta) { + __cw1200_sta_notify(priv->hw, priv->vif, + ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, sta); + } + rcu_read_unlock(); +} + +static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) +{ + struct sk_buff *skb; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + }; + u16 tim_offset, tim_length; + + ap_printk(KERN_DEBUG "[AP] %s mcast: %s.\n", + __func__, aid0_bit_set ? "ena" : "dis"); + + skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, + &tim_offset, &tim_length); + if (!skb) { + if (!__cw1200_flush(priv, true)) + wsm_unlock_tx(priv); + return -ENOENT; + } + + if (tim_offset && tim_length >= 6) { + /* Ignore DTIM count from mac80211: + * firmware handles DTIM internally. */ + skb->data[tim_offset + 2] = 0; + + /* Set/reset aid0 bit */ + if (aid0_bit_set) + skb->data[tim_offset + 4] |= 1; + else + skb->data[tim_offset + 4] &= ~1; + } + + update_ie.ies = &skb->data[tim_offset]; + update_ie.length = tim_length; + WARN_ON(wsm_update_ie(priv, &update_ie)); + + dev_kfree_skb(skb); + + return 0; +} + +void cw1200_set_tim_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, set_tim_work); + (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set); +} + +int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set) +{ + struct cw1200_common *priv = dev->priv; + queue_work(priv->workqueue, &priv->set_tim_work); + return 0; +} + +static int cw1200_set_btcoexinfo(struct cw1200_common *priv) +{ + struct wsm_override_internal_txrate arg; + int ret = 0; + + if (priv->mode == NL80211_IFTYPE_STATION) { + /* Plumb PSPOLL and NULL template */ + WARN_ON(cw1200_upload_pspoll(priv)); + WARN_ON(cw1200_upload_null(priv)); + } else { + return 0; + } + + memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); + + if (!priv->vif->p2p) { + /* STATION mode */ + if (priv->bss_params.operationalRateSet & ~0xF) { + ap_printk(KERN_DEBUG "[STA] STA has ERP rates\n"); + /* G or BG mode */ + arg.internalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + } else { + ap_printk(KERN_DEBUG "[STA] STA has non ERP rates\n"); + /* B only mode */ + arg.internalTxRate = (__ffs( + priv->association_mode.basicRateSet)); + } + arg.nonErpInternalTxRate = (__ffs( + priv->association_mode.basicRateSet)); + } else { + /* P2P mode */ + arg.internalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + arg.nonErpInternalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + } + + ap_printk(KERN_DEBUG "[STA] BTCOEX_INFO" + "MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n", + priv->mode, + arg.internalTxRate, + arg.nonErpInternalTxRate); + + ret = WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + &arg, sizeof(arg))); + + return ret; +} + +void cw1200_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct cw1200_common *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + + mutex_lock(&priv->conf_mutex); + if (changed & BSS_CHANGED_BSSID) { + memcpy(priv->bssid, info->bssid, ETH_ALEN); + cw1200_setup_mac(priv); + } + + /* TODO: BSS_CHANGED_IBSS */ + + if (changed & BSS_CHANGED_ARP_FILTER) { + struct wsm_arp_ipv4_filter filter = {0}; + int i; + + ap_printk(KERN_DEBUG "[STA] BSS_CHANGED_ARP_FILTER " + "enabled: %d, cnt: %d\n", + info->arp_filter_enabled, + info->arp_addr_cnt); + + if (info->arp_filter_enabled) + filter.enable = __cpu_to_le32(1); + + /* Currently only one IP address is supported by firmware. + * In case of more IPs arp filtering will be disabled. */ + if (info->arp_addr_cnt > 0 && + info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { + for (i = 0; i < info->arp_addr_cnt; i++) { + filter.ipv4Address[i] = info->arp_addr_list[i]; + ap_printk(KERN_DEBUG "[STA] addr[%d]: 0x%X\n", + i, filter.ipv4Address[i]); + } + } else + filter.enable = 0; + + ap_printk(KERN_DEBUG "[STA] arp ip filter enable: %d\n", + __le32_to_cpu(filter.enable)); + + if (wsm_set_arp_ipv4_filter(priv, &filter)) + WARN_ON(1); + } + + + if (changed & BSS_CHANGED_BEACON) { + ap_printk(KERN_DEBUG "BSS_CHANGED_BEACON\n"); + WARN_ON(cw1200_update_beaconing(priv)); + WARN_ON(cw1200_upload_beacon(priv)); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + ap_printk(KERN_DEBUG "BSS_CHANGED_BEACON_ENABLED\n"); + + if (priv->enable_beacon != info->enable_beacon) { + WARN_ON(cw1200_enable_beaconing(priv, + info->enable_beacon)); + priv->enable_beacon = info->enable_beacon; + } + } + + if (changed & BSS_CHANGED_BEACON_INT) { + ap_printk(KERN_DEBUG "CHANGED_BEACON_INT\n"); + /* Restart AP only when connected */ + if (priv->join_status == CW1200_JOIN_STATUS_AP) + WARN_ON(cw1200_update_beaconing(priv)); + } + + + if (changed & BSS_CHANGED_ASSOC) { + wsm_lock_tx(priv); + priv->wep_default_key_id = -1; + wsm_unlock_tx(priv); + + if (!info->assoc /* && !info->ibss_joined */) { + priv->cqm_link_loss_count = 60; + priv->cqm_beacon_loss_count = 20; + priv->cqm_tx_failure_thold = 0; + } + priv->cqm_tx_failure_count = 0; + } + + if (changed & + (BSS_CHANGED_ASSOC | + BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_ERP_PREAMBLE | + BSS_CHANGED_HT | + BSS_CHANGED_ERP_SLOT)) { + ap_printk(KERN_DEBUG "BSS_CHANGED_ASSOC.\n"); + if (info->assoc) { /* TODO: ibss_joined */ + int dtim_interval = conf->ps_dtim_period; + int listen_interval = conf->listen_interval; + struct ieee80211_sta *sta = NULL; + + /* Associated: kill join timeout */ + cancel_delayed_work_sync(&priv->join_timeout); + + rcu_read_lock(); + if (info->bssid) + sta = ieee80211_find_sta(vif, info->bssid); + if (sta) { + BUG_ON(!priv->channel); + priv->ht_info.ht_cap = sta->ht_cap; + priv->bss_params.operationalRateSet = + __cpu_to_le32( + cw1200_rate_mask_to_wsm(priv, + sta->supp_rates[priv->channel->band])); + priv->ht_info.channel_type = + info->channel_type; + priv->ht_info.operation_mode = + info->ht_operation_mode; + } else { + memset(&priv->ht_info, 0, + sizeof(priv->ht_info)); + priv->bss_params.operationalRateSet = -1; + } + rcu_read_unlock(); + + if (sta) { + __le32 val = 0; + if (priv->ht_info.operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) { + ap_printk(KERN_DEBUG"[STA]" + " Non-GF STA present\n"); + /* Non Green field capable STA */ + val = __cpu_to_le32(BIT(1)); + } + WARN_ON(wsm_write_mib(priv, + WSM_MID_ID_SET_HT_PROTECTION, + &val, sizeof(val))); + } + + priv->association_mode.greenfieldMode = + cw1200_ht_greenfield(&priv->ht_info); + priv->association_mode.flags = + WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | + WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | + WSM_ASSOCIATION_MODE_USE_HT_MODE | + WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | + WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; + priv->association_mode.preambleType = + info->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG; + priv->association_mode.basicRateSet = __cpu_to_le32( + cw1200_rate_mask_to_wsm(priv, + info->basic_rates)); + priv->association_mode.mpduStartSpacing = + cw1200_ht_ampdu_density(&priv->ht_info); + +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + priv->cqm_beacon_loss_count = + info->cqm_beacon_miss_thold; + priv->cqm_tx_failure_thold = + info->cqm_tx_fail_thold; + priv->cqm_tx_failure_count = 0; + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ + + priv->bss_params.beaconLostCount = + priv->cqm_beacon_loss_count ? + priv->cqm_beacon_loss_count : + priv->cqm_link_loss_count; + + priv->bss_params.aid = info->aid; + + if (dtim_interval < 1) + dtim_interval = 1; + if (dtim_interval < priv->join_dtim_period) + dtim_interval = priv->join_dtim_period; + if (listen_interval < dtim_interval) + listen_interval = 0; + + ap_printk(KERN_DEBUG "[STA] DTIM %d, listen %d\n", + dtim_interval, listen_interval); + ap_printk(KERN_DEBUG "[STA] Preamble: %d, " \ + "Greenfield: %d, Aid: %d, " \ + "Rates: 0x%.8X, Basic: 0x%.8X\n", + priv->association_mode.preambleType, + priv->association_mode.greenfieldMode, + priv->bss_params.aid, + priv->bss_params.operationalRateSet, + priv->association_mode.basicRateSet); + WARN_ON(wsm_set_association_mode(priv, + &priv->association_mode)); + WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); + priv->setbssparams_done = true; + WARN_ON(wsm_set_beacon_wakeup_period(priv, + dtim_interval, listen_interval)); + cw1200_set_pm(priv, &priv->powersave_mode); + + if (priv->is_BT_Present) + WARN_ON(cw1200_set_btcoexinfo(priv)); +#if 0 + /* It's better to override internal TX rete; otherwise + * device sends RTS at too high rate. However device + * can't receive CTS at 1 and 2 Mbps. Well, 5.5 is a + * good choice for RTS/CTS, but that means PS poll + * will be sent at the same rate - impact on link + * budget. Not sure what is better.. */ + + /* Update: internal rate selection algorythm is not + * bad: if device is not receiving CTS at high rate, + * it drops RTS rate. + * So, conclusion: if-0 the code. Keep code just for + * information: + * Do not touch WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE! */ + + /* ~3 is a bug in device: RTS/CTS is not working at + * low rates */ + + __le32 internal_tx_rate = __cpu_to_le32(__ffs( + priv->association_mode.basicRateSet & ~3)); + WARN_ON(wsm_write_mib(priv, + WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + &internal_tx_rate, + sizeof(internal_tx_rate))); +#endif + } else { + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + } + } + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT)) { + __le32 use_cts_prot = info->use_cts_prot ? + __cpu_to_le32(1) : 0; + + ap_printk(KERN_DEBUG "[STA] CTS protection %d\n", + info->use_cts_prot); + WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, + &use_cts_prot, sizeof(use_cts_prot))); + } + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { + __le32 slot_time = info->use_short_slot ? + __cpu_to_le32(9) : __cpu_to_le32(20); + ap_printk(KERN_DEBUG "[STA] Slot time :%d us.\n", + __le32_to_cpu(slot_time)); + WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME, + &slot_time, sizeof(slot_time))); + } + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { + struct wsm_rcpi_rssi_threshold threshold = { + .rssiRcpiMode = WSM_RCPI_RSSI_USE_RSSI, + .rollingAverageCount = 1, + }; + +#if 0 + /* For verification purposes */ + info->cqm_rssi_thold = -50; + info->cqm_rssi_hyst = 4; +#endif /* 0 */ + + ap_printk(KERN_DEBUG "[CQM] RSSI threshold " + "subscribe: %d +- %d\n", + info->cqm_rssi_thold, info->cqm_rssi_hyst); +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + ap_printk(KERN_DEBUG "[CQM] Beacon loss subscribe: %d\n", + info->cqm_beacon_miss_thold); + ap_printk(KERN_DEBUG "[CQM] TX failure subscribe: %d\n", + info->cqm_tx_fail_thold); + priv->cqm_rssi_thold = info->cqm_rssi_thold; + priv->cqm_rssi_hyst = info->cqm_rssi_hyst; +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ + if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { + /* RSSI subscription enabled */ + /* TODO: It's not a correct way of setting threshold. + * Upper and lower must be set equal here and adjusted + * in callback. However current implementation is much + * more relaible and stable. */ + threshold.upperThreshold = + info->cqm_rssi_thold + info->cqm_rssi_hyst; + threshold.lowerThreshold = + info->cqm_rssi_thold; + threshold.rssiRcpiMode |= + WSM_RCPI_RSSI_THRESHOLD_ENABLE; + } else { + /* There is a bug in FW, see sta.c. We have to enable + * dummy subscription to get correct RSSI values. */ + threshold.rssiRcpiMode |= + WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER; + } + WARN_ON(wsm_set_rcpi_rssi_threshold(priv, &threshold)); + +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold; + priv->cqm_tx_failure_count = 0; + + if (priv->cqm_beacon_loss_count != + info->cqm_beacon_miss_thold) { + priv->cqm_beacon_loss_count = + info->cqm_beacon_miss_thold; + priv->bss_params.beaconLostCount = + priv->cqm_beacon_loss_count ? + priv->cqm_beacon_loss_count : + priv->cqm_link_loss_count; + WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); + priv->setbssparams_done = true; + } +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ + } + mutex_unlock(&priv->conf_mutex); +} + +void cw1200_multicast_start_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, multicast_start_work); + long tmo = priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024; + + if (!priv->aid0_bit_set) { + wsm_lock_tx(priv); + cw1200_set_tim_impl(priv, true); + priv->aid0_bit_set = true; + mod_timer(&priv->mcast_timeout, tmo); + wsm_unlock_tx(priv); + } +} + +void cw1200_multicast_stop_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, multicast_stop_work); + + if (priv->aid0_bit_set) { + wsm_lock_tx(priv); + priv->aid0_bit_set = false; + cw1200_set_tim_impl(priv, false); + wsm_unlock_tx(priv); + } +} + +void cw1200_mcast_timeout(unsigned long arg) +{ + struct cw1200_common *priv = + (struct cw1200_common *)arg; + + spin_lock_bh(&priv->ps_state_lock); + priv->tx_multicast = priv->aid0_bit_set && + priv->buffered_multicasts; + if (priv->tx_multicast) + cw1200_bh_wakeup(priv); + spin_unlock_bh(&priv->ps_state_lock); +} + +int cw1200_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) +{ + /* Aggregation is implemented fully in firmware, + * including block ack negotiation. Do not allow + * mac80211 stack to do anything: it interferes with + * the firmware. */ + return -ENOTSUPP; +} + +/* ******************************************************************** */ +/* WSM callback */ +void cw1200_suspend_resume(struct cw1200_common *priv, + struct wsm_suspend_resume *arg) +{ + /* if () is intendend to protect against spam. FW sends + * "start multicast" request on every DTIM. */ + if (arg->stop || !arg->multicast || priv->buffered_multicasts) + ap_printk(KERN_DEBUG "[AP] %s: %s\n", + arg->stop ? "stop" : "start", + arg->multicast ? "broadcast" : "unicast"); + + if (arg->multicast) { + bool cancel_tmo = false; + spin_lock_bh(&priv->ps_state_lock); + if (arg->stop) { + priv->tx_multicast = false; + } else { + /* Firmware sends this indication every DTIM if there + * is a STA in powersave connected. There is no reason + * to suspend, following wakeup will consume much more + * power than it could be saved. */ + cw1200_pm_stay_awake(&priv->pm_state, + priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024); + priv->tx_multicast = priv->aid0_bit_set && + priv->buffered_multicasts; + if (priv->tx_multicast) { + cancel_tmo = true; + cw1200_bh_wakeup(priv); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (cancel_tmo) + del_timer_sync(&priv->mcast_timeout); + } else { + spin_lock_bh(&priv->ps_state_lock); + cw1200_ps_notify(priv, arg->link_id, arg->stop); + spin_unlock_bh(&priv->ps_state_lock); + if (!arg->stop) + cw1200_bh_wakeup(priv); + } + return; +} + +/* ******************************************************************** */ +/* AP privates */ + +static int cw1200_upload_beacon(struct cw1200_common *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_BEACON, + }; + + + frame.skb = ieee80211_beacon_get(priv->hw, priv->vif); + if (WARN_ON(!frame.skb)) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + if (!ret) { + /* TODO: Distille probe resp; remove TIM + * and other beacon-specific IEs */ + *(__le16 *)frame.skb->data = + __cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; + ret = wsm_set_template_frame(priv, &frame); + } + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_pspoll(struct cw1200_common *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PS_POLL, + .rate = 0xFF, + }; + + + frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); + if (WARN_ON(!frame.skb)) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_null(struct cw1200_common *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_NULL, + .rate = 0xFF, + }; + + + frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif); + if (WARN_ON(!frame.skb)) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_enable_beaconing(struct cw1200_common *priv, + bool enable) +{ + struct wsm_beacon_transmit transmit = { + .enableBeaconing = enable, + }; + + return wsm_beacon_transmit(priv, &transmit); +} + +static int cw1200_start_ap(struct cw1200_common *priv) +{ + int ret; + const u8 *ssidie; + struct sk_buff *skb; + int offset; + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct wsm_start start = { + .mode = priv->vif->p2p ? + WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, + .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, + .channelNumber = priv->channel->hw_value, + .beaconInterval = conf->beacon_int, + .DTIMPeriod = conf->dtim_period, + .preambleType = conf->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG, + .probeDelay = 100, + .basicRateSet = cw1200_rate_mask_to_wsm(priv, + conf->basic_rates), + }; + + /* Get SSID */ + skb = ieee80211_beacon_get(priv->hw, priv->vif); + if (WARN_ON(!skb)) + return -ENOMEM; + + offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + ssidie = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, + skb->len - offset); + + memset(priv->ssid, 0, sizeof(priv->ssid)); + if (ssidie) { + priv->ssid_length = ssidie[1]; + if (WARN_ON(priv->ssid_length > sizeof(priv->ssid))) + priv->ssid_length = sizeof(priv->ssid); + memcpy(priv->ssid, &ssidie[2], priv->ssid_length); + } else { + priv->ssid_length = 0; + } + dev_kfree_skb(skb); + + priv->beacon_int = conf->beacon_int; + priv->join_dtim_period = conf->dtim_period; + + start.ssidLength = priv->ssid_length; + memcpy(&start.ssid[0], priv->ssid, start.ssidLength); + + memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); + + ap_printk(KERN_DEBUG "[AP] ch: %d(%d), bcn: %d(%d), " + "brt: 0x%.8X, ssid: %.*s.\n", + start.channelNumber, start.band, + start.beaconInterval, start.DTIMPeriod, + start.basicRateSet, + start.ssidLength, start.ssid); + ret = WARN_ON(wsm_start(priv, &start)); + if (!ret) + ret = WARN_ON(cw1200_upload_keys(priv)); + if (!ret) { + WARN_ON(wsm_set_block_ack_policy(priv, + 0, 0)); + priv->join_status = CW1200_JOIN_STATUS_AP; + cw1200_update_filtering(priv); + } + return ret; +} + +static int cw1200_update_beaconing(struct cw1200_common *priv) +{ + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct wsm_reset reset = { + .link_id = 0, + .reset_statistics = true, + }; + + if (priv->mode == NL80211_IFTYPE_AP) { + /* TODO: check if changed channel, band */ + if (priv->join_status != CW1200_JOIN_STATUS_AP || + priv->beacon_int != conf->beacon_int) { + ap_printk(KERN_DEBUG "ap restarting\n"); + wsm_lock_tx(priv); + if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE) + WARN_ON(wsm_reset(priv, &reset)); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + WARN_ON(cw1200_start_ap(priv)); + wsm_unlock_tx(priv); + } else + ap_printk(KERN_DEBUG "ap started join_status: %d\n", + priv->join_status); + } + return 0; +} + +int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac) +{ + int i, ret = 0; + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && + priv->link_id_db[i].status) { + priv->link_id_db[i].timestamp = jiffies; + ret = i + 1; + break; + } + } + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) +{ + int i, ret = 0; + unsigned long max_inactivity = 0; + unsigned long now = jiffies; + + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (!priv->link_id_db[i].status) { + ret = i + 1; + break; + } else if (priv->link_id_db[i].status != CW1200_LINK_HARD && + !priv->tx_queue_stats.link_map_cache[i + 1]) { + + unsigned long inactivity = + now - priv->link_id_db[i].timestamp; + if (inactivity < max_inactivity) + continue; + max_inactivity = inactivity; + ret = i + 1; + } + } + if (ret) { + struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1]; + ap_printk(KERN_DEBUG "[AP] STA added, link_id: %d\n", + ret); + entry->status = CW1200_LINK_RESERVE; + memcpy(&entry->mac, mac, ETH_ALEN); + memset(&entry->buffered, 0, CW1200_MAX_TID); + skb_queue_head_init(&entry->rx_queue); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + } else { + wiphy_info(priv->hw->wiphy, + "[AP] Early: no more link IDs available.\n"); + } + + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +void cw1200_link_id_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, link_id_work); + wsm_flush_tx(priv); + cw1200_link_id_gc_work(&priv->link_id_gc_work.work); + wsm_unlock_tx(priv); +} + +void cw1200_link_id_gc_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, link_id_gc_work.work); + struct wsm_reset reset = { + .reset_statistics = false, + }; + struct wsm_map_link map_link = { + .link_id = 0, + }; + unsigned long now = jiffies; + unsigned long next_gc = -1; + long ttl; + bool need_reset; + u32 mask; + int i; + + if (priv->join_status != CW1200_JOIN_STATUS_AP) + return; + + wsm_lock_tx(priv); + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + need_reset = false; + mask = BIT(i + 1); + if (priv->link_id_db[i].status == CW1200_LINK_RESERVE || + (priv->link_id_db[i].status == CW1200_LINK_HARD && + !(priv->link_id_map & mask))) { + if (priv->link_id_map & mask) { + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + need_reset = true; + } + priv->link_id_map |= mask; + if (priv->link_id_db[i].status != CW1200_LINK_HARD) + priv->link_id_db[i].status = CW1200_LINK_SOFT; + memcpy(map_link.mac_addr, priv->link_id_db[i].mac, + ETH_ALEN); + spin_unlock_bh(&priv->ps_state_lock); + if (need_reset) { + reset.link_id = i + 1; + WARN_ON(wsm_reset(priv, &reset)); + } + map_link.link_id = i + 1; + WARN_ON(wsm_map_link(priv, &map_link)); + next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT); + spin_lock_bh(&priv->ps_state_lock); + } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) { + ttl = priv->link_id_db[i].timestamp - now + + CW1200_LINK_ID_GC_TIMEOUT; + if (ttl <= 0) { + need_reset = true; + priv->link_id_db[i].status = CW1200_LINK_OFF; + priv->link_id_map &= ~mask; + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + memset(map_link.mac_addr, 0, ETH_ALEN); + spin_unlock_bh(&priv->ps_state_lock); + reset.link_id = i + 1; + WARN_ON(wsm_reset(priv, &reset)); + spin_lock_bh(&priv->ps_state_lock); + } else { + next_gc = min(next_gc, (unsigned long)ttl); + } + } + if (need_reset) { + skb_queue_purge(&priv->link_id_db[i].rx_queue); + ap_printk(KERN_DEBUG "[AP] STA removed, link_id: %d\n", + reset.link_id); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (next_gc != -1) + queue_delayed_work(priv->workqueue, + &priv->link_id_gc_work, next_gc); + wsm_unlock_tx(priv); +} + diff --git a/drivers/staging/cw1200/ap.h b/drivers/staging/cw1200/ap.h new file mode 100644 index 00000000000..a9e85bd5516 --- /dev/null +++ b/drivers/staging/cw1200/ap.h @@ -0,0 +1,45 @@ +/* + * mac80211 STA and AP API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef AP_H_INCLUDED +#define AP_H_INCLUDED + +int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set); +int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta); +void cw1200_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed); +int cw1200_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size); + +void cw1200_suspend_resume(struct cw1200_common *priv, + struct wsm_suspend_resume *arg); +void cw1200_set_tim_work(struct work_struct *work); +void cw1200_multicast_start_work(struct work_struct *work); +void cw1200_multicast_stop_work(struct work_struct *work); +void cw1200_mcast_timeout(unsigned long arg); +int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac); +int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac); +void cw1200_link_id_work(struct work_struct *work); +void cw1200_link_id_gc_work(struct work_struct *work); + +#endif diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c new file mode 100644 index 00000000000..978e97592ce --- /dev/null +++ b/drivers/staging/cw1200/bh.c @@ -0,0 +1,541 @@ +/* + * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * Based on: + * ST-Ericsson UMAC CW1200 driver, which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <net/mac80211.h> +#include <linux/kthread.h> + +#include "cw1200.h" +#include "bh.h" +#include "hwio.h" +#include "wsm.h" +#include "sbus.h" + +#if defined(CONFIG_CW1200_BH_DEBUG) +#define bh_printk(...) printk(__VA_ARGS__) +#else +#define bh_printk(...) +#endif + +static int cw1200_bh(void *arg); + +/* TODO: Verify these numbers with WSM specification. */ +#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) +/* an SPI message cannot be bigger than (2"12-1)*2 bytes + * "*2" to cvt to bytes */ +#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2) +#define PIGGYBACK_CTRL_REG (2) +#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) + +/* Suspend state privates */ +enum cw1200_bh_pm_state { + CW1200_BH_RESUMED = 0, + CW1200_BH_SUSPEND, + CW1200_BH_SUSPENDED, + CW1200_BH_RESUME, +}; + +typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv, + u8 *data, size_t size); + + +int cw1200_register_bh(struct cw1200_common *priv) +{ + int err = 0; + struct sched_param param = { .sched_priority = 1 }; + bh_printk(KERN_DEBUG "[BH] register.\n"); + BUG_ON(priv->bh_thread); + atomic_set(&priv->bh_rx, 0); + atomic_set(&priv->bh_tx, 0); + atomic_set(&priv->bh_term, 0); + atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); + priv->buf_id_tx = 0; + priv->buf_id_rx = 0; + init_waitqueue_head(&priv->bh_wq); + init_waitqueue_head(&priv->bh_evt_wq); + priv->bh_thread = kthread_create(&cw1200_bh, priv, "cw1200_bh"); + if (IS_ERR(priv->bh_thread)) { + err = PTR_ERR(priv->bh_thread); + priv->bh_thread = NULL; + } else { + WARN_ON(sched_setscheduler(priv->bh_thread, + SCHED_FIFO, ¶m)); +#ifdef HAS_PUT_TASK_STRUCT + get_task_struct(priv->bh_thread); +#endif + wake_up_process(priv->bh_thread); + } + return err; +} + +void cw1200_unregister_bh(struct cw1200_common *priv) +{ + struct task_struct *thread = priv->bh_thread; + if (WARN_ON(!thread)) + return; + + priv->bh_thread = NULL; + bh_printk(KERN_DEBUG "[BH] unregister.\n"); + atomic_add(1, &priv->bh_term); + wake_up(&priv->bh_wq); + kthread_stop(thread); +#ifdef HAS_PUT_TASK_STRUCT + put_task_struct(thread); +#endif +} + +void cw1200_irq_handler(struct cw1200_common *priv) +{ + bh_printk(KERN_DEBUG "[BH] irq.\n"); + if (/* WARN_ON */(priv->bh_error)) + return; + + if (atomic_add_return(1, &priv->bh_rx) == 1) + wake_up(&priv->bh_wq); +} + +void cw1200_bh_wakeup(struct cw1200_common *priv) +{ + bh_printk(KERN_DEBUG "[BH] wakeup.\n"); + if (WARN_ON(priv->bh_error)) + return; + + if (atomic_add_return(1, &priv->bh_tx) == 1) + wake_up(&priv->bh_wq); +} + +int cw1200_bh_suspend(struct cw1200_common *priv) +{ + bh_printk(KERN_DEBUG "[BH] suspend.\n"); + if (WARN_ON(priv->bh_error)) + return 0; + + atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND); + wake_up(&priv->bh_wq); + return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; +} + +int cw1200_bh_resume(struct cw1200_common *priv) +{ + bh_printk(KERN_DEBUG "[BH] resume.\n"); + if (WARN_ON(priv->bh_error)) + return 0; + + atomic_set(&priv->bh_suspend, CW1200_BH_RESUME); + wake_up(&priv->bh_wq); + return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; +} + +static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv) +{ + ++priv->hw_bufs_used; +} + +int wsm_release_tx_buffer(struct cw1200_common *priv, int count) +{ + int ret = 0; + int hw_bufs_used = priv->hw_bufs_used; + + priv->hw_bufs_used -= count; + if (WARN_ON(priv->hw_bufs_used < 0)) + ret = -1; + else if (hw_bufs_used >= priv->wsm_caps.numInpChBufs) + ret = 1; + if (!priv->hw_bufs_used) + wake_up(&priv->bh_evt_wq); + return ret; +} + +static struct sk_buff *cw1200_get_skb(struct cw1200_common *priv, size_t len) +{ + struct sk_buff *skb; + size_t alloc_len = (len > SDIO_BLOCK_SIZE) ? len : SDIO_BLOCK_SIZE; + + if (len > SDIO_BLOCK_SIZE || !priv->skb_cache) { + skb = dev_alloc_skb(alloc_len + + WSM_TX_EXTRA_HEADROOM + + 8 /* TKIP IV */ + + 12 /* TKIP ICV + MIC */ + - 2 /* Piggyback */); + /* In AP mode RXed SKB can be looped back as a broadcast. + * Here we reserve enough space for headers. */ + skb_reserve(skb, WSM_TX_EXTRA_HEADROOM + + 8 /* TKIP IV */ + - WSM_RX_EXTRA_HEADROOM); + } else { + skb = priv->skb_cache; + priv->skb_cache = NULL; + } + return skb; +} + +static void cw1200_put_skb(struct cw1200_common *priv, struct sk_buff *skb) +{ + if (priv->skb_cache) + dev_kfree_skb(skb); + else + priv->skb_cache = skb; +} + +static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv, + u16 *ctrl_reg) +{ + int ret; + + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) { + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) + printk(KERN_ERR + "[BH] Failed to read control register.\n"); + } + + return ret; +} + +static int cw1200_device_wakeup(struct cw1200_common *priv) +{ + u16 ctrl_reg; + int ret; + + bh_printk(KERN_DEBUG "[BH] Device wakeup.\n"); + + /* To force the device to be always-on, the host sets WLAN_UP to 1 */ + ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, + ST90TDS_CONT_WUP_BIT); + if (WARN_ON(ret)) + return ret; + + ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg); + if (WARN_ON(ret)) + return ret; + + /* If the device returns WLAN_RDY as 1, the device is active and will + * remain active. */ + if (ctrl_reg & ST90TDS_CONT_RDY_BIT) { + bh_printk(KERN_DEBUG "[BH] Device awake.\n"); + return 1; + } + + return 0; +} + +/* Must be called from BH thraed. */ +void cw1200_enable_powersave(struct cw1200_common *priv, + bool enable) +{ + bh_printk(KERN_DEBUG "[BH] Powerave is %s.\n", + enable ? "enabled" : "disabled"); + priv->powersave_enabled = enable; +} + +static int cw1200_bh(void *arg) +{ + struct cw1200_common *priv = arg; + struct sk_buff *skb_rx = NULL; + size_t read_len = 0; + int rx, tx, term, suspend; + struct wsm_hdr *wsm; + size_t wsm_len; + int wsm_id; + u8 wsm_seq; + int rx_resync = 1; + u16 ctrl_reg = 0; + int tx_allowed; + int pending_tx = 0; + long status; + + for (;;) { + if (!priv->hw_bufs_used + && priv->powersave_enabled + && !priv->device_can_sleep) + status = 1 * HZ; + else if (priv->hw_bufs_used) + /* Interrupt loss detection */ + status = 1 * HZ; + else + status = MAX_SCHEDULE_TIMEOUT; + + status = wait_event_interruptible_timeout(priv->bh_wq, ({ + rx = atomic_xchg(&priv->bh_rx, 0); + tx = atomic_xchg(&priv->bh_tx, 0); + term = atomic_xchg(&priv->bh_term, 0); + suspend = atomic_read(&priv->bh_suspend); + (rx || tx || term || suspend); + }), status); + + if (status < 0 || term) + break; + + if (!status && priv->hw_bufs_used) { + wiphy_warn(priv->hw->wiphy, "Missed interrupt?\n"); + rx = 1; + } else if (!status) { + bh_printk(KERN_DEBUG "[BH] Device wakedown.\n"); + WARN_ON(cw1200_reg_write_16(priv, + ST90TDS_CONTROL_REG_ID, 0)); + priv->device_can_sleep = true; + continue; + } else if (suspend) { + bh_printk(KERN_DEBUG "[BH] Device suspend.\n"); + if (priv->powersave_enabled) { + WARN_ON(cw1200_reg_write_16(priv, + ST90TDS_CONTROL_REG_ID, 0)); + priv->device_can_sleep = true; + } + + atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED); + wake_up(&priv->bh_evt_wq); + status = wait_event_interruptible(priv->bh_wq, + CW1200_BH_RESUME == atomic_read( + &priv->bh_suspend)); + if (status < 0) { + wiphy_err(priv->hw->wiphy, + "%s: Failed to wait for resume: %ld.\n", + __func__, status); + break; + } + bh_printk(KERN_DEBUG "[BH] Device resume.\n"); + atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); + wake_up(&priv->bh_evt_wq); + atomic_add(1, &priv->bh_rx); + continue; + } + + tx += pending_tx; + pending_tx = 0; + + if (rx) { + size_t alloc_len; + u8 *data; + + if (WARN_ON(cw1200_bh_read_ctrl_reg( + priv, &ctrl_reg))) + break; +rx: + read_len = (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2; + if (!read_len) + goto tx; + + if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || + (read_len > EFFECTIVE_BUF_SIZE))) { + printk(KERN_DEBUG "Invalid read len: %d", + read_len); + break; + } + + /* Add SIZE of PIGGYBACK reg (CONTROL Reg) + * to the NEXT Message length + 2 Bytes for SKB */ + read_len = read_len + 2; + + BUG_ON(SDIO_BLOCK_SIZE & (SDIO_BLOCK_SIZE - 1)); + +#if defined(CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES) + alloc_len = priv->sbus_ops->align_size( + priv->sbus_priv, read_len); +#else /* CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES */ + /* Platform's SDIO workaround */ + alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1); + if (read_len & (SDIO_BLOCK_SIZE - 1)) + alloc_len += SDIO_BLOCK_SIZE; +#endif /* CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES */ + + skb_rx = cw1200_get_skb(priv, alloc_len); + if (WARN_ON(!skb_rx)) + break; + + skb_trim(skb_rx, 0); + skb_put(skb_rx, read_len); + data = skb_rx->data; + if (WARN_ON(!data)) + break; + + if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) + break; + + /* Piggyback */ + ctrl_reg = __le16_to_cpu( + ((__le16 *)data)[alloc_len / 2 - 1]); + + wsm = (struct wsm_hdr *)data; + wsm_len = __le32_to_cpu(wsm->len); + if (WARN_ON(wsm_len > read_len)) + break; + +#if defined(CONFIG_CW1200_WSM_DUMPS) + print_hex_dump_bytes("<-- ", DUMP_PREFIX_NONE, + data, wsm_len); +#endif /* CONFIG_CW1200_WSM_DUMPS */ + + wsm_id = __le32_to_cpu(wsm->id) & 0xFFF; + wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7; + + skb_trim(skb_rx, wsm_len); + + if (unlikely(wsm_id == 0x0800)) { + wsm_handle_exception(priv, + &data[sizeof(*wsm)], + wsm_len - sizeof(*wsm)); + break; + } else if (unlikely(!rx_resync)) { + if (WARN_ON(wsm_seq != priv->wsm_rx_seq)) + break; + } + priv->wsm_rx_seq = (wsm_seq + 1) & 7; + rx_resync = 0; + + if (wsm_id & 0x0400) { + int rc = wsm_release_tx_buffer(priv, 1); + if (WARN_ON(rc < 0)) + break; + else if (rc > 0) + tx = 1; + } + + /* cw1200_wsm_rx takes care on SKB livetime */ + if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx))) + break; + + if (skb_rx) { + cw1200_put_skb(priv, skb_rx); + skb_rx = NULL; + } + + read_len = 0; + } + +tx: + /* HACK! One buffer is reserved for control path */ + BUG_ON(priv->hw_bufs_used > priv->wsm_caps.numInpChBufs); + tx_allowed = + priv->hw_bufs_used < priv->wsm_caps.numInpChBufs; + + if (tx && tx_allowed) { + size_t tx_len; + u8 *data; + int ret; + + if (priv->device_can_sleep) { + ret = cw1200_device_wakeup(priv); + if (WARN_ON(ret < 0)) + break; + else if (ret) + priv->device_can_sleep = false; + else { + /* Wait for "awake" interrupt */ + pending_tx = tx; + continue; + } + } + + wsm_alloc_tx_buffer(priv); + ret = wsm_get_tx(priv, &data, &tx_len); + if (ret <= 0) { + wsm_release_tx_buffer(priv, 1); + if (WARN_ON(ret < 0)) + break; + } else { + wsm = (struct wsm_hdr *)data; + BUG_ON(tx_len < sizeof(*wsm)); + BUG_ON(__le32_to_cpu(wsm->len) != tx_len); + +#if 0 /* count is not implemented */ + if (ret > 1) + atomic_add(1, &priv->bh_tx); +#else + atomic_add(1, &priv->bh_tx); +#endif + + +#if defined(CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES) + tx_len = priv->sbus_ops->align_size( + priv->sbus_priv, tx_len); +#else /* CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES */ + /* HACK!!! Platform limitation. + * It is also supported by upper layer: + * there is always enough space at the + * end of the buffer. */ + if (tx_len & (SDIO_BLOCK_SIZE - 1)) { + tx_len &= ~(SDIO_BLOCK_SIZE - 1); + tx_len += SDIO_BLOCK_SIZE; + } +#endif /* CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES */ + + wsm->id &= __cpu_to_le32( + ~WSM_TX_SEQ(WSM_TX_SEQ_MAX)); + wsm->id |= cpu_to_le32( + WSM_TX_SEQ(priv->wsm_tx_seq)); + + if (WARN_ON(cw1200_data_write(priv, + data, tx_len))) { + wsm_release_tx_buffer(priv, 1); + break; + } + +#if defined(CONFIG_CW1200_WSM_DUMPS) + print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, + data, __le32_to_cpu(wsm->len)); +#endif /* CONFIG_CW1200_WSM_DUMPS */ + + wsm_txed(priv, data); + priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & + WSM_TX_SEQ_MAX; + } + } + + /* HACK!!! Device tends not to send interrupt + * if this extra check is missing */ + if (!(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)) { + if (WARN_ON(cw1200_bh_read_ctrl_reg( + priv, &ctrl_reg))) + break; + } + + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) + goto rx; + } + + if (skb_rx) { + cw1200_put_skb(priv, skb_rx); + skb_rx = NULL; + } + + + if (!term) { + cw1200_dbg(CW1200_DBG_ERROR, "[BH] Fatal error, exitting.\n"); + priv->bh_error = 1; + /* TODO: schedule_work(recovery) */ +#ifndef HAS_PUT_TASK_STRUCT + /* The only reason of having this stupid code here is + * that __put_task_struct is not exported by kernel. */ + for (;;) { + int status = wait_event_interruptible(priv->bh_wq, ({ + term = atomic_xchg(&priv->bh_term, 0); + (term); + })); + + if (status || term) + break; + } +#endif + } + return 0; +} diff --git a/drivers/staging/cw1200/bh.h b/drivers/staging/cw1200/bh.h new file mode 100644 index 00000000000..6d4d27b18c5 --- /dev/null +++ b/drivers/staging/cw1200/bh.h @@ -0,0 +1,32 @@ +/* + * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_BH_H +#define CW1200_BH_H + +/* extern */ struct cw1200_common; + +/* TODO: 512, actually. Was increased to 1024 + * for compatibility with particular FW. */ +#define SDIO_BLOCK_SIZE (1024) + +int cw1200_register_bh(struct cw1200_common *priv); +void cw1200_unregister_bh(struct cw1200_common *priv); +void cw1200_irq_handler(struct cw1200_common *priv); +void cw1200_bh_wakeup(struct cw1200_common *priv); +int cw1200_bh_suspend(struct cw1200_common *priv); +int cw1200_bh_resume(struct cw1200_common *priv); +/* Must be called from BH thread. */ +void cw1200_enable_powersave(struct cw1200_common *priv, + bool enable); +int wsm_release_tx_buffer(struct cw1200_common *priv, int count); + +#endif /* CW1200_BH_H */ diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h new file mode 100644 index 00000000000..3c3a50ce318 --- /dev/null +++ b/drivers/staging/cw1200/cw1200.h @@ -0,0 +1,265 @@ +/* + * Common private data for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * Based on the mac80211 Prism54 code, which is + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_H +#define CW1200_H + +#include <linux/wait.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/atomic.h> +#include <net/mac80211.h> + +#include "queue.h" +#include "wsm.h" +#include "scan.h" +#include "txrx.h" +#include "ht.h" +#include "pm.h" + +/* extern */ struct sbus_ops; +/* extern */ struct task_struct; +/* extern */ struct cw1200_debug_priv; +/* extern */ struct firmware; + +#if defined(CONFIG_CW1200_TXRX_DEBUG) +#define txrx_printk(...) printk(__VA_ARGS__) +#else +#define txrx_printk(...) +#endif + +#define CW1200_MAX_CTRL_FRAME_LEN (0x1000) + +#define CW1200_MAX_STA_IN_AP_MODE (5) +#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1) +#define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2) +#define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3) +#define CW1200_MAX_REQUEUE_ATTEMPTS (5) + +#define CW1200_MAX_TID (8) + +/* Please keep order */ +enum cw1200_join_status { + CW1200_JOIN_STATUS_PASSIVE = 0, + CW1200_JOIN_STATUS_MONITOR, + CW1200_JOIN_STATUS_STA, + CW1200_JOIN_STATUS_AP, +}; + +enum cw1200_link_status { + CW1200_LINK_OFF, + CW1200_LINK_RESERVE, + CW1200_LINK_SOFT, + CW1200_LINK_HARD, +}; + +struct cw1200_link_entry { + unsigned long timestamp; + enum cw1200_link_status status; + u8 mac[ETH_ALEN]; + u8 buffered[CW1200_MAX_TID]; + struct sk_buff_head rx_queue; +}; + +struct cw1200_common { + struct cw1200_queue tx_queue[4]; + struct cw1200_queue_stats tx_queue_stats; + struct cw1200_debug_priv *debug; + + struct ieee80211_hw *hw; + struct ieee80211_vif *vif; + struct device *pdev; + struct workqueue_struct *workqueue; + + struct mutex conf_mutex; + + const struct sbus_ops *sbus_ops; + struct sbus_priv *sbus_priv; + + /* HW type (HIF_...) */ + int hw_type; + int hw_revision; + + /* firmware/hardware info */ + unsigned int tx_hdr_len; + + /* Radio data */ + int output_power; + int noise; + + /* calibration, output power limit and rssi<->dBm conversation data */ + + /* BBP/MAC state */ + const struct firmware *sdd; + struct ieee80211_rate *rates; + struct ieee80211_rate *mcs_rates; + u8 mac_addr[ETH_ALEN]; + struct ieee80211_channel *channel; + u8 bssid[ETH_ALEN]; + struct wsm_edca_params edca; + struct wsm_association_mode association_mode; + struct wsm_set_bss_params bss_params; + struct cw1200_ht_info ht_info; + struct wsm_set_pm powersave_mode; + int cqm_rssi_thold; + unsigned cqm_rssi_hyst; + unsigned cqm_tx_failure_thold; + unsigned cqm_tx_failure_count; + int cqm_link_loss_count; + int cqm_beacon_loss_count; + int channel_switch_in_progress; + wait_queue_head_t channel_switch_done; + u8 long_frame_max_tx_count; + u8 short_frame_max_tx_count; + int mode; + bool enable_beacon; + int beacon_int; + size_t ssid_length; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + bool listening; + struct wsm_rx_filter rx_filter; + struct wsm_beacon_filter_table bf_table; + struct wsm_beacon_filter_control bf_control; + u8 ba_tid_mask; + struct wsm_multicast_filter multicast_filter; + struct cw1200_pm_state pm_state; + struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; + struct wsm_uapsd_info uapsd_info; + bool setbssparams_done; + bool is_BT_Present; + u8 conf_listen_interval; + u32 listen_interval; + + /* BH */ + atomic_t bh_rx; + atomic_t bh_tx; + atomic_t bh_term; + atomic_t bh_suspend; + struct task_struct *bh_thread; + int bh_error; + wait_queue_head_t bh_wq; + wait_queue_head_t bh_evt_wq; + int buf_id_tx; /* byte */ + int buf_id_rx; /* byte */ + int wsm_rx_seq; /* byte */ + int wsm_tx_seq; /* byte */ + int hw_bufs_used; + struct sk_buff *skb_cache; + bool powersave_enabled; + bool device_can_sleep; + + /* WSM */ + struct wsm_caps wsm_caps; + struct mutex wsm_cmd_mux; + struct wsm_buf wsm_cmd_buf; + struct wsm_cmd wsm_cmd; + wait_queue_head_t wsm_cmd_wq; + wait_queue_head_t wsm_startup_done; + struct wsm_cbc wsm_cbc; + atomic_t tx_lock; + + /* Scan status */ + struct cw1200_scan scan; + + /* WSM Join */ + enum cw1200_join_status join_status; + u8 join_bssid[ETH_ALEN]; + u32 pending_frame_id; + struct work_struct join_work; + struct delayed_work join_timeout; + struct work_struct unjoin_work; + struct work_struct offchannel_work; + int join_dtim_period; + bool delayed_unjoin; + + /* TX/RX and security */ + s8 wep_default_key_id; + struct work_struct wep_key_work; + u32 key_map; + struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1]; + unsigned long rx_timestamp; + + /* AP powersave */ + u32 link_id_map; + struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE]; + struct work_struct link_id_work; + struct delayed_work link_id_gc_work; + u32 sta_asleep_mask; + u32 pspoll_mask; + bool aid0_bit_set; + spinlock_t ps_state_lock; + bool buffered_multicasts; + bool tx_multicast; + struct work_struct set_tim_work; + struct work_struct multicast_start_work; + struct work_struct multicast_stop_work; + struct timer_list mcast_timeout; + + + /* WSM events and CQM implementation */ + spinlock_t event_queue_lock; + struct list_head event_queue; + struct work_struct event_handler; + struct delayed_work bss_loss_work; + struct delayed_work connection_loss_work; + struct work_struct tx_failure_work; + int delayed_link_loss; + + /* TX rate policy cache */ + struct tx_policy_cache tx_policy_cache; + struct work_struct tx_policy_upload_work; + + /* cryptographic engine information */ + + /* bit field of glowing LEDs */ + u16 softled_state; + + /* statistics */ + struct ieee80211_low_level_stats stats; +}; + +struct cw1200_sta_priv { + int link_id; +}; + +/* interfaces for the drivers */ +int cw1200_core_probe(const struct sbus_ops *sbus_ops, + struct sbus_priv *sbus, + struct device *pdev, + struct cw1200_common **pself); +void cw1200_core_release(struct cw1200_common *self); + +#define CW1200_DBG_MSG 0x00000001 +#define CW1200_DBG_NIY 0x00000002 +#define CW1200_DBG_SBUS 0x00000004 +#define CW1200_DBG_INIT 0x00000008 +#define CW1200_DBG_ERROR 0x00000010 +#define CW1200_DBG_LEVEL 0xFFFFFFFF + +#define cw1200_dbg(level, ...) \ + do { \ + if ((level) & CW1200_DBG_LEVEL) \ + printk(KERN_DEBUG __VA_ARGS__); \ + } while (0) + +#define STUB() \ + do { \ + cw1200_dbg(CW1200_DBG_NIY, "%s: STUB at line %d.\n", \ + __func__, __LINE__); \ + } while (0) + +#endif /* CW1200_H */ diff --git a/drivers/staging/cw1200/cw1200_plat.h b/drivers/staging/cw1200/cw1200_plat.h new file mode 100644 index 00000000000..4d30dbd3979 --- /dev/null +++ b/drivers/staging/cw1200/cw1200_plat.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef CW1200_PLAT_H_INCLUDED +#define CW1200_PLAT_H_INCLUDED + +#include <linux/ioport.h> + +struct cw1200_platform_data { + const char *mmc_id; + const struct resource *irq; + const struct resource *reset; + int (*power_ctrl)(const struct cw1200_platform_data *pdata, + bool enable); + int (*clk_ctrl)(const struct cw1200_platform_data *pdata, + bool enable); +}; + +/* Declaration only. Should be implemented in arch/xxx/mach-yyy */ +const struct cw1200_platform_data *cw1200_get_platform_data(void); + +#endif /* CW1200_PLAT_H_INCLUDED */ diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c new file mode 100644 index 00000000000..cd432655bff --- /dev/null +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -0,0 +1,436 @@ +/* + * Mac80211 SDIO driver for ST-Ericsson CW1200 device + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/card.h> +#include <linux/mmc/sdio.h> +#include <linux/spinlock.h> +#include <asm/mach-types.h> +#include <net/mac80211.h> + +#include "cw1200.h" +#include "sbus.h" +#include "cw1200_plat.h" + +MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>"); +MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("cw1200_wlan"); + +struct sbus_priv { + struct sdio_func *func; + struct cw1200_common *core; + const struct cw1200_platform_data *pdata; + spinlock_t lock; + sbus_irq_handler irq_handler; + void *irq_priv; +}; + +static const struct sdio_device_id cw1200_sdio_ids[] = { + { SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) }, + { /* end: all zeroes */ }, +}; + +/* sbus_ops implemetation */ + +static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self, + unsigned int addr, + void *dst, int count) +{ + return sdio_memcpy_fromio(self->func, dst, addr, count); +} + +static int cw1200_sdio_memcpy_toio(struct sbus_priv *self, + unsigned int addr, + const void *src, int count) +{ + return sdio_memcpy_toio(self->func, addr, (void *)src, count); +} + +static void cw1200_sdio_lock(struct sbus_priv *self) +{ + sdio_claim_host(self->func); +} + +static void cw1200_sdio_unlock(struct sbus_priv *self) +{ + sdio_release_host(self->func); +} + +#ifndef CONFIG_CW1200_USE_GPIO_IRQ +static void cw1200_sdio_irq_handler(struct sdio_func *func) +{ + struct sbus_priv *self = sdio_get_drvdata(func); + unsigned long flags; + + BUG_ON(!self); + spin_lock_irqsave(&self->lock, flags); + if (self->irq_handler) + self->irq_handler(self->irq_priv); + spin_unlock_irqrestore(&self->lock, flags); +} +#else /* CONFIG_CW1200_USE_GPIO_IRQ */ +static irqreturn_t cw1200_gpio_irq_handler(int irq, void *dev_id) +{ + struct sbus_priv *self = dev_id; + + BUG_ON(!self); + if (self->irq_handler) + self->irq_handler(self->irq_priv); + return IRQ_HANDLED; +} + +static int cw1200_request_irq(struct sbus_priv *self, + irq_handler_t handler) +{ + int ret; + int func_num; + const struct resource *irq = self->pdata->irq; + u8 cccr; + + ret = request_any_context_irq(irq->start, handler, + IRQF_TRIGGER_RISING, irq->name, self); + if (WARN_ON(ret < 0)) + goto exit; + + /* Hack to access Fuction-0 */ + func_num = self->func->num; + self->func->num = 0; + + cccr = sdio_readb(self->func, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto set_func; + + /* Master interrupt enable ... */ + cccr |= BIT(0); + + /* ... for our function */ + cccr |= BIT(func_num); + + sdio_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto set_func; + + /* Restore the WLAN function number */ + self->func->num = func_num; + return 0; + +set_func: + self->func->num = func_num; + free_irq(irq->start, self); +exit: + return ret; +} +#endif /* CONFIG_CW1200_USE_GPIO_IRQ */ + +static int cw1200_sdio_irq_subscribe(struct sbus_priv *self, + sbus_irq_handler handler, + void *priv) +{ + int ret; + unsigned long flags; + + if (!handler) + return -EINVAL; + + spin_lock_irqsave(&self->lock, flags); + self->irq_priv = priv; + self->irq_handler = handler; + spin_unlock_irqrestore(&self->lock, flags); + + printk(KERN_DEBUG "SW IRQ subscribe\n"); + sdio_claim_host(self->func); +#ifndef CONFIG_CW1200_USE_GPIO_IRQ + ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler); +#else + ret = cw1200_request_irq(self, cw1200_gpio_irq_handler); +#endif + sdio_release_host(self->func); + return ret; +} + +static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) +{ + int ret = 0; + unsigned long flags; +#ifdef CONFIG_CW1200_USE_GPIO_IRQ + const struct resource *irq = self->pdata->irq; +#endif + + WARN_ON(!self->irq_handler); + if (!self->irq_handler) + return 0; + + printk(KERN_DEBUG "SW IRQ unsubscribe\n"); +#ifndef CONFIG_CW1200_USE_GPIO_IRQ + sdio_claim_host(self->func); + ret = sdio_release_irq(self->func); + sdio_release_host(self->func); +#else + free_irq(irq->start, self); +#endif + + spin_lock_irqsave(&self->lock, flags); + self->irq_priv = NULL; + self->irq_handler = NULL; + spin_unlock_irqrestore(&self->lock, flags); + + return ret; +} + +static int cw1200_detect_card(const struct cw1200_platform_data *pdata) +{ + /* HACK!!! + * Rely on mmc->class_dev.class set in mmc_alloc_host + * Tricky part: a new mmc hook is being (temporary) created + * to discover mmc_host class. + * Do you know more elegant way how to enumerate mmc_hosts? + */ + + struct mmc_host *mmc = NULL; + struct class_dev_iter iter; + struct device *dev; + + mmc = mmc_alloc_host(0, NULL); + if (!mmc) + return -ENOMEM; + + BUG_ON(!mmc->class_dev.class); + class_dev_iter_init(&iter, mmc->class_dev.class, NULL, NULL); + for (;;) { + dev = class_dev_iter_next(&iter); + if (!dev) { + printk(KERN_ERR "cw1200: %s is not found.\n", + pdata->mmc_id); + break; + } else { + struct mmc_host *host = container_of(dev, + struct mmc_host, class_dev); + + if (dev_name(&host->class_dev) && + strcmp(dev_name(&host->class_dev), + pdata->mmc_id)) + continue; + + mmc_detect_change(host, 10); + break; + } + } + mmc_free_host(mmc); + return 0; +} + +static int cw1200_sdio_off(const struct cw1200_platform_data *pdata) +{ + const struct resource *reset = pdata->reset; + gpio_set_value(reset->start, 0); + cw1200_detect_card(pdata); + gpio_free(reset->start); + return 0; +} + +static int cw1200_sdio_on(const struct cw1200_platform_data *pdata) +{ + const struct resource *reset = pdata->reset; + gpio_request(reset->start, reset->name); + gpio_direction_output(reset->start, 1); + /* It is not stated in the datasheet, but at least some of devices + * have problems with reset if this stage is omited. */ + msleep(50); + gpio_direction_output(reset->start, 0); + /* A valid reset shall be obtained by maintaining WRESETN + * active (low) for at least two cycles of LP_CLK after VDDIO + * is stable within it operating range. */ + msleep(1); + gpio_set_value(reset->start, 1); + /* The host should wait 32 ms after the WRESETN release + * for the on-chip LDO to stabilize */ + msleep(32); + cw1200_detect_card(pdata); + return 0; +} + +static int cw1200_sdio_reset(struct sbus_priv *self) +{ + cw1200_sdio_off(self->pdata); + msleep(1000); + cw1200_sdio_on(self->pdata); + return 0; +} + +static size_t cw1200_sdio_align_size(struct sbus_priv *self, size_t size) +{ + size_t aligned = sdio_align_size(self->func, size); + /* HACK!!! Problems with DMA size on u8500 platform */ + if ((aligned & 0x1F) && (aligned & ~0x1F)) { + aligned &= ~0x1F; + aligned += 0x20; + } + + return aligned; +} + +static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend) +{ + int ret; + const struct resource *irq = self->pdata->irq; + struct sdio_func *func = self->func; + + sdio_claim_host(func); + if (suspend) + ret = mmc_host_disable(func->card->host); + else + ret = mmc_host_enable(func->card->host); + sdio_release_host(func); + + if (!ret && irq) + ret = irq_set_irq_wake(irq->start, suspend); + + return ret; +} + +static struct sbus_ops cw1200_sdio_sbus_ops = { + .sbus_memcpy_fromio = cw1200_sdio_memcpy_fromio, + .sbus_memcpy_toio = cw1200_sdio_memcpy_toio, + .lock = cw1200_sdio_lock, + .unlock = cw1200_sdio_unlock, + .irq_subscribe = cw1200_sdio_irq_subscribe, + .irq_unsubscribe = cw1200_sdio_irq_unsubscribe, + .reset = cw1200_sdio_reset, + .align_size = cw1200_sdio_align_size, + .power_mgmt = cw1200_sdio_pm, +}; + +/* Probe Function to be called by SDIO stack when device is discovered */ +static int cw1200_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct sbus_priv *self; + int status; + + cw1200_dbg(CW1200_DBG_INIT, "Probe called\n"); + + self = kzalloc(sizeof(*self), GFP_KERNEL); + if (!self) { + cw1200_dbg(CW1200_DBG_ERROR, "Can't allocate SDIO sbus_priv."); + return -ENOMEM; + } + + spin_lock_init(&self->lock); + self->pdata = cw1200_get_platform_data(); + self->func = func; + sdio_set_drvdata(func, self); + sdio_claim_host(func); + sdio_enable_func(func); + sdio_release_host(func); + + status = cw1200_core_probe(&cw1200_sdio_sbus_ops, + self, &func->dev, &self->core); + if (status) { + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + sdio_set_drvdata(func, NULL); + kfree(self); + } + + return status; +} + +/* Disconnect Function to be called by SDIO stack when + * device is disconnected */ +static void cw1200_sdio_disconnect(struct sdio_func *func) +{ + struct sbus_priv *self = sdio_get_drvdata(func); + + if (self) { + if (self->core) { + cw1200_core_release(self->core); + self->core = NULL; + } + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + sdio_set_drvdata(func, NULL); + kfree(self); + } +} + +static struct sdio_driver sdio_driver = { + .name = "cw1200_wlan", + .id_table = cw1200_sdio_ids, + .probe = cw1200_sdio_probe, + .remove = cw1200_sdio_disconnect, +}; + +/* Init Module function -> Called by insmod */ +static int __init cw1200_sdio_init(void) +{ + const struct cw1200_platform_data *pdata; + int ret; + + pdata = cw1200_get_platform_data(); + + ret = sdio_register_driver(&sdio_driver); + if (ret) + goto err_reg; + + if (pdata->clk_ctrl) { + ret = pdata->clk_ctrl(pdata, true); + if (ret) + goto err_clk; + } + + if (pdata->power_ctrl) { + ret = pdata->power_ctrl(pdata, true); + if (ret) + goto err_power; + } + + ret = cw1200_sdio_on(pdata); + if (ret) + goto err_on; + + return 0; + +err_on: + if (pdata->power_ctrl) + pdata->power_ctrl(pdata, false); +err_power: + if (pdata->clk_ctrl) + pdata->clk_ctrl(pdata, false); +err_clk: + sdio_unregister_driver(&sdio_driver); +err_reg: + return ret; +} + +/* Called at Driver Unloading */ +static void __exit cw1200_sdio_exit(void) +{ + const struct cw1200_platform_data *pdata; + pdata = cw1200_get_platform_data(); + sdio_unregister_driver(&sdio_driver); + cw1200_sdio_off(pdata); + if (pdata->power_ctrl) + pdata->power_ctrl(pdata, false); + if (pdata->clk_ctrl) + pdata->clk_ctrl(pdata, false); +} + + +module_init(cw1200_sdio_init); +module_exit(cw1200_sdio_exit); diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c new file mode 100644 index 00000000000..297b83e1ef5 --- /dev/null +++ b/drivers/staging/cw1200/debug.c @@ -0,0 +1,467 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * DebugFS code + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include "cw1200.h" +#include "debug.h" + +/* join_status */ +static const char * const cw1200_debug_join_status[] = { + "passive", + "monitor", + "station", + "access point", +}; + +/* WSM_JOIN_PREAMBLE_... */ +static const char * const cw1200_debug_preamble[] = { + "long", + "short", + "long on 1 and 2 Mbps", +}; + +static const char * const cw1200_debug_fw_types[] = { + "ETF", + "WFM", + "WSM", + "HI test", + "Platform test", +}; + +static const char * const cw1200_debug_link_id[] = { + "OFF", + "REQ", + "SOFT", + "HARD", +}; + +static const char *cw1200_debug_mode(int mode) +{ + switch (mode) { + case NL80211_IFTYPE_UNSPECIFIED: + return "unspecified"; + case NL80211_IFTYPE_MONITOR: + return "monitor"; + case NL80211_IFTYPE_STATION: + return "station"; + case NL80211_IFTYPE_ADHOC: + return "ad-hok"; + case NL80211_IFTYPE_MESH_POINT: + return "mesh point"; + case NL80211_IFTYPE_AP: + return "access point"; + case NL80211_IFTYPE_P2P_CLIENT: + return "p2p client"; + case NL80211_IFTYPE_P2P_GO: + return "p2p go"; + default: + return "unsupported"; + } +} + +static void cw1200_queue_status_show(struct seq_file *seq, + struct cw1200_queue *q) +{ + int i; + seq_printf(seq, "Queue %d:\n", q->queue_id); + seq_printf(seq, " capacity: %d\n", q->capacity); + seq_printf(seq, " queued: %d\n", q->num_queued); + seq_printf(seq, " pending: %d\n", q->num_pending); + seq_printf(seq, " sent: %d\n", q->num_sent); + seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no"); + seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no"); + seq_puts(seq, " link map: 0-> "); + for (i = 0; i < q->stats->map_capacity; ++i) + seq_printf(seq, "%.2d ", q->link_map_cache[i]); + seq_printf(seq, "<-%d\n", q->stats->map_capacity); +} + +static void cw1200_debug_print_map(struct seq_file *seq, + struct cw1200_common *priv, + const char *label, + u32 map) +{ + int i; + seq_printf(seq, "%s0-> ", label); + for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i) + seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : ".."); + seq_printf(seq, "<-%d\n", priv->tx_queue_stats.map_capacity - 1); +} + +static int cw1200_status_show(struct seq_file *seq, void *v) +{ + int i; + struct list_head *item; + struct cw1200_common *priv = seq->private; + struct cw1200_debug_priv *d = priv->debug; + seq_puts(seq, "CW1200 Wireless LAN driver status\n"); + seq_printf(seq, "Hardware: %d.%d\n", + priv->wsm_caps.hardwareId, + priv->wsm_caps.hardwareSubId); + seq_printf(seq, "Firmware: %s %d.%d\n", + cw1200_debug_fw_types[priv->wsm_caps.firmwareType], + priv->wsm_caps.firmwareVersion, + priv->wsm_caps.firmwareBuildNumber); + seq_printf(seq, "FW API: %d\n", + priv->wsm_caps.firmwareApiVer); + seq_printf(seq, "FW caps: 0x%.4X\n", + priv->wsm_caps.firmwareCap); + seq_printf(seq, "Mode: %s%s\n", + cw1200_debug_mode(priv->mode), + priv->listening ? " (listening)" : ""); + seq_printf(seq, "Assoc: %s\n", + cw1200_debug_join_status[priv->join_status]); + if (priv->channel) + seq_printf(seq, "Channel: %d%s\n", + priv->channel->hw_value, + priv->channel_switch_in_progress ? + " (switching)" : ""); + if (priv->rx_filter.promiscuous) + seq_puts(seq, "Filter: promisc\n"); + else if (priv->rx_filter.fcs) + seq_puts(seq, "Filter: fcs\n"); + if (priv->rx_filter.bssid) + seq_puts(seq, "Filter: bssid\n"); + if (priv->bf_control.bcn_count) + seq_puts(seq, "Filter: beacons\n"); + + if (priv->enable_beacon || + priv->mode == NL80211_IFTYPE_AP || + priv->mode == NL80211_IFTYPE_ADHOC || + priv->mode == NL80211_IFTYPE_MESH_POINT || + priv->mode == NL80211_IFTYPE_P2P_GO) + seq_printf(seq, "Beaconing: %s\n", + priv->enable_beacon ? + "enabled" : "disabled"); + if (priv->ssid_length || + priv->mode == NL80211_IFTYPE_AP || + priv->mode == NL80211_IFTYPE_ADHOC || + priv->mode == NL80211_IFTYPE_MESH_POINT || + priv->mode == NL80211_IFTYPE_P2P_GO) + seq_printf(seq, "SSID: %.*s\n", + priv->ssid_length, priv->ssid); + + for (i = 0; i < 4; ++i) { + seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i, + priv->edca.params[i].cwMin, + priv->edca.params[i].cwMax, + priv->edca.params[i].aifns, + priv->edca.params[i].txOpLimit, + priv->edca.params[i].maxReceiveLifetime); + } + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + static const char *pmMode = "unknown"; + switch (priv->powersave_mode.pmMode) { + case WSM_PSM_ACTIVE: + pmMode = "off"; + break; + case WSM_PSM_PS: + pmMode = "on"; + break; + case WSM_PSM_FAST_PS: + pmMode = "dynamic"; + break; + } + seq_printf(seq, "Preamble: %s\n", + cw1200_debug_preamble[ + priv->association_mode.preambleType]); + seq_printf(seq, "AMPDU spcn: %d\n", + priv->association_mode.mpduStartSpacing); + seq_printf(seq, "Basic rate: 0x%.8X\n", + le32_to_cpu(priv->association_mode.basicRateSet)); + seq_printf(seq, "Bss lost: %d beacons\n", + priv->bss_params.beaconLostCount); + seq_printf(seq, "AID: %d\n", + priv->bss_params.aid); + seq_printf(seq, "Rates: 0x%.8X\n", + priv->bss_params.operationalRateSet); + seq_printf(seq, "Powersave: %s\n", pmMode); + } + seq_printf(seq, "HT: %s\n", + cw1200_is_ht(&priv->ht_info) ? "on" : "off"); + if (cw1200_is_ht(&priv->ht_info)) { + seq_printf(seq, "Greenfield: %s\n", + cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no"); + seq_printf(seq, "AMPDU dens: %d\n", + cw1200_ht_ampdu_density(&priv->ht_info)); + } + seq_printf(seq, "RSSI thold: %d\n", + priv->cqm_rssi_thold); + seq_printf(seq, "RSSI hyst: %d\n", + priv->cqm_rssi_hyst); + seq_printf(seq, "TXFL thold: %d\n", + priv->cqm_tx_failure_thold); + seq_printf(seq, "Linkloss: %d\n", + priv->cqm_link_loss_count); + seq_printf(seq, "Bcnloss: %d\n", + priv->cqm_beacon_loss_count); + seq_printf(seq, "Long retr: %d\n", + priv->long_frame_max_tx_count); + seq_printf(seq, "Short retr: %d\n", + priv->short_frame_max_tx_count); + spin_lock_bh(&priv->tx_policy_cache.lock); + i = 0; + list_for_each(item, &priv->tx_policy_cache.used) + ++i; + spin_unlock_bh(&priv->tx_policy_cache.lock); + seq_printf(seq, "RC in use: %d\n", i); + + seq_puts(seq, "\n"); + for (i = 0; i < 4; ++i) { + cw1200_queue_status_show(seq, &priv->tx_queue[i]); + seq_puts(seq, "\n"); + } + + cw1200_debug_print_map(seq, priv, "Link map: ", + priv->link_id_map); + cw1200_debug_print_map(seq, priv, "Asleep map: ", + priv->sta_asleep_mask); + cw1200_debug_print_map(seq, priv, "PSPOLL map: ", + priv->pspoll_mask); + + seq_puts(seq, "\n"); + + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (priv->link_id_db[i].status) { + seq_printf(seq, "Link %d: %s, %pM\n", + i + 1, cw1200_debug_link_id[ + priv->link_id_db[i].status], + priv->link_id_db[i].mac); + } + } + + seq_puts(seq, "\n"); + + seq_printf(seq, "BH status: %s\n", + atomic_read(&priv->bh_term) ? "terminated" : "alive"); + seq_printf(seq, "Pending RX: %d\n", + atomic_read(&priv->bh_rx)); + seq_printf(seq, "Pending TX: %d\n", + atomic_read(&priv->bh_tx)); + if (priv->bh_error) + seq_printf(seq, "BH errcode: %d\n", + priv->bh_error); + seq_printf(seq, "TX bufs: %d x %d bytes\n", + priv->wsm_caps.numInpChBufs, + priv->wsm_caps.sizeInpChBuf); + seq_printf(seq, "Used bufs: %d\n", + priv->hw_bufs_used); + seq_printf(seq, "Powermgmt: %s\n", + priv->powersave_enabled ? "on" : "off"); + seq_printf(seq, "Device: %s\n", + priv->device_can_sleep ? "alseep" : "awake"); + + spin_lock(&priv->wsm_cmd.lock); + seq_printf(seq, "WSM status: %s\n", + priv->wsm_cmd.done ? "idle" : "active"); + seq_printf(seq, "WSM cmd: 0x%.4X (%d bytes)\n", + priv->wsm_cmd.cmd, priv->wsm_cmd.len); + seq_printf(seq, "WSM retval: %d\n", + priv->wsm_cmd.ret); + spin_unlock(&priv->wsm_cmd.lock); + + seq_printf(seq, "Datapath: %s\n", + atomic_read(&priv->tx_lock) ? "locked" : "unlocked"); + if (atomic_read(&priv->tx_lock)) + seq_printf(seq, "TXlock cnt: %d\n", + atomic_read(&priv->tx_lock)); + + seq_printf(seq, "TXed: %d\n", + d->tx); + seq_printf(seq, "AGG TXed: %d\n", + d->tx_agg); + seq_printf(seq, "MULTI TXed: %d (%d)\n", + d->tx_multi, d->tx_multi_frames); + seq_printf(seq, "RXed: %d\n", + d->rx); + seq_printf(seq, "AGG RXed: %d\n", + d->rx_agg); + seq_printf(seq, "TX miss: %d\n", + d->tx_cache_miss); + seq_printf(seq, "TX align: %d\n", + d->tx_align); + seq_printf(seq, "TX TTL: %d\n", + d->tx_ttl); + seq_printf(seq, "Scan: %s\n", + atomic_read(&priv->scan.in_progress) ? "active" : "idle"); + seq_printf(seq, "Led state: 0x%.2X\n", + priv->softled_state); + + return 0; +} + +static int cw1200_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, &cw1200_status_show, + inode->i_private); +} + +static const struct file_operations fops_status = { + .open = cw1200_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int cw1200_counters_show(struct seq_file *seq, void *v) +{ + int ret; + struct cw1200_common *priv = seq->private; + struct wsm_counters_table counters; + + ret = wsm_get_counters_table(priv, &counters); + if (ret) + return ret; + +#define CAT_STR(x, y) x ## y +#define PUT_COUNTER(tab, name) \ + seq_printf(seq, "%s:" tab "%d\n", #name, \ + __le32_to_cpu(counters.CAT_STR(count, name))) + + PUT_COUNTER("\t\t", PlcpErrors); + PUT_COUNTER("\t\t", FcsErrors); + PUT_COUNTER("\t\t", TxPackets); + PUT_COUNTER("\t\t", RxPackets); + PUT_COUNTER("\t\t", RxPacketErrors); + PUT_COUNTER("\t", RxDecryptionFailures); + PUT_COUNTER("\t\t", RxMicFailures); + PUT_COUNTER("\t", RxNoKeyFailures); + PUT_COUNTER("\t", TxMulticastFrames); + PUT_COUNTER("\t", TxFramesSuccess); + PUT_COUNTER("\t", TxFrameFailures); + PUT_COUNTER("\t", TxFramesRetried); + PUT_COUNTER("\t", TxFramesMultiRetried); + PUT_COUNTER("\t", RxFrameDuplicates); + PUT_COUNTER("\t\t", RtsSuccess); + PUT_COUNTER("\t\t", RtsFailures); + PUT_COUNTER("\t\t", AckFailures); + PUT_COUNTER("\t", RxMulticastFrames); + PUT_COUNTER("\t", RxFramesSuccess); + PUT_COUNTER("\t", RxCMACICVErrors); + PUT_COUNTER("\t\t", RxCMACReplays); + PUT_COUNTER("\t", RxMgmtCCMPReplays); + +#undef PUT_COUNTER +#undef CAT_STR + + return 0; +} + +static int cw1200_counters_open(struct inode *inode, struct file *file) +{ + return single_open(file, &cw1200_counters_show, + inode->i_private); +} + +static const struct file_operations fops_counters = { + .open = cw1200_counters_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int cw1200_generic_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t cw1200_11n_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + struct ieee80211_supported_band *band = + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + return simple_read_from_buffer(user_buf, count, ppos, + band->ht_cap.ht_supported ? "1\n" : "0\n", 2); +} + +static ssize_t cw1200_11n_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + struct ieee80211_supported_band *band[2] = { + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ], + priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ], + }; + char buf[1]; + int ena = 0; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + if (buf[0] == 1) + ena = 1; + + band[0]->ht_cap.ht_supported = ena; +#ifdef CONFIG_CW1200_5GHZ_SUPPORT + band[1]->ht_cap.ht_supported = ena; +#endif /* CONFIG_CW1200_5GHZ_SUPPORT */ + + return count; +} + +static const struct file_operations fops_11n = { + .open = cw1200_generic_open, + .read = cw1200_11n_read, + .write = cw1200_11n_write, + .llseek = default_llseek, +}; + +int cw1200_debug_init(struct cw1200_common *priv) +{ + struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), + GFP_KERNEL); + priv->debug = d; + if (!d) + return -ENOMEM; + + d->debugfs_phy = debugfs_create_dir("cw1200", + priv->hw->wiphy->debugfsdir); + if (!d->debugfs_phy) + goto err; + + if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, + priv, &fops_status)) + goto err; + + if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy, + priv, &fops_counters)) + goto err; + + if (!debugfs_create_file("11n", S_IRUSR | S_IWUSR, + d->debugfs_phy, priv, &fops_11n)) + goto err; + + return 0; + +err: + priv->debug = NULL; + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + return -ENOMEM; +} + +void cw1200_debug_release(struct cw1200_common *priv) +{ + struct cw1200_debug_priv *d = priv->debug; + priv->debug = NULL; + + if (d) { + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + } +} diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h new file mode 100644 index 00000000000..aab0c061648 --- /dev/null +++ b/drivers/staging/cw1200/debug.h @@ -0,0 +1,112 @@ +#ifndef CW1200_DEBUG_H_INCLUDED +#define CW1200_DEBUG_H_INCLUDED + +struct cw200_common; + +#ifdef CONFIG_CW1200_DEBUGFS + +struct cw1200_debug_priv { + struct dentry *debugfs_phy; + int tx; + int tx_agg; + int rx; + int rx_agg; + int tx_multi; + int tx_multi_frames; + int tx_cache_miss; + int tx_align; + int tx_ttl; +}; + +int cw1200_debug_init(struct cw1200_common *priv); +void cw1200_debug_release(struct cw1200_common *priv); + +static inline void cw1200_debug_txed(struct cw1200_common *priv) +{ + ++priv->debug->tx; +} + +static inline void cw1200_debug_txed_agg(struct cw1200_common *priv) +{ + ++priv->debug->tx_agg; +} + +static inline void cw1200_debug_txed_multi(struct cw1200_common *priv, + int count) +{ + ++priv->debug->tx_multi; + priv->debug->tx_multi_frames += count; +} + +static inline void cw1200_debug_rxed(struct cw1200_common *priv) +{ + ++priv->debug->rx; +} + +static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv) +{ + ++priv->debug->rx_agg; +} + +static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv) +{ + ++priv->debug->tx_cache_miss; +} + +static inline void cw1200_debug_tx_align(struct cw1200_common *priv) +{ + ++priv->debug->tx_align; +} + +static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) +{ + ++priv->debug->tx_ttl; +} + +#else /* CONFIG_CW1200_DEBUGFS */ + +static inline int cw1200_debug_init(struct cw1200_common *priv) +{ + return 0; +} + +static inline void cw1200_debug_release(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_txed(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_txed_agg(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_txed_multi(struct cw1200_common *priv, + int count) +{ +} + +static inline void cw1200_debug_rxed(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_tx_align(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) +{ +} + +#endif /* CONFIG_CW1200_DEBUGFS */ + +#endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/staging/cw1200/fwio.c b/drivers/staging/cw1200/fwio.c new file mode 100644 index 00000000000..72b77bc8bb0 --- /dev/null +++ b/drivers/staging/cw1200/fwio.c @@ -0,0 +1,594 @@ +/* + * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/vmalloc.h> +#include <linux/sched.h> +#include <linux/firmware.h> + +#include "cw1200.h" +#include "fwio.h" +#include "hwio.h" +#include "sbus.h" +#include "bh.h" + +static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision) +{ + int hw_type = -1; + u32 silicon_type = (config_reg_val >> 24) & 0x3; + u32 silicon_vers = (config_reg_val >> 31) & 0x1; + + /* Check if we have CW1200 or STLC9000 */ + if ((silicon_type == 0x1) || (silicon_type == 0x2)) { + *major_revision = silicon_type; + if (silicon_vers) + hw_type = HIF_8601_VERSATILE; + else + hw_type = HIF_8601_SILICON; + } else { + *major_revision = 1; + hw_type = HIF_9000_SILICON_VERSTAILE; + } + + return hw_type; +} + +static int config_reg_read_stlc9000(struct cw1200_common *priv, + u16 reg, u32 *val) +{ + u16 val16; + int ret = cw1200_reg_read_16(priv, reg, &val16); + if (ret < 0) + return ret; + *val = val16; + return 0; +} + +static int config_reg_write_stlc9000(struct cw1200_common *priv, + u16 reg, u32 val) +{ + return cw1200_reg_write_16(priv, reg, (u16)val); +} + +static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) +{ + int ret, block, num_blocks; + unsigned i; + u32 val32; + u32 put = 0, get = 0; + u8 *buf = NULL; + const char *fw_path; + const struct firmware *firmware = NULL; + + /* Macroses are local. */ +#define APB_WRITE(reg, val) \ + do { \ + ret = cw1200_apb_write_32(priv, CW12000_APB(reg), (val)); \ + if (ret < 0) { \ + cw1200_dbg(CW1200_DBG_ERROR, \ + "%s: can't write %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) +#define APB_READ(reg, val) \ + do { \ + ret = cw1200_apb_read_32(priv, CW12000_APB(reg), &(val)); \ + if (ret < 0) { \ + cw1200_dbg(CW1200_DBG_ERROR, \ + "%s: can't read %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) +#define REG_WRITE(reg, val) \ + do { \ + ret = cw1200_reg_write_32(priv, (reg), (val)); \ + if (ret < 0) { \ + cw1200_dbg(CW1200_DBG_ERROR, \ + "%s: can't write %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) +#define REG_READ(reg, val) \ + do { \ + ret = cw1200_reg_read_32(priv, (reg), &(val)); \ + if (ret < 0) { \ + cw1200_dbg(CW1200_DBG_ERROR, \ + "%s: can't read %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) + + switch (priv->hw_revision) { + case CW1200_HW_REV_CUT10: + fw_path = FIRMWARE_CUT10; + break; + case CW1200_HW_REV_CUT11: + fw_path = FIRMWARE_CUT11; + break; + case CW1200_HW_REV_CUT20: + fw_path = FIRMWARE_CUT20; + break; + case CW1200_HW_REV_CUT22: + fw_path = FIRMWARE_CUT22; + break; + default: + cw1200_dbg(CW1200_DBG_ERROR, + "%s: invalid silicon revision %d.\n", + __func__, priv->hw_revision); + return -EINVAL; + } + + /* Initialize common registers */ + APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); + APB_WRITE(DOWNLOAD_PUT_REG, 0); + APB_WRITE(DOWNLOAD_GET_REG, 0); + APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); + APB_WRITE(DOWNLOAD_FLAGS_REG, 0); + + /* Write the NOP Instruction */ + REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); + REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); + + /* Release CPU from RESET */ + REG_READ(ST90TDS_CONFIG_REG_ID, val32); + val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT; + REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); + + /* Enable Clock */ + val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT; + REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); + + /* Load a firmware file */ + ret = request_firmware(&firmware, fw_path, priv->pdev); + if (ret) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't load firmware file %s.\n", + __func__, fw_path); + goto error; + } + BUG_ON(!firmware->data); + + buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); + if (!buf) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't allocate firmware buffer.\n", __func__); + ret = -ENOMEM; + goto error; + } + + /* Check if the bootloader is ready */ + for (i = 0; i < 100; i += 1 + i / 2) { + APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32); + if (val32 == DOWNLOAD_I_AM_HERE) + break; + mdelay(i); + } /* End of for loop */ + + if (val32 != DOWNLOAD_I_AM_HERE) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: bootloader is not ready.\n", __func__); + ret = -ETIMEDOUT; + goto error; + } + + /* Calculcate number of download blocks */ + num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1; + + /* Updating the length in Download Ctrl Area */ + val32 = firmware->size; /* Explicit cast from size_t to u32 */ + APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32); + + /* Firmware downloading loop */ + for (block = 0; block < num_blocks ; block++) { + size_t tx_size; + size_t block_size; + + /* check the download status */ + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: bootloader reported error %d.\n", + __func__, val32); + ret = -EIO; + goto error; + } + + /* loop until put - get <= 24K */ + for (i = 0; i < 100; i++) { + APB_READ(DOWNLOAD_GET_REG, get); + if ((put - get) <= + (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) + break; + mdelay(i); + } + + if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Timeout waiting for FIFO.\n", + __func__); + return -ETIMEDOUT; + } + + /* calculate the block size */ + tx_size = block_size = min((size_t)(firmware->size - put), + (size_t)DOWNLOAD_BLOCK_SIZE); + + memcpy(buf, &firmware->data[put], block_size); + if (block_size < DOWNLOAD_BLOCK_SIZE) { + memset(&buf[block_size], + 0, DOWNLOAD_BLOCK_SIZE - block_size); + tx_size = DOWNLOAD_BLOCK_SIZE; + } + + /* send the block to sram */ + ret = cw1200_apb_write(priv, + CW12000_APB(DOWNLOAD_FIFO_OFFSET + + (put & (DOWNLOAD_FIFO_SIZE - 1))), + buf, tx_size); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't write block at line %d.\n", + __func__, __LINE__); + goto error; + } + + /* update the put register */ + put += block_size; + APB_WRITE(DOWNLOAD_PUT_REG, put); + } /* End of firmware download loop */ + + /* Wait for the download completion */ + for (i = 0; i < 300; i += 1 + i / 2) { + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) + break; + mdelay(i); + } + if (val32 != DOWNLOAD_SUCCESS) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: wait for download completion failed. " \ + "Read: 0x%.8X\n", __func__, val32); + ret = -ETIMEDOUT; + goto error; + } else { + cw1200_dbg(CW1200_DBG_MSG, + "Firmware download completed.\n"); + ret = 0; + } + +error: + kfree(buf); + if (firmware) + release_firmware(firmware); + return ret; + +#undef APB_WRITE +#undef APB_READ +#undef REG_WRITE +#undef REG_READ +} + +int cw1200_load_firmware(struct cw1200_common *priv) +{ + int ret; + int i; + u32 val32; + u16 val16; + u32 dpll = 0; + int major_revision; + int (*config_reg_read)(struct cw1200_common *priv, u16 reg, u32 *val); + int (*config_reg_write)(struct cw1200_common *priv, u16 reg, u32 val); + + BUG_ON(!priv); + + /* Read CONFIG Register Value - We will read 32 bits */ + ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't read config register.\n", __func__); + goto out; + } + + priv->hw_type = cw1200_get_hw_type(val32, &major_revision); + if (priv->hw_type < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't deduct hardware type.\n", __func__); + ret = -ENOTSUPP; + goto out; + } + + switch (priv->hw_type) { + case HIF_8601_VERSATILE: + case HIF_8601_SILICON: + dpll = DPLL_INIT_VAL_CW1200; + config_reg_read = cw1200_reg_read_32; + config_reg_write = cw1200_reg_write_32; + break; + case HIF_9000_SILICON_VERSTAILE: + dpll = DPLL_INIT_VAL_9000; + config_reg_read = config_reg_read_stlc9000; + config_reg_write = config_reg_write_stlc9000; + break; + default: + BUG_ON(1); + } + + ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, dpll); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't write DPLL register.\n", __func__); + goto out; + } + + msleep(20); + + /* Read DPLL Reg value and compare with value written */ + ret = cw1200_reg_read_32(priv, + ST90TDS_TSET_GEN_R_W_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't read DPLL register.\n", __func__); + goto out; + } + + if (val32 != dpll) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: unable to initialise " \ + "DPLL register. Wrote 0x%.8X, read 0x%.8X.\n", + __func__, dpll, val32); + ret = -EIO; + goto out; + } + + /* Set wakeup bit in device */ + ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: set_wakeup: can't read " \ + "control register.\n", __func__); + goto out; + } + + ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, + val16 | ST90TDS_CONT_WUP_BIT); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: set_wakeup: can't write " \ + "control register.\n", __func__); + goto out; + } + + /* Wait for wakeup */ + for (i = 0 ; i < 300 ; i += 1 + i / 2) { + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, &val16); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: wait_for_wakeup: can't read " \ + "control register.\n", __func__); + goto out; + } + + if (val16 & ST90TDS_CONT_RDY_BIT) { + cw1200_dbg(CW1200_DBG_MSG, + "WLAN device is ready.\n"); + break; + } + msleep(i); + } + + if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: wait_for_wakeup: device is not responding.\n", + __func__); + ret = -ETIMEDOUT; + goto out; + } + + if (major_revision == 1) { + /* CW1200 Hardware detection logic : Check for CUT1.1 */ + ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32); + if (ret) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: HW detection: can't read CUT ID.\n", + __func__); + goto out; + } + + switch (val32) { + case CW1200_CUT_11_ID_STR: + cw1200_dbg(CW1200_DBG_MSG, + "Cut 1.1 silicon is detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT11; + break; + default: + cw1200_dbg(CW1200_DBG_MSG, + "Cut 1.0 silicon is detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT10; + break; + } + } else if (major_revision == 2) { + u32 ar1, ar2, ar3; + cw1200_dbg(CW1200_DBG_MSG, "Cut 2.x silicon is detected.\n"); + + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1); + if (ret) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: (1) HW detection: can't read CUT ID.\n", + __func__); + goto out; + } + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2); + if (ret) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: (2) HW detection: can't read CUT ID.\n", + __func__); + goto out; + } + + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3); + if (ret) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: (3) HW detection: can't read CUT ID.\n", + __func__); + goto out; + } + + if (ar1 == CW1200_CUT_22_ID_STR1 && + ar2 == CW1200_CUT_22_ID_STR2 && + ar3 == CW1200_CUT_22_ID_STR3) { + cw1200_dbg(CW1200_DBG_MSG, "Cut 2.2 detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT22; + } else { + cw1200_dbg(CW1200_DBG_MSG, "Cut 2.0 detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT20; + } + } else { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: unsupported silicon major revision %d.\n", + __func__, major_revision); + ret = -ENOTSUPP; + goto out; + } + + /* Checking for access mode */ + ret = config_reg_read(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: check_access_mode: can't read " \ + "config register.\n", __func__); + goto out; + } + + if (val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT) { + switch (priv->hw_type) { + case HIF_8601_SILICON: + cw1200_dbg(CW1200_DBG_MSG, + "%s: CW1200 detected.\n", __func__); + ret = cw1200_load_firmware_cw1200(priv); + break; + case HIF_8601_VERSATILE: + /* TODO: Not implemented yet! + ret = cw1200_load_firmware_cw1100(priv); + */ + ret = -ENOTSUPP; + goto out; + case HIF_9000_SILICON_VERSTAILE: + /* TODO: Not implemented yet! + ret = cw1200_load_firmware_stlc9000(priv); + */ + ret = -ENOTSUPP; + goto out; + default: + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Unknown hardware: %d.\n", + __func__, priv->hw_type); + } + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't download firmware.\n", __func__); + goto out; + } + } else { + cw1200_dbg(CW1200_DBG_MSG, + "%s: check_access_mode: device is already " \ + "in QUEUE mode.\n", __func__); + /* TODO: verify this branch. Do we need something to do? */ + } + + /* Register Interrupt Handler */ + ret = priv->sbus_ops->irq_subscribe(priv->sbus_priv, + (sbus_irq_handler)cw1200_irq_handler, priv); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't register IRQ handler.\n", __func__); + goto out; + } + + if (HIF_8601_SILICON == priv->hw_type) { + /* If device is CW1200 the IRQ enable/disable bits + * are in CONFIG register */ + ret = config_reg_read(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: enable_irq: can't read " \ + "config register.\n", __func__); + goto unsubscribe; + } + ret = config_reg_write(priv, ST90TDS_CONFIG_REG_ID, + val32 | ST90TDS_CONF_IRQ_RDY_ENABLE); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: enable_irq: can't write " \ + "config register.\n", __func__); + goto unsubscribe; + } + } else { + /* If device is STLC9000 the IRQ enable/disable bits + * are in CONTROL register */ + /* Enable device interrupts - Both DATA_RDY and WLAN_RDY */ + ret = cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: enable_irq: can't read " \ + "control register.\n", __func__); + goto unsubscribe; + } + ret = cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, + val16 | ST90TDS_CONT_IRQ_RDY_ENABLE); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: enable_irq: can't write " \ + "control register.\n", __func__); + goto unsubscribe; + } + + } + + /* Configure device for MESSSAGE MODE */ + ret = config_reg_read(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: set_mode: can't read config register.\n", + __func__); + goto unsubscribe; + } + ret = config_reg_write(priv, ST90TDS_CONFIG_REG_ID, + val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: set_mode: can't write config register.\n", + __func__); + goto unsubscribe; + } + + /* Unless we read the CONFIG Register we are + * not able to get an interrupt */ + mdelay(10); + config_reg_read(priv, ST90TDS_CONFIG_REG_ID, &val32); + +out: + return ret; + +unsubscribe: + priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); + return ret; +} + diff --git a/drivers/staging/cw1200/fwio.h b/drivers/staging/cw1200/fwio.h new file mode 100644 index 00000000000..cb91b8dc481 --- /dev/null +++ b/drivers/staging/cw1200/fwio.h @@ -0,0 +1,36 @@ +/* + * Firmware API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FWIO_H_INCLUDED +#define FWIO_H_INCLUDED + +#define FIRMWARE_CUT22 ("cw1200/wsm_22.bin") +#define FIRMWARE_CUT20 ("cw1200/wsm_20.bin") +#define FIRMWARE_CUT11 ("cw1200/wsm_11.bin") +#define FIRMWARE_CUT10 ("cw1200/wsm_10.bin") +#define SDD_FILE_22 ("cw1200/sdd_22.bin") +#define SDD_FILE_20 ("cw1200/sdd_20.bin") +#define SDD_FILE_11 ("cw1200/sdd_11.bin") +#define SDD_FILE_10 ("cw1200/sdd_10.bin") + +#define CW1200_HW_REV_CUT10 (10) +#define CW1200_HW_REV_CUT11 (11) +#define CW1200_HW_REV_CUT20 (20) +#define CW1200_HW_REV_CUT22 (22) + +int cw1200_load_firmware(struct cw1200_common *priv); + +#endif diff --git a/drivers/staging/cw1200/ht.h b/drivers/staging/cw1200/ht.h new file mode 100644 index 00000000000..5c486a634c7 --- /dev/null +++ b/drivers/staging/cw1200/ht.h @@ -0,0 +1,43 @@ +/* + * HT-related code for ST-Ericsson CW1200 driver + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_HT_H_INCLUDED +#define CW1200_HT_H_INCLUDED + +#include <net/mac80211.h> + +struct cw1200_ht_info { + struct ieee80211_sta_ht_cap ht_cap; + enum nl80211_channel_type channel_type; + u16 operation_mode; +}; + +static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info) +{ + return ht_info->channel_type != NL80211_CHAN_NO_HT; +} + +static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info) +{ + return cw1200_is_ht(ht_info) && + (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ht_info->operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); +} + +static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info) +{ + if (!cw1200_is_ht(ht_info)) + return 0; + return ht_info->ht_cap.ampdu_density; +} + +#endif /* CW1200_HT_H_INCLUDED */ diff --git a/drivers/staging/cw1200/hwio.c b/drivers/staging/cw1200/hwio.c new file mode 100644 index 00000000000..094ce8234f0 --- /dev/null +++ b/drivers/staging/cw1200/hwio.c @@ -0,0 +1,268 @@ +/* + * Low-level device IO routines for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * Based on: + * ST-Ericsson UMAC CW1200 driver, which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/types.h> + +#include "cw1200.h" +#include "hwio.h" +#include "sbus.h" + + /* Sdio addr is 4*spi_addr */ +#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2) +#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \ + ((((buf_id) & 0x1F) << 7) \ + | (((mpf) & 1) << 6) \ + | (((rfu) & 1) << 5) \ + | (((reg_id_ofs) & 0x1F) << 0)) + + +static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr, + void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit ; + + /* Check if buffer is aligned to 4 byte boundary */ + if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: buffer is not aligned.\n", __func__); + return -EINVAL; + } + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + BUG_ON(!priv->sbus_ops); + return priv->sbus_ops->sbus_memcpy_fromio(priv->sbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr, + const void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit ; + +#if 0 + /* Check if buffer is aligned to 4 byte boundary */ + if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { + cw1200_dbg(CW1200_DBG_ERROR, "%s: buffer is not aligned.\n", + __func__); + return -EINVAL; + } +#endif + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + BUG_ON(!priv->sbus_ops); + return priv->sbus_ops->sbus_memcpy_toio(priv->sbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static inline int __cw1200_reg_read_32(struct cw1200_common *priv, + u16 addr, u32 *val) +{ + return __cw1200_reg_read(priv, addr, val, sizeof(val), 0); +} + +static inline int __cw1200_reg_write_32(struct cw1200_common *priv, + u16 addr, u32 val) +{ + return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0); +} + +int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf, + size_t buf_len) +{ + int ret; + BUG_ON(!priv->sbus_ops); + priv->sbus_ops->lock(priv->sbus_priv); + ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0); + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf, + size_t buf_len) +{ + int ret; + BUG_ON(!priv->sbus_ops); + priv->sbus_ops->lock(priv->sbus_priv); + ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0); + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len) +{ + int ret; + BUG_ON(!priv->sbus_ops); + priv->sbus_ops->lock(priv->sbus_priv); + { + int buf_id_rx = priv->buf_id_rx; + ret = __cw1200_reg_read(priv, ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_rx + 1); + if (!ret) { + buf_id_rx = (buf_id_rx + 1) & 3; + priv->buf_id_rx = buf_id_rx; + } + } + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_data_write(struct cw1200_common *priv, const void *buf, + size_t buf_len) +{ + int ret; + BUG_ON(!priv->sbus_ops); + priv->sbus_ops->lock(priv->sbus_priv); + { + int buf_id_tx = priv->buf_id_tx; + ret = __cw1200_reg_write(priv, ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_tx); + if (!ret) { + buf_id_tx = (buf_id_tx + 1) & 31; + priv->buf_id_tx = buf_id_tx; + } + } + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr) +{ + u32 val32 = 0; + int i, ret; + + if ((buf_len / 2) >= 0x1000) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't read more than 0xfff words.\n", + __func__); + WARN_ON(1); + return -EINVAL; + goto out; + } + + priv->sbus_ops->lock(priv->sbus_priv); + /* Write address */ + ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't write address register.\n", + __func__); + goto out; + } + + /* Read CONFIG Register Value - We will read 32 bits */ + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't read config register.\n", + __func__); + goto out; + } + + /* Set PREFETCH bit */ + ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, + val32 | prefetch); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't write prefetch bit.\n", + __func__); + goto out; + } + + /* Check for PRE-FETCH bit to be cleared */ + for (i = 0; i < 20; i++) { + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't check prefetch bit.\n", + __func__); + goto out; + } + if (!(val32 & prefetch)) + break; + + mdelay(i); + } + + if (val32 & prefetch) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Prefetch bit is not cleared.\n", + __func__); + goto out; + } + + /* Read data port */ + ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't read data port.\n", + __func__); + goto out; + } + +out: + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, + size_t buf_len) +{ + int ret; + + if ((buf_len / 2) >= 0x1000) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't wrire more than 0xfff words.\n", + __func__); + WARN_ON(1); + return -EINVAL; + } + + priv->sbus_ops->lock(priv->sbus_priv); + + /* Write address */ + ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't write address register.\n", + __func__); + goto out; + } + + /* Write data port */ + ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID, + buf, buf_len, 0); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, "%s: Can't write data port.\n", + __func__); + goto out; + } + +out: + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + diff --git a/drivers/staging/cw1200/hwio.h b/drivers/staging/cw1200/hwio.h new file mode 100644 index 00000000000..427b75e0523 --- /dev/null +++ b/drivers/staging/cw1200/hwio.h @@ -0,0 +1,238 @@ +/* + * Low-level API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_HWIO_H_INCLUDED +#define CW1200_HWIO_H_INCLUDED + +/* extern */ struct cw1200_common; + +/* DPLL initial values */ +#define DPLL_INIT_VAL_9000 (0x00000191) +#define DPLL_INIT_VAL_CW1200 (0x0EC4F121) + +/* Hardware Type Definitions */ +#define HIF_8601_VERSATILE (0) +#define HIF_8601_SILICON (1) +#define HIF_9000_SILICON_VERSTAILE (2) + +#define CW1200_CUT_11_ID_STR (0x302E3830) +#define CW1200_CUT_22_ID_STR1 (0x302e3132) +#define CW1200_CUT_22_ID_STR2 (0x32302e30) +#define CW1200_CUT_22_ID_STR3 (0x3335) +#define CW1200_CUT_ID_ADDR (0xFFF17F90) +#define CW1200_CUT2_ID_ADDR (0xFFF1FF90) + +/* Download control area */ +/* boot loader start address in SRAM */ +#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000) +/* 32K, 0x4000 to 0xDFFF */ +#define DOWNLOAD_FIFO_OFFSET (0x00004000) +/* 32K */ +#define DOWNLOAD_FIFO_SIZE (0x00008000) +/* 128 bytes, 0xFF80 to 0xFFFF */ +#define DOWNLOAD_CTRL_OFFSET (0x0000FF80) +#define DOWNLOAD_CTRL_DATA_DWORDS (32-6) + +struct download_cntl_t { + /* size of whole firmware file (including Cheksum), host init */ + u32 ImageSize; + /* downloading flags */ + u32 Flags; + /* No. of bytes put into the download, init & updated by host */ + u32 Put; + /* last traced program counter, last ARM reg_pc */ + u32 TracePc; + /* No. of bytes read from the download, host init, device updates */ + u32 Get; + /* r0, boot losader status, host init to pending, device updates */ + u32 Status; + /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */ + u32 DebugData[DOWNLOAD_CTRL_DATA_DWORDS]; +}; + +#define DOWNLOAD_IMAGE_SIZE_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, ImageSize)) +#define DOWNLOAD_FLAGS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Flags)) +#define DOWNLOAD_PUT_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Put)) +#define DOWNLOAD_TRACE_PC_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, TracePc)) +#define DOWNLOAD_GET_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Get)) +#define DOWNLOAD_STATUS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Status)) +#define DOWNLOAD_DEBUG_DATA_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, DebugData)) +#define DOWNLOAD_DEBUG_DATA_LEN (108) + +#define DOWNLOAD_BLOCK_SIZE (1024) + +/* For boot loader detection */ +#define DOWNLOAD_ARE_YOU_HERE (0x87654321) +#define DOWNLOAD_I_AM_HERE (0x12345678) + +/* Download error code */ +#define DOWNLOAD_PENDING (0xFFFFFFFF) +#define DOWNLOAD_SUCCESS (0) +#define DOWNLOAD_EXCEPTION (1) +#define DOWNLOAD_ERR_MEM_1 (2) +#define DOWNLOAD_ERR_MEM_2 (3) +#define DOWNLOAD_ERR_SOFTWARE (4) +#define DOWNLOAD_ERR_FILE_SIZE (5) +#define DOWNLOAD_ERR_CHECKSUM (6) +#define DOWNLOAD_ERR_OVERFLOW (7) +#define DOWNLOAD_ERR_IMAGE (8) +#define DOWNLOAD_ERR_HOST (9) +#define DOWNLOAD_ERR_ABORT (10) + + +#define SYS_BASE_ADDR_SILICON (0) +#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000) +#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON) + +#define CW12000_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr)) + +/* *************************************************************** +*Device register definitions +*************************************************************** */ +/* WBF - SPI Register Addresses */ +#define ST90TDS_ADDR_ID_BASE (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONFIG_REG_ID (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONTROL_REG_ID (0x0001) +/* 16 bits, Q mode W/R */ +#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002) +/* 32 bits, AHB bus R/W */ +#define ST90TDS_AHB_DPORT_REG_ID (0x0003) +/* 16/32 bits */ +#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004) +/* 32 bits, APB bus R/W */ +#define ST90TDS_SRAM_DPORT_REG_ID (0x0005) +/* 32 bits, t_settle/general */ +#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006) +/* 16 bits, Q mode read, no length */ +#define ST90TDS_FRAME_OUT_REG_ID (0x0007) +#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID) + +/* WBF - Control register bit set */ +/* next o/p length, bit 11 to 0 */ +#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF) +#define ST90TDS_CONT_WUP_BIT (BIT(12)) +#define ST90TDS_CONT_RDY_BIT (BIT(13)) +#define ST90TDS_CONT_IRQ_ENABLE (BIT(14)) +#define ST90TDS_CONT_RDY_ENABLE (BIT(15)) +#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15)) + +/* SPI Config register bit set */ +#define ST90TDS_CONFIG_FRAME_BIT (BIT(2)) +#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4)) +#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3)) +#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4)) +#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5)) +#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6)) +#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7)) +/* TBD: Sure??? */ +#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7)) +#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8)) +#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9)) +/* QueueM */ +#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10)) +/* AHB bus */ +#define ST90TDS_CONFIG_AHB_PFETCH_BIT (BIT(11)) +#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12)) +/* APB bus */ +#define ST90TDS_CONFIG_PFETCH_BIT (BIT(13)) +/* cpu reset */ +#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14)) +#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15)) + +/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */ +#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17)) + +int cw1200_data_read(struct cw1200_common *priv, + void *buf, size_t buf_len); +int cw1200_data_write(struct cw1200_common *priv, + const void *buf, size_t buf_len); + +int cw1200_reg_read(struct cw1200_common *priv, u16 addr, + void *buf, size_t buf_len); +int cw1200_reg_write(struct cw1200_common *priv, u16 addr, + const void *buf, size_t buf_len); + +static inline int cw1200_reg_read_16(struct cw1200_common *priv, + u16 addr, u16 *val) +{ + return cw1200_reg_read(priv, addr, val, sizeof(val)); +} + +static inline int cw1200_reg_write_16(struct cw1200_common *priv, + u16 addr, u16 val) +{ + return cw1200_reg_write(priv, addr, &val, sizeof(val)); +} + +static inline int cw1200_reg_read_32(struct cw1200_common *priv, + u16 addr, u32 *val) +{ + return cw1200_reg_read(priv, addr, val, sizeof(val)); +} + +static inline int cw1200_reg_write_32(struct cw1200_common *priv, + u16 addr, u32 val) +{ + return cw1200_reg_write(priv, addr, &val, sizeof(val)); +} + +int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr); +int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, + size_t buf_len); + +static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr, + void *buf, size_t buf_len) +{ + return cw1200_indirect_read(priv, addr, buf, buf_len, + ST90TDS_CONFIG_PFETCH_BIT, ST90TDS_SRAM_DPORT_REG_ID); +} + +static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr, + void *buf, size_t buf_len) +{ + return cw1200_indirect_read(priv, addr, buf, buf_len, + ST90TDS_CONFIG_AHB_PFETCH_BIT, ST90TDS_AHB_DPORT_REG_ID); +} + +static inline int cw1200_apb_read_32(struct cw1200_common *priv, + u32 addr, u32 *val) +{ + return cw1200_apb_read(priv, addr, val, sizeof(val)); +} + +static inline int cw1200_apb_write_32(struct cw1200_common *priv, + u32 addr, u32 val) +{ + return cw1200_apb_write(priv, addr, &val, sizeof(val)); +} + +static inline int cw1200_ahb_read_32(struct cw1200_common *priv, + u32 addr, u32 *val) +{ + return cw1200_ahb_read(priv, addr, val, sizeof(val)); +} + +#endif /* CW1200_HWIO_H_INCLUDED */ diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c new file mode 100644 index 00000000000..3af5d1c21f2 --- /dev/null +++ b/drivers/staging/cw1200/main.c @@ -0,0 +1,540 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * Based on: + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> +#include <linux/vmalloc.h> +#include <linux/random.h> +#include <linux/sched.h> +#include <net/mac80211.h> + +#include "cw1200.h" +#include "txrx.h" +#include "sbus.h" +#include "fwio.h" +#include "hwio.h" +#include "bh.h" +#include "sta.h" +#include "ap.h" +#include "scan.h" +#include "debug.h" +#include "pm.h" + +MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>"); +MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("cw1200_core"); + +/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */ +static u8 cw1200_mac_template[ETH_ALEN] = {0x00, 0x80, 0xe1, 0x00, 0x00, 0x00}; +module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(macaddr, "MAC address"); + +/* TODO: use rates and channels from the device */ +#define RATETAB_ENT(_rate, _rateid, _flags) \ + { \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate cw1200_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, 0), + RATETAB_ENT(55, 2, 0), + RATETAB_ENT(110, 3, 0), + RATETAB_ENT(60, 6, 0), + RATETAB_ENT(90, 7, 0), + RATETAB_ENT(120, 8, 0), + RATETAB_ENT(180, 9, 0), + RATETAB_ENT(240, 10, 0), + RATETAB_ENT(360, 11, 0), + RATETAB_ENT(480, 12, 0), + RATETAB_ENT(540, 13, 0), +}; + +static struct ieee80211_rate cw1200_mcs_rates[] = { + RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), + RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), + RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), + RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), + RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), + RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), + RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), + RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), +}; + +#define cw1200_a_rates (cw1200_rates + 4) +#define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4) +#define cw1200_g_rates (cw1200_rates + 0) +#define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates)) +#define cw1200_n_rates (cw1200_mcs_rates) +#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates)) + + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel cw1200_2ghz_chantable[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +#ifdef CONFIG_CW1200_5GHZ_SUPPORT +static struct ieee80211_channel cw1200_5ghz_chantable[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(149, 0), + CHAN5G(153, 0), CHAN5G(157, 0), + CHAN5G(161, 0), CHAN5G(165, 0), + CHAN5G(184, 0), CHAN5G(188, 0), + CHAN5G(192, 0), CHAN5G(196, 0), + CHAN5G(200, 0), CHAN5G(204, 0), + CHAN5G(208, 0), CHAN5G(212, 0), + CHAN5G(216, 0), +}; +#endif /* CONFIG_CW1200_5GHZ_SUPPORT */ + +static struct ieee80211_supported_band cw1200_band_2ghz = { + .channels = cw1200_2ghz_chantable, + .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable), + .bitrates = cw1200_g_rates, + .n_bitrates = cw1200_g_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_SM_PS | + IEEE80211_HT_CAP_GRN_FLD | + /* HT Rx STBC: Rx support of one spatial stream */ + 0x0100 | + IEEE80211_HT_CAP_DELAY_BA | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +#ifdef CONFIG_CW1200_5GHZ_SUPPORT +static struct ieee80211_supported_band cw1200_band_5ghz = { + .channels = cw1200_5ghz_chantable, + .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), + .bitrates = cw1200_a_rates, + .n_bitrates = cw1200_a_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_SM_PS | + IEEE80211_HT_CAP_GRN_FLD | + /* HT Rx STBC: Rx support of one spatial stream */ + 0x0100 | + IEEE80211_HT_CAP_DELAY_BA | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; +#endif /* CONFIG_CW1200_5GHZ_SUPPORT */ + +static const unsigned long cw1200_ttl[] = { + 1 * HZ, /* VO */ + 2 * HZ, /* VI */ + 5 * HZ, /* BE */ + 10 * HZ /* BK */ +}; + +static const struct ieee80211_ops cw1200_ops = { + .start = cw1200_start, + .stop = cw1200_stop, + .add_interface = cw1200_add_interface, + .remove_interface = cw1200_remove_interface, + .tx = cw1200_tx, + .hw_scan = cw1200_hw_scan, + .set_tim = cw1200_set_tim, + .sta_notify = cw1200_sta_notify, + .sta_add = cw1200_sta_add, + .sta_remove = cw1200_sta_remove, + .set_key = cw1200_set_key, + .set_rts_threshold = cw1200_set_rts_threshold, + .config = cw1200_config, + .bss_info_changed = cw1200_bss_info_changed, + .prepare_multicast = cw1200_prepare_multicast, + .configure_filter = cw1200_configure_filter, + .conf_tx = cw1200_conf_tx, + .get_stats = cw1200_get_stats, + .ampdu_action = cw1200_ampdu_action, + .flush = cw1200_flush, +#ifdef CONFIG_PM + .suspend = cw1200_wow_suspend, + .resume = cw1200_wow_resume, +#endif /* CONFIG_PM */ + /* Intentionally not offloaded: */ + /*.channel_switch = cw1200_channel_switch, */ + /*.remain_on_channel = cw1200_remain_on_channel, */ + /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */ +}; + +struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) +{ + int i; + struct ieee80211_hw *hw; + struct cw1200_common *priv; + + hw = ieee80211_alloc_hw(priv_data_len, &cw1200_ops); + if (!hw) + return NULL; + + priv = hw->priv; + priv->hw = hw; + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->rates = cw1200_rates; /* TODO: fetch from FW */ + priv->mcs_rates = cw1200_n_rates; + /* Enable block ACK for every TID but voice. */ + priv->ba_tid_mask = 0x3F; + + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS | + IEEE80211_HW_SUPPORTS_UAPSD | + IEEE80211_HW_CONNECTION_MONITOR | + IEEE80211_HW_SUPPORTS_CQM_RSSI | + /* Aggregation is fully controlled by firmware. + * Do not need any support from the mac80211 stack */ + /* IEEE80211_HW_AMPDU_AGGREGATION | */ +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + IEEE80211_HW_SUPPORTS_P2P_PS | + IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | + IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ + IEEE80211_HW_BEACON_FILTER; + + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + + /* Support only for limited wowlan functionalities */ + hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | + WIPHY_WOWLAN_DISCONNECT; + hw->wiphy->wowlan.n_patterns = 0; + +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ + + hw->channel_change_time = 1000; /* TODO: find actual value */ + /* priv->beacon_req_id = cpu_to_le32(0); */ + hw->queues = 4; + priv->noise = -94; + + hw->max_rates = 8; + hw->max_rate_tries = 15; + hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM + + 8 /* TKIP IV */ + + 12 /* TKIP ICV and MIC */; + + hw->sta_data_size = sizeof(struct cw1200_sta_priv); + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz; +#ifdef CONFIG_CW1200_5GHZ_SUPPORT + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz; +#endif /* CONFIG_CW1200_5GHZ_SUPPORT */ + + hw->wiphy->max_scan_ssids = 2; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + + SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template); + + if (hw->wiphy->perm_addr[3] == 0 && + hw->wiphy->perm_addr[4] == 0 && + hw->wiphy->perm_addr[5] == 0) { + get_random_bytes(&hw->wiphy->perm_addr[3], 3); + } + + mutex_init(&priv->wsm_cmd_mux); + mutex_init(&priv->conf_mutex); + priv->workqueue = create_singlethread_workqueue("cw1200_wq"); + sema_init(&priv->scan.lock, 1); + INIT_WORK(&priv->scan.work, cw1200_scan_work); + INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work); + INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout); + INIT_WORK(&priv->join_work, cw1200_join_work); + INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout); + INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work); + INIT_WORK(&priv->offchannel_work, cw1200_offchannel_work); + INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work); + INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work); + INIT_LIST_HEAD(&priv->event_queue); + INIT_WORK(&priv->event_handler, cw1200_event_handler); + INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work); + INIT_DELAYED_WORK(&priv->connection_loss_work, + cw1200_connection_loss_work); + INIT_WORK(&priv->tx_failure_work, cw1200_tx_failure_work); + spin_lock_init(&priv->ps_state_lock); + INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); + INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); + INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); + INIT_WORK(&priv->link_id_work, cw1200_link_id_work); + INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work); + init_timer(&priv->mcast_timeout); + priv->mcast_timeout.data = (unsigned long)priv; + priv->mcast_timeout.function = cw1200_mcast_timeout; + + if (unlikely(cw1200_pm_init(&priv->pm_state, priv))) { + ieee80211_free_hw(hw); + return NULL; + } + + if (unlikely(cw1200_queue_stats_init(&priv->tx_queue_stats, + CW1200_LINK_ID_MAX, + cw1200_skb_dtor, + priv))) { + ieee80211_free_hw(hw); + return NULL; + } + + for (i = 0; i < 4; ++i) { + if (unlikely(cw1200_queue_init(&priv->tx_queue[i], + &priv->tx_queue_stats, i, 16, + cw1200_ttl[i]))) { + for (; i > 0; i--) + cw1200_queue_deinit(&priv->tx_queue[i - 1]); + cw1200_queue_stats_deinit(&priv->tx_queue_stats); + ieee80211_free_hw(hw); + return NULL; + } + } + + init_waitqueue_head(&priv->channel_switch_done); + init_waitqueue_head(&priv->wsm_cmd_wq); + init_waitqueue_head(&priv->wsm_startup_done); + wsm_buf_init(&priv->wsm_cmd_buf); + tx_policy_init(priv); + + return hw; +} +EXPORT_SYMBOL_GPL(cw1200_init_common); + +int cw1200_register_common(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int err; + + err = ieee80211_register_hw(dev); + if (err) { + cw1200_dbg(CW1200_DBG_ERROR, "Cannot register device (%d).\n", + err); + return err; + } + +#ifdef CONFIG_CW1200_LEDS + err = cw1200_init_leds(priv); + if (err) + return err; +#endif /* CONFIG_CW1200_LEDS */ + + cw1200_debug_init(priv); + + cw1200_dbg(CW1200_DBG_MSG, "is registered as '%s'\n", + wiphy_name(dev->wiphy)); + return 0; +} +EXPORT_SYMBOL_GPL(cw1200_register_common); + +void cw1200_free_common(struct ieee80211_hw *dev) +{ + /* struct cw1200_common *priv = dev->priv; */ + /* unsigned int i; */ + + ieee80211_free_hw(dev); +} +EXPORT_SYMBOL_GPL(cw1200_free_common); + +void cw1200_unregister_common(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int i; + + ieee80211_unregister_hw(dev); + + cw1200_debug_release(priv); + + priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); + cw1200_unregister_bh(priv); + +#ifdef CONFIG_CW1200_LEDS + cw1200_unregister_leds(priv); +#endif /* CONFIG_CW1200_LEDS */ + + mutex_destroy(&priv->conf_mutex); + + wsm_buf_deinit(&priv->wsm_cmd_buf); + + kfree(priv->scan.ie); + priv->scan.ie = NULL; + priv->scan.ie_len = 0; + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + + if (priv->skb_cache) { + dev_kfree_skb(priv->skb_cache); + priv->skb_cache = NULL; + } + + if (priv->sdd) { + release_firmware(priv->sdd); + priv->sdd = NULL; + } + + for (i = 0; i < 4; ++i) + cw1200_queue_deinit(&priv->tx_queue[i]); + cw1200_queue_stats_deinit(&priv->tx_queue_stats); + cw1200_pm_deinit(&priv->pm_state); +} +EXPORT_SYMBOL_GPL(cw1200_unregister_common); + +int cw1200_core_probe(const struct sbus_ops *sbus_ops, + struct sbus_priv *sbus, + struct device *pdev, + struct cw1200_common **pself) +{ + int err = -ENOMEM; + struct ieee80211_hw *dev; + struct cw1200_common *priv; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + + dev = cw1200_init_common(sizeof(struct cw1200_common)); + if (!dev) + goto err; + + priv = dev->priv; + + priv->sbus_ops = sbus_ops; + priv->sbus_priv = sbus; + priv->pdev = pdev; + SET_IEEE80211_DEV(priv->hw, pdev); + + /* WSM callbacks. */ + priv->wsm_cbc.scan_complete = cw1200_scan_complete_cb; + priv->wsm_cbc.tx_confirm = cw1200_tx_confirm_cb; + priv->wsm_cbc.rx = cw1200_rx_cb; + priv->wsm_cbc.suspend_resume = cw1200_suspend_resume; + /* priv->wsm_cbc.set_pm_complete = cw1200_set_pm_complete_cb; */ + priv->wsm_cbc.channel_switch = cw1200_channel_switch_cb; + + err = cw1200_register_bh(priv); + if (err) + goto err1; + + err = cw1200_load_firmware(priv); + if (err) + goto err2; + + if (wait_event_interruptible_timeout(priv->wsm_startup_done, + priv->wsm_caps.firmwareReady, 3*HZ) <= 0) { + /* TODO: Needs to find how to reset device */ + /* in QUEUE mode properly. */ + goto err3; + } + + /* Set low-power mode. */ + WARN_ON(wsm_set_operational_mode(priv, &mode)); + + /* Enable multi-TX confirmation */ + WARN_ON(wsm_use_multi_tx_conf(priv, true)); + + err = cw1200_register_common(dev); + if (err) { + priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); + goto err3; + } + + *pself = dev->priv; + return err; + +err3: + sbus_ops->reset(sbus); +err2: + cw1200_unregister_bh(priv); +err1: + cw1200_free_common(dev); +err: + return err; +} +EXPORT_SYMBOL_GPL(cw1200_core_probe); + +void cw1200_core_release(struct cw1200_common *self) +{ + cw1200_unregister_common(self->hw); + cw1200_free_common(self->hw); + return; +} +EXPORT_SYMBOL_GPL(cw1200_core_release); diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c new file mode 100644 index 00000000000..7c1a957de3e --- /dev/null +++ b/drivers/staging/cw1200/pm.c @@ -0,0 +1,346 @@ +/* + * Mac80211 power management API for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/platform_device.h> +#include "cw1200.h" +#include "pm.h" +#include "sta.h" +#include "bh.h" +#include "sbus.h" + +static int cw1200_suspend_late(struct device *dev); +static void cw1200_pm_release(struct device *dev); +static int cw1200_pm_probe(struct platform_device *pdev); + +/* private */ +struct cw1200_suspend_state { + unsigned long bss_loss_tmo; + unsigned long connection_loss_tmo; + unsigned long join_tmo; + unsigned long direct_probe; + unsigned long link_id_gc; +}; + +static const struct dev_pm_ops cw1200_pm_ops = { + .suspend_noirq = cw1200_suspend_late, +}; + +static struct platform_driver cw1200_power_driver = { + .probe = cw1200_pm_probe, + .driver = { + .name = "cw1200_power", + .pm = &cw1200_pm_ops, + }, +}; + +static int cw1200_pm_init_common(struct cw1200_pm_state *pm, + struct cw1200_common *priv) +{ + int ret; + + spin_lock_init(&pm->lock); + ret = platform_driver_register(&cw1200_power_driver); + if (ret) + return ret; + pm->pm_dev = platform_device_alloc("cw1200_power", 0); + if (!pm->pm_dev) { + platform_driver_unregister(&cw1200_power_driver); + return ENOMEM; + } + + pm->pm_dev->dev.platform_data = priv; + ret = platform_device_add(pm->pm_dev); + if (ret) { + kfree(pm->pm_dev); + pm->pm_dev = NULL; + } + + return ret; +} + +static void cw1200_pm_deinit_common(struct cw1200_pm_state *pm) +{ + platform_driver_unregister(&cw1200_power_driver); + if (pm->pm_dev) { + pm->pm_dev->dev.platform_data = NULL; + platform_device_unregister(pm->pm_dev); + pm->pm_dev = NULL; + } +} + +#ifdef CONFIG_WAKELOCK + +int cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv) +{ + int ret = cw1200_pm_init_common(pm, priv); + if (!ret) + wake_lock_init(&pm->wakelock, + WAKE_LOCK_SUSPEND, "cw1200_wlan"); + return ret; +} + +void cw1200_pm_deinit(struct cw1200_pm_state *pm) +{ + if (wake_lock_active(&pm->wakelock)) + wake_unlock(&pm->wakelock); + wake_lock_destroy(&pm->wakelock); + cw1200_pm_deinit_common(pm); +} + +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo) +{ + long cur_tmo; + spin_lock_bh(&pm->lock); + cur_tmo = pm->wakelock.expires - jiffies; + if (!wake_lock_active(&pm->wakelock) || + cur_tmo < (long)tmo) + wake_lock_timeout(&pm->wakelock, tmo); + spin_unlock_bh(&pm->lock); +} + +#else /* CONFIG_WAKELOCK */ + +static void cw1200_pm_stay_awake_tmo(unsigned long arg) +{ +} + +int cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv) +{ + int ret = cw1200_pm_init_common(pm, priv); + if (!ret) { + init_timer(&pm->stay_awake); + pm->stay_awake.data = (unsigned long)pm; + pm->stay_awake.function = cw1200_pm_stay_awake_tmo; + } + return ret; +} + +void cw1200_pm_deinit(struct cw1200_pm_state *pm) +{ + del_timer_sync(&pm->stay_awake); + cw1200_pm_deinit_common(pm); +} + +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo) +{ + long cur_tmo; + spin_lock_bh(&pm->lock); + cur_tmo = pm->stay_awake.expires - jiffies; + if (!timer_pending(&pm->stay_awake) || + cur_tmo < (long)tmo) + mod_timer(&pm->stay_awake, jiffies + tmo); + spin_unlock_bh(&pm->lock); +} + +#endif /* CONFIG_WAKELOCK */ + +static long cw1200_suspend_work(struct delayed_work *work) +{ + int ret = cancel_delayed_work(work); + long tmo; + if (ret > 0) { + /* Timer is pending */ + tmo = work->timer.expires - jiffies; + if (tmo < 0) + tmo = 0; + } else { + tmo = -1; + } + return tmo; +} + +static int cw1200_resume_work(struct cw1200_common *priv, + struct delayed_work *work, + unsigned long tmo) +{ + if ((long)tmo < 0) + return 1; + + return queue_delayed_work(priv->workqueue, work, tmo); +} + +static int cw1200_suspend_late(struct device *dev) +{ + struct cw1200_common *priv = dev->platform_data; + if (atomic_read(&priv->bh_rx)) { + wiphy_dbg(priv->hw->wiphy, + "%s: Suspend interrupted.\n", + __func__); + return -EAGAIN; + } + return 0; +} + +static void cw1200_pm_release(struct device *dev) +{ +} + +static int cw1200_pm_probe(struct platform_device *pdev) +{ + pdev->dev.release = cw1200_pm_release; + return 0; +} + +int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_pm_state *pm_state = &priv->pm_state; + struct cw1200_suspend_state *state; + int ret; + +#ifndef CONFIG_WAKELOCK + spin_lock_bh(&pm_state->lock); + ret = timer_pending(&pm_state->stay_awake); + spin_unlock_bh(&pm_state->lock); + if (ret) + return -EAGAIN; +#endif + + /* Do not suspend when datapath is not idle */ + if (priv->tx_queue_stats.num_queued) + return -EBUSY; + + /* Make sure there is no configuration requests in progress. */ + if (!mutex_trylock(&priv->conf_mutex)) + return -EBUSY; + + /* Ensure pending operations are done. + * Note also that wow_suspend must return in ~2.5sec, before + * watchdog is triggered. */ + if (priv->channel_switch_in_progress) + goto revert1; + + /* Do not suspend when join work is scheduled */ + if (work_pending(&priv->join_work)) + goto revert1; + + /* Do not suspend when scanning */ + if (down_trylock(&priv->scan.lock)) + goto revert1; + + /* Lock TX. */ + wsm_lock_tx_async(priv); + if (priv->hw_bufs_used) + goto revert3; + + /* Allocate state */ + state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL); + if (!state) + goto revert3; + + /* Store delayed work states. */ + state->bss_loss_tmo = + cw1200_suspend_work(&priv->bss_loss_work); + state->connection_loss_tmo = + cw1200_suspend_work(&priv->connection_loss_work); + state->join_tmo = + cw1200_suspend_work(&priv->join_timeout); + state->direct_probe = + cw1200_suspend_work(&priv->scan.probe_work); + state->link_id_gc = + cw1200_suspend_work(&priv->link_id_gc_work); + + /* Stop serving thread */ + if (cw1200_bh_suspend(priv)) + goto revert4; + + ret = timer_pending(&priv->mcast_timeout); + if (ret) + goto revert5; + + /* Store suspend state */ + pm_state->suspend_state = state; + + /* Enable IRQ wake */ + ret = priv->sbus_ops->power_mgmt(priv->sbus_priv, true); + if (ret) { + wiphy_err(priv->hw->wiphy, + "%s: PM request failed: %d. WoW is disabled.\n", + __func__, ret); + cw1200_wow_resume(hw); + return -EBUSY; + } + + /* Force resume if event is coming from the device. */ + if (atomic_read(&priv->bh_rx)) { + cw1200_wow_resume(hw); + return -EAGAIN; + } + + return 0; + +revert5: + WARN_ON(cw1200_bh_resume(priv)); +revert4: + cw1200_resume_work(priv, &priv->bss_loss_work, + state->bss_loss_tmo); + cw1200_resume_work(priv, &priv->connection_loss_work, + state->connection_loss_tmo); + cw1200_resume_work(priv, &priv->join_timeout, + state->join_tmo); + cw1200_resume_work(priv, &priv->scan.probe_work, + state->direct_probe); + cw1200_resume_work(priv, &priv->link_id_gc_work, + state->link_id_gc); + kfree(state); +revert3: + wsm_unlock_tx(priv); + up(&priv->scan.lock); +revert1: + mutex_unlock(&priv->conf_mutex); + return -EBUSY; +} + +int cw1200_wow_resume(struct ieee80211_hw *hw) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_pm_state *pm_state = &priv->pm_state; + struct cw1200_suspend_state *state; + + state = pm_state->suspend_state; + pm_state->suspend_state = NULL; + + /* Disable IRQ wake */ + priv->sbus_ops->power_mgmt(priv->sbus_priv, false); + + /* Resume BH thread */ + WARN_ON(cw1200_bh_resume(priv)); + + /* Resume delayed work */ + cw1200_resume_work(priv, &priv->bss_loss_work, + state->bss_loss_tmo); + cw1200_resume_work(priv, &priv->connection_loss_work, + state->connection_loss_tmo); + cw1200_resume_work(priv, &priv->join_timeout, + state->join_tmo); + cw1200_resume_work(priv, &priv->scan.probe_work, + state->direct_probe); + cw1200_resume_work(priv, &priv->link_id_gc_work, + state->link_id_gc); + + /* Unlock datapath */ + wsm_unlock_tx(priv); + + /* Unlock scan */ + up(&priv->scan.lock); + + /* Unlock configuration mutex */ + mutex_unlock(&priv->conf_mutex); + + /* Free memory */ + kfree(state); + + return 0; +} diff --git a/drivers/staging/cw1200/pm.h b/drivers/staging/cw1200/pm.h new file mode 100644 index 00000000000..0515f6cfb92 --- /dev/null +++ b/drivers/staging/cw1200/pm.h @@ -0,0 +1,49 @@ +/* + * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PM_H_INCLUDED +#define PM_H_INCLUDED + +#ifdef CONFIG_WAKELOCK +#include <linux/wakelock.h> +#endif + +/* ******************************************************************** */ +/* mac80211 API */ + +#ifdef CONFIG_PM + +/* extern */ struct cw1200_common; +/* private */ struct cw1200_suspend_state; + +struct cw1200_pm_state { + struct cw1200_suspend_state *suspend_state; +#ifdef CONFIG_WAKELOCK + struct wake_lock wakelock; +#else + struct timer_list stay_awake; +#endif + struct platform_device *pm_dev; + spinlock_t lock; +}; + +int cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv); +void cw1200_pm_deinit(struct cw1200_pm_state *pm); +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo); +int cw1200_wow_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan); +int cw1200_wow_resume(struct ieee80211_hw *hw); + +#endif /* CONFIG_PM */ + +#endif diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c new file mode 100644 index 00000000000..b1069255790 --- /dev/null +++ b/drivers/staging/cw1200/queue.c @@ -0,0 +1,562 @@ +/* + * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <net/mac80211.h> +#include <linux/sched.h> +#include "queue.h" +#include "cw1200.h" +#include "debug.h" + +/* private */ struct cw1200_queue_item +{ + struct list_head head; + struct sk_buff *skb; + u32 packetID; + unsigned long timestamp; + struct cw1200_txpriv txpriv; + u8 generation; +}; + +static inline void __cw1200_queue_lock(struct cw1200_queue *queue) +{ + struct cw1200_queue_stats *stats = queue->stats; + if (queue->tx_locked_cnt++ == 0) { + txrx_printk(KERN_DEBUG "[TX] Queue %d is locked.\n", + queue->queue_id); + ieee80211_stop_queue(stats->priv->hw, queue->queue_id); + } +} + +static inline void __cw1200_queue_unlock(struct cw1200_queue *queue) +{ + struct cw1200_queue_stats *stats = queue->stats; + BUG_ON(!queue->tx_locked_cnt); + if (--queue->tx_locked_cnt == 0) { + txrx_printk(KERN_DEBUG "[TX] Queue %d is unlocked.\n", + queue->queue_id); + ieee80211_wake_queue(stats->priv->hw, queue->queue_id); + } +} + +static inline void cw1200_queue_parse_id(u32 packetID, u8 *queue_generation, + u8 *queue_id, + u8 *item_generation, + u8 *item_id) +{ + *item_id = (packetID >> 0) & 0xFF; + *item_generation = (packetID >> 8) & 0xFF; + *queue_id = (packetID >> 16) & 0xFF; + *queue_generation = (packetID >> 24) & 0xFF; +} + +static inline u32 cw1200_queue_make_packet_id(u8 queue_generation, u8 queue_id, + u8 item_generation, u8 item_id) +{ + return ((u32)item_id << 0) | + ((u32)item_generation << 8) | + ((u32)queue_id << 16) | + ((u32)queue_generation << 24); +} + +static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats, + struct list_head *gc_list) +{ + struct cw1200_queue_item *item; + + while (!list_empty(gc_list)) { + item = list_first_entry( + gc_list, struct cw1200_queue_item, head); + list_del(&item->head); + stats->skb_dtor(stats->priv, item->skb, &item->txpriv); + kfree(item); + } +} + +static void cw1200_queue_register_post_gc(struct list_head *gc_list, + struct cw1200_queue_item *item) +{ + struct cw1200_queue_item *gc_item; + gc_item = kmalloc(sizeof(struct cw1200_queue_item), + GFP_KERNEL | GFP_ATOMIC); + BUG_ON(!gc_item); + memcpy(gc_item, item, sizeof(struct cw1200_queue_item)); + list_move_tail(&gc_item->head, gc_list); +} + +static void __cw1200_queue_gc(struct cw1200_queue *queue, + struct list_head *head, + bool unlock) +{ + struct cw1200_queue_stats *stats = queue->stats; + struct cw1200_queue_item *item = NULL; + bool wakeup_stats = false; + + while (!list_empty(&queue->queue)) { + item = list_first_entry( + &queue->queue, struct cw1200_queue_item, head); + if (jiffies - item->timestamp < queue->ttl) + break; + --queue->num_queued; + --queue->link_map_cache[item->txpriv.link_id]; + spin_lock_bh(&stats->lock); + --stats->num_queued; + if (!--stats->link_map_cache[item->txpriv.link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); + cw1200_debug_tx_ttl(stats->priv); + cw1200_queue_register_post_gc(head, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + } + + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + + if (queue->overfull) { + if (queue->num_queued <= (queue->capacity >> 1)) { + queue->overfull = false; + if (unlock) + __cw1200_queue_unlock(queue); + } else { + unsigned long tmo = item->timestamp + queue->ttl; + mod_timer(&queue->gc, tmo); + cw1200_pm_stay_awake(&stats->priv->pm_state, + tmo - jiffies); + } + } +} + +static void cw1200_queue_gc(unsigned long arg) +{ + LIST_HEAD(list); + struct cw1200_queue *queue = + (struct cw1200_queue *)arg; + + spin_lock_bh(&queue->lock); + __cw1200_queue_gc(queue, &list, true); + spin_unlock_bh(&queue->lock); + cw1200_queue_post_gc(queue->stats, &list); +} + +int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, + size_t map_capacity, + cw1200_queue_skb_dtor_t skb_dtor, + struct cw1200_common *priv) +{ + memset(stats, 0, sizeof(*stats)); + stats->map_capacity = map_capacity; + stats->skb_dtor = skb_dtor; + stats->priv = priv; + spin_lock_init(&stats->lock); + init_waitqueue_head(&stats->wait_link_id_empty); + + stats->link_map_cache = kzalloc(sizeof(int[map_capacity]), + GFP_KERNEL); + if (!stats->link_map_cache) + return -ENOMEM; + + return 0; +} + +int cw1200_queue_init(struct cw1200_queue *queue, + struct cw1200_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl) +{ + size_t i; + + memset(queue, 0, sizeof(*queue)); + queue->stats = stats; + queue->capacity = capacity; + queue->queue_id = queue_id; + queue->ttl = ttl; + INIT_LIST_HEAD(&queue->queue); + INIT_LIST_HEAD(&queue->pending); + INIT_LIST_HEAD(&queue->free_pool); + spin_lock_init(&queue->lock); + init_timer(&queue->gc); + queue->gc.data = (unsigned long)queue; + queue->gc.function = cw1200_queue_gc; + + queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, + GFP_KERNEL); + if (!queue->pool) + return -ENOMEM; + + queue->link_map_cache = kzalloc(sizeof(int[stats->map_capacity]), + GFP_KERNEL); + if (!queue->link_map_cache) { + kfree(queue->pool); + queue->pool = NULL; + return -ENOMEM; + } + + for (i = 0; i < capacity; ++i) + list_add_tail(&queue->pool[i].head, &queue->free_pool); + + return 0; +} + +int cw1200_queue_clear(struct cw1200_queue *queue) +{ + int i; + LIST_HEAD(gc_list); + struct cw1200_queue_stats *stats = queue->stats; + + spin_lock_bh(&queue->lock); + queue->generation++; + list_splice_tail_init(&queue->queue, &queue->pending); + while (!list_empty(&queue->pending)) { + struct cw1200_queue_item *item = list_first_entry( + &queue->pending, struct cw1200_queue_item, head); + WARN_ON(!item->skb); + cw1200_queue_register_post_gc(&gc_list, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + } + queue->num_queued = 0; + queue->num_pending = 0; + + spin_lock_bh(&stats->lock); + for (i = 0; i < stats->map_capacity; ++i) { + stats->num_queued -= queue->link_map_cache[i]; + stats->link_map_cache[i] -= queue->link_map_cache[i]; + queue->link_map_cache[i] = 0; + } + spin_unlock_bh(&stats->lock); + if (unlikely(queue->overfull)) { + queue->overfull = false; + __cw1200_queue_unlock(queue); + } + spin_unlock_bh(&queue->lock); + wake_up(&stats->wait_link_id_empty); + cw1200_queue_post_gc(stats, &gc_list); + return 0; +} + +void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats) +{ + kfree(stats->link_map_cache); + stats->link_map_cache = NULL; +} + +void cw1200_queue_deinit(struct cw1200_queue *queue) +{ + cw1200_queue_clear(queue); + del_timer_sync(&queue->gc); + INIT_LIST_HEAD(&queue->free_pool); + kfree(queue->pool); + kfree(queue->link_map_cache); + queue->pool = NULL; + queue->link_map_cache = NULL; + queue->capacity = 0; +} + +size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, + u32 link_id_map) +{ + size_t ret; + int i, bit; + size_t map_capacity = queue->stats->map_capacity; + + if (!link_id_map) + return 0; + + spin_lock_bh(&queue->lock); + if (likely(link_id_map == (u32) -1)) + ret = queue->num_queued - queue->num_pending; + else { + ret = 0; + for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) { + if (link_id_map & bit) + ret += queue->link_map_cache[i]; + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int cw1200_queue_put(struct cw1200_queue *queue, + struct sk_buff *skb, + struct cw1200_txpriv *txpriv) +{ + int ret = 0; + LIST_HEAD(gc_list); + struct cw1200_queue_stats *stats = queue->stats; + + if (txpriv->link_id >= queue->stats->map_capacity) + return -EINVAL; + + spin_lock_bh(&queue->lock); + if (!WARN_ON(list_empty(&queue->free_pool))) { + struct cw1200_queue_item *item = list_first_entry( + &queue->free_pool, struct cw1200_queue_item, head); + BUG_ON(item->skb); + + list_move_tail(&item->head, &queue->queue); + item->skb = skb; + item->txpriv = *txpriv; + item->generation = 0; + item->packetID = cw1200_queue_make_packet_id( + queue->generation, queue->queue_id, + item->generation, item - queue->pool); + item->timestamp = jiffies; + + ++queue->num_queued; + ++queue->link_map_cache[txpriv->link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[txpriv->link_id]; + spin_unlock_bh(&stats->lock); + + if (queue->num_queued >= queue->capacity) { + queue->overfull = true; + __cw1200_queue_gc(queue, &gc_list, false); + if (queue->overfull) + __cw1200_queue_lock(queue); + + } + } else { + ret = -ENOENT; + } + spin_unlock_bh(&queue->lock); + if (unlikely(!list_empty(&gc_list))) + cw1200_queue_post_gc(stats, &gc_list); + return ret; +} + +int cw1200_queue_get(struct cw1200_queue *queue, + u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + const struct cw1200_txpriv **txpriv) +{ + int ret = -ENOENT; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + bool wakeup_stats = false; + + spin_lock_bh(&queue->lock); + list_for_each_entry(item, &queue->queue, head) { + if (link_id_map & BIT(item->txpriv.link_id)) { + ret = 0; + break; + } + } + + if (!WARN_ON(ret)) { + *tx = (struct wsm_tx *)item->skb->data; + *tx_info = IEEE80211_SKB_CB(item->skb); + *txpriv = &item->txpriv; + (*tx)->packetID = __cpu_to_le32(item->packetID); + list_move_tail(&item->head, &queue->pending); + ++queue->num_pending; + --queue->link_map_cache[item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + --stats->num_queued; + if (!--stats->link_map_cache[item->txpriv.link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); + } + spin_unlock_bh(&queue->lock); + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + return ret; +} + +int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + + cw1200_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (unlikely(queue_generation != queue->generation)) { + ret = -ENOENT; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + WARN_ON(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + WARN_ON(1); + ret = -ENOENT; + } else { + --queue->num_pending; + ++queue->link_map_cache[item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + item->generation = ++item_generation; + item->packetID = cw1200_queue_make_packet_id( + queue_generation, queue_id, item_generation, item_id); + list_move(&item->head, &queue->queue); + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int cw1200_queue_requeue_all(struct cw1200_queue *queue) +{ + struct cw1200_queue_stats *stats = queue->stats; + spin_lock_bh(&queue->lock); + while (!list_empty(&queue->pending)) { + struct cw1200_queue_item *item = list_entry( + queue->pending.prev, struct cw1200_queue_item, head); + + --queue->num_pending; + ++queue->link_map_cache[item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + ++item->generation; + item->packetID = cw1200_queue_make_packet_id( + queue->generation, queue->queue_id, + item->generation, item - queue->pool); + list_move(&item->head, &queue->queue); + } + spin_unlock_bh(&queue->lock); + + return 0; +} + +int cw1200_queue_remove(struct cw1200_queue *queue, u32 packetID) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + struct sk_buff *gc_skb = NULL; + struct cw1200_txpriv gc_txpriv; + + cw1200_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (unlikely(queue_generation != queue->generation)) { + ret = -ENOENT; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + WARN_ON(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + WARN_ON(1); + ret = -ENOENT; + } else { + gc_txpriv = item->txpriv; + gc_skb = item->skb; + item->skb = NULL; + --queue->num_pending; + --queue->num_queued; + ++queue->num_sent; + ++item->generation; + /* Do not use list_move_tail here, but list_move: + * try to utilize cache row. + */ + list_move(&item->head, &queue->free_pool); + + if (unlikely(queue->overfull) && + (queue->num_queued <= (queue->capacity >> 1))) { + queue->overfull = false; + __cw1200_queue_unlock(queue); + } + } + spin_unlock_bh(&queue->lock); + + if (gc_skb) + stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv); + + return ret; +} + +int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + cw1200_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (unlikely(queue_generation != queue->generation)) { + ret = -ENOENT; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + WARN_ON(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + WARN_ON(1); + ret = -ENOENT; + } else { + *skb = item->skb; + *txpriv = &item->txpriv; + } + spin_unlock_bh(&queue->lock); + return ret; +} + +void cw1200_queue_lock(struct cw1200_queue *queue) +{ + spin_lock_bh(&queue->lock); + __cw1200_queue_lock(queue); + spin_unlock_bh(&queue->lock); +} + +void cw1200_queue_unlock(struct cw1200_queue *queue) +{ + spin_lock_bh(&queue->lock); + __cw1200_queue_unlock(queue); + spin_unlock_bh(&queue->lock); +} + +bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, + u32 link_id_map) +{ + bool empty = true; + + spin_lock_bh(&stats->lock); + if (link_id_map == (u32)-1) + empty = stats->num_queued == 0; + else { + int i; + for (i = 0; i < stats->map_capacity; ++i) { + if (link_id_map & BIT(i)) { + if (stats->link_map_cache[i]) { + empty = false; + break; + } + } + } + } + spin_unlock_bh(&stats->lock); + + return empty; +} diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h new file mode 100644 index 00000000000..2403b2519ba --- /dev/null +++ b/drivers/staging/cw1200/queue.h @@ -0,0 +1,113 @@ +/* + * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_QUEUE_H_INCLUDED +#define CW1200_QUEUE_H_INCLUDED + +/* private */ struct cw1200_queue_item; + +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct cw1200_common; +/* extern */ struct ieee80211_tx_queue_stats; +/* extern */ struct cw1200_txpriv; + +/* forward */ struct cw1200_queue_stats; + +typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv); + +struct cw1200_queue { + struct cw1200_queue_stats *stats; + size_t capacity; + size_t num_queued; + size_t num_pending; + size_t num_sent; + struct cw1200_queue_item *pool; + struct list_head queue; + struct list_head free_pool; + struct list_head pending; + int tx_locked_cnt; + int *link_map_cache; + bool overfull; + spinlock_t lock; + u8 queue_id; + u8 generation; + struct timer_list gc; + unsigned long ttl; +}; + +struct cw1200_queue_stats { + spinlock_t lock; + int *link_map_cache; + int num_queued; + size_t map_capacity; + wait_queue_head_t wait_link_id_empty; + cw1200_queue_skb_dtor_t skb_dtor; + struct cw1200_common *priv; +}; + +struct cw1200_txpriv { + u8 link_id; + u8 raw_link_id; + u8 tid; + u8 rate_id; + u8 offset; +}; + +int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, + size_t map_capacity, + cw1200_queue_skb_dtor_t skb_dtor, + struct cw1200_common *priv); +int cw1200_queue_init(struct cw1200_queue *queue, + struct cw1200_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl); +int cw1200_queue_clear(struct cw1200_queue *queue); +void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats); +void cw1200_queue_deinit(struct cw1200_queue *queue); + +size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, + u32 link_id_map); +int cw1200_queue_put(struct cw1200_queue *queue, + struct sk_buff *skb, + struct cw1200_txpriv *txpriv); +int cw1200_queue_get(struct cw1200_queue *queue, + u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + const struct cw1200_txpriv **txpriv); +int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID); +int cw1200_queue_requeue_all(struct cw1200_queue *queue); +int cw1200_queue_remove(struct cw1200_queue *queue, + u32 packetID); +int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv); +void cw1200_queue_lock(struct cw1200_queue *queue); +void cw1200_queue_unlock(struct cw1200_queue *queue); + +bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, + u32 link_id_map); + +static inline u8 cw1200_queue_get_queue_id(u32 packetID) +{ + return (packetID >> 16) & 0xFF; +} + +static inline u8 cw1200_queue_get_generation(u32 packetID) +{ + return (packetID >> 8) & 0xFF; +} + +#endif /* CW1200_QUEUE_H_INCLUDED */ diff --git a/drivers/staging/cw1200/sbus.h b/drivers/staging/cw1200/sbus.h new file mode 100644 index 00000000000..a911ef1a0e6 --- /dev/null +++ b/drivers/staging/cw1200/sbus.h @@ -0,0 +1,38 @@ +/* + * Common sbus abstraction layer interface for cw1200 wireless driver + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_SBUS_H +#define CW1200_SBUS_H + +/* + * sbus priv forward definition. + * Implemented and instantiated in particular modules. + */ +struct sbus_priv; + +typedef void (*sbus_irq_handler)(void *priv); + +struct sbus_ops { + int (*sbus_memcpy_fromio)(struct sbus_priv *self, unsigned int addr, + void *dst, int count); + int (*sbus_memcpy_toio)(struct sbus_priv *self, unsigned int addr, + const void *src, int count); + void (*lock)(struct sbus_priv *self); + void (*unlock)(struct sbus_priv *self); + int (*irq_subscribe)(struct sbus_priv *self, sbus_irq_handler handler, + void *priv); + int (*irq_unsubscribe)(struct sbus_priv *self); + int (*reset)(struct sbus_priv *self); + size_t (*align_size)(struct sbus_priv *self, size_t size); + int (*power_mgmt)(struct sbus_priv *self, bool suspend); +}; + +#endif /* CW1200_SBUS_H */ diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c new file mode 100644 index 00000000000..d97bc8182b0 --- /dev/null +++ b/drivers/staging/cw1200/scan.c @@ -0,0 +1,434 @@ +/* + * Scan implementation for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/sched.h> +#include "cw1200.h" +#include "scan.h" +#include "sta.h" +#include "pm.h" + +static void cw1200_scan_restart_delayed(struct cw1200_common *priv); + +static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) +{ + int ret, i; + int tmo = 2000; + + for (i = 0; i < scan->numOfChannels; ++i) + tmo += scan->ch[i].maxChannelTime + 10; + + atomic_set(&priv->scan.in_progress, 1); + cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000); + queue_delayed_work(priv->workqueue, &priv->scan.timeout, + tmo * HZ / 1000); + ret = wsm_scan(priv, scan); + if (unlikely(ret)) { + atomic_set(&priv->scan.in_progress, 0); + cancel_delayed_work_sync(&priv->scan.timeout); + cw1200_scan_restart_delayed(priv); + } + return ret; +} + +int cw1200_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct cw1200_common *priv = hw->priv; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + int i; + + if (!priv->vif) + return -EINVAL; + + /* Scan when P2P_GO corrupt firmware MiniAP mode */ + if (priv->join_status == CW1200_JOIN_STATUS_AP) + return -EOPNOTSUPP; + + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) + req->n_ssids = 0; + + wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n", + req->n_ssids); + + if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) + return -EINVAL; + + if (req->ie_len != priv->scan.ie_len || + memcmp(req->ie, priv->scan.ie, req->ie_len)) { + frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0, + req->ie, req->ie_len); + if (!frame.skb) + return -ENOMEM; + kfree(priv->scan.ie); + priv->scan.ie = NULL; + priv->scan.ie_len = 0; + if (req->ie_len) { + priv->scan.ie = kmalloc(req->ie_len, GFP_KERNEL); + if (priv->scan.ie) { + memcpy(priv->scan.ie, req->ie, req->ie_len); + priv->scan.ie_len = req->ie_len; + } + } + } + + /* will be unlocked in cw1200_scan_work() */ + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + if (frame.skb) { + int ret = wsm_set_template_frame(priv, &frame); + if (ret) { + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); + dev_kfree_skb(frame.skb); + return ret; + } + } + + wsm_lock_tx(priv); + + BUG_ON(priv->scan.req); + priv->scan.req = req; + priv->scan.n_ssids = 0; + priv->scan.status = 0; + priv->scan.begin = &req->channels[0]; + priv->scan.curr = priv->scan.begin; + priv->scan.end = &req->channels[req->n_channels]; + priv->scan.output_power = priv->output_power; + + for (i = 0; i < req->n_ssids; ++i) { + if (req->ssids[i].ssid_len) { + struct wsm_ssid *dst = + &priv->scan.ssids[priv->scan.n_ssids]; + BUG_ON(req->ssids[i].ssid_len > sizeof(dst->ssid)); + memcpy(&dst->ssid[0], req->ssids[i].ssid, + sizeof(dst->ssid)); + dst->length = req->ssids[i].ssid_len; + ++priv->scan.n_ssids; + } + } + + mutex_unlock(&priv->conf_mutex); + + if (frame.skb) + dev_kfree_skb(frame.skb); + queue_work(priv->workqueue, &priv->scan.work); + return 0; +} + +void cw1200_scan_work(struct work_struct *work) +{ + struct cw1200_common *priv = container_of(work, struct cw1200_common, + scan.work); + struct ieee80211_channel **it; + struct wsm_scan scan = { + .scanType = WSM_SCAN_TYPE_FOREGROUND, + .scanFlags = WSM_SCAN_FLAG_SPLIT_METHOD, + }; + bool first_run = priv->scan.begin == priv->scan.curr && + priv->scan.begin != priv->scan.end; + int i; + + if (first_run) { + /* Firmware gets crazy if scan request is sent + * when STA is joined but not yet associated. + * Force unjoin in this case. */ + if (cancel_delayed_work_sync(&priv->join_timeout) > 0) + cw1200_join_timeout(&priv->join_timeout.work); + } + + mutex_lock(&priv->conf_mutex); + + if (first_run) { + if (priv->join_status == CW1200_JOIN_STATUS_STA && + !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.pmMode = WSM_PSM_PS; + wsm_set_pm(priv, &pm); + } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + /* FW bug: driver has to restart p2p-dev mode + * after scan */ + cw1200_disable_listening(priv); + } + } + + if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) { + if (priv->scan.output_power != priv->output_power) + WARN_ON(wsm_set_output_power(priv, + priv->output_power * 10)); + if (priv->join_status == CW1200_JOIN_STATUS_STA && + !(priv->powersave_mode.pmMode & WSM_PSM_PS)) + wsm_set_pm(priv, &priv->powersave_mode); + + if (priv->scan.req) + wiphy_dbg(priv->hw->wiphy, + "[SCAN] Scan completed.\n"); + else + wiphy_dbg(priv->hw->wiphy, + "[SCAN] Scan canceled.\n"); + + priv->scan.req = NULL; + cw1200_scan_restart_delayed(priv); + wsm_unlock_tx(priv); + mutex_unlock(&priv->conf_mutex); + ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0); + up(&priv->scan.lock); + return; + } else { + struct ieee80211_channel *first = *priv->scan.curr; + for (it = priv->scan.curr + 1, i = 1; + it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; + ++it, ++i) { + if ((*it)->band != first->band) + break; + if (((*it)->flags ^ first->flags) & + IEEE80211_CHAN_PASSIVE_SCAN) + break; + if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + (*it)->max_power != first->max_power) + break; + } + scan.band = first->band; + /* TODO: Is it optimal? */ + scan.maxTransmitRate = WSM_TRANSMIT_RATE_1; + /* TODO: Is it optimal? */ + scan.numOfProbeRequests = + (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2; + scan.numOfSSIDs = priv->scan.n_ssids; + scan.ssids = &priv->scan.ssids[0]; + scan.numOfChannels = it - priv->scan.curr; + /* TODO: Is it optimal? */ + scan.probeDelay = 100; + /* It is not stated in WSM specification, however + * FW team says that driver may not use FG scan + * when joined. */ + if (priv->join_status == CW1200_JOIN_STATUS_STA) + scan.scanType = WSM_SCAN_TYPE_BACKGROUND; + scan.ch = kzalloc( + sizeof(struct wsm_scan_ch[it - priv->scan.curr]), + GFP_KERNEL); + if (!scan.ch) { + priv->scan.status = -ENOMEM; + goto fail; + } + for (i = 0; i < scan.numOfChannels; ++i) { + scan.ch[i].number = priv->scan.curr[i]->hw_value; + scan.ch[i].minChannelTime = 50; + scan.ch[i].maxChannelTime = 110; + } + if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + priv->scan.output_power != first->max_power) { + priv->scan.output_power = first->max_power; + WARN_ON(wsm_set_output_power(priv, + priv->scan.output_power * 10)); + } + priv->scan.status = cw1200_scan_start(priv, &scan); + kfree(scan.ch); + if (WARN_ON(priv->scan.status)) + goto fail; + priv->scan.curr = it; + } + mutex_unlock(&priv->conf_mutex); + return; + +fail: + priv->scan.curr = priv->scan.end; + mutex_unlock(&priv->conf_mutex); + queue_work(priv->workqueue, &priv->scan.work); + return; +} + +static void cw1200_scan_restart_delayed(struct cw1200_common *priv) +{ + if (priv->delayed_link_loss) { + int tmo = priv->cqm_beacon_loss_count; + + if (priv->scan.direct_probe) + tmo = 0; + + priv->delayed_link_loss = 0; + /* Restart beacon loss timer and requeue + BSS loss work. */ + wiphy_dbg(priv->hw->wiphy, + "[CQM] Requeue BSS loss in %d " + "beacons.\n", tmo); + cancel_delayed_work_sync(&priv->bss_loss_work); + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, + tmo * HZ / 10); + } + + /* FW bug: driver has to restart p2p-dev mode after scan. */ + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + cw1200_enable_listening(priv); + cw1200_update_filtering(priv); + } + + if (priv->delayed_unjoin) { + priv->delayed_unjoin = false; + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } +} + +static void cw1200_scan_complete(struct cw1200_common *priv) +{ + if (priv->scan.direct_probe) { + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n"); + cw1200_scan_restart_delayed(priv); + priv->scan.direct_probe = 0; + up(&priv->scan.lock); + wsm_unlock_tx(priv); + } else { + cw1200_scan_work(&priv->scan.work); + } +} + +void cw1200_scan_complete_cb(struct cw1200_common *priv, + struct wsm_scan_complete *arg) +{ + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) + /* STA is stopped. */ + return; + + if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { + priv->scan.status = 1; + queue_delayed_work(priv->workqueue, + &priv->scan.timeout, 0); + } +} + +void cw1200_scan_timeout(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, scan.timeout.work); + if (likely(atomic_xchg(&priv->scan.in_progress, 0))) { + if (priv->scan.status > 0) + priv->scan.status = 0; + else if (!priv->scan.status) { + wiphy_warn(priv->hw->wiphy, + "Timeout waiting for scan " + "complete notification.\n"); + priv->scan.status = -ETIMEDOUT; + } + cw1200_scan_complete(priv); + } +} + +void cw1200_probe_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, scan.probe_work.work); + u8 queueId = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queueId]; + const struct cw1200_txpriv *txpriv; + struct wsm_tx *wsm; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + struct wsm_ssid ssids[1] = {{ + .length = 0, + } }; + struct wsm_scan_ch ch[1] = {{ + .minChannelTime = 0, + .maxChannelTime = 10, + } }; + struct wsm_scan scan = { + .scanType = WSM_SCAN_TYPE_FOREGROUND, + .numOfProbeRequests = 1, + .probeDelay = 0, + .numOfChannels = 1, + .ssids = ssids, + .ch = ch, + }; + u8 *ies; + size_t ies_len; + int ret; + + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n"); + + BUG_ON(queueId >= 4); + BUG_ON(!priv->channel); + + mutex_lock(&priv->conf_mutex); + if (unlikely(down_trylock(&priv->scan.lock))) { + /* Scan is already in progress. Requeue self. */ + schedule(); + queue_delayed_work(priv->workqueue, + &priv->scan.probe_work, HZ / 10); + mutex_unlock(&priv->conf_mutex); + return; + } + + if (cw1200_queue_get_skb(queue, priv->pending_frame_id, + &frame.skb, &txpriv)) { + wsm_unlock_tx(priv); + return; + } + wsm = (struct wsm_tx *)frame.skb->data; + scan.maxTransmitRate = wsm->maxTxRate; + scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + if (priv->join_status == CW1200_JOIN_STATUS_STA) + scan.scanType = WSM_SCAN_TYPE_BACKGROUND; + ch[0].number = priv->channel->hw_value; + + skb_pull(frame.skb, txpriv->offset); + + ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; + ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); + + if (ies_len) { + u8 *ssidie = + (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); + if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) { + u8 *nextie = &ssidie[2 + ssidie[1]]; + /* Remove SSID from the IE list. It has to be provided + * as a separate argument in cw1200_scan_start call */ + + /* Store SSID localy */ + ssids[0].length = ssidie[1]; + memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length); + scan.numOfSSIDs = 1; + + /* Remove SSID from IE list */ + ssidie[1] = 0; + memmove(&ssidie[2], nextie, &ies[ies_len] - nextie); + skb_trim(frame.skb, frame.skb->len - ssids[0].length); + } + } + + /* FW bug: driver has to restart p2p-dev mode after scan */ + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + cw1200_disable_listening(priv); + ret = WARN_ON(wsm_set_template_frame(priv, &frame)); + priv->scan.direct_probe = 1; + if (!ret) { + wsm_flush_tx(priv); + ret = WARN_ON(cw1200_scan_start(priv, &scan)); + } + mutex_unlock(&priv->conf_mutex); + + skb_push(frame.skb, txpriv->offset); + if (!ret) + IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK; + BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id)); + + if (ret) { + priv->scan.direct_probe = 0; + up(&priv->scan.lock); + wsm_unlock_tx(priv); + } + + return; +} diff --git a/drivers/staging/cw1200/scan.h b/drivers/staging/cw1200/scan.h new file mode 100644 index 00000000000..fd59123e742 --- /dev/null +++ b/drivers/staging/cw1200/scan.h @@ -0,0 +1,56 @@ +/* + * Scan interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SCAN_H_INCLUDED +#define SCAN_H_INCLUDED + +#include <linux/semaphore.h> +#include "wsm.h" + +/* external */ struct sk_buff; +/* external */ struct cfg80211_scan_request; +/* external */ struct ieee80211_channel; +/* external */ struct ieee80211_hw; +/* external */ struct work_struct; + +struct cw1200_scan { + struct semaphore lock; + struct work_struct work; + struct delayed_work timeout; + struct cfg80211_scan_request *req; + struct ieee80211_channel **begin; + struct ieee80211_channel **curr; + struct ieee80211_channel **end; + struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; + u8 *ie; + size_t ie_len; + int output_power; + int n_ssids; + int status; + atomic_t in_progress; + /* Direct probe requests workaround */ + struct delayed_work probe_work; + int direct_probe; +}; + +int cw1200_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req); +void cw1200_scan_work(struct work_struct *work); +void cw1200_scan_timeout(struct work_struct *work); +void cw1200_scan_complete_cb(struct cw1200_common *priv, + struct wsm_scan_complete *arg); + +/* ******************************************************************** */ +/* Raw probe requests TX workaround */ +void cw1200_probe_work(struct work_struct *work); + +#endif diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c new file mode 100644 index 00000000000..9eb7879bab2 --- /dev/null +++ b/drivers/staging/cw1200/sta.c @@ -0,0 +1,1424 @@ +/* + * Mac80211 STA API for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/vmalloc.h> +#include <linux/sched.h> +#include <linux/firmware.h> + +#include "cw1200.h" +#include "sta.h" +#include "fwio.h" +#include "bh.h" +#include "debug.h" + +#if defined(CONFIG_CW1200_STA_DEBUG) +#define sta_printk(...) printk(__VA_ARGS__) +#else +#define sta_printk(...) +#endif + + +static int cw1200_cancel_scan(struct cw1200_common *priv); + +static inline void __cw1200_free_event_queue(struct list_head *list) +{ + while (!list_empty(list)) { + struct cw1200_wsm_event *event = + list_first_entry(list, struct cw1200_wsm_event, + link); + list_del(&event->link); + kfree(event); + } +} + +/* ******************************************************************** */ +/* STA API */ + +int cw1200_start(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int ret = 0; + + mutex_lock(&priv->conf_mutex); + + /* default ECDA */ + WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, false); + WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, false); + WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, false); + WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, false); + ret = wsm_set_edca_params(priv, &priv->edca); + if (WARN_ON(ret)) + goto out; + + ret = cw1200_set_uapsd_param(priv, &priv->edca); + if (WARN_ON(ret)) + goto out; + + priv->setbssparams_done = false; + + memset(priv->bssid, ~0, ETH_ALEN); + memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); + priv->mode = NL80211_IFTYPE_MONITOR; + priv->softled_state = 0; + priv->wep_default_key_id = -1; + + priv->cqm_link_loss_count = 60; + priv->cqm_beacon_loss_count = 20; + + /* Temporary configuration - beacon filter table */ + priv->bf_table.numOfIEs = __cpu_to_le32(1); + priv->bf_table.entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC; + priv->bf_table.entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED; + priv->bf_table.entry[0].oui[0] = 0x50; + priv->bf_table.entry[0].oui[1] = 0x6F; + priv->bf_table.entry[0].oui[2] = 0x9A; + + priv->bf_control.enabled = 1; + ret = cw1200_setup_mac(priv); + if (WARN_ON(ret)) + goto out; + + /* err = cw1200_set_leds(priv); */ + +out: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_stop(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + LIST_HEAD(list); + int i; + + wsm_lock_tx(priv); + + while (down_trylock(&priv->scan.lock)) { + /* Scan is in progress. Force it to stop. */ + priv->scan.req = NULL; + schedule(); + } + up(&priv->scan.lock); + + cancel_delayed_work_sync(&priv->scan.probe_work); + cancel_delayed_work_sync(&priv->scan.timeout); + cancel_delayed_work_sync(&priv->join_timeout); + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + cancel_delayed_work_sync(&priv->link_id_gc_work); + flush_workqueue(priv->workqueue); + del_timer_sync(&priv->mcast_timeout); + + mutex_lock(&priv->conf_mutex); + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->listening = false; + + priv->softled_state = 0; + /* cw1200_set_leds(priv); */ + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + __cw1200_free_event_queue(&list); + + priv->delayed_link_loss = 0; + + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + + for (i = 0; i < 4; i++) + cw1200_queue_clear(&priv->tx_queue[i]); + + /* HACK! */ + if (atomic_xchg(&priv->tx_lock, 1) != 1) + sta_printk(KERN_DEBUG "[STA] TX is force-unlocked " + "due to stop request.\n"); + + wsm_unlock_tx(priv); + + mutex_unlock(&priv->conf_mutex); +} + +int cw1200_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + int ret; + struct cw1200_common *priv = dev->priv; + /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ + + mutex_lock(&priv->conf_mutex); + + if (priv->mode != NL80211_IFTYPE_MONITOR) { + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + priv->mode = vif->type; + break; + default: + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + priv->vif = vif; + memcpy(priv->mac_addr, vif->addr, ETH_ALEN); + + ret = WARN_ON(cw1200_setup_mac(priv)); + /* Enable auto-calibration */ + /* Exception in subsequent channel switch; disabled. + WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, + &auto_calibration_mode, sizeof(auto_calibration_mode))); + */ + + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct cw1200_common *priv = dev->priv; + struct wsm_reset reset = { + .reset_statistics = true, + }; + int i; + + mutex_lock(&priv->conf_mutex); + wsm_lock_tx(priv); + switch (priv->join_status) { + case CW1200_JOIN_STATUS_STA: + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + break; + case CW1200_JOIN_STATUS_AP: + for (i = 0; priv->link_id_map; ++i) { + if (priv->link_id_map & BIT(i)) { + reset.link_id = i; + wsm_reset(priv, &reset); + priv->link_id_map &= ~BIT(i); + } + } + memset(priv->link_id_db, 0, + sizeof(priv->link_id_db)); + priv->sta_asleep_mask = 0; + priv->enable_beacon = false; + priv->tx_multicast = false; + priv->aid0_bit_set = false; + priv->buffered_multicasts = false; + priv->pspoll_mask = 0; + reset.link_id = 0; + wsm_reset(priv, &reset); + break; + case CW1200_JOIN_STATUS_MONITOR: + cw1200_update_listening(priv, false); + break; + default: + break; + } + priv->vif = NULL; + priv->mode = NL80211_IFTYPE_MONITOR; + memset(priv->mac_addr, 0, ETH_ALEN); + memset(priv->bssid, 0, ETH_ALEN); + cw1200_free_keys(priv); + cw1200_setup_mac(priv); + priv->listening = false; + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + if (!__cw1200_flush(priv, true)) + wsm_unlock_tx(priv); + wsm_unlock_tx(priv); + + mutex_unlock(&priv->conf_mutex); +} + +int cw1200_config(struct ieee80211_hw *dev, u32 changed) +{ + int ret = 0; + struct cw1200_common *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + + mutex_lock(&priv->conf_mutex); + /* TODO: IEEE80211_CONF_CHANGE_QOS */ + if (changed & IEEE80211_CONF_CHANGE_POWER) { + priv->output_power = conf->power_level; + sta_printk(KERN_DEBUG "[STA] TX power: %d\n", + priv->output_power); + WARN_ON(wsm_set_output_power(priv, priv->output_power * 10)); + } + + if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { + /* TODO: Not sure. Needs to be verified. */ + /* TODO: DTIM skipping */ + int dtim_interval = conf->ps_dtim_period; + int listen_interval = conf->listen_interval; + if (dtim_interval < 1) + dtim_interval = 1; + if (listen_interval < dtim_interval) + listen_interval = 0; + /* TODO: max_sleep_period is not supported + * and silently skipped. */ + sta_printk(KERN_DEBUG "[STA] DTIM %d, listen %d\n", + dtim_interval, listen_interval); + WARN_ON(wsm_set_beacon_wakeup_period(priv, + dtim_interval, listen_interval)); + } + + + if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && + (priv->channel != conf->channel)) { + struct ieee80211_channel *ch = conf->channel; + struct wsm_switch_channel channel = { + .newChannelNumber = ch->hw_value, + }; + cw1200_cancel_scan(priv); + sta_printk(KERN_DEBUG "[STA] Freq %d (wsm ch: %d).\n", + ch->center_freq, ch->hw_value); + + ret = WARN_ON(__cw1200_flush(priv, false)); + if (!ret) { + while(down_trylock(&priv->scan.lock)) { + sta_printk(KERN_DEBUG "[STA] waiting, " + "scan in progress.\n"); + msleep(100); + } + + ret = WARN_ON(wsm_switch_channel(priv, &channel)); + if (!ret) { + ret = wait_event_timeout( + priv->channel_switch_done, + !priv->channel_switch_in_progress, + 3 * HZ); + /* TODO: We should check also switch channel + * complete indication + */ + if (ret) { + priv->channel = ch; + ret = 0; + } else + ret = -ETIMEDOUT; + } else + wsm_unlock_tx(priv); + + up(&priv->scan.lock); + } + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + if (!(conf->flags & IEEE80211_CONF_PS)) + priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; + else if (conf->dynamic_ps_timeout <= 0) + priv->powersave_mode.pmMode = WSM_PSM_PS; + else + priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; + + /* Firmware requires that value for this 1-byte field must + * be specified in units of 500us. Values above the 128ms + * threshold are not supported. */ + if (conf->dynamic_ps_timeout >= 0x80) + priv->powersave_mode.fastPsmIdlePeriod = 0xFF; + else + priv->powersave_mode.fastPsmIdlePeriod = + conf->dynamic_ps_timeout << 1; + + if (priv->join_status == CW1200_JOIN_STATUS_STA) + cw1200_set_pm(priv, &priv->powersave_mode); + } + +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + if (changed & IEEE80211_CONF_CHANGE_P2P_PS) { + struct wsm_p2p_ps_modeinfo *modeinfo; + modeinfo = &priv->p2p_ps_modeinfo; + sta_printk(KERN_DEBUG "[STA] IEEE80211_CONF_CHANGE_P2P_PS\n"); + + if (conf->p2p_ps.ctwindow >= 128) + modeinfo->oppPsCTWindow = 127; + else if (conf->p2p_ps.ctwindow >= 0) + modeinfo->oppPsCTWindow = conf->p2p_ps.ctwindow; + + switch (conf->p2p_ps.opp_ps) { + case 0: + modeinfo->oppPsCTWindow &= ~(BIT(7)); + break; + case 1: + modeinfo->oppPsCTWindow |= BIT(7); + break; + default: + break; + } + + /* Notice of Absence */ + modeinfo->count = conf->p2p_ps.count; + modeinfo->startTime = __cpu_to_le32(conf->p2p_ps.start); + modeinfo->duration = __cpu_to_le32(conf->p2p_ps.duration); + modeinfo->interval = __cpu_to_le32(conf->p2p_ps.interval); + + if (conf->p2p_ps.count) + modeinfo->dtimCount = 1; + else + modeinfo->dtimCount = 0; + + if (priv->join_status == CW1200_JOIN_STATUS_STA || + priv->join_status == CW1200_JOIN_STATUS_AP) { +#if defined(CONFIG_CW1200_STA_DEBUG) + print_hex_dump_bytes("p2p_ps_modeinfo: ", + DUMP_PREFIX_NONE, + (u8 *)modeinfo, + sizeof(*modeinfo)); +#endif /* CONFIG_CW1200_STA_DEBUG */ + WARN_ON(wsm_set_p2p_ps_modeinfo(priv, modeinfo)); + } + } +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + /* TBD: It looks like it's transparent + * there's a monitor interface present -- use this + * to determine for example whether to calculate + * timestamps for packets or not, do not use instead + * of filter flags! */ + } + + if (changed & IEEE80211_CONF_CHANGE_IDLE) { + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + + wsm_lock_tx(priv); + /* Disable p2p-dev mode forced by TX request */ + if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && + (conf->flags & IEEE80211_CONF_IDLE) && + !priv->listening) { + cw1200_disable_listening(priv); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + } + WARN_ON(wsm_set_operational_mode(priv, &mode)); + wsm_unlock_tx(priv); + } + + if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { + sta_printk(KERN_DEBUG "[STA] Retry limits: %d (long), " \ + "%d (short).\n", + conf->long_frame_max_tx_count, + conf->short_frame_max_tx_count); + spin_lock_bh(&priv->tx_policy_cache.lock); + priv->long_frame_max_tx_count = conf->long_frame_max_tx_count; + priv->short_frame_max_tx_count = + (conf->short_frame_max_tx_count < 0x0F) ? + conf->short_frame_max_tx_count : 0x0F; + priv->hw->max_rate_tries = priv->short_frame_max_tx_count; + spin_unlock_bh(&priv->tx_policy_cache.lock); + /* TBD: I think we don't need tx_policy_force_upload(). + * Outdated policies will leave cache in a normal way. */ + /* WARN_ON(tx_policy_force_upload(priv)); */ + } + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_update_filtering(struct cw1200_common *priv) +{ + int ret; + + if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) + return; + + ret = wsm_set_rx_filter(priv, &priv->rx_filter); + if (!ret) + ret = wsm_set_beacon_filter_table(priv, &priv->bf_table); + if (!ret) + ret = wsm_beacon_filter_control(priv, &priv->bf_control); + if (!ret) + ret = wsm_set_bssid_filtering(priv, !priv->rx_filter.bssid); + if (!ret) + ret = wsm_set_multicast_filter(priv, &priv->multicast_filter); + if (ret) + wiphy_err(priv->hw->wiphy, + "%s: Update filtering failed: %d.\n", + __func__, ret); + return; +} + +u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct cw1200_common *priv = hw->priv; + struct netdev_hw_addr *ha; + int count = 0; + + /* Disable multicast filtering */ + memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); + + if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) + return 0; + + /* Enable if requested */ + netdev_hw_addr_list_for_each(ha, mc_list) { + sta_printk(KERN_DEBUG "[STA] multicast: %pM\n", ha->addr); + memcpy(&priv->multicast_filter.macAddress[count], + ha->addr, ETH_ALEN); + count++; + } + + if (count) { + priv->multicast_filter.enable = __cpu_to_le32(1); + priv->multicast_filter.numOfAddresses = __cpu_to_le32(count); + } + + return netdev_hw_addr_list_count(mc_list); +} + +void cw1200_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct cw1200_common *priv = dev->priv; + bool listening = !!(*total_flags & + (FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ)); + + *total_flags &= FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS | + FIF_FCSFAIL | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ; + + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + + priv->rx_filter.promiscuous = (*total_flags & FIF_PROMISC_IN_BSS) + ? 1 : 0; + priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS | + FIF_PROBE_REQ)) ? 1 : 0; + priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; + priv->bf_control.bcn_count = (*total_flags & + (FIF_BCN_PRBRESP_PROMISC | + FIF_PROMISC_IN_BSS | + FIF_PROBE_REQ)) ? 1 : 0; + if (priv->listening ^ listening) { + priv->listening = listening; + wsm_lock_tx(priv); + cw1200_update_listening(priv, listening); + wsm_unlock_tx(priv); + } + cw1200_update_filtering(priv); + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); +} + +int cw1200_conf_tx(struct ieee80211_hw *dev, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct cw1200_common *priv = dev->priv; + int ret = 0; + /* To prevent re-applying PM request OID again and again*/ + bool old_uapsdFlags; + + mutex_lock(&priv->conf_mutex); + + if (queue < dev->queues) { + old_uapsdFlags = priv->uapsd_info.uapsdFlags; + + WSM_EDCA_SET(&priv->edca, queue, params->aifs, + params->cw_min, params->cw_max, params->txop, + params->uapsd); + ret = wsm_set_edca_params(priv, &priv->edca); + + if (!ret && (priv->mode == NL80211_IFTYPE_STATION)) { + ret = cw1200_set_uapsd_param(priv, &priv->edca); + if (!ret && priv->setbssparams_done && + (priv->join_status == CW1200_JOIN_STATUS_STA) && + (old_uapsdFlags != priv->uapsd_info.uapsdFlags)) + cw1200_set_pm(priv, &priv->powersave_mode); + } + } else + ret = -EINVAL; + + mutex_unlock(&priv->conf_mutex); + return ret; +} + +int cw1200_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct cw1200_common *priv = dev->priv; + + memcpy(stats, &priv->stats, sizeof(*stats)); + return 0; +} + +/* +int cw1200_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats) +{ + int i; + struct cw1200_common *priv = dev->priv; + + for (i = 0; i < dev->queues; ++i) + cw1200_queue_get_stats(&priv->tx_queue[i], &stats[i]); + + return 0; +} +*/ + +int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) +{ + struct wsm_set_pm pm = *arg; + + if (priv->uapsd_info.uapsdFlags != 0) + pm.pmMode &= ~WSM_PSM_FAST_PS_FLAG; + + return wsm_set_pm(priv, &pm); +} + +int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret = -EOPNOTSUPP; + struct cw1200_common *priv = dev->priv; + + mutex_lock(&priv->conf_mutex); + + if (cmd == SET_KEY) { + u8 *peer_addr = NULL; + int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? + 1 : 0; + int idx = cw1200_alloc_key(priv); + struct wsm_add_key *wsm_key = &priv->keys[idx]; + + if (idx < 0) { + ret = -EINVAL; + goto finally; + } + + BUG_ON(pairwise && !sta); + if (sta) + peer_addr = sta->addr; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (key->keylen > 16) { + cw1200_free_key(priv, idx); + ret = -EINVAL; + goto finally; + } + + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; + memcpy(wsm_key->wepPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wepPairwiseKey.keyData, + &key->key[0], key->keylen); + wsm_key->wepPairwiseKey.keyLength = key->keylen; + } else { + wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; + memcpy(wsm_key->wepGroupKey.keyData, + &key->key[0], key->keylen); + wsm_key->wepGroupKey.keyLength = key->keylen; + wsm_key->wepGroupKey.keyId = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_TKIP: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; + memcpy(wsm_key->tkipPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->tkipPairwiseKey.tkipKeyData, + &key->key[0], 16); + memcpy(wsm_key->tkipPairwiseKey.txMicKey, + &key->key[16], 8); + memcpy(wsm_key->tkipPairwiseKey.rxMicKey, + &key->key[24], 8); + } else { + size_t mic_offset = + (priv->mode == NL80211_IFTYPE_AP) ? + 16 : 24; + wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; + memcpy(wsm_key->tkipGroupKey.tkipKeyData, + &key->key[0], 16); + memcpy(wsm_key->tkipGroupKey.rxMicKey, + &key->key[mic_offset], 8); + + /* TODO: Where can I find TKIP SEQ? */ + memset(wsm_key->tkipGroupKey.rxSeqCounter, + 0, 8); + wsm_key->tkipGroupKey.keyId = key->keyidx; + + print_hex_dump_bytes("TKIP: ", DUMP_PREFIX_NONE, + key->key, key->keylen); + } + break; + case WLAN_CIPHER_SUITE_CCMP: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; + memcpy(wsm_key->aesPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->aesPairwiseKey.aesKeyData, + &key->key[0], 16); + } else { + wsm_key->type = WSM_KEY_TYPE_AES_GROUP; + memcpy(wsm_key->aesGroupKey.aesKeyData, + &key->key[0], 16); + /* TODO: Where can I find AES SEQ? */ + memset(wsm_key->aesGroupKey.rxSeqCounter, + 0, 8); + wsm_key->aesGroupKey.keyId = key->keyidx; + } + break; +#ifdef CONFIG_CW1200_WAPI_SUPPORT + case WLAN_CIPHER_SUITE_SMS4: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; + memcpy(wsm_key->wapiPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wapiPairwiseKey.wapiKeyData, + &key->key[0], 16); + memcpy(wsm_key->wapiPairwiseKey.micKeyData, + &key->key[16], 16); + wsm_key->wapiPairwiseKey.keyId = key->keyidx; + } else { + wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; + memcpy(wsm_key->wapiGroupKey.wapiKeyData, + &key->key[0], 16); + memcpy(wsm_key->wapiGroupKey.micKeyData, + &key->key[16], 16); + wsm_key->wapiGroupKey.keyId = key->keyidx; + } + break; +#endif /* CONFIG_CW1200_WAPI_SUPPORT */ + default: + WARN_ON(1); + cw1200_free_key(priv, idx); + ret = -EOPNOTSUPP; + goto finally; + } + ret = WARN_ON(wsm_add_key(priv, wsm_key)); + if (!ret) + key->hw_key_idx = idx; + else + cw1200_free_key(priv, idx); + } else if (cmd == DISABLE_KEY) { + struct wsm_remove_key wsm_key = { + .entryIndex = key->hw_key_idx, + }; + + if (wsm_key.entryIndex > WSM_KEY_MAX_INDEX) { + ret = -EINVAL; + goto finally; + } + + cw1200_free_key(priv, wsm_key.entryIndex); + ret = wsm_remove_key(priv, &wsm_key); + } else { + BUG_ON("Unsupported command"); + } + +finally: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_wep_key_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, wep_key_work); + u8 queueId = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queueId]; + __le32 wep_default_key_id = __cpu_to_le32( + priv->wep_default_key_id); + + BUG_ON(queueId >= 4); + + sta_printk(KERN_DEBUG "[STA] Setting default WEP key: %d\n", + priv->wep_default_key_id); + wsm_flush_tx(priv); + WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, + &wep_default_key_id, sizeof(wep_default_key_id))); + cw1200_queue_requeue(queue, priv->pending_frame_id); + wsm_unlock_tx(priv); +} + +int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + int ret; + __le32 val32; + + if (value != (u32) -1) + val32 = __cpu_to_le32(value); + else + val32 = 0; /* disabled */ + + /* mutex_lock(&priv->conf_mutex); */ + ret = WARN_ON(wsm_write_mib(hw->priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, + &val32, sizeof(val32))); + /* mutex_unlock(&priv->conf_mutex); */ + return ret; +} + +int __cw1200_flush(struct cw1200_common *priv, bool drop) +{ + int i, ret; + + for (;;) { + /* TODO: correct flush handling is required when dev_stop. + * Temporary workaround: 2s + */ + if (drop) { + for (i = 0; i < 4; ++i) + cw1200_queue_clear(&priv->tx_queue[i]); + } else { + ret = wait_event_timeout( + priv->tx_queue_stats.wait_link_id_empty, + cw1200_queue_stats_is_empty( + &priv->tx_queue_stats, -1), + 2 * HZ); + } + + if (!drop && unlikely(ret <= 0)) { + ret = -ETIMEDOUT; + break; + } else { + ret = 0; + } + + wsm_lock_tx(priv); + if (unlikely(!cw1200_queue_stats_is_empty( + &priv->tx_queue_stats, -1))) { + /* Highly unlekely: WSM requeued frames. */ + wsm_unlock_tx(priv); + continue; + } + break; + } + return ret; +} + +void cw1200_flush(struct ieee80211_hw *hw, bool drop) +{ + struct cw1200_common *priv = hw->priv; + + switch (priv->mode) { + case NL80211_IFTYPE_MONITOR: + drop = true; + break; + case NL80211_IFTYPE_AP: + if (!priv->enable_beacon) + drop = true; + break; + } + + if (!WARN_ON(__cw1200_flush(priv, drop))) + wsm_unlock_tx(priv); + + return; +} + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_channel_switch_cb(struct cw1200_common *priv) +{ + wsm_unlock_tx(priv); +} + +void cw1200_free_event_queue(struct cw1200_common *priv) +{ + LIST_HEAD(list); + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + + __cw1200_free_event_queue(&list); +} + +void cw1200_event_handler(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, event_handler); + struct cw1200_wsm_event *event; + LIST_HEAD(list); + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + + list_for_each_entry(event, &list, link) { + switch (event->evt.eventId) { + case WSM_EVENT_ERROR: + /* I even don't know what is it about.. */ + STUB(); + break; + case WSM_EVENT_BSS_LOST: + { + sta_printk(KERN_DEBUG "[CQM] BSS lost.\n"); + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + if (!down_trylock(&priv->scan.lock)) { + up(&priv->scan.lock); + priv->delayed_link_loss = 0; + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, 0); + } else { + /* Scan is in progress. Delay reporting. */ + /* Scan complete will trigger bss_loss_work */ + priv->delayed_link_loss = 1; + /* Also we're starting watchdog. */ + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, 10 * HZ); + } + break; + } + case WSM_EVENT_BSS_REGAINED: + { + sta_printk(KERN_DEBUG "[CQM] BSS regained.\n"); + priv->delayed_link_loss = 0; + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + break; + } + case WSM_EVENT_RADAR_DETECTED: + STUB(); + break; + case WSM_EVENT_RCPI_RSSI: + { + int rssi = (int)(s8)(event->evt.eventData & 0xFF); + int cqm_evt = (rssi <= priv->cqm_rssi_thold) ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + sta_printk(KERN_DEBUG "[CQM] RSSI event: %d", rssi); + ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, + GFP_KERNEL); + break; + } + case WSM_EVENT_BT_INACTIVE: + STUB(); + break; + case WSM_EVENT_BT_ACTIVE: + STUB(); + break; + } + } + __cw1200_free_event_queue(&list); +} + +void cw1200_bss_loss_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, bss_loss_work.work); + int timeout; /* in beacons */ + + timeout = priv->cqm_link_loss_count - + priv->cqm_beacon_loss_count; + + if (priv->cqm_beacon_loss_count) { + sta_printk(KERN_DEBUG "[CQM] Beacon loss.\n"); + if (timeout <= 0) + timeout = 0; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + ieee80211_cqm_beacon_miss_notify(priv->vif, GFP_KERNEL); +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ + } else { + timeout = 0; + } + + cancel_delayed_work_sync(&priv->connection_loss_work); + queue_delayed_work(priv->workqueue, + &priv->connection_loss_work, + timeout * HZ / 10); +} + +void cw1200_connection_loss_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + connection_loss_work.work); + sta_printk(KERN_DEBUG "[CQM] Reporting connection loss.\n"); + ieee80211_connection_loss(priv->vif); +} + +void cw1200_tx_failure_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, tx_failure_work); + sta_printk(KERN_DEBUG "[CQM] Reporting TX failure.\n"); +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + ieee80211_cqm_tx_fail_notify(priv->vif, GFP_KERNEL); +#else /* CONFIG_CW1200_USE_STE_EXTENSIONS */ + (void)priv; +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ +} + +/* ******************************************************************** */ +/* Internal API */ + + + +/* +* This function is called to Parse the SDD file + *to extract listen_interval and PTA related information +*/ +static int cw1200_parse_SDD_file(struct cw1200_common *priv) +{ + u8 *sdd_data = (u8 *)priv->sdd->data; + struct cw1200_sdd { + u8 id ; + u8 length ; + u8 data[] ; + } *pElement; + int parsedLength = 0; + #define SDD_PTA_CFG_ELT_ID 0xEB + #define FIELD_OFFSET(type, field) ((u8 *)&((type*)0)->field - (u8 *)0) + + priv->is_BT_Present = false; + + pElement = (struct cw1200_sdd *)sdd_data; + + pElement = (struct cw1200_sdd *)((u8*)pElement + + FIELD_OFFSET(struct cw1200_sdd, data) + pElement->length); + + parsedLength += (FIELD_OFFSET(struct cw1200_sdd, data) + + pElement->length); + + while (parsedLength <= priv->sdd->size) { + switch (pElement->id) { + case SDD_PTA_CFG_ELT_ID: + { + priv->conf_listen_interval = + (*((u16 *)pElement->data+1) >> 7) & 0x1F; + priv->is_BT_Present = true; + sta_printk(KERN_DEBUG "PTA element found.\n"); + sta_printk(KERN_DEBUG "Listen Interval %d\n", + priv->conf_listen_interval); + } + break; + + default: + break; + } + + pElement = (struct cw1200_sdd *) + ((u8 *)pElement + FIELD_OFFSET(struct cw1200_sdd, data) + + pElement->length); + parsedLength += + (FIELD_OFFSET(struct cw1200_sdd, data) + pElement->length); + } + + if (priv->is_BT_Present == false) { + sta_printk(KERN_DEBUG "PTA element NOT found.\n"); + priv->conf_listen_interval = 0; + } + return 0; +} + + +int cw1200_setup_mac(struct cw1200_common *priv) +{ + /* TBD: Do you know how to assing MAC address without + * annoying uploading RX data? */ + u8 prev_mac[ETH_ALEN]; + + /* NOTE: There is a bug in FW: it reports signal + * as RSSI if RSSI subscription is enabled. + * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */ + struct wsm_rcpi_rssi_threshold threshold = { + .rssiRcpiMode = WSM_RCPI_RSSI_USE_RSSI | + WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER, + .rollingAverageCount = 16, + }; + int ret = 0; + + if (wsm_get_station_id(priv, &prev_mac[0]) + || memcmp(prev_mac, priv->mac_addr, ETH_ALEN)) { + const char *sdd_path = NULL; + struct wsm_configuration cfg = { + .dot11StationId = &priv->mac_addr[0], + }; + + if (!priv->sdd) { + switch (priv->hw_revision) { + case CW1200_HW_REV_CUT10: + sdd_path = SDD_FILE_10; + break; + case CW1200_HW_REV_CUT11: + sdd_path = SDD_FILE_11; + break; + case CW1200_HW_REV_CUT20: + sdd_path = SDD_FILE_20; + break; + case CW1200_HW_REV_CUT22: + sdd_path = SDD_FILE_22; + break; + default: + BUG_ON(1); + } + + ret = request_firmware(&priv->sdd, + sdd_path, priv->pdev); + + if (unlikely(ret)) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't load sdd file %s.\n", + __func__, sdd_path); + return ret; + } + } + + cfg.dpdData = priv->sdd->data; + cfg.dpdData_size = priv->sdd->size; + ret = WARN_ON(wsm_configuration(priv, &cfg)); + /* Parse SDD file for PTA element */ + cw1200_parse_SDD_file(priv); + } + if (ret) + return ret; + + /* Configure RSSI/SCPI reporting as RSSI. */ + WARN_ON(wsm_set_rcpi_rssi_threshold(priv, &threshold)); + + /* TODO: */ + switch (priv->mode) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP: + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + /* TODO: Not verified yet. */ + STUB(); + break; + } + + return 0; +} + +void cw1200_offchannel_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, offchannel_work); + u8 queueId = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queueId]; + + BUG_ON(queueId >= 4); + BUG_ON(!priv->channel); + + mutex_lock(&priv->conf_mutex); + if (likely(!priv->join_status)) { + wsm_flush_tx(priv); + cw1200_update_listening(priv, true); + cw1200_update_filtering(priv); + } + if (unlikely(!priv->join_status)) + cw1200_queue_remove(queue, priv->pending_frame_id); + else + cw1200_queue_requeue(queue, priv->pending_frame_id); + mutex_unlock(&priv->conf_mutex); + wsm_unlock_tx(priv); +} + +void cw1200_join_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, join_work); + u8 queueId = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queueId]; + const struct cw1200_txpriv *txpriv = NULL; + struct sk_buff *skb = NULL; + const struct wsm_tx *wsm; + const struct ieee80211_hdr *frame; + const u8 *bssid; + struct cfg80211_bss *bss; + const u8 *ssidie; + const u8 *dtimie; + const struct ieee80211_tim_ie *tim = NULL; + + BUG_ON(queueId >= 4); + if (cw1200_queue_get_skb(queue, priv->pending_frame_id, + &skb, &txpriv)) { + wsm_unlock_tx(priv); + return; + } + wsm = (struct wsm_tx *)&skb->data[0]; + frame = (struct ieee80211_hdr *)&skb->data[txpriv->offset]; + bssid = &frame->addr1[0]; /* AP SSID in a 802.11 frame */ + + BUG_ON(!wsm); + BUG_ON(!priv->channel); + + if (unlikely(priv->join_status)) { + wsm_lock_tx(priv); + cw1200_unjoin_work(&priv->unjoin_work); + } + + cancel_delayed_work_sync(&priv->join_timeout); + + bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, + bssid, NULL, 0, 0, 0); + if (!bss) { + cw1200_queue_remove(queue, priv->pending_frame_id); + wsm_unlock_tx(priv); + return; + } + ssidie = cfg80211_find_ie(WLAN_EID_SSID, + bss->information_elements, + bss->len_information_elements); + dtimie = cfg80211_find_ie(WLAN_EID_TIM, + bss->information_elements, + bss->len_information_elements); + if (dtimie) + tim = (struct ieee80211_tim_ie *)&dtimie[2]; + + mutex_lock(&priv->conf_mutex); + { + struct wsm_join join = { + .mode = (bss->capability & WLAN_CAPABILITY_IBSS) ? + WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, + .preambleType = WSM_JOIN_PREAMBLE_SHORT, + .probeForJoin = 1, + /* dtimPeriod will be updated after association */ + .dtimPeriod = 1, + .beaconInterval = bss->beacon_interval, + /* basicRateSet will be updated after association */ + .basicRateSet = 7, + }; + + /* BT Coex related changes */ + if (priv->is_BT_Present) { + if (((priv->conf_listen_interval * 100) % + bss->beacon_interval) == 0) + priv->listen_interval = + ((priv->conf_listen_interval * 100) / + bss->beacon_interval); + else + priv->listen_interval = + ((priv->conf_listen_interval * 100) / + bss->beacon_interval + 1); + } + + if (tim && tim->dtim_period > 1) { + join.dtimPeriod = tim->dtim_period; + priv->join_dtim_period = tim->dtim_period; + sta_printk(KERN_DEBUG "[STA] Join DTIM: %d\n", + join.dtimPeriod); + } + + join.channelNumber = priv->channel->hw_value; + join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + + memcpy(&join.bssid[0], bssid, sizeof(join.bssid)); + memcpy(&priv->join_bssid[0], bssid, sizeof(priv->join_bssid)); + + if (ssidie) { + join.ssidLength = ssidie[1]; + if (WARN_ON(join.ssidLength > sizeof(join.ssid))) + join.ssidLength = sizeof(join.ssid); + memcpy(&join.ssid[0], &ssidie[2], join.ssidLength); + } + + if (priv->vif->p2p) + join.flags |= WSM_JOIN_FLAGS_P2P_GO; + + wsm_flush_tx(priv); + + /* Queue unjoin if not associated in 3 sec. */ + queue_delayed_work(priv->workqueue, + &priv->join_timeout, 3 * HZ); + + cw1200_update_listening(priv, false); + if (wsm_join(priv, &join)) { + memset(&priv->join_bssid[0], + 0, sizeof(priv->join_bssid)); + cw1200_queue_remove(queue, priv->pending_frame_id); + cancel_delayed_work_sync(&priv->join_timeout); + cw1200_update_listening(priv, priv->listening); + } else { + /* Upload keys */ + WARN_ON(cw1200_upload_keys(priv)); + WARN_ON(wsm_keep_alive_period(priv, 30 /* sec */)); + cw1200_queue_requeue(queue, priv->pending_frame_id); + priv->join_status = CW1200_JOIN_STATUS_STA; + } + WARN_ON(wsm_set_block_ack_policy(priv, + priv->ba_tid_mask, priv->ba_tid_mask)); + cw1200_update_filtering(priv); + } + mutex_unlock(&priv->conf_mutex); + cfg80211_put_bss(bss); + wsm_unlock_tx(priv); +} + +void cw1200_join_timeout(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, join_timeout.work); + sta_printk(KERN_DEBUG "[WSM] Issue unjoin command (TMO).\n"); + wsm_lock_tx(priv); + cw1200_unjoin_work(&priv->unjoin_work); +} + +void cw1200_unjoin_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, unjoin_work); + + struct wsm_reset reset = { + .reset_statistics = true, + }; + + mutex_lock(&priv->conf_mutex); + if (unlikely(atomic_read(&priv->scan.in_progress))) { + if (priv->delayed_unjoin) { + wiphy_dbg(priv->hw->wiphy, + "%s: Delayed unjoin " + "is already scheduled.\n", + __func__); + wsm_unlock_tx(priv); + } else { + priv->delayed_unjoin = true; + } + mutex_unlock(&priv->conf_mutex); + return; + } + + if (priv->join_status && + priv->join_status > CW1200_JOIN_STATUS_STA) { + wiphy_err(priv->hw->wiphy, + "%s: Unexpected: join status: %d\n", + __func__, priv->join_status); + BUG_ON(1); + } + if (priv->join_status) { + memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + + /* Unjoin is a reset. */ + wsm_flush_tx(priv); + WARN_ON(wsm_reset(priv, &reset)); + priv->join_dtim_period = 0; + WARN_ON(cw1200_setup_mac(priv)); + cw1200_free_event_queue(priv); + cancel_work_sync(&priv->event_handler); + cancel_delayed_work_sync(&priv->connection_loss_work); + cw1200_update_listening(priv, priv->listening); + WARN_ON(wsm_set_block_ack_policy(priv, + priv->ba_tid_mask, priv->ba_tid_mask)); + cw1200_update_filtering(priv); + priv->setbssparams_done = false; + + sta_printk(KERN_DEBUG "[STA] Unjoin.\n"); + } + mutex_unlock(&priv->conf_mutex); + wsm_unlock_tx(priv); +} + +int cw1200_enable_listening(struct cw1200_common *priv) +{ + struct wsm_start start = { + .mode = WSM_START_MODE_P2P_DEV, + .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, + .channelNumber = priv->channel->hw_value, + .beaconInterval = 100, + .DTIMPeriod = 1, + .probeDelay = 0, + .basicRateSet = 0x0F, + }; + return wsm_start(priv, &start); +} + +int cw1200_disable_listening(struct cw1200_common *priv) +{ + int ret; + struct wsm_reset reset = { + .reset_statistics = true, + }; + ret = wsm_reset(priv, &reset); + return ret; +} + +void cw1200_update_listening(struct cw1200_common *priv, bool enabled) +{ + if (enabled) { + switch (priv->join_status) { + case CW1200_JOIN_STATUS_PASSIVE: + if (!WARN_ON(cw1200_enable_listening(priv))) + priv->join_status = CW1200_JOIN_STATUS_MONITOR; + break; + default: + break; + } + } else { + switch (priv->join_status) { + case CW1200_JOIN_STATUS_MONITOR: + if (!WARN_ON(cw1200_disable_listening(priv))) + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + default: + break; + } + } +} + +int cw1200_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg) +{ + int ret; + u16 uapsdFlags = 0; + + /* Here's the mapping AC [queue, bit] + VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]*/ + + if (arg->params[0].uapsdEnable) + uapsdFlags |= 1 << 3; + + if (arg->params[1].uapsdEnable) + uapsdFlags |= 1 << 2; + + if (arg->params[2].uapsdEnable) + uapsdFlags |= 1 << 1; + + if (arg->params[3].uapsdEnable) + uapsdFlags |= 1; + + /* Currently pseudo U-APSD operation is not supported, so setting + * MinAutoTriggerInterval, MaxAutoTriggerInterval and + * AutoTriggerStep to 0 */ + + priv->uapsd_info.uapsdFlags = cpu_to_le16(uapsdFlags); + priv->uapsd_info.minAutoTriggerInterval = 0; + priv->uapsd_info.maxAutoTriggerInterval = 0; + priv->uapsd_info.autoTriggerStep = 0; + + ret = wsm_set_uapsd_info(priv, &priv->uapsd_info); + return ret; +} + +/* ******************************************************************** */ +/* STA privates */ + +static int cw1200_cancel_scan(struct cw1200_common *priv) +{ + /* STUB(); */ + return 0; +} diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h new file mode 100644 index 00000000000..88a08aaceaf --- /dev/null +++ b/drivers/staging/cw1200/sta.h @@ -0,0 +1,84 @@ +/* + * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef STA_H_INCLUDED +#define STA_H_INCLUDED + +/* ******************************************************************** */ +/* mac80211 API */ + +int cw1200_start(struct ieee80211_hw *dev); +void cw1200_stop(struct ieee80211_hw *dev); +int cw1200_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +void cw1200_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +int cw1200_config(struct ieee80211_hw *dev, u32 changed); +void cw1200_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast); +int cw1200_conf_tx(struct ieee80211_hw *dev, u16 queue, + const struct ieee80211_tx_queue_params *params); +int cw1200_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats); +/* Not more a part of interface? +int cw1200_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats); +*/ +int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); + +int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value); + +void cw1200_flush(struct ieee80211_hw *hw, bool drop); + +u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list); + +int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); + +/* ******************************************************************** */ +/* WSM callbacks */ + +/* void cw1200_set_pm_complete_cb(struct cw1200_common *priv, + struct wsm_set_pm_complete *arg); */ +void cw1200_channel_switch_cb(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* WSM events */ + +void cw1200_free_event_queue(struct cw1200_common *priv); +void cw1200_event_handler(struct work_struct *work); +void cw1200_bss_loss_work(struct work_struct *work); +void cw1200_connection_loss_work(struct work_struct *work); +void cw1200_keep_alive_work(struct work_struct *work); +void cw1200_tx_failure_work(struct work_struct *work); + +/* ******************************************************************** */ +/* Internal API */ + +int cw1200_setup_mac(struct cw1200_common *priv); +void cw1200_join_work(struct work_struct *work); +void cw1200_join_timeout(struct work_struct *work); +void cw1200_unjoin_work(struct work_struct *work); +void cw1200_offchannel_work(struct work_struct *work); +void cw1200_wep_key_work(struct work_struct *work); +void cw1200_update_listening(struct cw1200_common *priv, bool enabled); +void cw1200_update_filtering(struct cw1200_common *priv); +int __cw1200_flush(struct cw1200_common *priv, bool drop); +int cw1200_enable_listening(struct cw1200_common *priv); +int cw1200_disable_listening(struct cw1200_common *priv); +int cw1200_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg); + +#endif diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c new file mode 100644 index 00000000000..30532f3ea90 --- /dev/null +++ b/drivers/staging/cw1200/txrx.c @@ -0,0 +1,1161 @@ +/* + * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <net/mac80211.h> +#include <linux/etherdevice.h> + +#include "cw1200.h" +#include "wsm.h" +#include "bh.h" +#include "ap.h" +#include "debug.h" + +#if defined(CONFIG_CW1200_TX_POLICY_DEBUG) +#define tx_policy_printk(...) printk(__VA_ARGS__) +#else +#define tx_policy_printk(...) +#endif + +#define CW1200_INVALID_RATE_ID (0xFF) + +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb); +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate); + +/* ******************************************************************** */ +/* TX queue lock / unlock */ + +static inline void cw1200_tx_queues_lock(struct cw1200_common *priv) +{ + int i; + for (i = 0; i < 4; ++i) + cw1200_queue_lock(&priv->tx_queue[i]); +} + +static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv) +{ + int i; + for (i = 0; i < 4; ++i) + cw1200_queue_unlock(&priv->tx_queue[i]); +} + +/* ******************************************************************** */ +/* TX policy cache implementation */ + +static void tx_policy_dump(struct tx_policy *policy) +{ + tx_policy_printk(KERN_DEBUG "[TX policy] " + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X" + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X" + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n", + policy->raw[0] & 0x0F, policy->raw[0] >> 4, + policy->raw[1] & 0x0F, policy->raw[1] >> 4, + policy->raw[2] & 0x0F, policy->raw[2] >> 4, + policy->raw[3] & 0x0F, policy->raw[3] >> 4, + policy->raw[4] & 0x0F, policy->raw[4] >> 4, + policy->raw[5] & 0x0F, policy->raw[5] >> 4, + policy->raw[6] & 0x0F, policy->raw[6] >> 4, + policy->raw[7] & 0x0F, policy->raw[7] >> 4, + policy->raw[8] & 0x0F, policy->raw[8] >> 4, + policy->raw[9] & 0x0F, policy->raw[9] >> 4, + policy->raw[10] & 0x0F, policy->raw[10] >> 4, + policy->raw[11] & 0x0F, policy->raw[11] >> 4, + policy->defined); +} + +static void tx_policy_build(const struct cw1200_common *priv, + /* [out] */ struct tx_policy *policy, + struct ieee80211_tx_rate *rates, size_t count) +{ + int i, j; + unsigned limit = priv->short_frame_max_tx_count; + unsigned total = 0; + BUG_ON(rates[0].idx < 0); + memset(policy, 0, sizeof(*policy)); + + /* minstrel is buggy a little bit, so distille + * incoming rates first. */ + + /* Sort rates in descending order. */ + for (i = 1; i < count; ++i) { + if (rates[i].idx < 0) { + count = i; + break; + } + if (rates[i].idx > rates[i - 1].idx) { + struct ieee80211_tx_rate tmp = rates[i - 1]; + rates[i - 1] = rates[i]; + rates[i] = tmp; + } + } + + /* Eliminate duplicates. */ + total = rates[0].count; + for (i = 0, j = 1; j < count; ++j) { + if (rates[j].idx == rates[i].idx) { + rates[i].count += rates[j].count; + } else if (rates[j].idx > rates[i].idx) { + break; + } else { + ++i; + if (i != j) + rates[i] = rates[j]; + } + total += rates[j].count; + } + count = i + 1; + + /* Re-fill policy trying to keep every requested rate and with + * respect to the global max tx retransmission count. */ + if (limit < count) + limit = count; + if (total > limit) { + for (i = 0; i < count; ++i) { + int left = count - i - 1; + if (rates[i].count > limit - left) + rates[i].count = limit - left; + limit -= rates[i].count; + } + } + policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1; + + for (i = 0; i < count; ++i) { + register unsigned rateid, off, shift, retries; + + rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value; + off = rateid >> 3; /* eq. rateid / 8 */ + shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ + + retries = rates[i].count; + if (unlikely(retries > 0x0F)) + rates[i].count = retries = 0x0F; + policy->tbl[off] |= __cpu_to_le32(retries << shift); + policy->retry_count += retries; + } + + tx_policy_printk(KERN_DEBUG "[TX policy] Policy (%d): " \ + "%d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n", + count, + rates[0].idx, rates[0].count, + rates[1].idx, rates[1].count, + rates[2].idx, rates[2].count, + rates[3].idx, rates[3].count, + rates[4].idx, rates[4].count); +} + +static inline bool tx_policy_is_equal(const struct tx_policy *wanted, + const struct tx_policy *cached) +{ + size_t count = wanted->defined >> 1; + if (wanted->defined > cached->defined) + return false; + if (count) { + if (memcmp(wanted->raw, cached->raw, count)) + return false; + } + if (wanted->defined & 1) { + if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F)) + return false; + } + return true; +} + +static int tx_policy_find(struct tx_policy_cache *cache, + const struct tx_policy *wanted) +{ + /* O(n) complexity. Not so good, but there's only 8 entries in + * the cache. + * Also lru helps to reduce search time. */ + struct tx_policy_cache_entry *it; + /* First search for policy in "used" list */ + list_for_each_entry(it, &cache->used, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + /* Then - in "free list" */ + list_for_each_entry(it, &cache->free, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + return -1; +} + +static inline void tx_policy_use(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + ++entry->policy.usage_count; + list_move(&entry->link, &cache->used); +} + +static inline int tx_policy_release(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + int ret = --entry->policy.usage_count; + if (!ret) + list_move(&entry->link, &cache->free); + return ret; +} + +/* ******************************************************************** */ +/* External TX policy cache API */ + +void tx_policy_init(struct cw1200_common *priv) +{ + struct tx_policy_cache *cache = &priv->tx_policy_cache; + int i; + + memset(cache, 0, sizeof(*cache)); + + spin_lock_init(&cache->lock); + INIT_LIST_HEAD(&cache->used); + INIT_LIST_HEAD(&cache->free); + + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) + list_add(&cache->cache[i].link, &cache->free); +} + +static int tx_policy_get(struct cw1200_common *priv, + struct ieee80211_tx_rate *rates, + size_t count, bool *renew) +{ + int idx; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + struct tx_policy wanted; + + tx_policy_build(priv, &wanted, rates, count); + + spin_lock_bh(&cache->lock); + BUG_ON(list_empty(&cache->free)); + idx = tx_policy_find(cache, &wanted); + if (idx >= 0) { + tx_policy_printk(KERN_DEBUG "[TX policy] Used TX policy: %d\n", + idx); + *renew = false; + } else { + struct tx_policy_cache_entry *entry; + *renew = true; + /* If policy is not found create a new one + * using the oldest entry in "free" list */ + entry = list_entry(cache->free.prev, + struct tx_policy_cache_entry, link); + entry->policy = wanted; + idx = entry - cache->cache; + tx_policy_printk(KERN_DEBUG "[TX policy] New TX policy: %d\n", + idx); + tx_policy_dump(&entry->policy); + } + tx_policy_use(cache, &cache->cache[idx]); + if (unlikely(list_empty(&cache->free))) { + /* Lock TX queues. */ + cw1200_tx_queues_lock(priv); + } + spin_unlock_bh(&cache->lock); + return idx; +} + +static void tx_policy_put(struct cw1200_common *priv, int idx) +{ + int usage, locked; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + + spin_lock_bh(&cache->lock); + locked = list_empty(&cache->free); + usage = tx_policy_release(cache, &cache->cache[idx]); + if (unlikely(locked) && !usage) { + /* Unlock TX queues. */ + cw1200_tx_queues_unlock(priv); + } + spin_unlock_bh(&cache->lock); +} + +/* +bool tx_policy_cache_full(struct cw1200_common *priv) +{ + bool ret; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + spin_lock_bh(&cache->lock); + ret = list_empty(&cache->free); + spin_unlock_bh(&cache->lock); + return ret; +} +*/ + +static int tx_policy_upload(struct cw1200_common *priv) +{ + struct tx_policy_cache *cache = &priv->tx_policy_cache; + int i; + struct wsm_set_tx_rate_retry_policy arg = { + .hdr = { + .numTxRatePolicies = 0, + } + }; + spin_lock_bh(&cache->lock); + + /* Upload only modified entries. */ + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) { + struct tx_policy *src = &cache->cache[i].policy; + if (src->retry_count && !src->uploaded) { + struct wsm_set_tx_rate_retry_policy_policy *dst = + &arg.tbl[arg.hdr.numTxRatePolicies]; + dst->policyIndex = i; + dst->shortRetryCount = priv->short_frame_max_tx_count; + dst->longRetryCount = priv->long_frame_max_tx_count; + + /* BIT(2) - Terminate retries when Tx rate retry policy + * finishes. + * BIT(3) - Count initial frame transmission as part of + * rate retry counting but not as a retry + * attempt */ + dst->policyFlags = BIT(2) | BIT(3); + + memcpy(dst->rateCountIndices, src->tbl, + sizeof(dst->rateCountIndices)); + src->uploaded = 1; + ++arg.hdr.numTxRatePolicies; + } + } + spin_unlock_bh(&cache->lock); + cw1200_debug_tx_cache_miss(priv); + tx_policy_printk(KERN_DEBUG "[TX policy] Upload %d policies\n", + arg.hdr.numTxRatePolicies); + return wsm_set_tx_rate_retry_policy(priv, &arg); +} + +void tx_policy_upload_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, tx_policy_upload_work); + + tx_policy_printk(KERN_DEBUG "[TX] TX policy upload.\n"); + WARN_ON(tx_policy_upload(priv)); + + wsm_unlock_tx(priv); + cw1200_tx_queues_unlock(priv); +} + +/* ******************************************************************** */ +/* cw1200 TX implementation */ + +struct cw1200_txinfo { + struct sk_buff *skb; + unsigned queue; + struct ieee80211_tx_info *tx_info; + const struct ieee80211_rate *rate; + struct ieee80211_hdr *hdr; + size_t hdrlen; + const u8 *da; + struct cw1200_sta_priv *sta_priv; + struct cw1200_txpriv txpriv; +}; + +u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates) +{ + u32 ret = 0; + int i; + for (i = 0; i < 32; ++i) { + if (rates & BIT(i)) + ret |= BIT(priv->rates[i].hw_value); + } + return ret; +} + +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate) +{ + if (rate->idx < 0) + return NULL; + if (rate->flags & IEEE80211_TX_RC_MCS) + return &priv->mcs_rates[rate->idx]; + return &priv->hw->wiphy->bands[priv->channel->band]-> + bitrates[rate->idx]; +} + +static int +cw1200_tx_h_calc_link_ids(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + + if (likely(t->tx_info->control.sta && t->sta_priv->link_id)) + t->txpriv.raw_link_id = + t->txpriv.link_id = + t->sta_priv->link_id; + else if (priv->mode != NL80211_IFTYPE_AP) + t->txpriv.raw_link_id = + t->txpriv.link_id = 0; + else if (is_multicast_ether_addr(t->da)) { + if (priv->enable_beacon) { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM; + } else { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = 0; + } + } else { + t->txpriv.link_id = cw1200_find_link_id(priv, t->da); + if (!t->txpriv.link_id) + t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da); + if (!t->txpriv.link_id) { + wiphy_err(priv->hw->wiphy, + "%s: No more link IDs available.\n", + __func__); + return -ENOENT; + } + t->txpriv.raw_link_id = t->txpriv.link_id; + } + if (t->txpriv.raw_link_id) + priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp = + jiffies; + +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + if (t->tx_info->control.sta && + (t->tx_info->control.sta->uapsd_queues & BIT(t->queue))) + t->txpriv.link_id = CW1200_LINK_ID_UAPSD; +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ + return 0; +} + +static void +cw1200_tx_h_pm(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (unlikely(ieee80211_is_auth(t->hdr->frame_control))) { + u32 mask = ~BIT(t->txpriv.raw_link_id); + spin_lock_bh(&priv->ps_state_lock); + priv->sta_asleep_mask &= mask; + priv->pspoll_mask &= mask; + spin_unlock_bh(&priv->ps_state_lock); + } +} + +static void +cw1200_tx_h_calc_tid(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (ieee80211_is_data_qos(t->hdr->frame_control)) { + u8 *qos = ieee80211_get_qos_ctl(t->hdr); + t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; + } else if (ieee80211_is_data(t->hdr->frame_control)) { + t->txpriv.tid = 0; + } +} + +/* IV/ICV injection. */ +/* TODO: Quite unoptimal. It's better co modify mac80211 + * to reserve space for IV */ +static int +cw1200_tx_h_crypt(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + size_t iv_len; + size_t icv_len; + u8 *icv; + u8 *newhdr; + + if (!t->tx_info->control.hw_key || + !(t->hdr->frame_control & + __cpu_to_le32(IEEE80211_FCTL_PROTECTED))) + return 0; + + iv_len = t->tx_info->control.hw_key->iv_len; + icv_len = t->tx_info->control.hw_key->icv_len; + + if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) + icv_len += 8; /* MIC */ + + if ((skb_headroom(t->skb) + skb_tailroom(t->skb) < + iv_len + icv_len + WSM_TX_EXTRA_HEADROOM) || + (skb_headroom(t->skb) < + iv_len + WSM_TX_EXTRA_HEADROOM)) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated for crypto headers.\n" + "headroom: %d, tailroom: %d, " + "req_headroom: %d, req_tailroom: %d\n" + "Please fix it in cw1200_get_skb().\n", + skb_headroom(t->skb), skb_tailroom(t->skb), + iv_len + WSM_TX_EXTRA_HEADROOM, icv_len); + return -ENOMEM; + } else if (skb_tailroom(t->skb) < icv_len) { + size_t offset = icv_len - skb_tailroom(t->skb); + u8 *p; + wiphy_warn(priv->hw->wiphy, + "Slowpath: tailroom is not big enough. " + "Req: %d, got: %d.\n", + icv_len, skb_tailroom(t->skb)); + + p = skb_push(t->skb, offset); + memmove(p, &p[offset], t->skb->len - offset); + skb_trim(t->skb, t->skb->len - offset); + } + + newhdr = skb_push(t->skb, iv_len); + memmove(newhdr, newhdr + iv_len, t->hdrlen); + t->hdr = (struct ieee80211_hdr *) newhdr; + t->hdrlen += iv_len; + icv = skb_put(t->skb, icv_len); + + return 0; +} + +static int +cw1200_tx_h_align(struct cw1200_common *priv, + struct cw1200_txinfo *t, + u8 *flags) +{ + size_t offset = (size_t)t->skb->data & 3; + + if (!offset) + return 0; + + if (offset & 1) { + wiphy_err(priv->hw->wiphy, + "Bug: attempt to transmit a frame " + "with wrong alignment: %d\n", + offset); + return -EINVAL; + } + + if (skb_headroom(t->skb) < offset) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated " + "for DMA alignment.\n" + "headroom: %d\n", + skb_headroom(t->skb)); + return -ENOMEM; + } + skb_push(t->skb, offset); + t->hdrlen += offset; + t->txpriv.offset += offset; + *flags |= WSM_TX_2BYTES_SHIFT; + cw1200_debug_tx_align(priv); + return 0; +} + +static int +cw1200_tx_h_action(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + struct ieee80211_mgmt *mgmt = + (struct ieee80211_mgmt *)t->hdr; + if (ieee80211_is_action(t->hdr->frame_control) && + mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + else + return 0; +} + +/* Add WSM header */ +static struct wsm_tx * +cw1200_tx_h_wsm(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + struct wsm_tx *wsm; + + if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated " + "for WSM header.\n" + "headroom: %d\n", + skb_headroom(t->skb)); + return NULL; + } + + wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx)); + t->txpriv.offset += sizeof(struct wsm_tx); + memset(wsm, 0, sizeof(*wsm)); + wsm->hdr.len = __cpu_to_le16(t->skb->len); + wsm->hdr.id = __cpu_to_le16(0x0004); + wsm->queueId = wsm_queue_id_to_wsm(t->queue); + return wsm; +} + +/* BT Coex specific handling */ +static void +cw1200_tx_h_bt(struct cw1200_common *priv, + struct cw1200_txinfo *t, + struct wsm_tx *wsm) +{ + u8 priority = 0; + + if (!priv->is_BT_Present) + return; + + if (unlikely(ieee80211_is_nullfunc(t->hdr->frame_control))) + priority = WSM_EPTA_PRIORITY_MGT; + else if (ieee80211_is_data(t->hdr->frame_control)) { + /* Skip LLC SNAP header (+6) */ + u8 *payload = &t->skb->data[t->hdrlen]; + u16 *ethertype = (u16 *) &payload[6]; + if (unlikely(*ethertype == __be16_to_cpu(ETH_P_PAE))) + priority = WSM_EPTA_PRIORITY_EAPOL; + } else if (unlikely(ieee80211_is_assoc_req(t->hdr->frame_control) || + ieee80211_is_reassoc_req(t->hdr->frame_control))) { + struct ieee80211_mgmt *mgt_frame = + (struct ieee80211_mgmt *)t->hdr; + + if (mgt_frame->u.assoc_req.listen_interval < + priv->listen_interval) { + txrx_printk(KERN_DEBUG + "Modified Listen Interval to %d from %d\n", + priv->listen_interval, + mgt_frame->u.assoc_req.listen_interval); + /* Replace listen interval derieved from + * the one read from SDD */ + mgt_frame->u.assoc_req.listen_interval = + priv->listen_interval; + } + } + + if (likely(!priority)) { + if (ieee80211_is_action(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_ACTION; + else if (ieee80211_is_mgmt(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_MGT; + else if ((wsm->queueId == WSM_QUEUE_VOICE)) + priority = WSM_EPTA_PRIORITY_VOICE; + else if ((wsm->queueId == WSM_QUEUE_VIDEO)) + priority = WSM_EPTA_PRIORITY_VIDEO; + else + priority = WSM_EPTA_PRIORITY_DATA; + } + + txrx_printk(KERN_DEBUG "[TX] EPTA priority %d.\n", + priority); + + wsm->flags |= priority << 1; +} + +static void +cw1200_tx_h_rate_policy(struct cw1200_common *priv, + struct cw1200_txinfo *t, + struct wsm_tx *wsm) +{ + bool tx_policy_renew = false; + + wsm->maxTxRate = t->rate->hw_value; + if (t->rate->flags & IEEE80211_TX_RC_MCS) { + if (cw1200_ht_greenfield(&priv->ht_info)) + wsm->htTxParameters |= + __cpu_to_le32(WSM_HT_TX_GREENFIELD); + else + wsm->htTxParameters |= + __cpu_to_le32(WSM_HT_TX_MIXED); + } + + t->txpriv.rate_id = tx_policy_get(priv, + t->tx_info->control.rates, IEEE80211_TX_MAX_RATES, + &tx_policy_renew); + wsm->flags |= t->txpriv.rate_id << 4; + + if (tx_policy_renew) { + tx_policy_printk(KERN_DEBUG "[TX] TX policy renew.\n"); + /* It's not so optimal to stop TX queues every now and then. + * Maybe it's better to reimplement task scheduling with + * a counter. */ + /* cw1200_tx_queues_lock(priv); */ + /* Definetly better. TODO. */ + wsm_lock_tx_async(priv); + cw1200_tx_queues_lock(priv); + queue_work(priv->workqueue, &priv->tx_policy_upload_work); + } +} + +static bool +cw1200_tx_h_pm_state(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + int was_buffered = 1; + + if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM && + !priv->buffered_multicasts) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_start_work); + } + + if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID) + was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1] + .buffered[t->txpriv.tid]++; + + return !was_buffered; +} + +/* ******************************************************************** */ + +void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct cw1200_common *priv = dev->priv; + struct cw1200_txinfo t = { + .skb = skb, + .queue = skb_get_queue_mapping(skb), + .tx_info = IEEE80211_SKB_CB(skb), + .hdr = (struct ieee80211_hdr *)skb->data, + .txpriv.tid = CW1200_MAX_TID, + .txpriv.rate_id = CW1200_INVALID_RATE_ID, + }; + struct wsm_tx *wsm; + bool tid_update = 0; + u8 flags = 0; + int ret; + + t.rate = cw1200_get_tx_rate(priv, + &t.tx_info->control.rates[0]), + t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control); + t.da = ieee80211_get_DA(t.hdr); + t.sta_priv = + (struct cw1200_sta_priv *)&t.tx_info->control.sta->drv_priv; + + if (WARN_ON(t.queue >= 4 || !t.rate)) + goto drop; + + ret = cw1200_tx_h_calc_link_ids(priv, &t); + if (ret) + goto drop; + + txrx_printk(KERN_DEBUG "[TX] TX %d bytes " + "(queue: %d, link_id: %d (%d)).\n", + skb->len, t.queue, t.txpriv.link_id, + t.txpriv.raw_link_id); + + cw1200_tx_h_pm(priv, &t); + cw1200_tx_h_calc_tid(priv, &t); + ret = cw1200_tx_h_crypt(priv, &t); + if (ret) + goto drop; + ret = cw1200_tx_h_align(priv, &t, &flags); + if (ret) + goto drop; + ret = cw1200_tx_h_action(priv, &t); + if (ret) + goto drop; + wsm = cw1200_tx_h_wsm(priv, &t); + if (!wsm) { + ret = -ENOMEM; + goto drop; + } + wsm->flags |= flags; + cw1200_tx_h_bt(priv, &t, wsm); + cw1200_tx_h_rate_policy(priv, &t, wsm); + + spin_lock_bh(&priv->ps_state_lock); + { + tid_update = cw1200_tx_h_pm_state(priv, &t); + BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue], + t.skb, &t.txpriv)); + } + spin_unlock_bh(&priv->ps_state_lock); + +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + if (tid_update) + ieee80211_sta_set_buffered(t.tx_info->control.sta, + t.txpriv.tid, true); +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ + + cw1200_bh_wakeup(priv); + + return; + +drop: + cw1200_skb_dtor(priv, skb, &t.txpriv); + return; +} + +/* ******************************************************************** */ + +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + + /* Filter block ACK negotiation: fully controlled by firmware */ + if (mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + + return 0; +} + +static int cw1200_handle_pspoll(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_sta *sta; + struct ieee80211_pspoll *pspoll = + (struct ieee80211_pspoll *) skb->data; + int link_id = 0; + u32 pspoll_mask = 0; + int drop = 1; + int i; + + if (priv->join_status != CW1200_JOIN_STATUS_AP) + goto done; + if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN)) + goto done; + + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, pspoll->ta); + if (sta) { + struct cw1200_sta_priv *sta_priv; + sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; + link_id = sta_priv->link_id; + pspoll_mask = BIT(sta_priv->link_id); + } + rcu_read_unlock(); + if (!link_id) + goto done; + + priv->pspoll_mask |= pspoll_mask; + drop = 0; + + /* Do not report pspols if data for given link id is + * queued already. */ + for (i = 0; i < 4; ++i) { + if (cw1200_queue_get_num_queued( + &priv->tx_queue[i], + pspoll_mask)) { + cw1200_bh_wakeup(priv); + drop = 1; + break; + } + } + txrx_printk(KERN_DEBUG "[RX] PSPOLL: %s\n", drop ? "local" : "fwd"); +done: + return drop; +} + +/* ******************************************************************** */ + +void cw1200_tx_confirm_cb(struct cw1200_common *priv, + struct wsm_tx_confirm *arg) +{ + u8 queue_id = cw1200_queue_get_queue_id(arg->packetID); + struct cw1200_queue *queue = &priv->tx_queue[queue_id]; + struct sk_buff *skb; + const struct cw1200_txpriv *txpriv; + + txrx_printk(KERN_DEBUG "[TX] TX confirm: %d, %d.\n", + arg->status, arg->ackFailures); + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + return; + } + + if (WARN_ON(queue_id >= 4)) + return; + + if (arg->status) + txrx_printk(KERN_DEBUG "TX failed: %d.\n", + arg->status); + + if ((arg->status == WSM_REQUEUE) && + (arg->flags & WSM_TX_STATUS_REQUEUE)) { + /* "Requeue" means "implicit suspend" */ + struct wsm_suspend_resume suspend = { + .link_id = arg->link_id, + .stop = 1, + .multicast = !arg->link_id, + }; + cw1200_suspend_resume(priv, &suspend); + wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d)." + " STAs asleep: 0x%.8X\n", + arg->link_id, + cw1200_queue_get_generation(arg->packetID) + 1, + priv->sta_asleep_mask); + WARN_ON(cw1200_queue_requeue(queue, + arg->packetID)); + } else if (!WARN_ON(cw1200_queue_get_skb( + queue, arg->packetID, &skb, &txpriv))) { + struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); + int tx_count = arg->ackFailures; + u8 ht_flags = 0; + int i; + + if (cw1200_ht_greenfield(&priv->ht_info)) + ht_flags |= IEEE80211_TX_RC_GREEN_FIELD; + + if (likely(!arg->status)) { + tx->flags |= IEEE80211_TX_STAT_ACK; + priv->cqm_tx_failure_count = 0; + ++tx_count; + cw1200_debug_txed(priv); + if (arg->flags & WSM_TX_STATUS_AGGREGATION) { + /* Do not report aggregation to mac80211: + * it confuses minstrel a lot. */ + /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ + cw1200_debug_txed_agg(priv); + } + } else { + /* TODO: Update TX failure counters */ + if (unlikely(priv->cqm_tx_failure_thold && + (++priv->cqm_tx_failure_count > + priv->cqm_tx_failure_thold))) { + priv->cqm_tx_failure_thold = 0; + queue_work(priv->workqueue, + &priv->tx_failure_work); + } + if (tx_count) + ++tx_count; + } + + for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { + if (tx->status.rates[i].count >= tx_count) { + tx->status.rates[i].count = tx_count; + break; + } + tx_count -= tx->status.rates[i].count; + if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS) + tx->status.rates[i].flags |= ht_flags; + } + + for (++i; i < IEEE80211_TX_MAX_RATES; ++i) { + tx->status.rates[i].count = 0; + tx->status.rates[i].idx = -1; + } + + cw1200_queue_remove(queue, arg->packetID); + } +} + +static void cw1200_notify_buffered_tx(struct cw1200_common *priv, + struct sk_buff *skb, int link_id, int tid) +{ +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + u8 *buffered; + u8 still_buffered = 0; + + if (link_id && tid < CW1200_MAX_TID) { + buffered = priv->link_id_db + [link_id - 1].buffered; + + spin_lock_bh(&priv->ps_state_lock); + if (!WARN_ON(!buffered[tid])) + still_buffered = --buffered[tid]; + spin_unlock_bh(&priv->ps_state_lock); + + if (!still_buffered && tid < CW1200_MAX_TID) { + hdr = (struct ieee80211_hdr *) skb->data; + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, hdr->addr1); + if (sta) + ieee80211_sta_set_buffered(sta, tid, false); + rcu_read_unlock(); + } + } +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ +} + +void cw1200_skb_dtor(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv) +{ + skb_pull(skb, txpriv->offset); + if (txpriv->rate_id != CW1200_INVALID_RATE_ID) { + cw1200_notify_buffered_tx(priv, skb, + txpriv->raw_link_id, txpriv->tid); + tx_policy_put(priv, txpriv->rate_id); + } + ieee80211_tx_status(priv->hw, skb); +} + +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + struct sk_buff **skb_p) +{ + struct sk_buff *skb = *skb_p; + struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; + struct cw1200_link_entry *entry = NULL; + unsigned long grace_period; + bool early_data = false; + hdr->flag = 0; + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + goto drop; + } + + if (arg->link_id && arg->link_id <= CW1200_MAX_STA_IN_AP_MODE) { + entry = &priv->link_id_db[arg->link_id - 1]; + if (entry->status == CW1200_LINK_SOFT && + ieee80211_is_data(frame->frame_control)) + early_data = true; + entry->timestamp = jiffies; + } + + if (unlikely(arg->status)) { + if (arg->status == WSM_STATUS_MICFAILURE) { + txrx_printk(KERN_DEBUG "[RX] MIC failure.\n"); + hdr->flag |= RX_FLAG_MMIC_ERROR; + } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { + txrx_printk(KERN_DEBUG "[RX] No key found.\n"); + goto drop; + } else { + txrx_printk(KERN_DEBUG "[RX] Receive failure: %d.\n", + arg->status); + goto drop; + } + } + + if (skb->len < sizeof(struct ieee80211_pspoll)) { + wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. " + "Size is lesser than IEEE header.\n"); + goto drop; + } + + if (unlikely(ieee80211_is_pspoll(frame->frame_control))) + if (cw1200_handle_pspoll(priv, skb)) + goto drop; + + hdr->mactime = 0; /* Not supported by WSM */ + hdr->band = (arg->channelNumber > 14) ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + hdr->freq = ieee80211_channel_to_frequency( + arg->channelNumber, + hdr->band); + + if (arg->rxedRate >= 14) { + hdr->flag |= RX_FLAG_HT; + hdr->rate_idx = arg->rxedRate - 14; + } else if (arg->rxedRate >= 4) { + hdr->rate_idx = arg->rxedRate - 2; + } else { + hdr->rate_idx = arg->rxedRate; + } + + hdr->signal = (s8)arg->rcpiRssi; + hdr->antenna = 0; + + if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + size_t iv_len = 0, icv_len = 0; + size_t hdrlen = ieee80211_hdrlen(frame->frame_control); + + hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; + + /* Oops... There is no fast way to ask mac80211 about + * IV/ICV lengths. Even defineas are not exposed.*/ + switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + case WSM_RX_STATUS_WEP: + iv_len = 4 /* WEP_IV_LEN */; + icv_len = 4 /* WEP_ICV_LEN */; + break; + case WSM_RX_STATUS_TKIP: + iv_len = 8 /* TKIP_IV_LEN */; + icv_len = 4 /* TKIP_ICV_LEN */ + + 8 /*MICHAEL_MIC_LEN*/; + hdr->flag |= RX_FLAG_MMIC_STRIPPED; + break; + case WSM_RX_STATUS_AES: + iv_len = 8 /* CCMP_HDR_LEN */; + icv_len = 8 /* CCMP_MIC_LEN */; + break; + case WSM_RX_STATUS_WAPI: + iv_len = 18 /* WAPI_HDR_LEN */; + icv_len = 16 /* WAPI_MIC_LEN */; + break; + default: + WARN_ON("Unknown encryption type"); + goto drop; + } + + if (skb->len < hdrlen + iv_len + icv_len) { + wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. " + "Size is lesser than crypto headers.\n"); + goto drop; + } + + /* Remove IV, ICV and MIC */ + skb_trim(skb, skb->len - icv_len); + memmove(skb->data + iv_len, skb->data, hdrlen); + skb_pull(skb, iv_len); + } + + cw1200_debug_rxed(priv); + if (arg->flags & WSM_RX_STATUS_AGGREGATE) + cw1200_debug_rxed_agg(priv); + + if (ieee80211_is_action(frame->frame_control) && + (arg->flags & WSM_RX_STATUS_ADDRESS1)) + if (cw1200_handle_action_rx(priv, skb)) + return; + + /* Stay awake for 1sec. after frame is received to give + * userspace chance to react and acquire appropriate + * wakelock. */ + if (ieee80211_is_auth(frame->frame_control)) + grace_period = 5 * HZ; + else + grace_period = 1 * HZ; + cw1200_pm_stay_awake(&priv->pm_state, grace_period); + + if (unlikely(early_data)) { + spin_lock_bh(&priv->ps_state_lock); + /* Double-check status with lock held */ + if (entry->status == CW1200_LINK_SOFT) + skb_queue_tail(&entry->rx_queue, skb); + else + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + } else { + ieee80211_rx_irqsafe(priv->hw, skb); + } + *skb_p = NULL; + + return; + +drop: + /* TODO: update failure counters */ + return; +} + +/* ******************************************************************** */ +/* Security */ + +int cw1200_alloc_key(struct cw1200_common *priv) +{ + int idx; + + idx = ffs(~priv->key_map) - 1; + if (idx < 0 || idx > WSM_KEY_MAX_INDEX) + return -1; + + priv->key_map |= BIT(idx); + priv->keys[idx].entryIndex = idx; + return idx; +} + +void cw1200_free_key(struct cw1200_common *priv, int idx) +{ + BUG_ON(!(priv->key_map & BIT(idx))); + memset(&priv->keys[idx], 0, sizeof(priv->keys[idx])); + priv->key_map &= ~BIT(idx); +} + +void cw1200_free_keys(struct cw1200_common *priv) +{ + memset(&priv->keys, 0, sizeof(priv->keys)); + priv->key_map = 0; +} + +int cw1200_upload_keys(struct cw1200_common *priv) +{ + int idx, ret = 0; + for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx) + if (priv->key_map & BIT(idx)) { + ret = wsm_add_key(priv, &priv->keys[idx]); + if (ret < 0) + break; + } + return ret; +} diff --git a/drivers/staging/cw1200/txrx.h b/drivers/staging/cw1200/txrx.h new file mode 100644 index 00000000000..9f4f40ea31c --- /dev/null +++ b/drivers/staging/cw1200/txrx.h @@ -0,0 +1,89 @@ +/* + * Datapath interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_TXRX_H +#define CW1200_TXRX_H + +#include <linux/list.h> + +/* extern */ struct ieee80211_hw; +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct wsm_rx; +/* extern */ struct wsm_tx_confirm; +/* extern */ struct cw1200_txpriv; + +struct tx_policy { + union { + __le32 tbl[3]; + u8 raw[12]; + }; + u8 defined; /* TODO: u32 or u8, profile and select best */ + u8 usage_count; /* --// -- */ + u8 retry_count; /* --// -- */ + u8 uploaded; +}; + +struct tx_policy_cache_entry { + struct tx_policy policy; + struct list_head link; +}; + +#define TX_POLICY_CACHE_SIZE (8) +struct tx_policy_cache { + struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE]; + struct list_head used; + struct list_head free; + spinlock_t lock; +}; + +/* ******************************************************************** */ +/* TX policy cache */ +/* Intention of TX policy cache is an overcomplicated WSM API. + * Device does not accept per-PDU tx retry sequence. + * It uses "tx retry policy id" instead, so driver code has to sync + * linux tx retry sequences with a retry policy table in the device. + */ +void tx_policy_init(struct cw1200_common *priv); +void tx_policy_upload_work(struct work_struct *work); + +/* ******************************************************************** */ +/* TX implementation */ + +u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, + u32 rates); +void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb); +void cw1200_skb_dtor(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv); + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_tx_confirm_cb(struct cw1200_common *priv, + struct wsm_tx_confirm *arg); +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* Timeout */ + +void cw1200_tx_timeout(struct work_struct *work); + +/* ******************************************************************** */ +/* Security */ +int cw1200_alloc_key(struct cw1200_common *priv); +void cw1200_free_key(struct cw1200_common *priv, int idx); +void cw1200_free_keys(struct cw1200_common *priv); +int cw1200_upload_keys(struct cw1200_common *priv); + +#endif /* CW1200_TXRX_H */ diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c new file mode 100644 index 00000000000..a8c49494e49 --- /dev/null +++ b/drivers/staging/cw1200/wsm.c @@ -0,0 +1,1703 @@ +/* + * WSM host interface (HI) implementation for + * ST-Ericsson CW1200 mac80211 drivers. + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/skbuff.h> +#include <linux/wait.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/sched.h> + +#include "cw1200.h" +#include "wsm.h" +#include "bh.h" +#include "debug.h" + +#if defined(CONFIG_CW1200_WSM_DEBUG) +#define wsm_printk(...) printk(__VA_ARGS__) +#else +#define wsm_printk(...) +#endif + +#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ +#define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */ +#define WSM_CMD_START_TIMEOUT (7 * HZ) +#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ +#define WSM_CMD_LAST_CHANCE_TIMEOUT (10 * HZ) + +#define WSM_SKIP(buf, size) \ + do { \ + if (unlikely((buf)->data + size > (buf)->end)) \ + goto underflow; \ + (buf)->data += size; \ + } while (0) + +#define WSM_GET(buf, ptr, size) \ + do { \ + if (unlikely((buf)->data + size > (buf)->end)) \ + goto underflow; \ + memcpy(ptr, (buf)->data, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_GET(buf, type, cvt) \ + ({ \ + type val; \ + if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ + goto underflow; \ + val = cvt(*(type *)(buf)->data); \ + (buf)->data += sizeof(type); \ + val; \ + }) + +#define WSM_GET8(buf) __WSM_GET(buf, u8, (u8)) +#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu) +#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu) + +#define WSM_PUT(buf, ptr, size) \ + do { \ + if (unlikely((buf)->data + size > (buf)->end)) \ + if (unlikely(wsm_buf_reserve((buf), size))) \ + goto nomem; \ + memcpy((buf)->data, ptr, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_PUT(buf, val, type, cvt) \ + do { \ + if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ + if (unlikely(wsm_buf_reserve((buf), sizeof(type)))) \ + goto nomem; \ + *(type *)(buf)->data = cvt(val); \ + (buf)->data += sizeof(type); \ + } while (0) + +#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8)) +#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16) +#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32) + +static void wsm_buf_reset(struct wsm_buf *buf); +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); + +static int wsm_cmd_send(struct cw1200_common *priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo); + +static inline void wsm_cmd_lock(struct cw1200_common *priv) +{ + mutex_lock(&priv->wsm_cmd_mux); +} + +static inline void wsm_cmd_unlock(struct cw1200_common *priv) +{ + mutex_unlock(&priv->wsm_cmd_mux); +} + +/* ******************************************************************** */ +/* WSM API implementation */ + +static int wsm_generic_confirm(struct cw1200_common *priv, + void *arg, + struct wsm_buf *buf) +{ + u32 status = WSM_GET32(buf); + if (status != WSM_STATUS_SUCCESS) + return -EINVAL; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); + WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); + WSM_PUT32(buf, arg->dot11RtsThreshold); + + /* DPD block. */ + WSM_PUT16(buf, arg->dpdData_size + 12); + WSM_PUT16(buf, 1); /* DPD version */ + WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); + WSM_PUT16(buf, 5); /* DPD flags */ + WSM_PUT(buf, arg->dpdData, arg->dpdData_size); + + ret = wsm_cmd_send(priv, buf, arg, 0x0009, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_configuration_confirm(struct cw1200_common *priv, + struct wsm_configuration *arg, + struct wsm_buf *buf) +{ + int i; + int status; + + status = WSM_GET32(buf); + if (WARN_ON(status != WSM_STATUS_SUCCESS)) + return -EINVAL; + + WSM_GET(buf, arg->dot11StationId, ETH_ALEN); + arg->dot11FrequencyBandsSupported = WSM_GET8(buf); + WSM_SKIP(buf, 1); + arg->supportedRateMask = WSM_GET32(buf); + for (i = 0; i < 2; ++i) { + arg->txPowerRange[i].min_power_level = WSM_GET32(buf); + arg->txPowerRange[i].max_power_level = WSM_GET32(buf); + arg->txPowerRange[i].stepping = WSM_GET32(buf); + } + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u16 cmd = 0x000A | WSM_TX_LINK_ID(arg->link_id); + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); + ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +struct wsm_mib { + u16 mibId; + void *buf; + size_t buf_size; +}; + +int wsm_read_mib(struct cw1200_common *priv, u16 mibId, void *_buf, + size_t buf_size) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mibId = mibId, + .buf = _buf, + .buf_size = buf_size, + }; + wsm_cmd_lock(priv); + + WSM_PUT16(buf, mibId); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, &mib_buf, 0x0005, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_read_mib_confirm(struct cw1200_common *priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + u16 size; + if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) + return -EINVAL; + + if (WARN_ON(WSM_GET16(buf) != arg->mibId)) + return -EINVAL; + + size = WSM_GET16(buf); + if (size > arg->buf_size) + size = arg->buf_size; + + WSM_GET(buf, arg->buf, size); + arg->buf_size = size; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_write_mib(struct cw1200_common *priv, u16 mibId, void *_buf, + size_t buf_size) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mibId = mibId, + .buf = _buf, + .buf_size = buf_size, + }; + + wsm_cmd_lock(priv); + + WSM_PUT16(buf, mibId); + WSM_PUT16(buf, buf_size); + WSM_PUT(buf, _buf, buf_size); + + ret = wsm_cmd_send(priv, buf, &mib_buf, 0x0006, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_write_mib_confirm(struct cw1200_common *priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + int ret; + + ret = wsm_generic_confirm(priv, arg, buf); + if (ret) + return ret; + + if (arg->mibId == 0x1006) { + /* OperationalMode: update PM status. */ + const char *p = arg->buf; + cw1200_enable_powersave(priv, + (p[0] & 0x0F) ? true : false); + } + return 0; +} + +/* ******************************************************************** */ + +int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg) +{ + int i; + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + if (unlikely(arg->numOfChannels > 48)) + return -EINVAL; + + if (unlikely(arg->numOfSSIDs > 2)) + return -EINVAL; + + if (unlikely(arg->band > 1)) + return -EINVAL; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->band); + WSM_PUT8(buf, arg->scanType); + WSM_PUT8(buf, arg->scanFlags); + WSM_PUT8(buf, arg->maxTransmitRate); + WSM_PUT32(buf, arg->autoScanInterval); + WSM_PUT8(buf, arg->numOfProbeRequests); + WSM_PUT8(buf, arg->numOfChannels); + WSM_PUT8(buf, arg->numOfSSIDs); + WSM_PUT8(buf, arg->probeDelay); + + for (i = 0; i < arg->numOfChannels; ++i) { + WSM_PUT16(buf, arg->ch[i].number); + WSM_PUT16(buf, 0); + WSM_PUT32(buf, arg->ch[i].minChannelTime); + WSM_PUT32(buf, arg->ch[i].maxChannelTime); + WSM_PUT32(buf, 0); + } + + for (i = 0; i < arg->numOfSSIDs; ++i) { + WSM_PUT32(buf, arg->ssids[i].length); + WSM_PUT(buf, &arg->ssids[i].ssid[0], + sizeof(arg->ssids[i].ssid)); + } + + ret = wsm_cmd_send(priv, buf, NULL, 0x0007, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_stop_scan(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x0008, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + + +static int wsm_tx_confirm(struct cw1200_common *priv, + struct wsm_buf *buf, + int link_id) +{ + struct wsm_tx_confirm tx_confirm; + + tx_confirm.packetID = WSM_GET32(buf); + tx_confirm.status = WSM_GET32(buf); + tx_confirm.txedRate = WSM_GET8(buf); + tx_confirm.ackFailures = WSM_GET8(buf); + tx_confirm.flags = WSM_GET16(buf); + tx_confirm.mediaDelay = WSM_GET32(buf); + tx_confirm.txQueueDelay = WSM_GET32(buf); + tx_confirm.link_id = link_id; + + if (priv->wsm_cbc.tx_confirm) + priv->wsm_cbc.tx_confirm(priv, &tx_confirm); + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_multi_tx_confirm(struct cw1200_common *priv, + struct wsm_buf *buf, int link_id) +{ + int ret; + int count; + int i; + + count = WSM_GET32(buf); + if (WARN_ON(count <= 0)) + return -EINVAL; + else if (count > 1) { + ret = wsm_release_tx_buffer(priv, count - 1); + if (ret < 0) + return ret; + else if (ret > 0) + cw1200_bh_wakeup(priv); + } + + cw1200_debug_txed_multi(priv, count); + for (i = 0; i < count; ++i) { + ret = wsm_tx_confirm(priv, buf, link_id); + if (ret) + return ret; + } + return ret; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +static int wsm_join_confirm(struct cw1200_common *priv, + struct wsm_join *arg, + struct wsm_buf *buf) +{ + if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) + return -EINVAL; + + arg->minPowerLevel = WSM_GET32(buf); + arg->maxPowerLevel = WSM_GET32(buf); + + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +int wsm_join(struct cw1200_common *priv, struct wsm_join *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channelNumber); + WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); + WSM_PUT16(buf, arg->atimWindow); + WSM_PUT8(buf, arg->preambleType); + WSM_PUT8(buf, arg->probeForJoin); + WSM_PUT8(buf, arg->dtimPeriod); + WSM_PUT8(buf, arg->flags); + WSM_PUT32(buf, arg->ssidLength); + WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); + WSM_PUT32(buf, arg->beaconInterval); + WSM_PUT32(buf, arg->basicRateSet); + + ret = wsm_cmd_send(priv, buf, arg, 0x000B, WSM_CMD_JOIN_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_bss_params(struct cw1200_common *priv, + const struct wsm_set_bss_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, 0); + WSM_PUT8(buf, arg->beaconLostCount); + WSM_PUT16(buf, arg->aid); + WSM_PUT32(buf, arg->operationalRateSet); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0011, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT(buf, arg, sizeof(*arg)); + + ret = wsm_cmd_send(priv, buf, NULL, 0x000C, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->entryIndex); + WSM_PUT8(buf, 0); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, 0x000D, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_tx_queue_params(struct cw1200_common *priv, + const struct wsm_set_tx_queue_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + /* TODO: verify me. */ + WSM_PUT8(buf, arg->queueId); + WSM_PUT8(buf, 0); + WSM_PUT8(buf, arg->ackPolicy); + WSM_PUT8(buf, 0); + WSM_PUT32(buf, arg->maxTransmitLifetime); + WSM_PUT16(buf, arg->allowedMediumTime); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_edca_params(struct cw1200_common *priv, + const struct wsm_edca_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + /* TODO: verify me. */ + /* Implemented according to specification. Note that there is a + * mismatch in BK and BE mapping. */ + + WSM_PUT16(buf, arg->params[1].cwMin); + WSM_PUT16(buf, arg->params[0].cwMin); + WSM_PUT16(buf, arg->params[2].cwMin); + WSM_PUT16(buf, arg->params[3].cwMin); + + WSM_PUT16(buf, arg->params[1].cwMax); + WSM_PUT16(buf, arg->params[0].cwMax); + WSM_PUT16(buf, arg->params[2].cwMax); + WSM_PUT16(buf, arg->params[3].cwMax); + + WSM_PUT8(buf, arg->params[1].aifns); + WSM_PUT8(buf, arg->params[0].aifns); + WSM_PUT8(buf, arg->params[2].aifns); + WSM_PUT8(buf, arg->params[3].aifns); + + WSM_PUT16(buf, arg->params[1].txOpLimit); + WSM_PUT16(buf, arg->params[0].txOpLimit); + WSM_PUT16(buf, arg->params[2].txOpLimit); + WSM_PUT16(buf, arg->params[3].txOpLimit); + + WSM_PUT32(buf, arg->params[1].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[0].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[2].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[3].maxReceiveLifetime); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0013, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_switch_channel(struct cw1200_common *priv, + const struct wsm_switch_channel *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_lock_tx(priv); + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->channelMode); + WSM_PUT8(buf, arg->channelSwitchCount); + WSM_PUT16(buf, arg->newChannelNumber); + + priv->channel_switch_in_progress = 1; + + ret = wsm_cmd_send(priv, buf, NULL, 0x0016, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + if (ret) { + wsm_unlock_tx(priv); + priv->channel_switch_in_progress = 0; + } + return ret; + +nomem: + wsm_cmd_unlock(priv); + wsm_unlock_tx(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->pmMode); + WSM_PUT8(buf, arg->fastPsmIdlePeriod); + WSM_PUT8(buf, arg->apPsmChangePeriod); + WSM_PUT8(buf, arg->minAutoPsPollPeriod); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0010, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channelNumber); + WSM_PUT32(buf, arg->CTWindow); + WSM_PUT32(buf, arg->beaconInterval); + WSM_PUT8(buf, arg->DTIMPeriod); + WSM_PUT8(buf, arg->preambleType); + WSM_PUT8(buf, arg->probeDelay); + WSM_PUT8(buf, arg->ssidLength); + WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); + WSM_PUT32(buf, arg->basicRateSet); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0017, WSM_CMD_START_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_beacon_transmit(struct cw1200_common *priv, + const struct wsm_beacon_transmit *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->enableBeaconing ? 1 : 0); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0018, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start_find(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_stop_find(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id); + + wsm_cmd_lock(priv); + + WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_update_ie(struct cw1200_common *priv, + const struct wsm_update_ie *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT16(buf, arg->what); + WSM_PUT16(buf, arg->count); + WSM_PUT(buf, arg->ies, arg->length); + + ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; + +} + +/* ******************************************************************** */ +/* WSM indication events implementation */ + +static int wsm_startup_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + u16 status; + char fw_label[129]; + static const char * const fw_types[] = { + "ETF", + "WFM", + "WSM", + "HI test", + "Platform test" + }; + + priv->wsm_caps.numInpChBufs = WSM_GET16(buf); + priv->wsm_caps.sizeInpChBuf = WSM_GET16(buf); + priv->wsm_caps.hardwareId = WSM_GET16(buf); + priv->wsm_caps.hardwareSubId = WSM_GET16(buf); + status = WSM_GET16(buf); + priv->wsm_caps.firmwareCap = WSM_GET16(buf); + priv->wsm_caps.firmwareType = WSM_GET16(buf); + priv->wsm_caps.firmwareApiVer = WSM_GET16(buf); + priv->wsm_caps.firmwareBuildNumber = WSM_GET16(buf); + priv->wsm_caps.firmwareVersion = WSM_GET16(buf); + WSM_GET(buf, &fw_label[0], sizeof(fw_label) - 1); + fw_label[sizeof(fw_label) - 1] = 0; /* Do not trust FW too much. */ + + if (WARN_ON(status)) + return -EINVAL; + + if (WARN_ON(priv->wsm_caps.firmwareType > 4)) + return -EINVAL; + + printk(KERN_INFO "CW1200 WSM init done.\n" + " Input buffers: %d x %d bytes\n" + " Hardware: %d.%d\n" + " %s firmware [%s], ver: %d, build: %d," + " api: %d, cap: 0x%.4X\n", + priv->wsm_caps.numInpChBufs, priv->wsm_caps.sizeInpChBuf, + priv->wsm_caps.hardwareId, priv->wsm_caps.hardwareSubId, + fw_types[priv->wsm_caps.firmwareType], + &fw_label[0], priv->wsm_caps.firmwareVersion, + priv->wsm_caps.firmwareBuildNumber, + priv->wsm_caps.firmwareApiVer, priv->wsm_caps.firmwareCap); + + priv->wsm_caps.firmwareReady = 1; + + wake_up(&priv->wsm_startup_done); + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_receive_indication(struct cw1200_common *priv, + int link_id, + struct wsm_buf *buf, + struct sk_buff **skb_p) +{ + priv->rx_timestamp = jiffies; + if (priv->wsm_cbc.rx) { + struct wsm_rx rx; + size_t hdr_len; + __le16 fctl; + + rx.status = WSM_GET32(buf); + rx.channelNumber = WSM_GET16(buf); + rx.rxedRate = WSM_GET8(buf); + rx.rcpiRssi = WSM_GET8(buf); + rx.flags = WSM_GET32(buf); + rx.link_id = link_id; + fctl = *(__le16 *)buf->data; + hdr_len = buf->data - buf->begin; + skb_pull(*skb_p, hdr_len); + if (!rx.status && unlikely(ieee80211_is_deauth(fctl))) { + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + /* Shedule unjoin work */ + wsm_printk(KERN_DEBUG \ + "[WSM] Issue unjoin command (RX).\n"); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } + } + priv->wsm_cbc.rx(priv, &rx, skb_p); + if (*skb_p) + skb_push(*skb_p, hdr_len); + } + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf) +{ + int first; + struct cw1200_wsm_event *event; + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + return 0; + } + + event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL); + + event->evt.eventId = __le32_to_cpu(WSM_GET32(buf)); + event->evt.eventData = __le32_to_cpu(WSM_GET32(buf)); + + wsm_printk(KERN_DEBUG "[WSM] Event: %d(%d)\n", + event->evt.eventId, event->evt.eventData); + + spin_lock(&priv->event_queue_lock); + first = list_empty(&priv->event_queue); + list_add_tail(&event->link, &priv->event_queue); + spin_unlock(&priv->event_queue_lock); + + if (first) + queue_work(priv->workqueue, &priv->event_handler); + + return 0; + +underflow: + kfree(event); + return -EINVAL; +} + +static int wsm_channel_switch_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + wsm_unlock_tx(priv); /* Re-enable datapath */ + WARN_ON(WSM_GET32(buf)); + + priv->channel_switch_in_progress = 0; + wake_up(&priv->channel_switch_done); + + if (priv->wsm_cbc.channel_switch) + priv->wsm_cbc.channel_switch(priv); + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_set_pm_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + return 0; +} + +static int wsm_scan_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + if (priv->wsm_cbc.scan_complete) { + struct wsm_scan_complete arg; + arg.status = WSM_GET32(buf); + arg.psm = WSM_GET8(buf); + arg.numChannels = WSM_GET8(buf); + priv->wsm_cbc.scan_complete(priv, &arg); + } + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_find_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + /* TODO: Implement me. */ + STUB(); + return 0; +} + +static int wsm_suspend_resume_indication(struct cw1200_common *priv, + int link_id, struct wsm_buf *buf) +{ + if (priv->wsm_cbc.suspend_resume) { + u32 flags; + struct wsm_suspend_resume arg; + + flags = WSM_GET32(buf); + arg.link_id = link_id; + arg.stop = !(flags & 1); + arg.multicast = !!(flags & 8); + arg.queue = (flags >> 1) & 3; + + priv->wsm_cbc.suspend_resume(priv, &arg); + } + return 0; + +underflow: + return -EINVAL; +} + + +/* ******************************************************************** */ +/* WSM TX */ + +int wsm_cmd_send(struct cw1200_common *priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo) +{ + size_t buf_len = buf->data - buf->begin; + int ret; + + if (cmd == 0x0006) /* Write MIB */ + wsm_printk(KERN_DEBUG "[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%d)\n", + cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]), + buf_len); + else + wsm_printk(KERN_DEBUG "[WSM] >>> 0x%.4X (%d)\n", cmd, buf_len); + + /* Fill HI message header */ + /* BH will add sequence number */ + ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); + ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); + + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(priv->wsm_cmd.ptr); + priv->wsm_cmd.done = 0; + priv->wsm_cmd.ptr = buf->begin; + priv->wsm_cmd.len = buf_len; + priv->wsm_cmd.arg = arg; + priv->wsm_cmd.cmd = cmd; + spin_unlock(&priv->wsm_cmd.lock); + + cw1200_bh_wakeup(priv); + + if (unlikely(priv->bh_error)) { + /* Do not wait for timeout if BH is dead. Exit immediately. */ + ret = 0; + } else { + long rx_timestamp; + /* Firmware prioritizes data traffic over control confirm. + * Loop below checks if data was RXed and increases timeout + * accordingly. */ + do { + /* It's safe to use unprotected access to + * wsm_cmd.done here */ + ret = wait_event_timeout( + priv->wsm_cmd_wq, + priv->wsm_cmd.done, tmo); + rx_timestamp = jiffies - priv->rx_timestamp; + if (unlikely(rx_timestamp < 0)) + rx_timestamp = tmo + 1; + } while (!ret && rx_timestamp <= tmo); + } + + if (unlikely(ret == 0)) { + u16 raceCheck; + + spin_lock(&priv->wsm_cmd.lock); + raceCheck = priv->wsm_cmd.cmd; + priv->wsm_cmd.arg = NULL; + priv->wsm_cmd.ptr = NULL; + spin_unlock(&priv->wsm_cmd.lock); + + /* Race condition check to make sure _confirm is not called + * after exit of _send */ + if (raceCheck == 0xFFFF) { + /* If wsm_handle_rx got stuck in _confirm we will hang + * system there. It's better than silently currupt + * stack or heap, isn't it? */ + BUG_ON(wait_event_timeout( + priv->wsm_cmd_wq, priv->wsm_cmd.done, + WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + } + ret = -ETIMEDOUT; + } else { + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(!priv->wsm_cmd.done); + ret = priv->wsm_cmd.ret; + spin_unlock(&priv->wsm_cmd.lock); + } + wsm_buf_reset(buf); + return ret; +} + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct cw1200_common *priv) +{ + wsm_cmd_lock(priv); + if (atomic_add_return(1, &priv->tx_lock) == 1) { + WARN_ON(wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); + } + wsm_cmd_unlock(priv); +} + +void wsm_lock_tx_async(struct cw1200_common *priv) +{ + if (atomic_add_return(1, &priv->tx_lock) == 1) + wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); +} + +void wsm_flush_tx(struct cw1200_common *priv) +{ + BUG_ON(!atomic_read(&priv->tx_lock)); + WARN_ON(wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); +} + +void wsm_unlock_tx(struct cw1200_common *priv) +{ + int tx_lock; + tx_lock = atomic_sub_return(1, &priv->tx_lock); + if (tx_lock < 0) { + BUG_ON(1); + } else if (tx_lock == 0) { + cw1200_bh_wakeup(priv); + wsm_printk(KERN_DEBUG "[WSM] TX is unlocked.\n"); + } +} + +/* ******************************************************************** */ +/* WSM RX */ + +int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) +{ + struct wsm_buf buf; + u32 reason; + u32 reg[18]; + char fname[48]; + size_t i; + + static const char *reason_str[] = { + "undefined instruction", + "prefetch abort", + "data abort", + "unknown error", + }; + + buf.begin = buf.data = data; + buf.end = &buf.begin[len]; + + reason = WSM_GET32(&buf); + for (i = 0; i < ARRAY_SIZE(reg); ++i) + reg[i] = WSM_GET32(&buf); + WSM_GET(&buf, fname, sizeof(fname)); + + if (reason < 4) + wiphy_err(priv->hw->wiphy, + "Firmware exception: %s.\n", + reason_str[reason]); + else + wiphy_err(priv->hw->wiphy, + "Firmware assert at %.*s, line %d\n", + sizeof(fname), fname, reg[1]); + + for (i = 0; i < 12; i += 4) + wiphy_err(priv->hw->wiphy, + "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", + i + 0, reg[i + 0], i + 1, reg[i + 1], + i + 2, reg[i + 2], i + 3, reg[i + 3]); + wiphy_err(priv->hw->wiphy, + "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", + reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); + i += 4; + wiphy_err(priv->hw->wiphy, + "CPSR: 0x%.8X, SPSR: 0x%.8X\n", + reg[i + 0], reg[i + 1]); + + print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE, + fname, sizeof(fname)); + return 0; + +underflow: + wiphy_err(priv->hw->wiphy, + "Firmware exception.\n"); + print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, + data, len); + return -EINVAL; +} + +int wsm_handle_rx(struct cw1200_common *priv, int id, + struct wsm_hdr *wsm, struct sk_buff **skb_p) +{ + int ret = 0; + struct wsm_buf wsm_buf; + int link_id = (id >> 6) & 0x0F; + + /* Strip link id. */ + id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + + wsm_buf.begin = (u8 *)&wsm[0]; + wsm_buf.data = (u8 *)&wsm[1]; + wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)]; + + wsm_printk(KERN_DEBUG "[WSM] <<< 0x%.4X (%d)\n", id, + wsm_buf.end - wsm_buf.begin); + + if (id == 0x404) { + ret = wsm_tx_confirm(priv, &wsm_buf, link_id); + } else if (id == 0x41E) { + ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id); + } else if (id & 0x0400) { + void *wsm_arg; + u16 wsm_cmd; + + /* Do not trust FW too much. Protection against repeated + * response and race condition removal (see above). */ + spin_lock(&priv->wsm_cmd.lock); + wsm_arg = priv->wsm_cmd.arg; + wsm_cmd = priv->wsm_cmd.cmd & + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + priv->wsm_cmd.cmd = 0xFFFF; + spin_unlock(&priv->wsm_cmd.lock); + + if (WARN_ON((id & ~0x0400) != wsm_cmd)) { + /* Note that any non-zero is a fatal retcode. */ + ret = -EINVAL; + goto out; + } + + switch (id) { + case 0x0409: + /* Note that wsm_arg can be NULL in case of timeout in + * wsm_cmd_send(). */ + if (likely(wsm_arg)) + ret = wsm_configuration_confirm(priv, wsm_arg, + &wsm_buf); + break; + case 0x0405: + if (likely(wsm_arg)) + ret = wsm_read_mib_confirm(priv, wsm_arg, + &wsm_buf); + break; + case 0x0406: + if (likely(wsm_arg)) + ret = wsm_write_mib_confirm(priv, wsm_arg, + &wsm_buf); + break; + case 0x040B: + if (likely(wsm_arg)) + ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf); + break; + case 0x0407: /* start-scan */ + case 0x0408: /* stop-scan */ + case 0x040A: /* wsm_reset */ + case 0x040C: /* add_key */ + case 0x040D: /* remove_key */ + case 0x0410: /* wsm_set_pm */ + case 0x0411: /* set_bss_params */ + case 0x0412: /* set_tx_queue_params */ + case 0x0413: /* set_edca_params */ + case 0x0416: /* switch_channel */ + case 0x0417: /* start */ + case 0x0418: /* beacon_transmit */ + case 0x0419: /* start_find */ + case 0x041A: /* stop_find */ + case 0x041B: /* update_ie */ + case 0x041C: /* map_link */ + WARN_ON(wsm_arg != NULL); + ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf); + if (ret) + wiphy_warn(priv->hw->wiphy, + "wsm_generic_confirm " + "failed for request 0x%.4X.\n", + id & ~0x0400); + break; + default: + BUG_ON(1); + } + + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.ret = ret; + priv->wsm_cmd.done = 1; + spin_unlock(&priv->wsm_cmd.lock); + ret = 0; /* Error response from device should ne stop BH. */ + + wake_up(&priv->wsm_cmd_wq); + } else if (id & 0x0800) { + switch (id) { + case 0x0801: + ret = wsm_startup_indication(priv, &wsm_buf); + break; + case 0x0804: + ret = wsm_receive_indication(priv, link_id, + &wsm_buf, skb_p); + break; + case 0x0805: + ret = wsm_event_indication(priv, &wsm_buf); + break; + case 0x080A: + ret = wsm_channel_switch_indication(priv, &wsm_buf); + break; + case 0x0809: + ret = wsm_set_pm_indication(priv, &wsm_buf); + break; + case 0x0806: + ret = wsm_scan_complete_indication(priv, &wsm_buf); + break; + case 0x080B: + ret = wsm_find_complete_indication(priv, &wsm_buf); + break; + case 0x080C: + ret = wsm_suspend_resume_indication(priv, + link_id, &wsm_buf); + break; + default: + STUB(); + } + } else { + WARN_ON(1); + ret = -EINVAL; + } +out: + return ret; +} + +static bool wsm_handle_tx_data(struct cw1200_common *priv, + const struct wsm_tx *wsm, + const struct ieee80211_tx_info *tx_info, + const struct cw1200_txpriv *txpriv, + struct cw1200_queue *queue) +{ + bool handled = false; + const struct ieee80211_hdr *frame = + (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; + __le16 fctl = frame->frame_control; + enum { + doProbe, + doDrop, + doJoin, + doOffchannel, + doWep, + doTx, + } action = doTx; + + switch (priv->mode) { + case NL80211_IFTYPE_STATION: + if (unlikely( + (priv->join_status <= CW1200_JOIN_STATUS_MONITOR) || + memcmp(frame->addr1, priv->join_bssid, + sizeof(priv->join_bssid)))) { + if (ieee80211_is_auth(fctl)) + action = doJoin; + else if (ieee80211_is_probe_req(fctl)) + action = doTx; + else if (priv->join_status >= + CW1200_JOIN_STATUS_MONITOR) + action = doTx; + else + action = doOffchannel; + } + break; + case NL80211_IFTYPE_AP: + if (unlikely(!priv->join_status)) + action = doDrop; + else if (unlikely(!(BIT(txpriv->raw_link_id) & + (BIT(0) | priv->link_id_map)))) { + wiphy_warn(priv->hw->wiphy, + "A frame with expired link id " + "is dropped.\n"); + action = doDrop; + } + if (cw1200_queue_get_generation(wsm->packetID) > + CW1200_MAX_REQUEUE_ATTEMPTS) { + /* HACK!!! WSM324 firmware has tendency to requeue + * multicast frames in a loop, causing performance + * drop and high power consumption of the driver. + * In this situation it is better just to drop + * the problematic frame. */ + wiphy_warn(priv->hw->wiphy, + "Too many attempts " + "to requeue a frame. " + "Frame is dropped.\n"); + action = doDrop; + } + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + STUB(); + case NL80211_IFTYPE_MONITOR: + default: + action = doDrop; + break; + } + + if (action == doTx) { + if (unlikely(ieee80211_is_probe_req(fctl))) + action = doProbe; + else if ((fctl & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) && + tx_info->control.hw_key && + unlikely(tx_info->control.hw_key->keyidx != + priv->wep_default_key_id) && + (tx_info->control.hw_key->cipher == + WLAN_CIPHER_SUITE_WEP40 || + tx_info->control.hw_key->cipher == + WLAN_CIPHER_SUITE_WEP104)) + action = doWep; + } + + switch (action) { + case doProbe: + { + /* An interesting FW "feature". Device filters + * probe responses. + * The easiest way to get it back is to convert + * probe request into WSM start_scan command. */ + wsm_printk(KERN_DEBUG \ + "[WSM] Convert probe request to scan.\n"); + wsm_lock_tx_async(priv); + priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + queue_delayed_work(priv->workqueue, + &priv->scan.probe_work, 0); + handled = true; + } + break; + case doDrop: + { + /* See detailed description of "join" below. + * We are dropping everything except AUTH in non-joined mode. */ + wsm_printk(KERN_DEBUG "[WSM] Drop frame (0x%.4X).\n", fctl); + BUG_ON(cw1200_queue_remove(queue, + __le32_to_cpu(wsm->packetID))); + handled = true; + } + break; + case doJoin: + { + /* There is one more interesting "feature" + * in FW: it can't do RX/TX before "join". + * "Join" here is not an association, + * but just a syncronization between AP and STA. + * priv->join_status is used only in bh thread and does + * not require protection */ + wsm_printk(KERN_DEBUG "[WSM] Issue join command.\n"); + wsm_lock_tx_async(priv); + priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + if (queue_work(priv->workqueue, &priv->join_work) <= 0) + wsm_unlock_tx(priv); + handled = true; + } + break; + case doOffchannel: + { + wsm_printk(KERN_DEBUG "[WSM] Offchannel TX request.\n"); + wsm_lock_tx_async(priv); + priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + if (queue_work(priv->workqueue, &priv->offchannel_work) <= 0) + wsm_unlock_tx(priv); + handled = true; + } + break; + case doWep: + { + wsm_printk(KERN_DEBUG "[WSM] Issue set_default_wep_key.\n"); + wsm_lock_tx_async(priv); + priv->wep_default_key_id = tx_info->control.hw_key->keyidx; + priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0) + wsm_unlock_tx(priv); + handled = true; + } + break; + case doTx: + { +#if 0 + /* Kept for history. If you want to implement wsm->more, + * make sure you are able to send a frame after that. */ + wsm->more = (count > 1) ? 1 : 0; + if (wsm->more) { + /* HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * It's undocumented in WSM spec, but CW1200 hangs + * if 'more' is set and no TX is performed due to TX + * buffers limitation. */ + if (priv->hw_bufs_used + 1 == + priv->wsm_caps.numInpChBufs) + wsm->more = 0; + } + + /* BUG!!! FIXME: we can't use 'more' at all: we don't know + * future. It could be a request from upper layer with TX lock + * requirements (scan, for example). If "more" is set device + * will not send data and wsm_tx_lock() will fail... + * It's not obvious how to fix this deadlock. Any ideas? + * As a workaround more is set to 0. */ + wsm->more = 0; +#endif /* 0 */ + + if (ieee80211_is_deauth(fctl) && + priv->mode != NL80211_IFTYPE_AP) { + /* Shedule unjoin work */ + wsm_printk(KERN_DEBUG "[WSM] Issue unjoin command" + " (TX).\n"); +#if 0 + wsm->more = 0; +#endif /* 0 */ + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } + } + break; + } + return handled; +} + +static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, + struct cw1200_queue **queue_p, + u32 *tx_allowed_mask_p, + bool *more) +{ + int i; + struct cw1200_queue *queue = NULL; + u32 tx_allowed_mask; + int mcasts = 0; + + /* Search for a queue with multicast frames buffered */ + if (priv->tx_multicast) { + tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM); + for (i = 0; i < 4; ++i) { + mcasts += cw1200_queue_get_num_queued( + &priv->tx_queue[i], tx_allowed_mask); + if (!queue && mcasts) + queue = &priv->tx_queue[i]; + if (mcasts > 1) + break; + } + if (mcasts) + goto found; + } + + /* Search for unicast traffic */ + for (i = 0; i < 4; ++i) { + queue = &priv->tx_queue[i]; + tx_allowed_mask = ~priv->sta_asleep_mask; + tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD); + if (priv->sta_asleep_mask) { + tx_allowed_mask |= priv->pspoll_mask; + tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); + } else { + tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); + } + if (cw1200_queue_get_num_queued( + queue, tx_allowed_mask)) + goto found; + } + return -ENOENT; + +found: + *queue_p = queue; + *tx_allowed_mask_p = tx_allowed_mask; + *more = mcasts > 1; + return 0; +} + +int wsm_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len) +{ + struct wsm_tx *wsm = NULL; + struct ieee80211_tx_info *tx_info; + struct cw1200_queue *queue = NULL; + u32 tx_allowed_mask = 0; + const struct cw1200_txpriv *txpriv = NULL; + /* + * Count was intended as an input for wsm->more flag. + * During implementation it was found that wsm->more + * is not usable, see details above. It is kept just + * in case you would like to try to implement it again. + */ + int count = 0; + + /* More is used only for broadcasts. */ + bool more = false; + + if (priv->wsm_cmd.ptr) { + ++count; + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(!priv->wsm_cmd.ptr); + *data = priv->wsm_cmd.ptr; + *tx_len = priv->wsm_cmd.len; + spin_unlock(&priv->wsm_cmd.lock); + } else { + for (;;) { + int ret; + + if (atomic_add_return(0, &priv->tx_lock)) + break; + + spin_lock_bh(&priv->ps_state_lock); + + ret = wsm_get_tx_queue_and_mask(priv, &queue, + &tx_allowed_mask, &more); + + if (priv->buffered_multicasts && + (ret || !more) && + (priv->tx_multicast || + !priv->sta_asleep_mask)) { + priv->buffered_multicasts = false; + if (priv->tx_multicast) + queue_work(priv->workqueue, + &priv->multicast_stop_work); + } + + spin_unlock_bh(&priv->ps_state_lock); + + if (ret) + break; + + if (cw1200_queue_get(queue, + tx_allowed_mask, + &wsm, &tx_info, &txpriv)) + continue; + + if (wsm_handle_tx_data(priv, wsm, + tx_info, txpriv, queue)) + continue; /* Handled by WSM */ + + wsm->hdr.id &= __cpu_to_le16( + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); + wsm->hdr.id |= cpu_to_le16( + WSM_TX_LINK_ID(txpriv->raw_link_id)); + priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); + + *data = (u8 *)wsm; + *tx_len = __le16_to_cpu(wsm->hdr.len); + + + if (more) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + /* more buffered multicast/broadcast frames + * ==> set MoreData flag in IEEE 802.11 header + * to inform PS STAs */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + + wsm_printk(KERN_DEBUG "[WSM] >>> 0x%.4X (%d) %p %c\n", + 0x0004, *tx_len, *data, + wsm->more ? 'M' : ' '); + ++count; + break; + } + } + + return count; +} + +void wsm_txed(struct cw1200_common *priv, u8 *data) +{ + if (data == priv->wsm_cmd.ptr) { + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.ptr = NULL; + spin_unlock(&priv->wsm_cmd.lock); + } +} + +/* ******************************************************************** */ +/* WSM buffer */ + +void wsm_buf_init(struct wsm_buf *buf) +{ + BUG_ON(buf->begin); + buf->begin = kmalloc(SDIO_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); + buf->end = buf->begin ? &buf->begin[SDIO_BLOCK_SIZE] : buf->begin; + wsm_buf_reset(buf); +} + +void wsm_buf_deinit(struct wsm_buf *buf) +{ + kfree(buf->begin); + buf->begin = buf->data = buf->end = NULL; +} + +static void wsm_buf_reset(struct wsm_buf *buf) +{ + if (buf->begin) { + buf->data = &buf->begin[4]; + *(u32 *)buf->begin = 0; + } else + buf->data = buf->begin; +} + +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) +{ + size_t pos = buf->data - buf->begin; + size_t size = pos + extra_size; + + + if (size & (SDIO_BLOCK_SIZE - 1)) { + size &= SDIO_BLOCK_SIZE; + size += SDIO_BLOCK_SIZE; + } + + buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA); + if (buf->begin) { + buf->data = &buf->begin[pos]; + buf->end = &buf->begin[size]; + return 0; + } else { + buf->end = buf->data = buf->begin; + return -ENOMEM; + } +} + + diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h new file mode 100644 index 00000000000..6698d771396 --- /dev/null +++ b/drivers/staging/cw1200/wsm.h @@ -0,0 +1,1727 @@ +/* + * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> + * + * Based on CW1200 UMAC WSM API, which is + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stewart Mathers <stewart.mathers@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_WSM_H_INCLUDED +#define CW1200_WSM_H_INCLUDED + +#include <linux/spinlock.h> + +struct cw1200_common; + +/* Bands */ +/* Radio band 2.412 -2.484 GHz. */ +#define WSM_PHY_BAND_2_4G (0) + +/* Radio band 4.9375-5.8250 GHz. */ +#define WSM_PHY_BAND_5G (1) + +/* Transmit rates */ +/* 1 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_1 (0) + +/* 2 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_2 (1) + +/* 5.5 Mbps ERP-CCK, ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_5 (2) */ + +/* 11 Mbps ERP-CCK, ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_11 (3) */ + +/* 22 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_22 (4) */ + +/* 33 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_33 (5) */ + +/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_6 (6) + +/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_9 (7) + +/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_12 (8) + +/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_18 (9) + +/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_24 (10) + +/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_36 (11) + +/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_48 (12) + +/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_54 (13) + +/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_6 (14) + +/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_13 (15) + +/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_19 (16) + +/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_26 (17) + +/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_39 (18) + +/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */ +#define WSM_TRANSMIT_RATE_HT_52 (19) + +/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_58 (20) + +/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */ +#define WSM_TRANSMIT_RATE_HT_65 (21) + +/* Scan types */ +/* Foreground scan */ +#define WSM_SCAN_TYPE_FOREGROUND (0) + +/* Background scan */ +#define WSM_SCAN_TYPE_BACKGROUND (1) + +/* Auto scan */ +#define WSM_SCAN_TYPE_AUTO (2) + +/* Scan flags */ +/* Forced background scan means if the station cannot */ +/* enter the power-save mode, it shall force to perform a */ +/* background scan. Only valid when ScanType is */ +/* background scan. */ +#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0)) + +/* The WLAN device scans one channel at a time so */ +/* that disturbance to the data traffic is minimized. */ +#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1)) + +/* Preamble Type. Long if not set. */ +#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2)) + +/* 11n Tx Mode. Mixed if not set. */ +#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3)) + +/* Scan constraints */ +/* Maximum number of channels to be scanned. */ +#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48) + +/* The maximum number of SSIDs that the device can scan for. */ +#define WSM_SCAN_MAX_NUM_OF_SSIDS (2) + +/* Power management modes */ +/* 802.11 Active mode */ +#define WSM_PSM_ACTIVE (0) + +/* 802.11 PS mode */ +#define WSM_PSM_PS BIT(0) + +/* Fast Power Save bit */ +#define WSM_PSM_FAST_PS_FLAG BIT(7) + +/* Dynamic aka Fast power save */ +#define WSM_PSM_FAST_PS (BIT(0) | BIT(7)) + +/* Undetermined */ +/* Note : Undetermined status is reported when the */ +/* NULL data frame used to advertise the PM mode to */ +/* the AP at Pre or Post Background Scan is not Acknowledged */ +#define WSM_PSM_UNKNOWN BIT(1) + +/* Queue IDs */ +/* best effort/legacy */ +#define WSM_QUEUE_BEST_EFFORT (0) + +/* background */ +#define WSM_QUEUE_BACKGROUND (1) + +/* video */ +#define WSM_QUEUE_VIDEO (2) + +/* voice */ +#define WSM_QUEUE_VOICE (3) + +/* HT TX parameters */ +/* Non-HT */ +#define WSM_HT_TX_NON_HT (0) + +/* Mixed format */ +#define WSM_HT_TX_MIXED (1) + +/* Greenfield format */ +#define WSM_HT_TX_GREENFIELD (2) + +/* STBC allowed */ +#define WSM_HT_TX_STBC (BIT(7)) + +/* EPTA prioirty flags for BT Coex */ +/* default epta priority */ +#define WSM_EPTA_PRIORITY_DEFAULT 4 +/* use for normal data */ +#define WSM_EPTA_PRIORITY_DATA 4 +/* use for connect/disconnect/roaming*/ +#define WSM_EPTA_PRIORITY_MGT 5 +/* use for action frames */ +#define WSM_EPTA_PRIORITY_ACTION 5 +/* use for AC_VI data */ +#define WSM_EPTA_PRIORITY_VIDEO 5 +/* use for AC_VO data */ +#define WSM_EPTA_PRIORITY_VOICE 6 +/* use for EAPOL exchange */ +#define WSM_EPTA_PRIORITY_EAPOL 7 + +/* TX status */ +/* Frame was sent aggregated */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_AGGREGATION (BIT(0)) + +/* Host should requeue this frame later. */ +/* Valid only when status is WSM_REQUEUE. */ +#define WSM_TX_STATUS_REQUEUE (BIT(1)) + +/* Normal Ack */ +#define WSM_TX_STATUS_NORMAL_ACK (0<<2) + +/* No Ack */ +#define WSM_TX_STATUS_NO_ACK (1<<2) + +/* No explicit acknowledgement */ +#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2) + +/* Block Ack */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_BLOCK_ACK (3<<2) + +/* RX status */ +/* Unencrypted */ +#define WSM_RX_STATUS_UNENCRYPTED (0<<0) + +/* WEP */ +#define WSM_RX_STATUS_WEP (1<<0) + +/* TKIP */ +#define WSM_RX_STATUS_TKIP (2<<0) + +/* AES */ +#define WSM_RX_STATUS_AES (3<<0) + +/* WAPI */ +#define WSM_RX_STATUS_WAPI (4<<0) + +/* Macro to fetch encryption subfield. */ +#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07) + +/* Frame was part of an aggregation */ +#define WSM_RX_STATUS_AGGREGATE (BIT(3)) + +/* Frame was first in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4)) + +/* Frame was last in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5)) + +/* Indicates a defragmented frame */ +#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6)) + +/* Indicates a Beacon frame */ +#define WSM_RX_STATUS_BEACON (BIT(7)) + +/* Indicates STA bit beacon TIM field */ +#define WSM_RX_STATUS_TIM (BIT(8)) + +/* Indicates Beacon frame's virtual bitmap contains multicast bit */ +#define WSM_RX_STATUS_MULTICAST (BIT(9)) + +/* Indicates frame contains a matching SSID */ +#define WSM_RX_STATUS_MATCHING_SSID (BIT(10)) + +/* Indicates frame contains a matching BSSI */ +#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11)) + +/* Indicates More bit set in Framectl field */ +#define WSM_RX_STATUS_MORE_DATA (BIT(12)) + +/* Indicates frame received during a measurement process */ +#define WSM_RX_STATUS_MEASUREMENT (BIT(13)) + +/* Indicates frame received as an HT packet */ +#define WSM_RX_STATUS_HT (BIT(14)) + +/* Indicates frame received with STBC */ +#define WSM_RX_STATUS_STBC (BIT(15)) + +/* Indicates Address 1 field matches dot11StationId */ +#define WSM_RX_STATUS_ADDRESS1 (BIT(16)) + +/* Indicates Group address present in the Address 1 field */ +#define WSM_RX_STATUS_GROUP (BIT(17)) + +/* Indicates Broadcast address present in the Address 1 field */ +#define WSM_RX_STATUS_BROADCAST (BIT(18)) + +/* Indicates group key used with encrypted frames */ +#define WSM_RX_STATUS_GROUP_KEY (BIT(19)) + +/* Macro to fetch encryption key index. */ +#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F) + +/* Frame Control field starts at Frame offset + 2 */ +#define WSM_TX_2BYTES_SHIFT (BIT(7)) + +/* Join mode */ +/* IBSS */ +#define WSM_JOIN_MODE_IBSS (0) + +/* BSS */ +#define WSM_JOIN_MODE_BSS (1) + +/* PLCP preamble type */ +/* For long preamble */ +#define WSM_JOIN_PREAMBLE_LONG (0) + +/* For short preamble (Long for 1Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT (1) + +/* For short preamble (Long for 1 and 2Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT_2 (2) + +/* Join flags */ +/* Unsynchronized */ +#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0) +/* The BSS owner is a P2P GO */ +#define WSM_JOIN_FLAGS_P2P_GO BIT(1) +/* Force to join BSS with the BSSID and the + * SSID specified without waiting for beacons. The + * ProbeForJoin parameter is ignored. */ +#define WSM_JOIN_FLAGS_FORCE BIT(2) +/* Give probe request/response higher + * priority over the BT traffic */ +#define WSM_JOIN_FLAGS_PRIO BIT(3) + +/* Key types */ +#define WSM_KEY_TYPE_WEP_DEFAULT (0) +#define WSM_KEY_TYPE_WEP_PAIRWISE (1) +#define WSM_KEY_TYPE_TKIP_GROUP (2) +#define WSM_KEY_TYPE_TKIP_PAIRWISE (3) +#define WSM_KEY_TYPE_AES_GROUP (4) +#define WSM_KEY_TYPE_AES_PAIRWISE (5) +#define WSM_KEY_TYPE_WAPI_GROUP (6) +#define WSM_KEY_TYPE_WAPI_PAIRWISE (7) + +/* Key indexes */ +#define WSM_KEY_MAX_INDEX (10) + +/* ACK policy */ +#define WSM_ACK_POLICY_NORMAL (0) +#define WSM_ACK_POLICY_NO_ACK (1) + +/* Start modes */ +#define WSM_START_MODE_AP (0) /* Mini AP */ +#define WSM_START_MODE_P2P_GO (1) /* P2P GO */ +#define WSM_START_MODE_P2P_DEV (2) /* P2P device */ + +/* SetAssociationMode MIB flags */ +#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0)) +#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1)) +#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2)) +#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3)) +#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4)) + +/* RcpiRssiThreshold MIB flags */ +#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0)) +#define WSM_RCPI_RSSI_USE_RSSI (BIT(1)) +#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2)) +#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3)) + +/* Update-ie constants */ +#define WSM_UPDATE_IE_BEACON (BIT(0)) +#define WSM_UPDATE_IE_PROBE_RESP (BIT(1)) +#define WSM_UPDATE_IE_PROBE_REQ (BIT(2)) + +/* WSM events */ +/* Error */ +#define WSM_EVENT_ERROR (0) + +/* BSS lost */ +#define WSM_EVENT_BSS_LOST (1) + +/* BSS regained */ +#define WSM_EVENT_BSS_REGAINED (2) + +/* Radar detected */ +#define WSM_EVENT_RADAR_DETECTED (3) + +/* RCPI or RSSI threshold triggered */ +#define WSM_EVENT_RCPI_RSSI (4) + +/* BT inactive */ +#define WSM_EVENT_BT_INACTIVE (5) + +/* BT active */ +#define WSM_EVENT_BT_ACTIVE (6) + +/* MIB IDs */ +/* 4.1 dot11StationId */ +#define WSM_MIB_ID_DOT11_STATION_ID 0x0000 + +/* 4.2 dot11MaxtransmitMsduLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001 + +/* 4.3 dot11MaxReceiveLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002 + +/* 4.4 dot11SlotTime */ +#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003 + +/* 4.5 dot11GroupAddressesTable */ +#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004 +#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8 + +/* 4.6 dot11WepDefaultKeyId */ +#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005 + +/* 4.7 dot11CurrentTxPowerLevel */ +#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006 + +/* 4.8 dot11RTSThreshold */ +#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007 + +/* 4.9 NonErpProtection */ +#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000 + +/* 4.10 ArpIpAddressesTable */ +#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001 +#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1 + +/* 4.11 TemplateFrame */ +#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002 + +/* 4.12 RxFilter */ +#define WSM_MIB_ID_RX_FILTER 0x1003 + +/* 4.13 BeaconFilterTable */ +#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004 + +/* 4.14 BeaconFilterEnable */ +#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005 + +/* 4.15 OperationalPowerMode */ +#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006 + +/* 4.16 BeaconWakeUpPeriod */ +#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007 + +/* 4.17 RcpiRssiThreshold */ +#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009 + +/* 4.18 StatisticsTable */ +#define WSM_MIB_ID_STATISTICS_TABLE 0x100A + +/* 4.19 IbssPsConfig */ +#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B + +/* 4.20 CountersTable */ +#define WSM_MIB_ID_COUNTERS_TABLE 0x100C + +/* 4.21 BlockAckPolicy */ +#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E + +/* 4.22 OverrideInternalTxRate */ +#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F + +/* 4.23 SetAssociationMode */ +#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010 + +/* 4.24 UpdateEptaConfigData */ +#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011 + +/* 4.25 SelectCcaMethod */ +#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012 + +/* 4.26 SetUpasdInformation */ +#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013 + +/* 4.27 SetAutoCalibrationMode WBF00004073 */ +#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015 + +/* 4.28 SetTxRateRetryPolicy */ +#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016 + +/* 4.29 SetHostMessageTypeFilter */ +#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017 + +/* 4.30 P2PFindInfo */ +#define WSM_MIB_ID_P2P_FIND_INFO 0x1018 + +/* 4.31 P2PPsModeInfo */ +#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019 + +/* 4.32 SetEtherTypeDataFrameFilter */ +#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A + +/* 4.33 SetUDPPortDataFrameFilter */ +#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B + +/* 4.34 SetMagicDataFrameFilter */ +#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C + +/* This is the end of specification. */ + +/* 4.35 P2PDeviceInfo */ +#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D + +/* 4.36 SetWCDMABand */ +#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E + +/* 4.37 GroupTxSequenceCounter */ +#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F + +/* 1020 4.38 ProtectedMgmtPolicy */ + +/* 4.39 SetHtProtection */ +#define WSM_MID_ID_SET_HT_PROTECTION 0x1021 + +/* 4.40 GPIO Command */ +#define WSM_MIB_ID_GPIO_COMMAND 0x1022 + +/* 4.41 TSF Counter Value */ +#define WSM_MIB_ID_TSF_COUNTER 0x1023 + +/* Test Purposes Only */ +#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D + +/* 4.42 UseMultiTxConfMessage */ +#define WSM_MIB_USE_MULTI_TX_CONF 0x1024 + +/* 4.43 Keep-alive period */ +#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025 + +/* 4.44 Disable BSSID filter */ +#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026 + +/* Frame template types */ +#define WSM_FRAME_TYPE_PROBE_REQUEST (0) +#define WSM_FRAME_TYPE_BEACON (1) +#define WSM_FRAME_TYPE_NULL (2) +#define WSM_FRAME_TYPE_QOS_NULL (3) +#define WSM_FRAME_TYPE_PS_POLL (4) +#define WSM_FRAME_TYPE_PROBE_RESPONSE (5) + +#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */ + +/* Status */ +/* The WSM firmware has completed a request */ +/* successfully. */ +#define WSM_STATUS_SUCCESS (0) + +/* This is a generic failure code if other error codes do */ +/* not apply. */ +#define WSM_STATUS_FAILURE (1) + +/* A request contains one or more invalid parameters. */ +#define WSM_INVALID_PARAMETER (2) + +/* The request cannot perform because the device is in */ +/* an inappropriate mode. */ +#define WSM_ACCESS_DENIED (3) + +/* The frame received includes a decryption error. */ +#define WSM_STATUS_DECRYPTFAILURE (4) + +/* A MIC failure is detected in the received packets. */ +#define WSM_STATUS_MICFAILURE (5) + +/* The transmit request failed due to retry limit being */ +/* exceeded. */ +#define WSM_STATUS_RETRY_EXCEEDED (6) + +/* The transmit request failed due to MSDU life time */ +/* being exceeded. */ +#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7) + +/* The link to the AP is lost. */ +#define WSM_STATUS_LINK_LOST (8) + +/* No key was found for the encrypted frame */ +#define WSM_STATUS_NO_KEY_FOUND (9) + +/* Jammer was detected when transmitting this frame */ +#define WSM_STATUS_JAMMER_DETECTED (10) + +/* The message should be requeued later. */ +/* This is applicable only to Transmit */ +#define WSM_REQUEUE (11) + + +struct wsm_hdr { + __le16 len; + __le16 id; +}; + +#define WSM_TX_SEQ_MAX (7) +#define WSM_TX_SEQ(seq) \ + ((seq & WSM_TX_SEQ_MAX) << 13) +#define WSM_TX_LINK_ID_MAX (0x0F) +#define WSM_TX_LINK_ID(link_id) \ + ((link_id & WSM_TX_LINK_ID_MAX) << 6) + +/* ******************************************************************** */ +/* WSM capcbility */ + +struct wsm_caps { + u16 numInpChBufs; + u16 sizeInpChBuf; + u16 hardwareId; + u16 hardwareSubId; + u16 firmwareCap; + u16 firmwareType; + u16 firmwareApiVer; + u16 firmwareBuildNumber; + u16 firmwareVersion; + int firmwareReady; +}; + +/* ******************************************************************** */ +/* WSM commands */ + +struct wsm_tx_power_range { + int min_power_level; + int max_power_level; + u32 stepping; +}; + +/* 3.1 */ +struct wsm_configuration { + /* [in] */ u32 dot11MaxTransmitMsduLifeTime; + /* [in] */ u32 dot11MaxReceiveLifeTime; + /* [in] */ u32 dot11RtsThreshold; + /* [in, out] */ u8 *dot11StationId; + /* [in] */ const void *dpdData; + /* [in] */ size_t dpdData_size; + /* [out] */ u8 dot11FrequencyBandsSupported; + /* [out] */ u32 supportedRateMask; + /* [out] */ struct wsm_tx_power_range txPowerRange[2]; +}; + +int wsm_configuration(struct cw1200_common *priv, + struct wsm_configuration *arg); + +/* 3.3 */ +struct wsm_reset { + /* [in] */ int link_id; + /* [in] */ bool reset_statistics; +}; + +int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg); + +/* 3.5 */ +int wsm_read_mib(struct cw1200_common *priv, u16 mibId, void *buf, + size_t buf_size); + +/* 3.7 */ +int wsm_write_mib(struct cw1200_common *priv, u16 mibId, void *buf, + size_t buf_size); + +/* 3.9 */ +struct wsm_ssid { + u8 ssid[32]; + u32 length; +}; + +struct wsm_scan_ch { + u16 number; + u32 minChannelTime; + u32 maxChannelTime; + u32 txPowerLevel; +}; + +/* 3.13 */ +struct wsm_scan_complete { + /* WSM_STATUS_... */ + u32 status; + + /* WSM_PSM_... */ + u8 psm; + + /* Number of channels that the scan operation completed. */ + u8 numChannels; +}; + +typedef void (*wsm_scan_complete_cb) (struct cw1200_common *priv, + struct wsm_scan_complete *arg); + +/* 3.9 */ +struct wsm_scan { + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* WSM_SCAN_TYPE_... */ + /* [in] */ u8 scanType; + + /* WSM_SCAN_FLAG_... */ + /* [in] */ u8 scanFlags; + + /* WSM_TRANSMIT_RATE_... */ + /* [in] */ u8 maxTransmitRate; + + /* Interval period in TUs that the device shall the re- */ + /* execute the requested scan. Max value supported by the device */ + /* is 256s. */ + /* [in] */ u32 autoScanInterval; + + /* Number of probe requests (per SSID) sent to one (1) */ + /* channel. Zero (0) means that none is send, which */ + /* means that a passive scan is to be done. Value */ + /* greater than zero (0) means that an active scan is to */ + /* be done. */ + /* [in] */ u32 numOfProbeRequests; + + /* Number of channels to be scanned. */ + /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */ + /* [in] */ u8 numOfChannels; + + /* Number of SSID provided in the scan command (this */ + /* is zero (0) in broadcast scan) */ + /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */ + /* [in] */ u8 numOfSSIDs; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + /* [in] */ u8 probeDelay; + + /* SSIDs to be scanned [numOfSSIDs]; */ + /* [in] */ struct wsm_ssid *ssids; + + /* Channels to be scanned [numOfChannels]; */ + /* [in] */ struct wsm_scan_ch *ch; +}; + +int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg); + +/* 3.11 */ +int wsm_stop_scan(struct cw1200_common *priv); + +/* 3.14 */ +struct wsm_tx_confirm { + /* Packet identifier used in wsm_tx. */ + /* [out] */ u32 packetID; + + /* WSM_STATUS_... */ + /* [out] */ u32 status; + + /* WSM_TRANSMIT_RATE_... */ + /* [out] */ u8 txedRate; + + /* The number of times the frame was transmitted */ + /* without receiving an acknowledgement. */ + /* [out] */ u8 ackFailures; + + /* WSM_TX_STATUS_... */ + /* [out] */ u16 flags; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission as completed. */ + /* [out] */ u32 mediaDelay; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission was started. */ + /* [out] */ u32 txQueueDelay; + + /* [out]*/ u32 link_id; +}; + +/* 3.15 */ +typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv, + struct wsm_tx_confirm *arg); + +/* Note that ideology of wsm_tx struct is different against the rest of + * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input + * argument for WSM call, but a prepared bytestream to be sent to firmware. + * It is filled partly in cw1200_tx, partly in low-level WSM code. + * Please pay attention once again: ideology is different. + * + * Legend: + * - [in]: cw1200_tx must fill this field. + * - [wsm]: the field is filled by low-level WSM. + */ +struct wsm_tx { + /* common WSM header */ + /* [in/wsm] */ struct wsm_hdr hdr; + + /* Packet identifier that meant to be used in completion. */ + /* [in] */ __le32 packetID; + + /* WSM_TRANSMIT_RATE_... */ + /* [in] */ u8 maxTxRate; + + /* WSM_QUEUE_... */ + /* [in] */ u8 queueId; + + /* True: another packet is pending on the host for transmission. */ + /* [wsm] */ u8 more; + + /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */ + /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */ + /* Bits 3:1 - PTA Priority */ + /* Bits 6:4 - Tx Rate Retry Policy */ + /* Bit 7 - Reserved */ + /* [in] */ u8 flags; + + /* Should be 0. */ + /* [in] */ __le32 reserved; + + /* The elapsed time in TUs, after the initial transmission */ + /* of an MSDU, after which further attempts to transmit */ + /* the MSDU shall be terminated. Overrides the global */ + /* dot11MaxTransmitMsduLifeTime setting [optional] */ + /* Device will set the default value if this is 0. */ + /* [wsm] */ __le32 expireTime; + + /* WSM_HT_TX_... */ + /* [in] */ __le32 htTxParameters; +}; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */ +#define WSM_TX_EXTRA_HEADROOM (28) + +/* 3.16 */ +struct wsm_rx { + /* WSM_STATUS_... */ + /* [out] */ u32 status; + + /* Specifies the channel of the received packet. */ + /* [out] */ u16 channelNumber; + + /* WSM_TRANSMIT_RATE_... */ + /* [out] */ u8 rxedRate; + + /* This value is expressed in signed Q8.0 format for */ + /* RSSI and unsigned Q7.1 format for RCPI. */ + /* [out] */ u8 rcpiRssi; + + /* WSM_RX_STATUS_... */ + /* [out] */ u32 flags; + + /* An 802.11 frame. */ + /* [out] */ void *frame; + + /* Size of the frame */ + /* [out] */ size_t frame_size; + + /* Link ID */ + /* [out] */ int link_id; +}; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */ +#define WSM_RX_EXTRA_HEADROOM (16) + +typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg, + struct sk_buff **skb_p); + +/* 3.17 */ +struct wsm_event { + /* WSM_STATUS_... */ + /* [out] */ u32 eventId; + + /* Indication parameters. */ + /* For error indication, this shall be a 32-bit WSM status. */ + /* For RCPI or RSSI indication, this should be an 8-bit */ + /* RCPI or RSSI value. */ + /* [out] */ u32 eventData; +}; + +struct cw1200_wsm_event { + struct list_head link; + struct wsm_event evt; +}; + +/* 3.18 - 3.22 */ +/* Measurement. Skipped for now. Irrelevent. */ + +typedef void (*wsm_event_cb) (struct cw1200_common *priv, + struct wsm_event *arg); + +/* 3.23 */ +struct wsm_join { + /* WSM_JOIN_MODE_... */ + /* [in] */ u8 mode; + + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* Specifies the channel number to join. The channel */ + /* number will be mapped to an actual frequency */ + /* according to the band */ + /* [in] */ u16 channelNumber; + + /* Specifies the BSSID of the BSS or IBSS to be joined */ + /* or the IBSS to be started. */ + /* [in] */ u8 bssid[6]; + + /* ATIM window of IBSS */ + /* When ATIM window is zero the initiated IBSS does */ + /* not support power saving. */ + /* [in] */ u16 atimWindow; + + /* WSM_JOIN_PREAMBLE_... */ + /* [in] */ u8 preambleType; + + /* Specifies if a probe request should be send with the */ + /* specified SSID when joining to the network. */ + /* [in] */ u8 probeForJoin; + + /* DTIM Period (In multiples of beacon interval) */ + /* [in] */ u8 dtimPeriod; + + /* WSM_JOIN_FLAGS_... */ + /* [in] */ u8 flags; + + /* Length of the SSID */ + /* [in] */ u32 ssidLength; + + /* Specifies the SSID of the IBSS to join or start */ + /* [in] */ u8 ssid[32]; + + /* Specifies the time between TBTTs in TUs */ + /* [in] */ u32 beaconInterval; + + /* A bit mask that defines the BSS basic rate set. */ + /* [in] */ u32 basicRateSet; + + /* Minimum transmission power level in units of 0.1dBm */ + /* [out] */ int minPowerLevel; + + /* Maximum transmission power level in units of 0.1dBm */ + /* [out] */ int maxPowerLevel; +}; + +int wsm_join(struct cw1200_common *priv, struct wsm_join *arg); + +/* 3.25 */ +struct wsm_set_pm { + /* WSM_PSM_... */ + /* [in] */ u8 pmMode; + + /* in unit of 500us; 0 to use default */ + /* [in] */ u8 fastPsmIdlePeriod; + + /* in unit of 500us; 0 to use default */ + /* [in] */ u8 apPsmChangePeriod; + + /* in unit of 500us; 0 to disable auto-pspoll */ + /* [in] */ u8 minAutoPsPollPeriod; +}; + +int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); + +/* 3.27 */ +struct wsm_set_pm_complete { + u8 psm; /* WSM_PSM_... */ +}; + +typedef void (*wsm_set_pm_complete_cb) (struct cw1200_common *priv, + struct wsm_set_pm_complete *arg); + +/* 3.28 */ +struct wsm_set_bss_params { + /* The number of lost consecutive beacons after which */ + /* the WLAN device should indicate the BSS-Lost event */ + /* to the WLAN host driver. */ + u8 beaconLostCount; + + /* The AID received during the association process. */ + u16 aid; + + /* The operational rate set mask */ + u32 operationalRateSet; +}; + +int wsm_set_bss_params(struct cw1200_common *priv, + const struct wsm_set_bss_params *arg); + +/* 3.30 */ +struct wsm_add_key { + u8 type; /* WSM_KEY_TYPE_... */ + u8 entryIndex; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */ + u16 reserved; + union { + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 reserved; + u8 keyLength; /* Key length in bytes */ + u8 keyData[16]; /* Key data */ + } __packed wepPairwiseKey; + struct { + u8 keyId; /* Unique per key identifier + * (0..3) */ + u8 keyLength; /* Key length in bytes */ + u16 reserved; + u8 keyData[16]; /* Key data */ + } __packed wepGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 reserved[2]; + u8 tkipKeyData[16]; /* TKIP key data */ + u8 rxMicKey[8]; /* Rx MIC key */ + u8 txMicKey[8]; /* Tx MIC key */ + } __packed tkipPairwiseKey; + struct { + u8 tkipKeyData[16]; /* TKIP key data */ + u8 rxMicKey[8]; /* Rx MIC key */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + u8 rxSeqCounter[8]; /* Receive Sequence Counter */ + } __packed tkipGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u16 reserved; + u8 aesKeyData[16]; /* AES key data */ + } __packed aesPairwiseKey; + struct { + u8 aesKeyData[16]; /* AES key data */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + u8 rxSeqCounter[8]; /* Receive Sequence Counter */ + } __packed aesGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 keyId; /* Key ID */ + u8 reserved; + u8 wapiKeyData[16]; /* WAPI key data */ + u8 micKeyData[16]; /* MIC key data */ + } __packed wapiPairwiseKey; + struct { + u8 wapiKeyData[16]; /* WAPI key data */ + u8 micKeyData[16]; /* MIC key data */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + } __packed wapiGroupKey; + } __packed; +} __packed; + +int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg); + +/* 3.32 */ +struct wsm_remove_key { + /* Key entry index : 0-10 */ + u8 entryIndex; +}; + +int wsm_remove_key(struct cw1200_common *priv, + const struct wsm_remove_key *arg); + +/* 3.34 */ +struct wsm_set_tx_queue_params { + /* 0 best effort/legacy */ + /* 1 background */ + /* 2 video */ + /* 3 voice */ + u8 queueId; + + /* WSM_ACK_POLICY_... */ + u8 ackPolicy; + + /* Medium Time of TSPEC (in 32us units) allowed per */ + /* One Second Averaging Period for this queue. */ + u16 allowedMediumTime; + + /* dot11MaxTransmitMsduLifetime to be used for the */ + /* specified queue. */ + u32 maxTransmitLifetime; +}; + +int wsm_set_tx_queue_params(struct cw1200_common *priv, + const struct wsm_set_tx_queue_params *arg); + +/* 3.36 */ +struct wsm_edca_queue_params { + /* CWmin (in slots) for the access class. */ + /* [in] */ u16 cwMin; + + /* CWmax (in slots) for the access class. */ + /* [in] */ u16 cwMax; + + /* AIFS (in slots) for the access class. */ + /* [in] */ u8 aifns; + + /* TX OP Limit (in microseconds) for the access class. */ + /* [in] */ u16 txOpLimit; + + /* dot11MaxReceiveLifetime to be used for the specified */ + /* the access class. Overrides the global */ + /* dot11MaxReceiveLifetime value */ + /* [in] */ u32 maxReceiveLifetime; + + /* UAPSD trigger support for the access class. */ + /* [in] */ bool uapsdEnable; +}; + +struct wsm_edca_params { + /* NOTE: index is a linux queue id. */ + struct wsm_edca_queue_params params[4]; +}; + +#define WSM_EDCA_SET(edca, queue, aifs, cw_min, cw_max, txop, uapsd) \ + do { \ + struct wsm_edca_queue_params *p = &(edca)->params[queue]; \ + p->cwMin = (cw_min); \ + p->cwMax = (cw_max); \ + p->aifns = (aifs); \ + p->txOpLimit = (txop); \ + p->uapsdEnable = (uapsd); \ + } while (0) + +int wsm_set_edca_params(struct cw1200_common *priv, + const struct wsm_edca_params *arg); + +int wsm_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg); + +/* 3.38 */ +/* Set-System info. Skipped for now. Irrelevent. */ + +/* 3.40 */ +struct wsm_switch_channel { + /* 1 - means the STA shall not transmit any further */ + /* frames until the channel switch has completed */ + /* [in] */ u8 channelMode; + + /* Number of TBTTs until channel switch occurs. */ + /* 0 - indicates switch shall occur at any time */ + /* 1 - occurs immediately before the next TBTT */ + /* [in] */ u8 channelSwitchCount; + + /* The new channel number to switch to. */ + /* Note this is defined as per section 2.7. */ + /* [in] */ u16 newChannelNumber; +}; + +int wsm_switch_channel(struct cw1200_common *priv, + const struct wsm_switch_channel *arg); + +typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv); + +struct wsm_start { + /* WSM_START_MODE_... */ + /* [in] */ u8 mode; + + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* Channel number */ + /* [in] */ u16 channelNumber; + + /* Client Traffic window in units of TU */ + /* Valid only when mode == ..._P2P */ + /* [in] */ u32 CTWindow; + + /* Interval between two consecutive */ + /* beacon transmissions in TU. */ + /* [in] */ u32 beaconInterval; + + /* DTIM period in terms of beacon intervals */ + /* [in] */ u8 DTIMPeriod; + + /* WSM_JOIN_PREAMBLE_... */ + /* [in] */ u8 preambleType; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + /* [in] */ u8 probeDelay; + + /* Length of the SSID */ + /* [in] */ u8 ssidLength; + + /* SSID of the BSS or P2P_GO to be started now. */ + /* [in] */ u8 ssid[32]; + + /* The basic supported rates for the MiniAP. */ + /* [in] */ u32 basicRateSet; +}; + +int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg); + +struct wsm_beacon_transmit { + /* 1: enable; 0: disable */ + /* [in] */ u8 enableBeaconing; +}; + +int wsm_beacon_transmit(struct cw1200_common *priv, + const struct wsm_beacon_transmit *arg); + +int wsm_start_find(struct cw1200_common *priv); + +int wsm_stop_find(struct cw1200_common *priv); + +typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status); + +struct wsm_suspend_resume { + /* See 3.52 */ + /* Link ID */ + /* [out] */ int link_id; + /* Stop sending further Tx requests down to device for this link */ + /* [out] */ bool stop; + /* Transmit multicast Frames */ + /* [out] */ bool multicast; + /* The AC on which Tx to be suspended /resumed. */ + /* This is applicable only for U-APSD */ + /* WSM_QUEUE_... */ + /* [out] */ int queue; +}; + +typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv, + struct wsm_suspend_resume *arg); + +/* 3.54 Update-IE request. */ +struct wsm_update_ie { + /* WSM_UPDATE_IE_... */ + /* [in] */ u16 what; + /* [in] */ u16 count; + /* [in] */ u8 *ies; + /* [in] */ size_t length; +}; + +int wsm_update_ie(struct cw1200_common *priv, + const struct wsm_update_ie *arg); + +/* 3.56 */ +struct wsm_map_link { + /* MAC address of the remote device */ + /* [in] */ u8 mac_addr[6]; + /* [in] */ u8 link_id; +}; + +int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg); + +struct wsm_cbc { + wsm_scan_complete_cb scan_complete; + wsm_tx_confirm_cb tx_confirm; + wsm_rx_cb rx; + wsm_event_cb event; + wsm_set_pm_complete_cb set_pm_complete; + wsm_channel_switch_cb channel_switch; + wsm_find_complete_cb find_complete; + wsm_suspend_resume_cb suspend_resume; +}; + +/* ******************************************************************** */ +/* MIB shortcats */ + +static inline int wsm_set_output_power(struct cw1200_common *priv, + int power_level) +{ + __le32 val = __cpu_to_le32(power_level); + return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL, + &val, sizeof(val)); +} + +static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv, + unsigned dtim_interval, + unsigned listen_interval) +{ + struct { + u8 numBeaconPeriods; + u8 reserved; + __le16 listenInterval; + } val = { + dtim_interval, 0, __cpu_to_le16(listen_interval)}; + if (dtim_interval > 0xFF || listen_interval > 0xFFFF) + return -EINVAL; + else + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD, + &val, sizeof(val)); +} + +struct wsm_rcpi_rssi_threshold { + u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */ + u8 lowerThreshold; + u8 upperThreshold; + u8 rollingAverageCount; +}; + +static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv, + struct wsm_rcpi_rssi_threshold *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg, + sizeof(*arg)); +} + +struct wsm_counters_table { + __le32 countPlcpErrors; + __le32 countFcsErrors; + __le32 countTxPackets; + __le32 countRxPackets; + __le32 countRxPacketErrors; + __le32 countRxDecryptionFailures; + __le32 countRxMicFailures; + __le32 countRxNoKeyFailures; + __le32 countTxMulticastFrames; + __le32 countTxFramesSuccess; + __le32 countTxFrameFailures; + __le32 countTxFramesRetried; + __le32 countTxFramesMultiRetried; + __le32 countRxFrameDuplicates; + __le32 countRtsSuccess; + __le32 countRtsFailures; + __le32 countAckFailures; + __le32 countRxMulticastFrames; + __le32 countRxFramesSuccess; + __le32 countRxCMACICVErrors; + __le32 countRxCMACReplays; + __le32 countRxMgmtCCMPReplays; +}; + +static inline int wsm_get_counters_table(struct cw1200_common *priv, + struct wsm_counters_table *arg) +{ + return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE, + arg, sizeof(*arg)); +} + +static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac) +{ + return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN); +} + +struct wsm_rx_filter { + bool promiscuous; + bool bssid; + bool fcs; +}; + +static inline int wsm_set_rx_filter(struct cw1200_common *priv, + const struct wsm_rx_filter *arg) +{ + __le32 val = 0; + if (arg->promiscuous) + val |= __cpu_to_le32(BIT(0)); + if (arg->bssid) + val |= __cpu_to_le32(BIT(1)); + if (arg->fcs) + val |= __cpu_to_le32(BIT(2)); + return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val)); +} + +#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0) +#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1) +#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2) + +struct wsm_beacon_filter_table_entry { + u8 ieId; + u8 actionFlags; + u8 oui[3]; + u8 matchData[3]; +} __packed; + +struct wsm_beacon_filter_table { + __le32 numOfIEs; + struct wsm_beacon_filter_table_entry entry[10]; +} __packed; + +static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv, + struct wsm_beacon_filter_table *ft) +{ + size_t size = __le32_to_cpu(ft->numOfIEs) * + sizeof(struct wsm_beacon_filter_table_entry) + + sizeof(__le32); + + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size); +} + +struct wsm_beacon_filter_control { + int enabled; + int bcn_count; +}; + +static inline int wsm_beacon_filter_control(struct cw1200_common *priv, + struct wsm_beacon_filter_control *arg) +{ + struct { + __le32 enabled; + __le32 bcn_count; + } val; + val.enabled = __cpu_to_le32(arg->enabled); + val.bcn_count = __cpu_to_le32(arg->bcn_count); + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val, + sizeof(val)); +} + +enum wsm_power_mode { + wsm_power_mode_active = 0, + wsm_power_mode_doze = 1, + wsm_power_mode_quiescent = 2, +}; + +struct wsm_operational_mode { + enum wsm_power_mode power_mode; + int disableMoreFlagUsage; + int performAntDiversity; +}; + +static inline int wsm_set_operational_mode(struct cw1200_common *priv, + const struct wsm_operational_mode *arg) +{ + u8 val = arg->power_mode; + if (arg->disableMoreFlagUsage) + val |= BIT(4); + if (arg->performAntDiversity) + val |= BIT(5); + return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val, + sizeof(val)); +} + +struct wsm_template_frame { + u8 frame_type; + u8 rate; + struct sk_buff *skb; +}; + +static inline int wsm_set_template_frame(struct cw1200_common *priv, + struct wsm_template_frame *arg) +{ + int ret; + u8 *p = skb_push(arg->skb, 4); + p[0] = arg->frame_type; + p[1] = arg->rate; + ((u16 *) p)[1] = __cpu_to_le16(arg->skb->len - 4); + ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len); + skb_pull(arg->skb, 4); + return ret; +} + +static inline int wsm_set_block_ack_policy(struct cw1200_common *priv, + u8 blockAckTxTidPolicy, + u8 blockAckRxTidPolicy) +{ + struct { + u8 blockAckTxTidPolicy; + u8 reserved1; + u8 blockAckRxTidPolicy; + u8 reserved2; + } val = { + .blockAckTxTidPolicy = blockAckTxTidPolicy, + .blockAckRxTidPolicy = blockAckRxTidPolicy, + }; + return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val, + sizeof(val)); +} + +struct wsm_association_mode { + u8 flags; /* WSM_ASSOCIATION_MODE_... */ + u8 preambleType; /* WSM_JOIN_PREAMBLE_... */ + u8 greenfieldMode; /* 1 for greenfield */ + u8 mpduStartSpacing; + __le32 basicRateSet; +}; + +static inline int wsm_set_association_mode(struct cw1200_common *priv, + struct wsm_association_mode *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg, + sizeof(*arg)); +} + +struct wsm_set_tx_rate_retry_policy_header { + u8 numTxRatePolicies; + u8 reserved[3]; +} __packed; + +struct wsm_set_tx_rate_retry_policy_policy { + u8 policyIndex; + u8 shortRetryCount; + u8 longRetryCount; + u8 policyFlags; + u8 rateRecoveryCount; + u8 reserved[3]; + __le32 rateCountIndices[3]; +} __packed; + +struct wsm_set_tx_rate_retry_policy { + struct wsm_set_tx_rate_retry_policy_header hdr; + struct wsm_set_tx_rate_retry_policy_policy tbl[8]; +} __packed; + +static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv, + struct wsm_set_tx_rate_retry_policy *arg) +{ + size_t size = sizeof(struct wsm_set_tx_rate_retry_policy_header) + + arg->hdr.numTxRatePolicies * + sizeof(struct wsm_set_tx_rate_retry_policy_policy); + return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, + size); +} + +/* Undocumented MIBs: */ +/* 4.35 P2PDeviceInfo */ +#define D11_MAX_SSID_LEN (32) + +struct wsm_p2p_device_type { + __le16 categoryId; + u8 oui[4]; + __le16 subCategoryId; +} __packed; + +struct wsm_p2p_device_info { + struct wsm_p2p_device_type primaryDevice; + u8 reserved1[3]; + u8 devNameSize; + u8 localDevName[D11_MAX_SSID_LEN]; + u8 reserved2[3]; + u8 numSecDevSupported; + struct wsm_p2p_device_type secondaryDevices[0]; +} __packed; + +/* 4.36 SetWCDMABand - WO */ +struct wsm_cdma_band { + u8 WCDMA_Band; + u8 reserved[3]; +} __packed; + +/* 4.37 GroupTxSequenceCounter - RO */ +struct wsm_group_tx_seq { + __le32 bits_47_16; + __le16 bits_15_00; + __le16 reserved; +} __packed; + +/* 4.39 SetHtProtection - WO */ +#define WSM_DUAL_CTS_PROT_ENB (1 << 0) +#define WSM_NON_GREENFIELD_STA PRESENT(1 << 1) +#define WSM_HT_PROT_MODE__NO_PROT (0 << 2) +#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2) +#define WSM_HT_PROT_MODE__20_MHZ (2 << 2) +#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2) +#define WSM_LSIG_TXOP_PROT_FULL (1 << 4) +#define WSM_LARGE_L_LENGTH_PROT (1 << 5) + +struct wsm_ht_protection { + __le32 flags; +} __packed; + +/* 4.40 GPIO Command - R/W */ +#define WSM_GPIO_COMMAND_SETUP 0 +#define WSM_GPIO_COMMAND_READ 1 +#define WSM_GPIO_COMMAND_WRITE 2 +#define WSM_GPIO_COMMAND_RESET 3 +#define WSM_GPIO_ALL_PINS 0xFF + +struct wsm_gpio_command { + u8 GPIO_Command; + u8 pin; + __le16 config; +} __packed; + +/* 4.41 TSFCounter - RO */ +struct wsm_tsf_counter { + __le64 TSF_Counter; +} __packed; + +/* 4.43 Keep alive period */ +struct wsm_keep_alive_period { + __le16 keepAlivePeriod; + u8 reserved[2]; +} __packed; + +static inline int wsm_keep_alive_period(struct cw1200_common *priv, + int period) +{ + struct wsm_keep_alive_period arg = { + .keepAlivePeriod = __cpu_to_le16(period), + }; + return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, + &arg, sizeof(arg)); +}; + +/* BSSID filtering */ +struct wsm_set_bssid_filtering { + u8 filter; + u8 reserved[3]; +} __packed; + +static inline int wsm_set_bssid_filtering(struct cw1200_common *priv, + bool enabled) +{ + struct wsm_set_bssid_filtering arg = { + .filter = !enabled, + }; + return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER, + &arg, sizeof(arg)); +} + +/* Multicat filtering - 4.5 */ +struct wsm_multicast_filter { + __le32 enable; + __le32 numOfAddresses; + u8 macAddress[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN]; +} __packed; + +static inline int wsm_set_multicast_filter(struct cw1200_common *priv, + struct wsm_multicast_filter *fp) +{ + return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + +/* ARP IPv4 filtering - 4.10 */ +struct wsm_arp_ipv4_filter { + __le32 enable; + __be32 ipv4Address[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES]; +} __packed; + +static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv, + struct wsm_arp_ipv4_filter *fp) +{ + return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + +/* P2P Power Save Mode Info - 4.31 */ +struct wsm_p2p_ps_modeinfo { + u8 oppPsCTWindow; + u8 count; + u8 reserved; + u8 dtimCount; + __le32 duration; + __le32 interval; + __le32 startTime; +} __packed; + +static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv, + struct wsm_p2p_ps_modeinfo *mi) +{ + return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi)); +} + +/* UseMultiTxConfMessage */ + +static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv, + bool enabled) +{ + __le32 arg = enabled ? __cpu_to_le32(1) : 0; + + return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF, + &arg, sizeof(arg)); +} + + +/* 4.26 SetUpasdInformation */ +struct wsm_uapsd_info { + __le16 uapsdFlags; + __le16 minAutoTriggerInterval; + __le16 maxAutoTriggerInterval; + __le16 autoTriggerStep; +}; + +static inline int wsm_set_uapsd_info(struct cw1200_common *priv, + struct wsm_uapsd_info *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION, + arg, sizeof(*arg)); +} + +/* 4.22 OverrideInternalTxRate */ +struct wsm_override_internal_txrate { + u8 internalTxRate; + u8 nonErpInternalTxRate; + u8 reserved[2]; +} __packed; + +static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv, + struct wsm_override_internal_txrate *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + arg, sizeof(*arg)); +} + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct cw1200_common *priv); +void wsm_lock_tx_async(struct cw1200_common *priv); +void wsm_flush_tx(struct cw1200_common *priv); +void wsm_unlock_tx(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* WSM / BH API */ + +int wsm_handle_exception(struct cw1200_common *priv, u8 * data, size_t len); +int wsm_handle_rx(struct cw1200_common *priv, int id, struct wsm_hdr *wsm, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* wsm_buf API */ + +struct wsm_buf { + u8 *begin; + u8 *data; + u8 *end; +}; + +void wsm_buf_init(struct wsm_buf *buf); +void wsm_buf_deinit(struct wsm_buf *buf); + +/* ******************************************************************** */ +/* wsm_cmd API */ + +struct wsm_cmd { + spinlock_t lock; + int done; + u8 *ptr; + size_t len; + void *arg; + int ret; + u16 cmd; +}; + +/* ******************************************************************** */ +/* WSM TX buffer access */ + +int wsm_get_tx(struct cw1200_common *priv, u8 ** data, size_t * tx_len); +void wsm_txed(struct cw1200_common *priv, u8 * data); + +/* ******************************************************************** */ +/* Queue mapping: WSM <---> linux */ +/* Linux: VO VI BE BK */ +/* WSM: BE BK VI VO */ + +static inline u8 wsm_queue_id_to_linux(u8 queueId) +{ + static const u8 queue_mapping[] = { + 2, 3, 1, 0 + }; + return queue_mapping[queueId]; +} + +static inline u8 wsm_queue_id_to_wsm(u8 queueId) +{ + static const u8 queue_mapping[] = { + 3, 2, 0, 1 + }; + return queue_mapping[queueId]; +} + +#endif /* CW1200_HWIO_H_INCLUDED */ diff --git a/drivers/staging/mmio/Kconfig b/drivers/staging/mmio/Kconfig new file mode 100644 index 00000000000..d6a5a9ad918 --- /dev/null +++ b/drivers/staging/mmio/Kconfig @@ -0,0 +1,11 @@ + +config U8500_MMIO + bool "ST-Ericsson MMIO (Camera) Driver" + depends on ARCH_U8500 + help + Enables the ST-Ericsson MMIO (Camera) Driver + +config U5500_MMIO + bool "ST-Ericsson U5500 MMIO (Camera) Driver" + depends on UX500_SOC_DB5500 + diff --git a/drivers/staging/mmio/Makefile b/drivers/staging/mmio/Makefile new file mode 100644 index 00000000000..bec2a6efe63 --- /dev/null +++ b/drivers/staging/mmio/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_U8500_MMIO) := st_mmio.o diff --git a/drivers/staging/mmio/mmio.h b/drivers/staging/mmio/mmio.h new file mode 100644 index 00000000000..1c6f68e3556 --- /dev/null +++ b/drivers/staging/mmio/mmio.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Joakim Axelsson <joakim.axelsson@stericsson.com> for ST-Ericsson + * Author: Rajat Verma <rajat.verma@stericsson.com> for ST-Ericsson + * License Terms: GNU General Public License v2 + */ + +#ifndef MMIO_H +#define MMIO_H + +#include <linux/ioctl.h> + +#define MMIO_NAME "mmio_camera" +#define SRA_SUPPORT 1 + +#ifdef SRA_SUPPORT +#define SREG_16_BIT (0x1) +#define SREG_32_BIT (0x2) +#endif +/* Kernel side interface for MMIO */ +/* Which camera is currently active */ +enum camera_slot_t { + PRIMARY_CAMERA = 0, + SECONDARY_CAMERA, + CAMERA_SLOT_END +}; +struct mmio_gpio { + int gpio; /* Set to zero if not in use */ + int active_high;/* Set if pin is active high */ + int udelay; /* Time to wait when activating the pin, in usec */ +}; +enum mmio_select_i2c_t { + MMIO_ACTIVATE_IPI2C2 = 0, + MMIO_ACTIVATE_I2C_HOST, + MMIO_DEACTIVATE_I2C +}; + +enum mmio_select_xshutdown_t { + MMIO_ENABLE_XSHUTDOWN_FW = 0, + MMIO_ENABLE_XSHUTDOWN_HOST, + MMIO_DISABLE_XSHUTDOWN +}; +struct mmio_platform_data { + struct device *dev; + enum camera_slot_t camera_slot; /* Which camera is currently used, + * Primary/Secondary */ + void *extra; /* Board's private data structure + * placeholder */ + int reset_ipgpio[CAMERA_SLOT_END]; /* Contains logical IP GPIO for + * reset pin */ + int sia_base; + int cr_base; + int (*platform_init)(struct mmio_platform_data *pdata); + void (*platform_exit)(struct mmio_platform_data *pdata); + int (*power_enable)(struct mmio_platform_data *pdata); + void (*power_disable)(struct mmio_platform_data *pdata); + int (*config_xshutdown_pins)(struct mmio_platform_data *pdata, + enum mmio_select_xshutdown_t select, int is_active_high); + int (*config_i2c_pins)(struct mmio_platform_data *pdata, + enum mmio_select_i2c_t select); + int (*clock_enable)(struct mmio_platform_data *pdata); + void (*clock_disable)(struct mmio_platform_data *pdata); + void (*set_xshutdown)(struct mmio_platform_data *pdata); +}; + +#define USER_SIDE_INTERFACE 1 +/* User side is only allowed to access code in USER_SIDE_INTERFACE block */ +#ifdef USER_SIDE_INTERFACE +enum mmio_bool_t { + MMIO_FALSE = 0, + MMIO_TRUE = !MMIO_FALSE, + MMIO_BOOL_MAX = 0x7FFFFFFF +}; + +struct xshutdown_info_t { + int ip_gpio; + int camera_function; +}; + +struct xp70_fw_t { + void __iomem *addr_sdram_ext; + void __iomem *addr_esram_ext; + void __iomem *addr_split; + void __iomem *addr_data; + unsigned int size_sdram_ext; + unsigned int size_esram_ext; + unsigned int size_split; + unsigned int size_data; +}; + +struct isp_write_t { + unsigned long t1_dest; + unsigned long *data; + unsigned long count; +}; + +struct trace_buf_t { + void *address; + unsigned int size; +}; + +#ifdef SRA_SUPPORT +struct s_reg { + unsigned int addr; + unsigned int value; + unsigned int mask; +}; + +struct s_reg_list { + unsigned int access_mode; + unsigned int entries; + struct s_reg *s_regs_p; +}; +#endif +struct mmio_input_output_t { + union { + enum mmio_bool_t power_on; + struct xp70_fw_t xp70_fw; + struct isp_write_t isp_write; + unsigned int addr_to_map; + struct xshutdown_info_t xshutdown_info; + enum camera_slot_t camera_slot; + struct trace_buf_t trace_buf; +#ifdef SRA_SUPPORT + struct s_reg_list s_reg_list; +#endif + } mmio_arg; +}; + +#define MMIO_TRUE (1) +#define MMIO_FALSE (0) +#define MMIO_INVALID (~0) + +/*Xshutdown from host takes two arguments*/ +#define MMIO_XSHUTDOWN_ENABLE (0x1) +#define MMIO_XSHUTDOWN_ACTIVE_HIGH (0x2) + +#define MMIO_MAGIC_NUMBER 0x15 + +#define MMIO_CAM_INITBOARD _IOW(MMIO_MAGIC_NUMBER, 1,\ +struct mmio_input_output_t*) +#define MMIO_CAM_PWR_SENSOR _IOW(MMIO_MAGIC_NUMBER, 2,\ +struct mmio_input_output_t*) +#define MMIO_CAM_SET_EXT_CLK _IOW(MMIO_MAGIC_NUMBER, 3,\ +struct mmio_input_output_t*) +#define MMIO_CAM_SET_PRI_HWIF _IO(MMIO_MAGIC_NUMBER, 4) +#define MMIO_CAM_SET_SEC_HWIF _IO(MMIO_MAGIC_NUMBER, 5) +#define MMIO_CAM_INITMMDSPTIMER _IO(MMIO_MAGIC_NUMBER, 6) +#define MMIO_CAM_LOAD_XP70_FW _IOW(MMIO_MAGIC_NUMBER, 7,\ +struct mmio_input_output_t*) +#define MMIO_CAM_MAP_STATS_AREA _IOWR(MMIO_MAGIC_NUMBER, 8,\ +struct mmio_input_output_t*) +#define MMIO_ACTIVATE_I2C2 _IOW(MMIO_MAGIC_NUMBER, 9, int*) +#define MMIO_ENABLE_XSHUTDOWN_FROM_HOST _IOW(MMIO_MAGIC_NUMBER, 10, int*) +#define MMIO_CAM_ISP_WRITE _IOW(MMIO_MAGIC_NUMBER, 11,\ +struct mmio_input_output_t*) +#define MMIO_CAM_GET_IP_GPIO _IOWR(MMIO_MAGIC_NUMBER, 12,\ +struct mmio_input_output_t*) +#define MMIO_CAM_DESINITBOARD _IO(MMIO_MAGIC_NUMBER, 13) +#define MMIO_CAM_SET_TRACE_BUFFER _IOW(MMIO_MAGIC_NUMBER, 14,\ +struct mmio_input_output_t*) + +#ifdef SRA_SUPPORT +#define MMIO_CAM_READ_REGS _IOWR(MMIO_MAGIC_NUMBER, 15,\ +struct mmio_input_output_t*) +#define MMIO_CAM_MODIFY_REGS _IOWR(MMIO_MAGIC_NUMBER, 16,\ +struct mmio_input_output_t*) +#define MMIO_CAM_WRITE_REGS _IOWR(MMIO_MAGIC_NUMBER, 17,\ +struct mmio_input_output_t*) +#endif + +#endif /* USER_SIDE_INTERFACE */ + +#endif +/* MMIO_H */ diff --git a/drivers/staging/mmio/st_mmio.c b/drivers/staging/mmio/st_mmio.c new file mode 100644 index 00000000000..655f7ce3a93 --- /dev/null +++ b/drivers/staging/mmio/st_mmio.c @@ -0,0 +1,1163 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pankaj Chauhan <pankaj.chauhan@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ +#include <linux/delay.h> +#include <linux/init.h> /* Initiliasation support */ +#include <linux/module.h> /* Module support */ +#include <linux/kernel.h> /* Kernel support */ +#include <linux/version.h> /* Kernel version */ +#include <linux/fs.h> /* File operations (fops) defines */ +#include <linux/errno.h> /* Defines standard err codes */ +#include <linux/io.h> +#include <linux/miscdevice.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> +#include <linux/workqueue.h> +#include <linux/mfd/dbx500-prcmu.h> +#include "mmio.h" + +#define ISP_REGION_IO (0xE0000000) +#define SIA_ISP_REG_ADDR (0x521E4) +#define SIA_BASE_ADDR (0x54000) +#define SIA_ISP_MEM (0x56000) +#define SIA_TIMER_ITC (0x5BC00) +#define SIA_ISP_MCU_SYS_SIZE (0x100000) +#define SIA_ISP_MEM_PAGE_REG (0x54070) +#define SIA_ISP_MCU_SYS_ADDR0_OFFSET (SIA_BASE_ADDR + 0x40) +#define SIA_ISP_MCU_SYS_SIZE0_OFFSET (SIA_BASE_ADDR + 0x42) +#define SIA_ISP_MCU_SYS_ADDR1_OFFSET (SIA_ISP_MCU_SYS_ADDR0_OFFSET + 0x04) +#define SIA_ISP_MCU_SYS_SIZE1_OFFSET (SIA_ISP_MCU_SYS_SIZE0_OFFSET + 0x04) +#define SIA_ISP_MCU_IO_ADDR0_HI (SIA_BASE_ADDR + 0x60) + +/* HTimer enable in CR register */ +#define CR_REG0_HTIMEN (1 << 26) +#define PICTOR_IN_XP70_L2_MEM_BASE_ADDR (0x40000) +#define PICTOR_IN_XP70_TCDM_MEM_BASE_ADDR (0x60000) +#define L2_PSRAM_MEM_SIZE (0x10000) + +#define FW_TO_HOST_ADDR_MASK (0x00001FFF) +#define FW_TO_HOST_ADDR_SHIFT (0xD) +#define FW_TO_HOST_CLR_MASK (0x3F) +#define PHY_TO_ISP_MCU_IO_ADDR0_HI(x) (((x) >> 24) << 8) +#define XP70_ADDR_MASK (0x00FFFFFF) + +#define CLOCK_ENABLE_DELAY (0x2) + +#define MAX_PRCMU_QOS_APP (0x64) + +#define ISP_WRITE_DATA_SIZE (0x4) + +#define clrbits32(_addr, _clear) \ + writel(readl(_addr) & ~(u32)(_clear), _addr) +#define setbits32(_addr, _set) \ + writel(readl(_addr) | (u32)(_set), _addr) + +#define XP70_BLOCK_SIZE 124 +#define XP70_NB_BLOCK 50 +/* + * For 30 fps video, there is 33 msec delay between every two frames + * MMIO driver reads traces from trace buffer every XP70_TIMEOUT_MSEC. + * If traces are not read in time from trace buffer, camera firmware + * will start overwiting the traces as size of trace buffer is limited. + */ +#define XP70_TIMEOUT_MSEC 30 +#define XP70_DEFAULT_MSG_ID (0xCDCDCDCD) +#define XP70_MAX_BLOCK_ID (0xFFFFFFFF) + +#define upper_16_bits(n) ((u16)((u32)(n) >> 16)) + +struct trace_block { + u32 msg_id; + char data[XP70_BLOCK_SIZE]; +}; + +struct mmio_trace { + u32 nb_block; + u32 block_size; + u32 block_id; + u32 overwrite_count; + struct trace_block block[XP70_NB_BLOCK]; +}; + +struct trace_buffer_status { + u32 prev_overwrite_count; + u32 prev_block_id; +}; + +struct mmio_info { + struct mmio_platform_data *pdata; /* Config from board */ + struct device *dev; /* My device */ + /* Runtime variables */ + struct miscdevice misc_dev; + void __iomem *siabase; + void __iomem *crbase; + /* States */ + int xshutdown_enabled; + int xshutdown_is_active_high; + /* tracing */ + struct trace_buffer_status trace_status; + struct mmio_trace *trace_buffer; + struct delayed_work trace_work; + int trace_allowed; +}; + +/* + * The one and only private data holder. Default inited to NULL. + * Declare it here so no code above can use it directly. + */ +static struct mmio_info *info; + +/* + * This function converts a given logical memory region size + * to appropriate ISP_MCU_SYS_SIZEx register value. + */ +static int get_mcu_sys_size(u32 size, u32 *val) +{ + int ret = 0; + + if (size > 0 && size <= SZ_4K) + *val = 4; + else if (size > SZ_4K && size <= SZ_8K) + *val = 5; + else if (size > SZ_8K && size <= SZ_16K) + *val = 6; + else if (size > SZ_16K && size <= SZ_32K) + *val = 7; + else if (size > SZ_32K && size <= SZ_64K) + *val = 0; + else if (size > SZ_64K && size <= SZ_1M) + *val = 1; + else if (size > SZ_1M && size <= SZ_16M) + *val = 2; + else if (size > SZ_16M && size <= SZ_256M) + *val = 3; + else + ret = -EINVAL; + + return ret; +} + +static int mmio_cam_pwr_sensor(struct mmio_info *info, int on) +{ + int err = 0; + + if (on) { + err = info->pdata->power_enable(info->pdata); + + if (err) + dev_err(info->dev, + "power_enable failed. err = %d\n", err); + + /* + * When switching from secondary YUV camera + * to primary Raw Bayer Camera, a hang is observed without the + * below delay. I2C access failure are observed while + * communicating with primary camera sensor indicating camera + * sensor was not powered up correctly. + */ + mdelay(CLOCK_ENABLE_DELAY); + } else { + info->pdata->power_disable(info->pdata); + } + + return err; +} + +static int mmio_cam_control_clocks(struct mmio_info *info, + enum mmio_bool_t power_on) +{ + int err = 0; + + if (power_on) { + err = info->pdata->clock_enable(info->pdata); + + if (err) + dev_err(info->dev, + "clock_enable failed, err = %d\n", + err); + } else { + info->pdata->clock_disable(info->pdata); + } + + return err; +} + +static int mmio_cam_set_pri_hwif(struct mmio_info *info) +{ + if (info->xshutdown_enabled) + info->pdata->set_xshutdown(info->pdata); + + return 0; +} + +static int mmio_cam_set_sec_hwif(struct mmio_info *info) +{ + if (info->xshutdown_enabled) + info->pdata->set_xshutdown(info->pdata); + + return 0; +} + +static int mmio_cam_init_mmdsp_timer(struct mmio_info *info) +{ + /* Disabling Accelerators timers */ + clrbits32(info->crbase, CR_REG0_HTIMEN); + /* Write MMDSPTimer */ + writel(0, info->siabase + SIA_TIMER_ITC); + /* Enabling Accelerators timers */ + setbits32(info->crbase, CR_REG0_HTIMEN); + return 0; +} + +static u32 t1_to_arm(u32 t1_addr, void __iomem *smia_base_address, + u16 *p_mem_page) +{ + u16 mem_page_update = 0; + mem_page_update = (t1_addr >> FW_TO_HOST_ADDR_SHIFT) & + FW_TO_HOST_CLR_MASK; + + if (mem_page_update != *p_mem_page) { + /* Update sia_mem_page register */ + dev_dbg(info->dev, "mem_page_update=0x%x, mem_page=0x%x\n", + mem_page_update, *p_mem_page); + writew(mem_page_update, smia_base_address + + SIA_ISP_MEM_PAGE_REG); + *p_mem_page = mem_page_update; + } + + return SIA_ISP_MEM + (t1_addr & FW_TO_HOST_ADDR_MASK); +} + +static int copy_user_buffer(void __iomem **dest_buf, + void __iomem *src_buf, u32 size) +{ + int err = 0; + + if (!src_buf) + return -EINVAL; + + *dest_buf = kmalloc(size, GFP_KERNEL); + + if (!dest_buf) { + err = -ENOMEM; + goto nomem; + } + + if (copy_from_user(*dest_buf, src_buf, size)) { + err = -EFAULT; + goto cp_failed; + } + + return err; +cp_failed: + kfree(*dest_buf); +nomem: + return err; +} +static int mmio_load_xp70_fw(struct mmio_info *info, + struct xp70_fw_t *xp70_fw) +{ + u32 i = 0; + u32 offset = 0; + u32 itval = 0; + u16 mem_page = 0; + void __iomem *addr_split = NULL; + void __iomem *addr_data = NULL; + int err = 0; + + if (xp70_fw->size_split != 0) { + err = copy_user_buffer(&addr_split, xp70_fw->addr_split, + xp70_fw->size_split); + + if (err) + goto err_exit; + + writel(0x0, info->siabase + SIA_ISP_REG_ADDR); + + /* Put the low 64k IRP firmware in ISP MCU L2 PSRAM */ + for (i = PICTOR_IN_XP70_L2_MEM_BASE_ADDR; + i < (PICTOR_IN_XP70_L2_MEM_BASE_ADDR + + L2_PSRAM_MEM_SIZE); i = i + 2) { + itval = t1_to_arm(i, info->siabase, &mem_page); + itval = ((u32) info->siabase) + itval; + /* Copy fw in L2 */ + writew((*((u16 *) addr_split + offset++)), itval); + } + + kfree(addr_split); + } + + if (xp70_fw->size_data != 0) { + mem_page = 0; + offset = 0; + err = copy_user_buffer(&addr_data, xp70_fw->addr_data, + xp70_fw->size_data); + + if (err) + goto err_exit; + + writel(0x0, info->siabase + SIA_ISP_REG_ADDR); + + for (i = PICTOR_IN_XP70_TCDM_MEM_BASE_ADDR; + i < (PICTOR_IN_XP70_TCDM_MEM_BASE_ADDR + + (xp70_fw->size_data)); i = i + 2) { + itval = t1_to_arm(i, info->siabase, &mem_page); + itval = ((u32) info->siabase) + itval; + /* Copy fw data in TCDM */ + writew((*((u16 *) addr_data + offset++)), itval); + } + + kfree(addr_data); + } + + if (xp70_fw->size_esram_ext != 0) { + /* + * ISP_MCU_SYS_ADDRx XP70 register (@ of ESRAM where the + * external code has been loaded + */ + writew(upper_16_bits(xp70_fw->addr_esram_ext), + info->siabase + SIA_ISP_MCU_SYS_ADDR0_OFFSET); + /* ISP_MCU_SYS_SIZEx XP70 register (size of the code =64KB) */ + writew(0x0, info->siabase + SIA_ISP_MCU_SYS_SIZE0_OFFSET); + } + + if (xp70_fw->size_sdram_ext != 0) { + /* + * ISP_MCU_SYS_ADDRx XP70 register (@ of SDRAM where the + * external code has been loaded + */ + writew(upper_16_bits(xp70_fw->addr_sdram_ext), + info->siabase + SIA_ISP_MCU_SYS_ADDR1_OFFSET); + /* ISP_MCU_SYS_SIZEx XP70 register */ + err = get_mcu_sys_size(xp70_fw->size_sdram_ext, &itval); + + if (err) + goto err_exit; + + writew(itval, info->siabase + SIA_ISP_MCU_SYS_SIZE1_OFFSET); + } + + return 0; +err_exit: + dev_err(info->dev, "Loading XP70 fw failed\n"); + return -EFAULT; +} + +static int mmio_map_statistics_mem_area(struct mmio_info *info, + void __iomem *addr_to_map) +{ + u16 value; + BUG_ON(addr_to_map == NULL); + /* 16 Mbyte aligned page */ + value = PHY_TO_ISP_MCU_IO_ADDR0_HI(*((u32 *)addr_to_map)); + writew(value, info->siabase + SIA_ISP_MCU_IO_ADDR0_HI); + /* Return the address in the XP70 address space */ + *((u32 *)addr_to_map) = (*((u32 *)addr_to_map) & XP70_ADDR_MASK) | + ISP_REGION_IO; + return 0; +} + +static int mmio_activate_i2c2(struct mmio_info *info, unsigned long enable) +{ + int err = 0; + + switch (enable) { + case MMIO_ACTIVATE_I2C_HOST: + /* Select I2C-2 */ + err = info->pdata->config_i2c_pins(info->pdata, + MMIO_ACTIVATE_I2C_HOST); + + if (err) { + dev_err(info->dev, "Failed to Enable I2C-2, err %d\n", + err); + goto out; + } + + break; + case MMIO_ACTIVATE_IPI2C2: + /* Select IPI2C */ + err = info->pdata->config_i2c_pins(info->pdata, + MMIO_ACTIVATE_IPI2C2); + + if (err) { + dev_err(info->dev, "Failed to Enable IPI2C, err %d\n", + err); + goto out; + } + + break; + case MMIO_DEACTIVATE_I2C: { + info->pdata->config_i2c_pins(info->pdata, MMIO_DEACTIVATE_I2C); + } + break; + default: + dev_warn(info->dev, "Invalid I2C2 config\n"); + err = -EINVAL; + break; + } + +out: + return err; +} + +static int mmio_enable_xshutdown_from_host(struct mmio_info *info, + unsigned long enable) +{ + int err = 0; + info->xshutdown_is_active_high = enable & MMIO_XSHUTDOWN_ACTIVE_HIGH; + + if (enable & MMIO_XSHUTDOWN_ENABLE) { + err = info->pdata->config_xshutdown_pins(info->pdata, + MMIO_ENABLE_XSHUTDOWN_HOST, enable & + MMIO_XSHUTDOWN_ACTIVE_HIGH); + } else { + info->pdata->config_xshutdown_pins(info->pdata, + MMIO_ENABLE_XSHUTDOWN_FW, -1); + /* + * XShutdown is controlled by firmware, initial output value is + * provided by firmware + */ + } + + info->xshutdown_enabled = enable & MMIO_XSHUTDOWN_ENABLE; + return 0; +} + +static int mmio_cam_initboard(struct mmio_info *info) +{ + int err = 0; + err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, MMIO_NAME, + MAX_PRCMU_QOS_APP); + + if (err) { + dev_err(info->dev, "Error adding PRCMU QoS requirement %d\n", + err); + goto out; + } + + /* Configure xshutdown to be disabled by default */ + err = mmio_enable_xshutdown_from_host(info, 0); + + if (err) + goto out; + + /* Enable IPI2C */ + err = mmio_activate_i2c2(info, MMIO_ACTIVATE_IPI2C2); +out: + return err; +} + +static int mmio_cam_desinitboard(struct mmio_info *info) +{ + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, MMIO_NAME); + return 0; +} + +static int mmio_isp_write(struct mmio_info *info, + struct isp_write_t *isp_write_p) +{ + int err = 0, i; + void __iomem *data = NULL; + void __iomem *addr = NULL; + u16 mem_page = 0; + + if (!isp_write_p->count) { + dev_warn(info->dev, "no data to write to isp\n"); + return -EINVAL; + } + + err = copy_user_buffer(&data, isp_write_p->data, + isp_write_p->count * ISP_WRITE_DATA_SIZE); + + if (err) + goto out; + + for (i = 0; i < isp_write_p->count; i++) { + addr = (void *)(info->siabase + t1_to_arm(isp_write_p->t1_dest + + ISP_WRITE_DATA_SIZE * i, + info->siabase, &mem_page)); + *((u32 *)addr) = *((u32 *)data + i); + } + + kfree(data); +out: + return err; +} + +static int mmio_set_trace_buffer(struct mmio_info *info, + struct trace_buf_t *buf) +{ + u32 i; + int ret = 0; + + if (info->trace_allowed != 1) { + dev_warn(info->dev, "trace disabled in kernel\n"); + ret = -EPERM; + goto out; + } + + if (!buf->size || !buf->address + || buf->size < sizeof(struct mmio_trace)) { + dev_err(info->dev, "invalid xp70 trace buffer\n"); + ret = -EINVAL; + goto out; + } + + if (info->trace_buffer) { + dev_info(info->dev, "unmap old buffer"); + iounmap(info->trace_buffer); + info->trace_buffer = NULL; + } + + info->trace_buffer = ioremap((u32)buf->address, buf->size); + + if (!info->trace_buffer) { + dev_err(info->dev, "failed to map trace buffer\n"); + ret = -ENOMEM; + goto out; + } + + dev_info(info->dev, "xp70 overwrite_cnt=%d (0x%x) blk_id=%d (0x%x)", + info->trace_buffer->overwrite_count, + info->trace_buffer->overwrite_count, + info->trace_buffer->block_id, info->trace_buffer->block_id); +#ifndef CAM_SHARED_MEM_DEBUG + + /* Reset the allocated buffer contents */ + for (i = 0; i < XP70_NB_BLOCK; i++) + info->trace_buffer->block[i].msg_id = XP70_DEFAULT_MSG_ID; + +#endif /* CAM_SHARED_MEMORY_DEBUG */ + dev_info(info->dev, "xp70 overwrite_cnt=%d (0x%x) blk_id=%d (0x%x)\n", + info->trace_buffer->overwrite_count, + info->trace_buffer->overwrite_count, + info->trace_buffer->block_id, info->trace_buffer->block_id); + info->trace_status.prev_overwrite_count = 0; + info->trace_status.prev_block_id = 0; + + /* schedule work */ + if (!schedule_delayed_work(&info->trace_work, + msecs_to_jiffies(XP70_TIMEOUT_MSEC))) + dev_err(info->dev, "failed to schedule work\n"); + +out: + return ret; +} + +static long mmio_ioctl(struct file *filp, u32 cmd, + unsigned long arg) +{ + struct mmio_input_output_t data; + int no_of_bytes; + int enable; + int ret = 0; + struct mmio_info *info = (struct mmio_info *)filp->private_data; + BUG_ON(info == NULL); + + switch (cmd) { + case MMIO_CAM_INITBOARD: + no_of_bytes = sizeof(struct mmio_input_output_t); + memset(&data, 0, sizeof(struct mmio_input_output_t)); + + if (copy_from_user(&data, (struct mmio_input_output_t *)arg, + no_of_bytes)) { + dev_err(info->dev, + "Copy from userspace failed\n"); + ret = -EFAULT; + break; + } + + info->pdata->camera_slot = data.mmio_arg.camera_slot; + ret = mmio_cam_initboard(info); + break; + case MMIO_CAM_DESINITBOARD: + ret = mmio_cam_desinitboard(info); + break; + case MMIO_CAM_PWR_SENSOR: + no_of_bytes = sizeof(struct mmio_input_output_t); + memset(&data, 0, sizeof(struct mmio_input_output_t)); + + if (copy_from_user + (&data, (struct mmio_input_output_t *)arg, + no_of_bytes)) { + dev_err(info->dev, + "Copy from userspace failed\n"); + ret = -EFAULT; + break; + } + + ret = mmio_cam_pwr_sensor(info, data.mmio_arg.power_on); + break; + case MMIO_CAM_SET_EXT_CLK: + no_of_bytes = sizeof(struct mmio_input_output_t); + memset(&data, 0, sizeof(struct mmio_input_output_t)); + + if (copy_from_user + (&data, (struct mmio_input_output_t *)arg, + no_of_bytes)) { + dev_err(info->dev, + "Copy from userspace failed\n"); + ret = -EFAULT; + break; + } + + ret = mmio_cam_control_clocks(info, data.mmio_arg.power_on); + break; + case MMIO_CAM_LOAD_XP70_FW: + no_of_bytes = sizeof(struct mmio_input_output_t); + memset(&data, 0, sizeof(struct mmio_input_output_t)); + + if (copy_from_user + (&data, (struct mmio_input_output_t *)arg, + no_of_bytes)) { + dev_err(info->dev, + "Copy from userspace failed\n"); + ret = -EFAULT; + break; + } + + ret = mmio_load_xp70_fw(info, &data.mmio_arg.xp70_fw); + break; + case MMIO_CAM_MAP_STATS_AREA: + no_of_bytes = sizeof(struct mmio_input_output_t); + memset(&data, 0, sizeof(struct mmio_input_output_t)); + + if (copy_from_user + (&data, (struct mmio_input_output_t *)arg, + no_of_bytes)) { + dev_err(info->dev, + "Copy from userspace failed\n"); + ret = -EFAULT; + break; + } + + ret = mmio_map_statistics_mem_area(info, + &data.mmio_arg.addr_to_map); + + if (0 != ret) { + dev_err(info->dev, + "Unable to map Statistics Mem area\n"); + break; + } + + if (copy_to_user((struct mmio_input_output_t *)arg, + &data, sizeof(no_of_bytes))) { + dev_err(info->dev, + "Copy to userspace failed\n"); + ret = -EFAULT; + break; + } + + break; + case MMIO_CAM_SET_PRI_HWIF: + ret = mmio_cam_set_pri_hwif(info); + break; + case MMIO_CAM_SET_SEC_HWIF: + ret = mmio_cam_set_sec_hwif(info); + break; + case MMIO_CAM_INITMMDSPTIMER: + ret = mmio_cam_init_mmdsp_timer(info); + break; + case MMIO_CAM_ISP_WRITE: + no_of_bytes = sizeof(struct mmio_input_output_t); + memset(&data, 0, sizeof(struct mmio_input_output_t)); + + if (copy_from_user + (&data, (struct mmio_input_output_t *)arg, + no_of_bytes)) { + dev_err(info->dev, + "Copy from userspace failed\n"); + ret = -EFAULT; + break; + } + + ret = mmio_isp_write(info, &data.mmio_arg.isp_write); + break; + case MMIO_ACTIVATE_I2C2: + no_of_bytes = sizeof(struct mmio_input_output_t); + memset(&data, 0, sizeof(struct mmio_input_output_t)); + + if (copy_from_user + (&enable, (int *)arg, sizeof(enable))) { + dev_err(info->dev, + "Copy from userspace failed\n"); + ret = -EFAULT; + break; + } + + ret = mmio_activate_i2c2(info, enable); + break; + case MMIO_ENABLE_XSHUTDOWN_FROM_HOST: + no_of_bytes = sizeof(struct mmio_input_output_t); + memset(&data, 0, sizeof(struct mmio_input_output_t)); + + if (copy_from_user + (&enable, (int *)arg, sizeof(enable))) { + dev_err(info->dev, + "Copy from userspace failed\n"); + ret = -EFAULT; + break; + } + + ret = mmio_enable_xshutdown_from_host(info, enable); + break; + case MMIO_CAM_GET_IP_GPIO: + no_of_bytes = sizeof(struct mmio_input_output_t); + memset(&data, 0, sizeof(struct mmio_input_output_t)); + + if (copy_from_user + (&data, (struct mmio_input_output_t *)arg, + no_of_bytes)) { + dev_err(info->dev, + "Copy from userspace failed\n"); + ret = -EFAULT; + break; + } + + data.mmio_arg.xshutdown_info.ip_gpio = + info->pdata->reset_ipgpio + [data.mmio_arg.xshutdown_info.camera_function]; + + if (copy_to_user((struct mmio_input_output_t *)arg, + &data, sizeof(no_of_bytes))) { + dev_err(info->dev, + "Copy to userspace failed\n"); + ret = -EFAULT; + break; + } + + break; + case MMIO_CAM_SET_TRACE_BUFFER: + no_of_bytes = sizeof(struct mmio_input_output_t); + memset(&data, 0, sizeof(struct mmio_input_output_t)); + + if (copy_from_user + (&data, (struct mmio_input_output_t *) arg, + no_of_bytes)) { + dev_err(info->dev, + "Copy from userspace failed\n"); + ret = -EFAULT; + break; + } + + ret = mmio_set_trace_buffer(info, &data.mmio_arg.trace_buf); + break; + default: + dev_err(info->dev, "Not an ioctl for this module\n"); + ret = -EINVAL; + break; + } + + return ret; +} + +static int mmio_release(struct inode *node, struct file *filp) +{ + struct mmio_info *info = filp->private_data; + BUG_ON(info == NULL); + mmio_activate_i2c2(info, MMIO_DEACTIVATE_I2C); + info->pdata->config_xshutdown_pins(info->pdata, MMIO_DISABLE_XSHUTDOWN, + -1); + + if (info->trace_buffer) { + iounmap(info->trace_buffer); + info->trace_buffer = NULL; + } + + flush_delayed_work(&info->trace_work); + return 0; +} + +static int mmio_open(struct inode *node, struct file *filp) +{ + filp->private_data = info; /* Hook our mmio info */ + return 0; +} + +static const struct file_operations mmio_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = mmio_ioctl, + .open = mmio_open, + .release = mmio_release, +}; + + +static ssize_t xp70_data_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + int i; + int len; + int size = 0; + int count = 0; + int first_index; + first_index = info->trace_status.prev_block_id + 1; + + if (!info->trace_buffer || info->trace_buffer->block_id == + XP70_MAX_BLOCK_ID) + goto out_unlock; + + if (info->trace_allowed != 1) { + dev_warn(info->dev, "xp70 trace disabled in kernel\n"); + size = sprintf(buf, "xp70 trace disabled in kernel, " + "use sysfs to enable\n"); + goto out_unlock; + } + + count = info->trace_buffer->block_id - info->trace_status.prev_block_id; + + if ((info->trace_buffer->overwrite_count - + info->trace_status.prev_overwrite_count) * XP70_NB_BLOCK + + (info->trace_buffer->block_id - + info->trace_status.prev_block_id) + >= XP70_NB_BLOCK) { + /* overflow case */ + info->trace_status.prev_block_id = + info->trace_buffer->block_id - XP70_NB_BLOCK; + first_index = info->trace_buffer->block_id + 1; + count = XP70_NB_BLOCK; + len = sprintf(buf, "XP70 trace overflow\n"); + size += len; + buf += len; + } + + for (i = first_index; count; count--) { + int msg_len; + + if (i < 0 || i >= XP70_NB_BLOCK || count > XP70_NB_BLOCK) { + dev_err(info->dev, "trace index out-of-bounds\n"); + goto out_unlock; + } + + msg_len = strnlen(info->trace_buffer->block[i].data, + XP70_BLOCK_SIZE); + + if (msg_len > 0) { + /* zero terminate full length message */ + if (msg_len == XP70_BLOCK_SIZE) + info->trace_buffer->block[i].data[ + XP70_BLOCK_SIZE - 1] = '\0'; + + len = snprintf(buf, PAGE_SIZE - size, "%d %s\n", + info->trace_buffer->block[i].msg_id, + info->trace_buffer->block[i].data); + + if (len > PAGE_SIZE - size) { + dev_err(info->dev, "sysfs buffer overflow\n"); + size = PAGE_SIZE; + goto out_unlock; + } + + size += len; + buf += len; + } + + i = (i + 1) % XP70_NB_BLOCK; + } + +out_unlock: + return size; +} + +static ssize_t xp70_trace_allowed_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + int len; + len = sprintf(buf, "%d\n", info->trace_allowed); + return len; +} + +static ssize_t xp70_trace_allowed_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if (count <= 0) { + dev_err(info->dev, "empty buffer to store\n"); + return 0; + } + + if (buf[0] == '1') + info->trace_allowed = 1; + else if (buf[0] == '0') + info->trace_allowed = 0; + else + dev_err(info->dev, "illegal trace_allowed val %c\n", + buf[0]); + + return count; +} + +static struct device_attribute xp70_device_attrs[] = { + __ATTR_RO(xp70_data), + __ATTR(trace_allowed, S_IRUGO | S_IWUSR, xp70_trace_allowed_show, + xp70_trace_allowed_store), + __ATTR_NULL +}; + +static void xp70_buffer_wqtask(struct work_struct *data) +{ + int i; + int first_index = info->trace_status.prev_block_id + 1; + int count; + + if (!info->trace_buffer) + goto out_err; + + dev_dbg(info->dev, "xp70 overwrite_cnt=%d (0x%x) blk_id=%d (0x%x)", + info->trace_buffer->overwrite_count, + info->trace_buffer->overwrite_count, + info->trace_buffer->block_id, info->trace_buffer->block_id); + + /* check if trace already started */ + if (info->trace_buffer->block_id == XP70_MAX_BLOCK_ID || + info->trace_buffer->block_id == XP70_DEFAULT_MSG_ID || + info->trace_buffer->overwrite_count == XP70_DEFAULT_MSG_ID) + goto out; + + if ((info->trace_buffer->overwrite_count - + info->trace_status.prev_overwrite_count) * XP70_NB_BLOCK + + (info->trace_buffer->block_id - + info->trace_status.prev_block_id) + >= XP70_NB_BLOCK) { + /* overflow case */ + info->trace_status.prev_block_id = + info->trace_buffer->block_id - XP70_NB_BLOCK; + first_index = info->trace_buffer->block_id + 1; + count = XP70_NB_BLOCK; + + if (printk_ratelimit()) + dev_info(info->dev, "XP70 trace overflow\n"); + } else if (info->trace_buffer->block_id + >= info->trace_status.prev_block_id) { + count = info->trace_buffer->block_id - + info->trace_status.prev_block_id; + } else { + u32 block_id, prev_block_id, diff; + block_id = (u32)(info->trace_buffer->block_id); + prev_block_id = (u32)(info->trace_status.prev_block_id); + diff = (block_id + XP70_NB_BLOCK) - prev_block_id; + count = (u32)diff; + } + + for (i = first_index; count; count--) { + if (i < 0 || i >= XP70_NB_BLOCK || count > XP70_NB_BLOCK) { + if (printk_ratelimit()) + dev_info(info->dev, "trace index out-of-bounds" + "i=%d count=%d XP70_NB_BLOCK=%d\n", + i, count, XP70_NB_BLOCK); + + break; + } + + if (info->trace_buffer->block[i].msg_id != + XP70_DEFAULT_MSG_ID) { + int msg_len = strnlen( + info->trace_buffer->block[i].data, + XP70_BLOCK_SIZE); + + /* zero terminate full length message */ + if (msg_len > 0) { + if (msg_len == XP70_BLOCK_SIZE) + info->trace_buffer->block[i].data[ + XP70_BLOCK_SIZE - 1] = '\0'; + + dev_info(info->dev, "%d %s\n", + info->trace_buffer->block[i].msg_id, + info->trace_buffer->block[i].data); + } + } + + i = (i + 1) % XP70_NB_BLOCK; + } + + info->trace_status.prev_overwrite_count = + info->trace_buffer->overwrite_count; + info->trace_status.prev_block_id = info->trace_buffer->block_id; +out: + + /* Schedule work */ + if (!schedule_delayed_work(&info->trace_work, + msecs_to_jiffies(XP70_TIMEOUT_MSEC))) + dev_info(info->dev, "failed to schedule work\n"); + +out_err: + return; +} + +/** +* mmio_probe() - Initialize MMIO Camera resources. +* @pdev: Platform device. +* +* Initialize the module and register misc device. +* +* Returns: +* 0 if there is no err. +* -ENOMEM if allocation fails. +* -EEXIST if device has already been started. +* Error codes from misc_register. +*/ +static int __devinit mmio_probe(struct platform_device *pdev) +{ + int err; + int i; + int ret; + printk(KERN_INFO "%s\n", __func__); + /* Initialize private data. */ + info = kzalloc(sizeof(struct mmio_info), GFP_KERNEL); + + if (!info) { + dev_err(&pdev->dev, "Could not alloc info struct\n"); + err = -ENOMEM; + goto err_alloc; + } + + /* Fill in private data */ + info->pdata = pdev->dev.platform_data; + info->dev = &pdev->dev; + info->pdata->dev = &pdev->dev; + info->misc_dev.minor = MISC_DYNAMIC_MINOR; + info->misc_dev.name = MMIO_NAME; + info->misc_dev.fops = &mmio_fops; + info->misc_dev.parent = pdev->dev.parent; + info->xshutdown_enabled = 0; + info->xshutdown_is_active_high = 0; + info->trace_allowed = 0; + /* Register Misc character device */ + err = misc_register(&(info->misc_dev)); + + if (err) { + dev_err(&pdev->dev, "Error %d registering misc dev!", err); + goto err_miscreg; + } + + /* Memory mapping */ + info->siabase = ioremap(info->pdata->sia_base, SIA_ISP_MCU_SYS_SIZE); + + if (!info->siabase) { + dev_err(info->dev, "Could not ioremap SIA_BASE\n"); + err = -ENOMEM; + goto err_ioremap_sia_base; + } + + info->crbase = ioremap(info->pdata->cr_base, PAGE_SIZE); + + if (!info->crbase) { + dev_err(info->dev, "Could not ioremap CR_BASE\n"); + err = -ENOMEM; + goto err_ioremap_cr_base; + } + + /* Initialize platform specific data */ + err = info->pdata->platform_init(info->pdata); + + if (err) + goto err_platform_init; + + /* create sysfs entries */ + for (i = 0; attr_name(xp70_device_attrs[i]); i++) { + ret = device_create_file(info->misc_dev.this_device, + &xp70_device_attrs[i]); + + if (ret) { + dev_err(info->dev, "Error creating SYSFS entry" + " %s (%d)\n", xp70_device_attrs[i].attr.name, + ret); + } + } + + INIT_DELAYED_WORK(&info->trace_work, xp70_buffer_wqtask); + dev_info(&pdev->dev, "MMIO driver initialized with minor=%d\n", + info->misc_dev.minor); + return 0; +err_platform_init: + iounmap(info->crbase); +err_ioremap_cr_base: + iounmap(info->siabase); +err_ioremap_sia_base: + misc_deregister(&info->misc_dev); +err_miscreg: + kfree(info); + info = NULL; +err_alloc: + return err; +} + +/** +* mmio_remove() - Release MMIO Camera resources. +* @pdev: Platform device. +* +* Remove misc device and free resources. +* +* Returns: +* 0 if success. +* Error codes from misc_deregister. +*/ +static int __devexit mmio_remove(struct platform_device *pdev) +{ + int err; + int i; + + if (!info) + return 0; + + flush_scheduled_work(); + + /* sysfs parameters */ + for (i = 0; attr_name(xp70_device_attrs[i]); i++) + device_remove_file(info->misc_dev.this_device, + &xp70_device_attrs[i]); + + err = misc_deregister(&info->misc_dev); + + if (err) + dev_err(&pdev->dev, "Error %d deregistering misc dev", err); + + info->pdata->platform_exit(info->pdata); + iounmap(info->siabase); + iounmap(info->crbase); + kfree(info); + info = NULL; + return 0; +} +static struct platform_driver mmio_driver = { + .driver = { + .name = MMIO_NAME, + .owner = THIS_MODULE, + }, + .probe = mmio_probe, + .remove = __devexit_p(mmio_remove) +}; + +/** +* mmio_init() - Initialize module. +* +* Registers platform driver. +*/ +static int __init mmio_init(void) +{ + printk(KERN_INFO "%s\n", __func__); + return platform_driver_register(&mmio_driver); +} + +/** +* mmio_exit() - Remove module. +* +* Unregisters platform driver. +*/ +static void __exit mmio_exit(void) +{ + printk(KERN_INFO "%s\n", __func__); + platform_driver_unregister(&mmio_driver); +} + +module_init(mmio_init); +module_exit(mmio_exit); + +MODULE_AUTHOR("Joakim Axelsson ST-Ericsson"); +MODULE_AUTHOR("Pankaj Chauhan ST-Ericsson"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MMIO Camera driver"); diff --git a/drivers/staging/nmf-cm/Kconfig b/drivers/staging/nmf-cm/Kconfig new file mode 100644 index 00000000000..9545fd5acd1 --- /dev/null +++ b/drivers/staging/nmf-cm/Kconfig @@ -0,0 +1,12 @@ + +config U8500_CM + tristate "U8500 Component Manager driver" + depends on UX500_SOC_DB8500 + help + This is the Component Manager driver. It is part of the + Nomadik Multiprocessing Framework. + + Note: This option allows the kernel developers to build + the driver in kernel to ease there life. By default, this driver + must be built outside this kernel source tree. + diff --git a/drivers/staging/nmf-cm/Make.config b/drivers/staging/nmf-cm/Make.config new file mode 100644 index 00000000000..ccbc150158b --- /dev/null +++ b/drivers/staging/nmf-cm/Make.config @@ -0,0 +1,8 @@ +# Copyright (C) ST-Ericsson SA 2011. All rights reserved. +# This code is ST-Ericsson proprietary and confidential. +# Any use of the code for whatever purpose is subject to +# specific written permission of ST-Ericsson SA. + +#CM driver file to copy but not to compile +CMENGINESRC_COPY_NO_BUILD = cm/engine/elf/src/elfxx.c + diff --git a/drivers/staging/nmf-cm/Makefile b/drivers/staging/nmf-cm/Makefile new file mode 100644 index 00000000000..b1a5b9afe1f --- /dev/null +++ b/drivers/staging/nmf-cm/Makefile @@ -0,0 +1,99 @@ +# +# Copyright (C) ST-Ericsson SA 2010 +# Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. +# License terms: GNU General Public License (GPL), version 2. +# + +# +# Rules to build kernel modules +# +ifneq ($(findstring KERNELRELEASE,$(.VARIABLES)),) + + # $(src): current relative dir; $(kbuild-dir): cur absolute dir + ifdef kbuild-dir + SRCDIR = $(realpath $(kbuild-dir)) + else + SRCDIR = $(realpath $(src)) + endif + include $(SRCDIR)/Make.config + ifndef FIXED_CPPFLAGS + # In Android env, we can not depend on files that are out of kernel tree. + # and thus we can't include $(SRCDIR)/../../../../mmenv/SharedARMFlags.mk + # where FIXED_CPPFLAGS is defined. + # So, define FIXED_CPPFLAGS here + FIXED_CPPFLAGS=-D__STN_8500=30 -DLINUX -D__ARM_LINUX + endif + EXTRA_CFLAGS := -I$(SRCDIR) $(FIXED_CPPFLAGS) + EXTRA_CFLAGS += -Wall -Werror + #EXTRA_CFLAGS += -DCM_DEBUG_ALLOC + + # + # CM object files to compile with + # + GENERIC_CM_FILES:=$(shell cd $(SRCDIR); find cm -name "*.c") + GENERIC_CM_FILES := $(filter-out $(CMENGINESRC_COPY_NO_BUILD), $(GENERIC_CM_FILES)) + + CM_OBJS := $(GENERIC_CM_FILES:.c=.o) + CM_OBJS += cmld.o cm_syscall.o osal-kernel.o cm_service.o cm_debug.o configuration.o + CM_OBJS += cm_dma.o + + obj-$(CONFIG_U8500_CM) := cm.o + + #Note: build system prepends the $(PWD) directory to these objects paths + cm-objs := $(CM_OBJS) + +else + + # CM module is built in kernel in android env + # or as module otherwise (OSI env, ...) + export CONFIG_U8500_CM ?= m + + ifeq ($(findstring install,$(MAKECMDGOALS)),) + # If not only performing install then include needed files for build + include $(MM_MAKEFILES_DIR)/SharedARMFlags.mk + export FIXED_CPPFLAGS + -include $(MM_MAKEFILES_DIR)/KernelConfig.mk + + ifeq ($(findstring clean,$(MAKECMDGOALS)),) + ifndef KERNEL_BUILD_DIR + $(error KERNEL_BUILD_DIR not defined) + endif + endif + endif + + include $(MM_MAKEFILES_DIR)/SharedConfig.mk + + module: + $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_BUILD_DIR) \ + M=$(PWD) INSTALL_HEADER_DIR=$(INSTALL_HEADER_DIR) \ + modules + + all: module + $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_BUILD_DIR) \ + M=$(PWD) INSTALL_HEADER_DIR=$(INSTALL_HEADER_DIR) \ + INSTALL_MOD_PATH=$(PWD)/lib/$(PLATFORM) \ + modules_install + rm -f $(PWD)/lib/$(PLATFORM)/lib/modules/*/modules.* + + # + # Rules to clean and install + # + clean: + @rm -rf $(PLATFORM) $(CM_OBJS) .built-in.o.cmd .cm*o.cmd Module.symvers \ + .tmp_versions modules.order cm.ko cm.o cm.mod.* lib \ + $(foreach f,$(CM_OBJS), $(dir $f).$(notdir $f).cmd) + + realclean: clean + $(foreach platform, \ + $(shell grep property ../../component/component.xml | cut -d\" -f 4), \ + rm -rf $(platform);) + @rm -rf *~ + + install: + $(GEN_LN) -d lib/$(PLATFORM)/lib $(INSTALL_LIB_DIR)/lib + + uninstall: + $(GEN_LN) -r -d lib/$(PLATFORM)/lib $(INSTALL_LIB_DIR)/lib + +endif #ifdef KERNELRELEASE + diff --git a/drivers/staging/nmf-cm/cm/engine/api/channel_engine.h b/drivers/staging/nmf-cm/cm/engine/api/channel_engine.h new file mode 100644 index 00000000000..19353ee7328 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/channel_engine.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief Communication Component Manager internal API type. + */ + +#ifndef CHANNEL_ENGINE_H +#define CHANNEL_ENGINE_H + +#include <nmf/inc/channel_type.h> +#include <nmf/inc/service_type.h> +#include <cm/engine/communication/inc/communication_type.h> + +/*! + * \brief Internal channel identification. + * + * Same as t_nmf_channel meaning but this the channel used internaly by + * OS Integration part + * + * \ingroup CM_OS_API + */ +typedef t_uint32 t_os_channel; + +/*! + * \brief Invalid value for os_channel + * + * Invalid value for os channel. + * + * \ingroup CM_OS_API + */ +#define NMF_OS_CHANNEL_INVALID_HANDLE 0xffffffff + +/*! + * \brief Structure used for storing required parameters for Interface Callback + * messages. + * + * This struture is used internally by CM_GetMessage() and CM_ExecuteMessage() as + * the message content in the given buffer. + * + * \ingroup CM_ENGINE_API + */ +typedef struct { + t_nmf_mpc2host_handle THIS; //!< Context of interface implementation + t_uint32 methodIndex; //!< Method index in interface + char params[1]; //!< Is of variable length concretely +} t_interface_data; + +/*! + * \brief Structure used for storing required parameters for Service Callback + * messages. + * + * This struture is used internally by CM_GetMessage() and CM_ExecuteMessage() as + * the message content in the given buffer. + * + * \ingroup CM_ENGINE_API + */ +typedef struct { + t_nmf_service_type type; //!< Type of the service message + t_nmf_service_data data; +} t_service_data; + +typedef enum { + MSG_INTERFACE, + MSG_SERVICE +} t_message_type; + +/*! + * \brief Structure used for storing required parameters for the internal NMF + * messages. + * + * This struture is used internally by CM_GetMessage() and CM_ExecuteMessage() as + * the message content in the given buffer. + * + * \ingroup CM_ENGINE_API + */ +typedef struct { + t_message_type type; //!< Type of the nmf message + union { + t_interface_data itf; + t_service_data srv; + } data; +} t_os_message; + +/*! + * \brief Structure used for storing required parameters for the internal NMF + * messages. + * + * This struture is used internally by CM_GetMessage() and CM_ExecuteMessage() as + * the message content in the given buffer. + * + * \ingroup CM_ENGINE_API + */ +typedef struct { + t_nmf_channel channel; //!< Channel (required to handle service message) + t_os_message osMsg; +} t_nmf_message; + +#endif /* CHANNEL_ENGINE_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/api/cm_engine.h b/drivers/staging/nmf-cm/cm/engine/api/cm_engine.h new file mode 100644 index 00000000000..0f4c1e4219e --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/cm_engine.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief CM Engine API. + * + * This file contains the Component Manager Engine API. + */ + +/*! + * \defgroup CM_ENGINE_MODULE CM Engine + */ +/*! + * \defgroup CM_ENGINE_API CM Engine API + * + * \note This API is not for user developers, this API is only an internal API. + * + * \warning All parameters in out from this API means that the parameter is a reference to a data that is complete by the call. + * + * This API is provided by CM Engine and shall be required by driver kernel part. + * \ingroup CM_ENGINE_MODULE + */ + +#ifndef CM_ENGINE_H_ +#define CM_ENGINE_H_ + +#include <cm/engine/api/configuration_engine.h> + +#include <cm/engine/api/component_engine.h> + +#include <cm/engine/api/memory_engine.h> + +#include <cm/engine/api/communication_engine.h> + +#include <cm/engine/api/perfmeter_engine.h> + +#include <cm/engine/api/executive_engine_mgt_engine.h> + +#include <cm/engine/api/repository_mgt_engine.h> + +#include <cm/engine/api/domain_engine.h> + +#include <cm/engine/api/migration_engine.h> + +#endif /*CM_ENGINE_H_*/ + diff --git a/drivers/staging/nmf-cm/cm/engine/api/communication_engine.h b/drivers/staging/nmf-cm/cm/engine/api/communication_engine.h new file mode 100644 index 00000000000..477a66a4002 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/communication_engine.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief Communication User Engine API. + * + * This file contains the Communication Engine API for manipulating components. + * + */ +#ifndef COMMUNICATION_ENGINE_H_ +#define COMMUNICATION_ENGINE_H_ + +#include <cm/engine/communication/inc/communication_type.h> + +/*! + * \brief Allocate Event buffer where parameters will be marshalled. + * + * In order to optimize call, this method don't need to be exported to user space, + * but must be used by CM driver. + * + * See \ref HOST2MPC "Host->MPC binding" for seeing an integration example. + * + * \note This method is not called from user space!!! + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_event_params_handle CM_ENGINE_AllocEvent(t_cm_bf_host2mpc_handle host2mpcId); + +/*! + * \brief Push a event in Fifo. + * + * In order to optimize call, this method don't need to be exported to user space, + * but must be used by CM driver. + * + * See \ref HOST2MPC "Host->MPC binding" for seeing an integration example. + * + * \note This method is not called from user space!!! + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_PushEvent(t_cm_bf_host2mpc_handle host2mpcId, t_event_params_handle h, t_uint32 methodIndex); + +/*! + * \brief Push a event in Fifo. + * + * In order to optimize call, this method need to be exported to user space + * and must be implemented by CM driver. + * + * See \ref HOST2MPC "Host->MPC binding" for seeing an integration example. + * + * \note No implementation of this method is provided in kernel CM engine!!! + * + * \ingroup CM_ENGINE_API + */ +PUBLIC t_cm_error CM_ENGINE_PushEventWithSize(t_cm_bf_host2mpc_handle host2mpcId, t_event_params_handle h, t_uint32 size, t_uint32 methodIndex); + +/*! + * \brief Aknowledge a Fifo that the received event has been demarshalled. + * + * In order to optimize call, this method don't need to be exported to user space, + * but must be used by CM driver. + * + * See \ref MPC2HOST "MPC->Host binding" for seeing an integration example. + * + * \note This method is not called from user space!!! + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED void CM_ENGINE_AcknowledgeEvent(t_cm_bf_mpc2host_handle mpc2hostId); + +#endif /*COMMUNICATION_ENGINE_H_*/ diff --git a/drivers/staging/nmf-cm/cm/engine/api/component_engine.h b/drivers/staging/nmf-cm/cm/engine/api/component_engine.h new file mode 100644 index 00000000000..cbd61769597 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/component_engine.h @@ -0,0 +1,403 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief Components Component Manager User Engine API. + * + * This file contains the Component Manager Engine API for manipulating components. + * + */ + +#ifndef COMPONENT_ENGINE_H_ +#define COMPONENT_ENGINE_H_ + +#include <cm/engine/memory/inc/domain_type.h> +#include <cm/engine/component/inc/component_type.h> +#include <cm/engine/communication/inc/communication_type.h> +#include <inc/nmf-limits.h> + +/*! + * \brief Instantiate a new component. + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_InstantiateComponent( + const char templateName[MAX_TEMPLATE_NAME_LENGTH], //!< [in] Null terminated string (Max size=\ref MAX_TEMPLATE_NAME_LENGTH) + t_cm_domain_id domainId, //!< [in] Domain + t_nmf_client_id clientId, //!< [in] Client ID (aka PID) + t_nmf_ee_priority priority, //!< [in] Component priority + const char localName[MAX_COMPONENT_NAME_LENGTH], //!< [in] Null terminated string (Max size=\ref MAX_COMPONENT_NAME_LENGTH) + const char *dataFile, //!< [in] Optional reference on file where component is stored + t_cm_instance_handle *component //!< [out] component + ); + +/*! + * \brief Start a component. + * + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_StartComponent( + t_cm_instance_handle component, + t_nmf_client_id clientId); + +/*! + * \brief Stop a component. + * + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_StopComponent( + t_cm_instance_handle component, + t_nmf_client_id clientId); + +/*! + * \brief Destroy a component. + * + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_DestroyComponent( + t_cm_instance_handle component, + t_nmf_client_id clientId); + +/*! + * \brief Stop and destroy all components belonging to the given client. + * + * \param[in] client + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_FlushComponents( + t_nmf_client_id client); + +/*! + * \brief Bind two components together. + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_BindComponent( + const t_cm_instance_handle client, //!< + const char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH], //!< Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH). + const t_cm_instance_handle server, //!< + const char providedItfServerName[MAX_INTERFACE_NAME_LENGTH], //!< Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH). + t_bool traced, //!< FALSE for synchronous binding, TRUE for traced one + t_nmf_client_id clientId, //!< Client ID + const char *dataFileTrace //!< Component file data in case on traced (Note: could be null if file already in cache) + ); + +/*! + * \brief Unbind a component. + * + * \param[in] client + * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH). + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_UnbindComponent( + const t_cm_instance_handle client, + const char * requiredItfClientName, + t_nmf_client_id clientId); + +/*! + * \brief Bind a component to void (silently ignore a call). + * + * \param[in] client + * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH). + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_BindComponentToVoid( + const t_cm_instance_handle client, + const char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH], + t_nmf_client_id clientId); + +/*! + * \brief Bind two components together in an asynchronous way + * (the components can be on the same MPC or on two different MPC) + * + * \param[in] client + * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH). + * \param[in] server + * \param[in] providedItfServerName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH). + * \param[in] fifosize + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_BindComponentAsynchronous( + const t_cm_instance_handle client, + const char * requiredItfClientName, + const t_cm_instance_handle server, + const char * providedItfServerName, + t_uint32 fifosize, + t_cm_mpc_memory_type eventMemType, + t_nmf_client_id clientId, + const char *dataFileSkeletonOrEvent, + const char *dataFileStub); + +/*! + * \brief Unbind a component previously binded asynchronously + * + * \param[in] client + * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH). + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentAsynchronous( + const t_cm_instance_handle client, + const char * requiredItfClientName, + t_nmf_client_id clientId); + +/*! + * \brief Bind the Host to a component. + * + * \param[in] server + * \param[in] providedItfServerName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH). + * \param[in] fifosize + * \param[out] host2mpcId + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_BindComponentFromCMCore( + const t_cm_instance_handle server, + const char * providedItfServerName, + t_uint32 fifosize, + t_cm_mpc_memory_type eventMemType, + t_cm_bf_host2mpc_handle *host2mpcId, + t_nmf_client_id clientId, + const char *dataFileSkeleton); + +/*! + * \brief Unbind a component from the Host. + * + * \param[in] host2mpcId + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentFromCMCore( + t_cm_bf_host2mpc_handle host2mpcId); + +/*! + * \brief Bind a component to the Host, see \ref CM_ENGINE_BindComponentToCMCore. + * + * See \ref MPC2HOST "MPC->Host binding" for seeing an integration example. + * + * \note This method is not called from CM Proxy, its only there for wrapping purpose!!! + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_BindComponentToCMCore( + const t_cm_instance_handle client, + const char *requiredItfClientName, + t_uint32 fifosize, + t_nmf_mpc2host_handle upLayerThis, + const char *dataFileStub, + t_cm_bf_mpc2host_handle *mpc2hostId, + t_nmf_client_id clientId); + +/*! + * \brief Unbind a component to the Host, see \ref CM_ENGINE_UnbindComponentToCMCore. + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentToCMCore( + const t_cm_instance_handle client, + const char *requiredItfClientName, + t_nmf_mpc2host_handle *upLayerThis, + t_nmf_client_id clientId); + +/*! + * \brief Read a value on an attribute exported by a component instance. + * + * \param[in] component + * \param[in] attrName Null terminated string (Max size=\ref MAX_ATTRIBUTE_NAME_LENGTH). + * \param[out] value + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_ReadComponentAttribute( + const t_cm_instance_handle component, + const char* attrName, + t_uint24 *value); + +/*! + * \brief Get the older component. + * + * \param[in] client + * \param[out] headerComponent + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentListHeader( + const t_nmf_client_id client, + t_cm_instance_handle *headerComponent); + +/*! + * \brief Get the next component. + * + * \param[in] client + * \param[in] prevComponent + * \param[out] nextComponent + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentListNext( + const t_nmf_client_id client, + const t_cm_instance_handle prevComponent, + t_cm_instance_handle *nextComponent); + +/*! + * \brief Get a component description + * + * \param[in] component + * \param[in] templateNameLength + * \param[in] localNameLength + * \param[out] templateName Null terminated string (Size=templateNameLength, Max size=\ref MAX_TEMPLATE_NAME_LENGTH). + * \param[out] coreId + * \param[out] localName Null terminated string (Size=localNameLength, Max size=\ref MAX_COMPONENT_NAME_LENGTH). + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentDescription( + const t_cm_instance_handle component, + char *templateName, + t_uint32 templateNameLength, + t_nmf_core_id *coreId, + char *localName, + t_uint32 localNameLength, + t_nmf_ee_priority *priority); + +/*! + * \brief Get number of interface required by a component. + * + * \param[in] component + * \param[out] numberRequiredInterfaces + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterfaceNumber( + const t_cm_instance_handle component, + t_uint8 *numberRequiredInterfaces); + +/*! + * \brief Return information about required interface. + * + * \param[in] component + * \param[in] index + * \param[in] itfNameLength + * \param[in] itfTypeLength + * \param[out] itfName Null terminated string (Size=itfNameLength, Max size=\ref MAX_INTERFACE_NAME_LENGTH). + * \param[out] itfType Null terminated string (Size=itfTypeLength, Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH). + * \param[out] collectionSize + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterface( + const t_cm_instance_handle component, + const t_uint8 index, + char *itfName, + t_uint32 itfNameLength, + char *itfType, + t_uint32 itfTypeLength, + t_cm_require_state *requireState, + t_sint16 *collectionSize); + +/*! + * \brief Get the component binded to a required interface. + * + * \param[in] component + * \param[in] itfName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH). + * \param[in] serverItfNameLength + * \param[out] server + * \param[out] serverItfName Null terminated string (Size=serverItfNameLength, Max size=\ref MAX_INTERFACE_NAME_LENGTH). + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterfaceBinding( + const t_cm_instance_handle component, + const char *itfName, + t_cm_instance_handle *server, + char *serverItfName, + t_uint32 serverItfNameLength); + +/*! + * \brief Get number of interface provided by a component. + * + * \param[in] component + * \param[out] numberProvidedInterfaces + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentProvidedInterfaceNumber( + const t_cm_instance_handle component, + t_uint8 *numberProvidedInterfaces); + +/*! + * \brief Return information about provided interface. + * + * \param[in] component + * \param[in] index + * \param[in] itfNameLength + * \param[in] itfTypeLength + * \param[out] itfName Null terminated string (Size=itfNameLength, Max size=\ref MAX_INTERFACE_NAME_LENGTH). + * \param[out] itfType Null terminated string (Size=itfTypeLength, Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH). + * \param[out] collectionSize + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentProvidedInterface( + const t_cm_instance_handle component, + const t_uint8 index, + char *itfName, + t_uint32 itfNameLength, + char *itfType, + t_uint32 itfTypeLength, + t_sint16 *collectionSize); + +/*! + * \brief Get number of properties of a component. + * + * \param[in] component + * \param[out] numberProperties + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyNumber( + const t_cm_instance_handle component, + t_uint8 *numberProperties); + +/*! + * \brief Return the name of a property. + * + * \param[in] component + * \param[in] index + * \param[in] propertyNameLength + * \param[out] propertyName Null terminated string (Size=propertyNameLength, Max size=\ref MAX_PROPERTY_NAME_LENGTH). + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyName( + const t_cm_instance_handle component, + const t_uint8 index, + char *propertyName, + t_uint32 propertyNameLength); + +/*! + * \brief Get property value of a component. + * + * \param[in] component + * \param[in] propertyName + * \param[in] propertyValueLength + * \param[out] propertyValue Null terminated string (Size=propertyValueLength, Max size=\ref MAX_PROPERTY_VALUE_LENGTH). + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyValue( + const t_cm_instance_handle component, + const char *propertyName, + char *propertyValue, + t_uint32 propertyValueLength); + +#endif /*COMPONENT_ENGINE_H_*/ diff --git a/drivers/staging/nmf-cm/cm/engine/api/configuration_engine.h b/drivers/staging/nmf-cm/cm/engine/api/configuration_engine.h new file mode 100644 index 00000000000..0336f62265e --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/configuration_engine.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief Configuration Component Manager User Engine API. + * + * This file contains the Configuration CM Engine API for manipulating CM. + * + */ + +#ifndef CONFIGURATION_ENGINE_H +#define CONFIGURATION_ENGINE_H + +#include <cm/engine/configuration/inc/configuration_type.h> + +/*! + * \brief Dynamically set some debug parameters of the CM + * + * \param[in] aCmdID The command for the parameter to update + * \param[in] aParam The actual value to set for the given command + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_SetMode(t_cm_cmd_id aCmdID, t_sint32 aParam); + +#endif /* CONFIGURATION_ENGINE_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/api/control/configuration_engine.h b/drivers/staging/nmf-cm/cm/engine/api/control/configuration_engine.h new file mode 100644 index 00000000000..a9543a2af39 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/control/configuration_engine.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief Configuration Component Manager User Engine API. + * + * This file contains the Configuration CM Engine API for manipulating CM. + */ + +#ifndef CONTROL_CONFIGURATION_ENGINE_H +#define CONTROL_CONFIGURATION_ENGINE_H + +#include <cm/engine/memory/inc/domain_type.h> +#include <cm/engine/memory/inc/memory_type.h> +#include <cm/engine/communication/inc/communication_type.h> + +/*****************************************************************************************/ +/* Component Manager dedicated (for Configuration purpose) structured types definition */ +/*****************************************************************************************/ + +/*! + * \brief Description of the Nomadik HW mapping configuration + * + * Describe the Nomadik mapping that is to say: + * - the ESRAM memory managed by the CM (The ESRAM address space SHALL BE declared as non cacheable, non bufferable inside host MMU table) + * - the mapping of the System HW Semaphore IP + * \ingroup CM_ENGINE_CONTROL_API + */ +typedef struct { + t_nmf_memory_segment esramDesc; //!< Description of the ESRAM memory mapping into Nomadik SOC + t_cm_system_address hwSemaphoresMappingBaseAddr; //!< Description of the System HW Semaphores IP mapping into Nomadik SOC +} t_nmf_hw_mapping_desc; + +/*! + * @defgroup t_nmf_nomadik_version t_nmf_nomadik_version + * \brief Description of the various supported Nomadik SOC version + * @{ + * \ingroup CM_ENGINE_CONTROL_API + */ +typedef t_uint8 t_nmf_nomadik_version; //!< Fake enumeration type +#define NOMADIK_8810 ((t_nmf_nomadik_version)0) //!< STn8810 chip (any cut) +#define NOMADIK_8815A0 ((t_nmf_nomadik_version)1) //!< STn8815 chip (cut A0) +#define NOMADIK_8815 ((t_nmf_nomadik_version)2) //!< STn8815 chip (other cuts) +#define NOMADIK_8820 ((t_nmf_nomadik_version)3) //!< STn8820 chip +#define NOMADIK_8500 ((t_nmf_nomadik_version)4) //!< STn8500 chip +/* @} */ + +/*! + * \brief Description of the configuration parameters of the Component Manager + * \ingroup CM_ENGINE_CONTROL_API + */ +typedef struct { + t_nmf_coms_location comsLocation; //!< Configure where CM Communications objects are put (see \ref t_nmf_coms_location) +} t_nmf_config_desc; + +/*! + * @defgroup t_nmf_power_ctx t_nmf_power_ctx + * \brief Definition of the CM-engine context + * + * OS integrator uses this value to known the context where the associated OSAL routine is called + * + * @{ + * \ingroup CM_ENGINE_CONTROL_API + */ + +typedef t_uint32 t_nmf_power_ctx; //!< Fake enumeration type +#define PWR_FLUSH_REQ_INTERRUPT_CTX ((t_nmf_power_ctx)0x00) //!< Interrupt context - called by \ref CM_ProcessMpcEvent +#define PWR_FLUSH_REQ_NORMAL_CTX ((t_nmf_power_ctx)0x01) //!< Normal context (CM user call) + +/* @} */ + + +/****************************************************************************************************************/ +/* Component Manager dedicated (for Media Processors Cores Configuration purpose) structured types definition */ +/****************************************************************************************************************/ +/*! + * @defgroup t_nmf_executive_engine_id t_nmf_executive_engine_id + * \brief Identification of the Media Processor Executive Engine to deploy + * @{ + * \ingroup CM_ENGINE_CONTROL_API + */ +typedef t_uint8 t_nmf_executive_engine_id; //!< Fake enumeration type +#define SYNCHRONOUS_EXECUTIVE_ENGINE ((t_nmf_executive_engine_id)0) //!< MPC Synchronous executive engine +#define HYBRID_EXECUTIVE_ENGINE ((t_nmf_executive_engine_id)1) //!< MPC Hybrid synchronous executive engine +/* @} */ + +/*! + * @defgroup t_nmf_semaphore_type_id t_nmf_semaphore_type_id + * \brief Definition of which type semaphore shall be used for the given Media Processor communication mechanism + * @{ + * \ingroup CM_ENGINE_CONTROL_API + */ +typedef t_uint8 t_nmf_semaphore_type_id; //!< Fake enumeration type +#define LOCAL_SEMAPHORES ((t_nmf_semaphore_type_id)0) //!< Embedded MMDSP macrocell semaphore, so CM_ProcessMpcEvent(<coreId>) shall be called under ISR connected to local MMDSP IRQ0 +#define SYSTEM_SEMAPHORES ((t_nmf_semaphore_type_id)1) //!< Shared system HW Semaphores, so CM_ProcessMpcEvent(ARM_CORE_ID) shall be called under ISR connected to shared HW Sem Host IRQ +/* @} */ + + +/*! + * \brief Opaque type for allocator, returned at CM configuration. + */ +typedef t_uint32 t_cfg_allocator_id; + +/********************************************************************************/ +/* Configuration Component Manager API prototypes */ +/********************************************************************************/ + +/*! + * \brief Initialisation part + * + * This routine initialize and configure the Component Manager. + * + * \param[in] pNmfHwMappingDesc hardware mapping description + * \param[in] pNmfConfigDesc NMF (mainly CM) Configuration description + * + * \exception TBD + * \return exception number. + * + * \warning The ESRAM address space SHALL BE declared as non cacheable, non bufferable inside host MMU table + * + * \ingroup CM_ENGINE_CONTROL_API + */ +PUBLIC t_cm_error CM_ENGINE_Init( + const t_nmf_hw_mapping_desc *pNmfHwMappingDesc, + const t_nmf_config_desc *pNmfConfigDesc + ); + + +/*! + * \brief Media Processor core initialisation part + * + * This routine configures a given Media Processor core + * + * \param[in] coreId Media Processor identifier + * \param[in] executiveEngineId Media Processor Executive Engine identifier + * \param[in] semaphoreTypeId Media Processor semaphores (to be used by communication mechanism) identifier + * \param[in] nbYramBanks is the number of tcm ram banks to reserved for y memory + * \param[in] mediaProcessorMappingBaseAddr Media Processor mapping into host CPU addressable space + * \param[in] commDomain Domain for allocating communication FIFOs + * \param[in] eeDomain Domain for EE instantiation + * \param[in] sdramCodeAllocId Allocator Id for the SDRAM Code segment + * \param[in] sdramDataAllocId Allocator Id for the SDRAM Data segment + * + * \exception TBD + * \return exception number. + * + * \warning The Media Processor mapping address space SHALL BE declared as non cacheable, non bufferable inside host MMU table + * + * \ingroup CM_ENGINE_CONTROL_API + */ +PUBLIC t_cm_error CM_ENGINE_ConfigureMediaProcessorCore( + t_nmf_core_id coreId, + t_nmf_executive_engine_id executiveEngineId, + t_nmf_semaphore_type_id semaphoreTypeId, + t_uint8 nbYramBanks, + const t_cm_system_address *mediaProcessorMappingBaseAddr, + const t_cm_domain_id eeDomain, + const t_cfg_allocator_id sdramCodeAllocId, + const t_cfg_allocator_id sdramDataAllocId + ); + +/*! + * \brief Configure a memory segment for later + * + * \exception TBD + * \return TBD + * + * \warning + * + * \ingroup CM_ENGINE_CONTROL_API + */ +PUBLIC t_cm_error CM_ENGINE_AddMpcSdramSegment( + const t_nmf_memory_segment *pDesc, //!< [in] Memory segment description. + t_cfg_allocator_id *allocId, //!< [out] Identifier of the created allocator. + const char *memoryname //!< [in] Memory purpose name + ); + +/********************************************************************************/ +/* Destruction Component Manager API prototypes */ +/********************************************************************************/ +/*! + * \brief Destruction part + * + * This routine destroyes and releases all resources used by the Component Manager. + * + * \ingroup CM_ENGINE_CONTROL_API + */ +PUBLIC void CM_ENGINE_Destroy(void); + + +#endif /* CONTROL_CONFIGURATION_ENGINE_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/api/control/control_engine.h b/drivers/staging/nmf-cm/cm/engine/api/control/control_engine.h new file mode 100644 index 00000000000..1d823b27fc1 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/control/control_engine.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief CM Engine API. + * + * This file contains the Component Manager Engine API. + */ +/*! + * \defgroup CM_ENGINE_CONTROL_API CM Engine Control API + * \note This API is not for OS integrator, it's only for low level system integration. + * \ingroup CM_ENGINE_MODULE + */ + +#ifndef CM_CONTROL_H_ +#define CM_CONTROL_H_ + +#include <cm/engine/api/control/configuration_engine.h> + +#include <cm/engine/api/control/irq_engine.h> + +#include <cm/engine/api/control/power_engine.h> + +#endif /*CM_CONTROL_H_*/ diff --git a/drivers/staging/nmf-cm/cm/engine/api/control/irq_engine.h b/drivers/staging/nmf-cm/cm/engine/api/control/irq_engine.h new file mode 100644 index 00000000000..20313f13256 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/control/irq_engine.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief NMF API for interrupt handler. + * + * This file contains the Component Manager API for interrupt handler. + */ +#ifndef CONTROL_IRQ_ENGINE_H +#define CONTROL_IRQ_ENGINE_H + +#include <share/inc/nmf.h> +#include <cm/inc/cm_type.h> +#include <nmf/inc/service_type.h> + +/*! + * \brief MPCs -> HOST communication handler + * + * This routine shall be integrated as interrupt handler into the OS + * + * If the given Media Processor Core has been configured (through CM_ConfigureMediaProcessorCore()) as using \ref LOCAL_SEMAPHORES, then + * the NMF communication mechanism will use the embedded MMDSP macrocell semaphore, + * so CM_ProcessMpcEvent(<\e coreId>) shall be called under ISR connected to local MMDSP IRQ0, with the related \e coreId as parameter. + * + * If the given Media Processor Core has been configured (through CM_ConfigureMediaProcessorCore()) as using \ref SYSTEM_SEMAPHORES, then + * the NMF communication mechanism will use the shared system HW Semaphores, + * so CM_ProcessMpcEvent(\ref ARM_CORE_ID) shall be called under ISR connected to shared HW Sem Host IRQ, with \ref ARM_CORE_ID as parameter. + * + * NB: A Media Processor Core belonging to the distribution pool shall be configured with \ref SYSTEM_SEMAPHORES + * + * \see t_nmf_semaphore_type_id description + * + * \param[in] coreId identification of the source of the interrupt + * + * \ingroup CM_ENGINE_CONTROL_API + */ +PUBLIC IMPORT_SHARED void CM_ProcessMpcEvent(t_nmf_core_id coreId); + +/*! + * \brief Service type + * + * \note We used an enumeration in structure since this description remain inside the kernel + * and we assume that everything in the kernel is compile with same compiler and option. + * + * \ingroup CM_ENGINE_CONTROL_API + */ +typedef enum { // Allowed since i + CM_MPC_SERVICE_NONE = 0, //!< No service found + CM_MPC_SERVICE_PANIC = 1, //!< Panic service found + CM_MPC_SERVICE_PRINT = 2 //!< Print service found +} t_cm_service_type; + //!< Service description type +/*! + * \brief Service description data + * + * + * \ingroup CM_ENGINE_CONTROL_API + */ +typedef struct { + union { + t_nmf_panic_data panic; //!< Panic description + struct { + t_uint32 dspAddress; + t_uint32 value1; + t_uint32 value2; + } print; //!< Printf like description + } u; //!< Union of service description +} t_cm_service_description; + +/*! + * \brief MPC Panic handler + * + * This routine shall be called as interrupt handler into the OS. + * + * So CM_getPanicDescription shall be called under ISR connected to local MMDSP IRQ1, with the related \e coreId as parameter. + * + * \param[in] coreId identification of the source of the interrupt + * \param[out] srcType Pointer on service type + * \param[out] srcDescr Pointer on service description + * + * \ingroup CM_ENGINE_CONTROL_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_getServiceDescription( + t_nmf_core_id coreId, + t_cm_service_type *srcType, + t_cm_service_description *srcDescr); + +/*! + * \brief Read a null terminated string inside an MPC + * + * This routine could be used to read the MPC string give as parameter during an CM_NMF_SERVICE_PRINT + * + * \param[in] coreId Identification of the code where read string + * \param[in] dspAddress Address of the string in the MPC + * \param[out] buffer Buffer pointer where returning null terminated string + * \param[in] bufferSize Buffer size + * + * \ingroup CM_ENGINE_CONTROL_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ReadMPCString( + t_nmf_core_id coreId, + t_uint32 dspAddress, + char * buffer, + t_uint32 bufferSize); + +#endif /* CONTROL_IRQ_ENGINE_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/api/domain_engine.h b/drivers/staging/nmf-cm/cm/engine/api/domain_engine.h new file mode 100644 index 00000000000..7cc6f33ed90 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/domain_engine.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief Public Component Manager Memory User SYSCALL API. + * + * This file contains the Component Manager SYSCALL API for manipulating domains. + * + */ + +#ifndef __INC_DOMAIN_ENGINE_H +#define __INC_DOMAIN_ENGINE_H + +#include <cm/engine/memory/inc/domain_type.h> + +/*! + * \brief Create a domain. + * + * Create a memory domain for use in the CM for component instantiation and memory allocation. + * + * \param[in] client Id of the client. + * \param[in] domain Description of domain memories. + * \param[out] handle Idetifier of the created domain + * + * \exception CM_INVALID_DOMAIN_DEFINITION + * \exception CM_INTERNAL_DOMAIN_OVERFLOW + * \exception CM_OK + * + * \return Error code. + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_CreateMemoryDomain( + const t_nmf_client_id client, + const t_cm_domain_memory *domain, + t_cm_domain_id *handle + ); + +/*! + * \brief Create a scratch domain. + * + * Create a scratch memory domain. Scratch domains + * are used to perform overlapping allocations. + * + * \param[in] client Id of the client. + * \param[in] parentId Identifier of the parent domain. + * \param[in] domain Description of domain memories. + * \param[out] handle Idetifier of the created domain + * + * \exception CM_INVALID_DOMAIN_DEFINITION + * \exception CM_INTERNAL_DOMAIN_OVERFLOW + * \exception CM_NO_MORE_MEMORY + * \exception CM_OK + * + * \return Error code. + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_CreateMemoryDomainScratch( + const t_nmf_client_id client, + const t_cm_domain_id parentId, + const t_cm_domain_memory *domain, + t_cm_domain_id *handle + ); + +/*! + * \brief Destroy a memory domain. + + * \param[in] handle Domain identifier to destroy. + * + * \exception CM_INVALID_DOMAIN_HANDLE + * \exception CM_OK + * + * \return Error code. + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_DestroyMemoryDomain( + t_cm_domain_id handle); + +/*! + * \brief Destroy all domains belonging to a given client. + * + * \param[in] client + * + * \return Error code. + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_FlushMemoryDomains( + t_nmf_client_id client); + +/*! + * \brief Retrieve the coreId for a given domain. Utility. + + * \param[in] domainId Domain identifier. + * \param[out] coreId Core identifier. + * + * \exception CM_INVALID_DOMAIN_HANDLE Invalid domain handle + * \exception CM_OK + * + * \return Error code. + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetDomainCoreId(const t_cm_domain_id domainId, t_nmf_core_id *coreId); + +#endif /* __INC_DOMAIN_ENGINE_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/api/executive_engine_mgt_engine.h b/drivers/staging/nmf-cm/cm/engine/api/executive_engine_mgt_engine.h new file mode 100644 index 00000000000..9cb8bc1481b --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/executive_engine_mgt_engine.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief CM executive engine management Engine API. + * + * This file contains the Component Manager executive engine management Engine API. + */ +#ifndef CM_EXECUTIVE_ENGINE_MANAGEMENT_ENGINE_H_ +#define CM_EXECUTIVE_ENGINE_MANAGEMENT_ENGINE_H_ + +#include <cm/inc/cm_type.h> + +/*! + * \brief Return executive engine handle for given core + * + * \param[in] coreId The core for which we want executive engine handle. + * \param[out] executiveEngineHandle executive engine instance (null if the executive engine is not loaded) + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetExecutiveEngineHandle( + t_cm_domain_id domainId, + t_cm_instance_handle *executiveEngineHandle); + +#endif /*CM_EXECUTIVE_ENGINE_MANAGEMENT_ENGINE_H_*/ diff --git a/drivers/staging/nmf-cm/cm/engine/api/memory_engine.h b/drivers/staging/nmf-cm/cm/engine/api/memory_engine.h new file mode 100644 index 00000000000..9f5e25b3ebf --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/memory_engine.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief Public Component Manager Memory User Engine API. + * + * This file contains the Component Manager Engine API for manipulating memory. + * + */ + +#ifndef CM_MEMORY_ENGINE_H_ +#define CM_MEMORY_ENGINE_H_ + +#include <cm/engine/memory/inc/domain_type.h> +#include <cm/engine/memory/inc/memory_type.h> + +/*! + * \brief Allocate memory in a Media Processor Core memory + * + * \param[in] domainId + * \param[in] memType + * \param[in] size + * \param[in] memAlignment + * \param[out] pHandle + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_AllocMpcMemory( + t_cm_domain_id domainId, + t_nmf_client_id clientId, //!< [in] Client ID (aka PID) + t_cm_mpc_memory_type memType, + t_cm_size size, + t_cm_mpc_memory_alignment memAlignment, + t_cm_memory_handle *pHandle + ); + + +/*! + * \brief Free a MPC memory block. + * + * \param[in] handle + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_FreeMpcMemory(t_cm_memory_handle handle); + +/*! + * \brief Get the start address of the MPC memory block seen by the host CPU (physical and logical) + * + * The logical system address returned by this method is valid only in kernel space and the physical + * address is accessible only from kernel space too. + * + * \see OSMem "OS Memory management" for seeing an integration example. + * + * \param[in] handle + * \param[out] pSystemAddress + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemorySystemAddress( + t_cm_memory_handle handle, + t_cm_system_address *pSystemAddress); + +/*! + * \brief Get the start address of the memory block seen by the Media Processor Core + * + * \param[in] handle + * \param[out] pMpcAddress + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemoryMpcAddress( + t_cm_memory_handle handle, + t_uint32 *pMpcAddress); + +/*! + * \brief Get the memory status for given memory type of a given Media Processor Core + * + * \param[in] domainId + * \param[in] memType + * \param[out] pStatus + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemoryStatus( + t_cm_domain_id domainId, + t_cm_mpc_memory_type memType, + t_cm_allocator_status *pStatus); + +#endif /* CM_MEMORY_ENGINE_H_ */ + diff --git a/drivers/staging/nmf-cm/cm/engine/api/migration_engine.h b/drivers/staging/nmf-cm/cm/engine/api/migration_engine.h new file mode 100644 index 00000000000..77a266d4459 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/migration_engine.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#ifndef CM_MIGRATION_ENGINE_H +#define CM_MIGRATION_ENGINE_H + +#include <cm/inc/cm_type.h> +#include <cm/engine/memory/inc/domain_type.h> + +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_Migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst); + +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_Unmigrate(void); + +#endif /* CM_MIGRATION_ENGINE_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/api/perfmeter_engine.h b/drivers/staging/nmf-cm/cm/engine/api/perfmeter_engine.h new file mode 100644 index 00000000000..bead49dc81e --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/perfmeter_engine.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief CM Performance Meter Engine API. + * + * This file contains the Component Manager Performance Meter Engine API. + */ +#ifndef CM_ENGINE_PERFMETER_ENGINE_H_ +#define CM_ENGINE_PERFMETER_ENGINE_H_ + +#include <cm/engine/perfmeter/inc/perfmeter_type.h> + +/*! + * \brief MPC cpu load + * + * \param[in] coreId identification of mpc from which we want cpu load + * \param[out] mpcLoadCounter will contain mpc cpu load counters value if success + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_getMpcLoadCounter( + t_nmf_core_id coreId, + t_cm_mpc_load_counter *mpcLoadCounter); + +/*! + * \brief MPC cpu load + * Same as \ref CM_ENGINE_getMpcLoadCounter() without lock + * + * \param[in] coreId identification of mpc from which we want cpu load + * \param[out] mpcLoadCounter will contain mpc cpu load counters value if success + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_GetMpcLoadCounter( + t_nmf_core_id coreId, + t_cm_mpc_load_counter *mpcLoadCounter); + +#endif /*CM_ENGINE_PERFMETER_ENGINE_H_*/ diff --git a/drivers/staging/nmf-cm/cm/engine/api/repository_mgt_engine.h b/drivers/staging/nmf-cm/cm/engine/api/repository_mgt_engine.h new file mode 100644 index 00000000000..b63c60d85eb --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/api/repository_mgt_engine.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief Repository Component Manager User Engine API. + * + * This file contains the Component Manager Engine API for manipulating the components files. + */ + +#ifndef REPOSITORY_MGT_ENGINE_H_ +#define REPOSITORY_MGT_ENGINE_H_ + +#include <inc/nmf-limits.h> +#include <cm/engine/repository_mgt/inc/repository_type.h> + +/*! + * \brief Get the name(s) of the component(s) to load. + * + * \param[in] client Handle of the client component (optional) + * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH) (optional). + * \param[in] server Handle of the server component (optional) + * \param[in] providedItfServerName Null terminated string (Max size==\ref MAX_INTERFACE_NAME_LENGTH) (optional). + * \param[out] fileList List of required component(s). + * \param[in,out] listSize Initial size of the list as input. Updated with the number of entries really used. + * \param[out] type Interface type of the client required or server provided interface. Null terminated string (Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH) (optional) . + * \param[out] methodNumber Number of method in the interface type of the client required interface. (only used when called from CM_BindComponentToUser) (optional) + * + * \note It returns the component(s) name(s) to load, depending on the first four parameters. + * + * - If all 4 are NULL, it returns the name of the Executive Engine components to load + * - If 'client' is NULL, it returns the name of the required components for a Bind From CMCore. + * - If 'server' is NULL, it returns the name of the required components for a Bind To CMCore. + * - If none is NULL, it returns the name of the required components for an asynchronous binding + * + * The names are returned in fileList, whose initial size is specified in listSize. + * (sizeList must be the number of provided entries of \ref MAX_INTERFACE_TYPE_NAME_LENGTH length + * If not enough space is provided, CM_NO_MORE_MEMORY is returned + * + * sizeList is updated with the number entries really filled. + * + * This method is also used to retrieve the interface type when called from CM_BindComponentToUser and CM_BindComponentFromUser + * and the number of methods when called from CM_BindComponentToUser. + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetRequiredComponentFiles( + // IN + t_action_to_do action, + const t_cm_instance_handle client, + const char *requiredItfClientName, + const t_cm_instance_handle server, + const char *providedItfServerName, + // OUT component to be pushed + char fileList[][MAX_INTERFACE_TYPE_NAME_LENGTH], + // IN max component allowed to be pushed + t_uint32 listSize, + // OUT interface information + char type[MAX_INTERFACE_TYPE_NAME_LENGTH], + t_uint32 *methodNumber); + +/*! + * \brief Push a component into the CM Component Cache. + * + * \param[in] name Component name, null terminated string (Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH) + * \param[in] data Pointer to _user_ data of the component. + * \param[in] size Size of the data. + * + * \note Push a component in the Component Cache + * The 'data' must be provided such a way that they can be freed by a call to OSAL_Free() + * The caller doesn't need and must NOT free the data, even in case of failure. + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_PushComponent(const char *name, const void *data, t_cm_size size); + +/*! + * \brief Remove a component from the CM Component Cache. + * + * \param[in] name Component name, null terminated string (Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH) + * + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_ReleaseComponent (const char *name); + +/*! + * \brief Check if the CM Component Cache is empty. + * + * \return a boolean value TRUE or FALSE. + * \ingroup CM_ENGINE_API + */ +PUBLIC IMPORT_SHARED t_bool CM_ENGINE_IsComponentCacheEmpty(void); +#endif /*REPOSITORY_MGT_ENGINE_H_*/ diff --git a/drivers/staging/nmf-cm/cm/engine/communication/fifo/inc/nmf_fifo_arm.h b/drivers/staging/nmf-cm/cm/engine/communication/fifo/inc/nmf_fifo_arm.h new file mode 100644 index 00000000000..0463d6a71a5 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/communication/fifo/inc/nmf_fifo_arm.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * + */ +#ifndef __INC_NMF_FIFO_ARM +#define __INC_NMF_FIFO_ARM + +#include <cm/inc/cm_type.h> +#include <share/communication/inc/nmf_fifo_desc.h> +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/component/inc/instance.h> +#include <cm/engine/memory/inc/domain.h> + +/* + * ARM Fifo descriptor (encapsulate the share one) + */ +typedef struct +{ + t_uint32 magic; + t_memory_handle chunkHandle; + t_nmf_core_id pusherCoreId; + t_nmf_core_id poperCoreId; + t_shared_addr dspAdress; + t_dsp_address_info dspAddressInfo; + t_nmf_fifo_desc *fifoDesc; //used for all fifo operations and systematically updated by the migrated offset (see cm_AllocEvent) + t_nmf_fifo_desc *fifoDescShadow; //shadow desc, is used to restore state after migration and perform the update of the real desc + + // ExtendedField + t_memory_handle extendedFieldHandle; + t_shared_field *extendedField; +} t_nmf_fifo_arm_desc; + +PUBLIC t_uint32 fifo_isFifoIdValid(t_nmf_fifo_arm_desc *pArmFifo); +PUBLIC t_nmf_fifo_arm_desc* fifo_alloc( + t_nmf_core_id pusherCoreId, t_nmf_core_id poperCoreId, + t_uint16 size_in_16bit, t_uint16 nbElem, t_uint16 nbExtendedSharedFields, + t_dsp_memory_type_id memType, t_dsp_memory_type_id memExtendedFieldType, t_cm_domain_id domainId); +PUBLIC void fifo_free(t_nmf_fifo_arm_desc *pArmFifo); +PUBLIC t_uint16 fifo_normalizeDepth(t_uint16 requestedDepth); + +PUBLIC t_shared_addr fifo_getAndAckNextElemToWritePointer(t_nmf_fifo_arm_desc *pArmFifo); +PUBLIC t_shared_addr fifo_getAndAckNextElemToReadPointer(t_nmf_fifo_arm_desc *pArmFifo); +PUBLIC t_shared_addr fifo_getNextElemToWritePointer(t_nmf_fifo_arm_desc *pArmFifo); +PUBLIC t_shared_addr fifo_getNextElemToReadPointer(t_nmf_fifo_arm_desc *pArmFifo); +PUBLIC void fifo_acknowledgeRead(t_nmf_fifo_arm_desc *pArmFifo); +PUBLIC void fifo_acknowledgeWrite(t_nmf_fifo_arm_desc *pArmFifo); +PUBLIC void fifo_coms_acknowledgeWriteAndInterruptGeneration(t_nmf_fifo_arm_desc *pArmFifo); + +PUBLIC t_cm_error fifo_params_setSharedField(t_nmf_fifo_arm_desc *pArmFifo, t_uint32 sharedFieldIndex, t_shared_field value); + +#endif /* __INC_NMF_FIFO_ARM */ diff --git a/drivers/staging/nmf-cm/cm/engine/communication/fifo/src/nmf_fifo_arm.c b/drivers/staging/nmf-cm/cm/engine/communication/fifo/src/nmf_fifo_arm.c new file mode 100644 index 00000000000..48d7f9e9f03 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/communication/fifo/src/nmf_fifo_arm.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * + */ +#include <share/communication/inc/nmf_fifo_desc.h> +#include <cm/engine/semaphores/inc/semaphores.h> +#include <cm/engine/component/inc/instance.h> +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> +#include "../inc/nmf_fifo_arm.h" + +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/memory/inc/domain.h> +#include <cm/engine/trace/inc/trace.h> + +/* define value of fifo magic number */ +#define NMF_FIFO_MAGIC_NB 0xF1F0BEEF + +PRIVATE t_uint16 fifo_getCount( + t_uint16 writeIndex, + t_uint16 readIndex, + t_uint16 fifoSize +) +{ + if (writeIndex >= readIndex) {return writeIndex - readIndex;} + else {return fifoSize - readIndex + writeIndex;} +} + +PRIVATE t_uint16 fifo_incrementIndex( + t_uint16 index, + t_uint16 wrappingValue +) +{ + if (++index == wrappingValue) {index = 0;} + + return index; +} + +PUBLIC t_uint16 fifo_normalizeDepth(t_uint16 requestedDepth) +{ + /* with new implementation we don't align on power of two */ + return requestedDepth; +} + +PUBLIC t_nmf_fifo_arm_desc* fifo_alloc( + t_nmf_core_id pusherCoreId, t_nmf_core_id poperCoreId, + t_uint16 size_in_16bit, t_uint16 nbElem, t_uint16 nbExtendedSharedFields, + t_dsp_memory_type_id memType, t_dsp_memory_type_id memExtendedFieldType, t_cm_domain_id domainId) +{ + t_uint16 realNbElem = nbElem + 1;/* we need one more elem in new implementation */ + t_uint16 sizeToAlloc = sizeof(t_nmf_fifo_desc) + ((size_in_16bit<<1)*realNbElem); + t_nmf_fifo_arm_desc *pArmFifoDesc; + + pArmFifoDesc = (t_nmf_fifo_arm_desc*)OSAL_Alloc(sizeof (t_nmf_fifo_arm_desc)); + if (pArmFifoDesc == NULL) + goto errorde; + + pArmFifoDesc->chunkHandle = cm_DM_Alloc(domainId, memType, + (sizeToAlloc/2), CM_MM_ALIGN_2WORDS, TRUE); /* size in 16-bit since we use EXT16 memory */ + if (pArmFifoDesc->chunkHandle == INVALID_MEMORY_HANDLE) + goto errorsh; + + pArmFifoDesc->magic = NMF_FIFO_MAGIC_NB; + pArmFifoDesc->pusherCoreId = pusherCoreId; + pArmFifoDesc->poperCoreId = poperCoreId; + + pArmFifoDesc->fifoDesc = (t_nmf_fifo_desc *)cm_DSP_GetHostLogicalAddress(pArmFifoDesc->chunkHandle); + cm_DSP_GetDspAddress(pArmFifoDesc->chunkHandle, &pArmFifoDesc->dspAdress); + + pArmFifoDesc->fifoDescShadow = pArmFifoDesc->fifoDesc; + cm_DSP_GetDspDataAddressInfo(cm_DM_GetDomainCoreId(domainId), pArmFifoDesc->dspAdress, &pArmFifoDesc->dspAddressInfo); + + pArmFifoDesc->extendedFieldHandle = INVALID_MEMORY_HANDLE; + pArmFifoDesc->extendedField = NULL; + + pArmFifoDesc->fifoDesc->elemSize = size_in_16bit; + pArmFifoDesc->fifoDesc->fifoFullValue = nbElem; + pArmFifoDesc->fifoDesc->wrappingValue = realNbElem; + + pArmFifoDesc->fifoDesc->semId = cm_SEM_Alloc(pusherCoreId, poperCoreId); + pArmFifoDesc->fifoDesc->readIndex = 0; + pArmFifoDesc->fifoDesc->writeIndex = 0; + + LOG_INTERNAL(2, "\n##### Fifo alloc 0x%x (0x%x)\n\n", pArmFifoDesc, pArmFifoDesc->fifoDesc, 0, 0, 0, 0); + + if (nbExtendedSharedFields >= 1) + { + if(poperCoreId == ARM_CORE_ID) + { + /* Optimization: Don't put extended Field in DSP memory since use only by ARM if popper */ + pArmFifoDesc->extendedField = (t_shared_field*)OSAL_Alloc(nbExtendedSharedFields * sizeof(t_shared_field)); + if (pArmFifoDesc->extendedField == NULL) + goto errorex; + + pArmFifoDesc->fifoDesc->extendedField = (t_uint32)pArmFifoDesc->extendedField; + } + else + { + pArmFifoDesc->extendedFieldHandle = cm_DM_Alloc(domainId, memExtendedFieldType, + nbExtendedSharedFields * sizeof(t_shared_field) / 4, CM_MM_ALIGN_WORD, TRUE); + if (pArmFifoDesc->extendedFieldHandle == INVALID_MEMORY_HANDLE) + goto errorex; + + pArmFifoDesc->extendedField = (t_shared_field*)cm_DSP_GetHostLogicalAddress(pArmFifoDesc->extendedFieldHandle); + cm_DSP_GetDspAddress(pArmFifoDesc->extendedFieldHandle, (t_uint32*)&pArmFifoDesc->fifoDesc->extendedField); + } + + pArmFifoDesc->extendedField[EXTENDED_FIELD_BCTHIS_OR_TOP] = (t_shared_field)0; + } + + return pArmFifoDesc; + +errorex: + (void)cm_DM_Free(pArmFifoDesc->chunkHandle, TRUE); +errorsh: + OSAL_Free(pArmFifoDesc); +errorde: + return NULL; +} + +PUBLIC t_uint32 fifo_isFifoIdValid(t_nmf_fifo_arm_desc *pArmFifo) +{ + if (((t_uint32)pArmFifo & CM_MM_ALIGN_WORD) != 0) {return FALSE;} + if (pArmFifo->magic == NMF_FIFO_MAGIC_NB) {return TRUE;} + else {return FALSE;} +} + +PUBLIC void fifo_free(t_nmf_fifo_arm_desc *pArmFifo) +{ + CM_ASSERT(pArmFifo->pusherCoreId != ARM_CORE_ID || pArmFifo->poperCoreId != ARM_CORE_ID); + + pArmFifo->magic = ~NMF_FIFO_MAGIC_NB; + + if(pArmFifo->extendedFieldHandle != INVALID_MEMORY_HANDLE) + (void)cm_DM_Free(pArmFifo->extendedFieldHandle, TRUE); + else if(pArmFifo->extendedField != NULL) + OSAL_Free(pArmFifo->extendedField); + + (void)cm_DM_Free(pArmFifo->chunkHandle, TRUE); + OSAL_Free(pArmFifo); +} + +PUBLIC t_shared_addr fifo_getAndAckNextElemToWritePointer(t_nmf_fifo_arm_desc *pArmFifo) +{ + t_shared_addr retValue; + + retValue = fifo_getNextElemToWritePointer(pArmFifo); + if (retValue != 0) + { + fifo_acknowledgeWrite(pArmFifo); + } + + return retValue; +} + +PUBLIC t_shared_addr fifo_getAndAckNextElemToReadPointer(t_nmf_fifo_arm_desc *pArmFifo) +{ + t_shared_addr retValue; + + retValue = fifo_getNextElemToReadPointer(pArmFifo); + if (retValue != 0) + { + fifo_acknowledgeRead(pArmFifo); + } + + return retValue; +} + +PUBLIC t_shared_addr fifo_getNextElemToWritePointer(t_nmf_fifo_arm_desc *pArmFifo) +{ + t_shared_addr retValue = 0; + t_nmf_fifo_desc *pDesc; + t_uint16 count; + + if ((NULL == pArmFifo) || (NULL == (pDesc = pArmFifo->fifoDesc))) + return 0; + + count = fifo_getCount(pDesc->writeIndex, pDesc->readIndex,pDesc->wrappingValue); + if (count < pDesc->fifoFullValue) + { + retValue = ((t_shared_addr)pDesc + sizeof(t_nmf_fifo_desc) + (pDesc->writeIndex*(pDesc->elemSize<<1))); + } + + return retValue; +} + +PUBLIC t_shared_addr fifo_getNextElemToReadPointer(t_nmf_fifo_arm_desc *pArmFifo) +{ + t_shared_addr retValue = 0; + t_nmf_fifo_desc *pDesc; + t_uint16 count; + + if ((NULL == pArmFifo) || (NULL == (pDesc = pArmFifo->fifoDesc))) + return 0; + + count = fifo_getCount(pDesc->writeIndex, pDesc->readIndex,pDesc->wrappingValue); + if (count != 0) + { + retValue = ((t_shared_addr)pDesc+ sizeof(t_nmf_fifo_desc) + (pDesc->readIndex*(pDesc->elemSize<<1))); + } + + return retValue; +} + +PUBLIC void fifo_acknowledgeRead(t_nmf_fifo_arm_desc *pArmFifo) +{ + t_nmf_fifo_desc *pDesc = pArmFifo->fifoDesc; + + pDesc->readIndex = fifo_incrementIndex(pDesc->readIndex, pDesc->wrappingValue); +} + +PUBLIC void fifo_acknowledgeWrite(t_nmf_fifo_arm_desc *pArmFifo) +{ + t_nmf_fifo_desc *pDesc = pArmFifo->fifoDesc; + + pDesc->writeIndex = fifo_incrementIndex(pDesc->writeIndex, pDesc->wrappingValue); +} + +PUBLIC void fifo_coms_acknowledgeWriteAndInterruptGeneration(t_nmf_fifo_arm_desc *pArmFifo) +{ + t_nmf_fifo_desc *pDesc = pArmFifo->fifoDesc; + + fifo_acknowledgeWrite(pArmFifo); + //Be sure before generate irq that fifo has been updated + OSAL_mb(); + cm_SEM_GenerateIrq[pArmFifo->poperCoreId](pArmFifo->poperCoreId, pDesc->semId); + //cm_SEM_Take[pArmFifo->poperCoreId](pArmFifo->poperCoreId, pDesc->semId); + //cm_SEM_GiveWithInterruptGeneration[pArmFifo->poperCoreId](pArmFifo->poperCoreId, pDesc->semId); +} + +PUBLIC t_cm_error fifo_params_setSharedField(t_nmf_fifo_arm_desc *pArmFifo, t_uint32 sharedFieldIndex, t_shared_field value) +{ + pArmFifo->extendedField[sharedFieldIndex] = value; + + return CM_OK; +} + diff --git a/drivers/staging/nmf-cm/cm/engine/communication/inc/communication.h b/drivers/staging/nmf-cm/cm/engine/communication/inc/communication.h new file mode 100644 index 00000000000..53ab87b7096 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/communication/inc/communication.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Components Management internal methods - Communication part. + * + */ +#ifndef __INC_NMF_COM +#define __INC_NMF_COM + +#include <cm/inc/cm_type.h> +#include <cm/engine/communication/fifo/inc/nmf_fifo_arm.h> +#include <cm/engine/memory/inc/memory.h> + +#include <cm/engine/communication/inc/communication_type.h> + +extern t_dsp_memory_type_id comsLocation; +extern t_dsp_memory_type_id paramsLocation; +extern t_dsp_memory_type_id extendedFieldLocation; + +PUBLIC t_cm_error cm_COM_Init(t_nmf_coms_location comsLocation); +PUBLIC t_cm_error cm_COM_AllocateMpc(t_nmf_core_id coreId); +PUBLIC void cm_COM_InitMpc(t_nmf_core_id coreId); +PUBLIC void cm_COM_FreeMpc(t_nmf_core_id coreId); + +PUBLIC t_cm_error cm_PushEventTrace(t_nmf_fifo_arm_desc*, t_event_params_handle h, t_uint32 methodIndex, t_uint32 isTrace); +PUBLIC t_cm_error cm_PushEvent(t_nmf_fifo_arm_desc *pArmFifo, t_event_params_handle h, t_uint32 methodIndex); +PUBLIC void cm_AcknowledgeEvent(t_nmf_fifo_arm_desc *pArmFifo); +PUBLIC t_event_params_handle cm_AllocEvent(t_nmf_fifo_arm_desc *pArmFifo); + +/*! + * \internal + * \brief Definition of custom value for userTHIS parameter of PostDfc OSAL call + * + * This value is used as 1st parameter of a pPostDfc call to indicate that a given interrupt is linked to an internal Component Manager event + */ +#define NMF_INTERNAL_USERTHIS ((void*)MASK_ALL32) + +typedef void (*t_callback_method)(t_nmf_core_id coreId, t_event_params_handle pParam); + +#endif /* __INC_NMF_COM */ diff --git a/drivers/staging/nmf-cm/cm/engine/communication/inc/communication_type.h b/drivers/staging/nmf-cm/cm/engine/communication/inc/communication_type.h new file mode 100644 index 00000000000..db426845a82 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/communication/inc/communication_type.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Communication Component Manager API type. + */ +#ifndef COMMUNICATION_TYPE_H_ +#define COMMUNICATION_TYPE_H_ + +#include <cm/inc/cm_type.h> + + +/*! + * \brief Buffer type used for (un)marshalling parameters. + * + * This buffer type is used for (un)marshalling paramaters. It can either be a + * shared memory buffer (ESRAM or SDRAM) or a pure host software memory (stack). + + * \ingroup CM_ENGINE_API + */ +typedef t_uint16 *t_event_params_handle; + +/*! + * \brief Component manager handle to Host -> MPC communication. + * + * \ingroup CM_ENGINE_API + */ +typedef t_uint32 t_cm_bf_host2mpc_handle; + +/*! + * \brief Component manager handle to MPC -> Host communication. + * + * \ingroup CM_ENGINE_API + */ +typedef t_uint32 t_cm_bf_mpc2host_handle; + +/*! + * \brief Component manager proxy handle to MPC -> Host skeleton context. + * + * \ingroup CM_ENGINE_API + */ +typedef t_uint32 t_nmf_mpc2host_handle; + +/*! + * @defgroup t_nmf_coms_location t_nmf_coms_location + * \brief Definition of the location of the internal CM communication objects + * + * @{ + * \ingroup CM_ENGINE_API + */ +typedef t_uint8 t_nmf_coms_location; //!< Fake enumeration type +#define COMS_IN_ESRAM ((t_nmf_coms_location)0) //!< All coms objects (coms and params fifos) will be in embedded RAM +#define COMS_IN_SDRAM ((t_nmf_coms_location)1) //!< All coms objects (coms and params fifos) will be in external RAM +/* @} */ + +#endif /*COMMUNICATION_TYPE_H_*/ diff --git a/drivers/staging/nmf-cm/cm/engine/communication/src/communication.c b/drivers/staging/nmf-cm/cm/engine/communication/src/communication.c new file mode 100644 index 00000000000..ead1e090d7c --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/communication/src/communication.c @@ -0,0 +1,328 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#include <cm/inc/cm_type.h> +#include "../inc/communication.h" +#include <share/communication/inc/communication_fifo.h> +#include <cm/engine/api/control/irq_engine.h> +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/component/inc/introspection.h> +#include <cm/engine/communication/fifo/inc/nmf_fifo_arm.h> +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> +#include <cm/engine/memory/inc/domain.h> +#include <cm/engine/memory/inc/migration.h> +#include <cm/engine/semaphores/inc/semaphores.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> + +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/trace/inc/xtitrace.h> + +#include <cm/engine/component/inc/initializer.h> + +#define ARM_DSP_EVENT_FIFO_SIZE 128 + +t_dsp_memory_type_id comsLocation; +t_dsp_memory_type_id paramsLocation; +t_dsp_memory_type_id extendedFieldLocation; + +#define __DEBUG + +#ifdef __DEBUG +PRIVATE volatile t_uint32 armdspCounter = 0; +PRIVATE volatile t_uint32 armdspIrqCounter = 0; +PRIVATE volatile t_uint32 dsparmCounter = 0; +PRIVATE volatile t_uint32 dsparmIrqCounter = 0; +#endif /* __DEBUG */ + +t_nmf_fifo_arm_desc* mpc2mpcComsFifoId[NB_CORE_IDS][NB_CORE_IDS]; + +PRIVATE const t_callback_method internalHostJumptable[] = { + processAsyncAcknowledge, + processAsyncAcknowledge, + processAsyncAcknowledge, + processSyncAcknowledge, + processAsyncAcknowledge, + processAsyncAcknowledge, + processAsyncAcknowledge, + processSyncAcknowledge, + processAsyncAcknowledge, + processSyncAcknowledge, + processSyncAcknowledge, // Start sync + processSyncAcknowledge // Stop sync +}; + +PUBLIC t_cm_error cm_COM_Init(t_nmf_coms_location _comsLocation) +{ + t_nmf_core_id coreId, localCoreId; + + /* + * Configure the default location of coms and params fifo (configuration by user) */ + switch(_comsLocation) + { + case COMS_IN_SDRAM: + comsLocation = SDRAM_EXT16; + paramsLocation = SDRAM_EXT16; + extendedFieldLocation = SDRAM_EXT24; + break; + case COMS_IN_ESRAM: + comsLocation = ESRAM_EXT16; + paramsLocation = ESRAM_EXT16; + extendedFieldLocation = ESRAM_EXT24; + break; + default: CM_ASSERT(0); + } + + for (coreId = ARM_CORE_ID; coreId < NB_CORE_IDS; coreId++) + { + for (localCoreId = ARM_CORE_ID; localCoreId < NB_CORE_IDS; localCoreId++) + { + mpc2mpcComsFifoId[coreId][localCoreId] = NULL; + } + } + + return CM_OK; +} + +PUBLIC t_cm_error cm_COM_AllocateMpc(t_nmf_core_id coreId) +{ + t_nmf_core_id localCoreId; + + /* + * Allocation of the coms fifo with neighbor MPCs + * if they are already initialized (known through initializedCoresMask) + */ + for (localCoreId = ARM_CORE_ID; localCoreId < NB_CORE_IDS; localCoreId++) + { + if (localCoreId == coreId) continue; /* no coms fifo with itself ;) */ + if(cm_DSP_GetState(localCoreId)->state != MPC_STATE_BOOTED) continue; + + /* + * coms fifo from other initialized MPCs to the given one + */ + if (mpc2mpcComsFifoId[coreId][localCoreId] != NULL) continue; /* coms fifo already allocated */ + + mpc2mpcComsFifoId[coreId][localCoreId] = fifo_alloc( + coreId, localCoreId, + EVENT_ELEM_SIZE_IN_BYTE/2, ARM_DSP_EVENT_FIFO_SIZE, + 0, comsLocation, extendedFieldLocation, cm_DSP_GetState(coreId)->domainEE + ); + if (mpc2mpcComsFifoId[coreId][localCoreId] == NULL) + goto oom; + + /* + * coms fifo from the given MPC to the other initialized ones + */ + if (mpc2mpcComsFifoId[localCoreId][coreId] != NULL) continue; /* coms fifo already allocated */ + + mpc2mpcComsFifoId[localCoreId][coreId] = fifo_alloc( + localCoreId, coreId, + EVENT_ELEM_SIZE_IN_BYTE/2, ARM_DSP_EVENT_FIFO_SIZE, + 0, comsLocation, extendedFieldLocation, cm_DSP_GetState(coreId)->domainEE + ); + if (mpc2mpcComsFifoId[localCoreId][coreId] == NULL) + goto oom; + } + + return CM_OK; +oom: + cm_COM_FreeMpc(coreId); + ERROR("CM_NO_MORE_MEMORY: fifo_alloc() failed in cm_COM_AllocateMpc()\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; +} + +PUBLIC void cm_COM_InitMpc(t_nmf_core_id coreId) +{ + // Here we assume that attribute are in XRAM, thus we don't need memory type + t_uint32* toNeighborsComsFifoIdSharedVar[NB_CORE_IDS]; + t_uint32* fromNeighborsComsFifoIdSharedVar[NB_CORE_IDS]; + + t_nmf_core_id localCoreId; + + /* + * Initialization of the core identifier of a given Executive Engine + * Used into communication scheme so the init is done here, will be moved MAY BE into EE loading module!!! + */ + cm_writeAttribute(cm_EEM_getExecutiveEngine(coreId)->instance, "semaphores/myCoreId", coreId); + + /* + * Initialization of the coms fifo with the Host for the given coreId + */ + for (localCoreId = FIRST_MPC_ID/* NOT ARM*/; localCoreId <= LAST_CORE_ID; localCoreId++) + { + // Note: This loop will also include coreId in order to fill + if(cm_DSP_GetState(localCoreId)->state != MPC_STATE_BOOTED) continue;/* no coms fifo initialisation with not booted MPC */ + + toNeighborsComsFifoIdSharedVar[localCoreId] = (t_uint32*)cm_getAttributeHostAddr(cm_EEM_getExecutiveEngine(localCoreId)->instance, "comms/toNeighborsComsFifoId"); + + fromNeighborsComsFifoIdSharedVar[localCoreId] = (t_uint32*)cm_getAttributeHostAddr(cm_EEM_getExecutiveEngine(localCoreId)->instance, "comms/fromNeighborsComsFifoId"); + } + + toNeighborsComsFifoIdSharedVar[coreId][ARM_CORE_ID] = mpc2mpcComsFifoId[coreId][ARM_CORE_ID]->dspAdress; + fromNeighborsComsFifoIdSharedVar[coreId][ARM_CORE_ID] = mpc2mpcComsFifoId[ARM_CORE_ID][coreId]->dspAdress; + + for (localCoreId = FIRST_MPC_ID/* NOT ARM*/; localCoreId <= LAST_CORE_ID; localCoreId++) + { + if (localCoreId == coreId) continue; /* no coms fifo with itself ;) */ + if(cm_DSP_GetState(localCoreId)->state != MPC_STATE_BOOTED) continue;/* no coms fifo initialisation with not booted MPC */ + + toNeighborsComsFifoIdSharedVar[coreId][localCoreId] = mpc2mpcComsFifoId[coreId][localCoreId]->dspAdress; + fromNeighborsComsFifoIdSharedVar[localCoreId][coreId] = mpc2mpcComsFifoId[coreId][localCoreId]->dspAdress; + + fromNeighborsComsFifoIdSharedVar[coreId][localCoreId] = mpc2mpcComsFifoId[localCoreId][coreId]->dspAdress; + toNeighborsComsFifoIdSharedVar[localCoreId][coreId] = mpc2mpcComsFifoId[localCoreId][coreId]->dspAdress; + } +} + +PUBLIC void cm_COM_FreeMpc(t_nmf_core_id coreId) +{ + t_nmf_core_id localCoreId; + + for (localCoreId = ARM_CORE_ID; localCoreId < NB_CORE_IDS; localCoreId++) + { + /* + * Free coms fifo from other initialized MPCs to the given one + */ + if ( mpc2mpcComsFifoId[coreId][localCoreId] != NULL) + { + fifo_free(mpc2mpcComsFifoId[coreId][localCoreId]); + mpc2mpcComsFifoId[coreId][localCoreId] = NULL; + } + + /* + * Free coms fifo from the given MPC to the other initialized ones + */ + if ( mpc2mpcComsFifoId[localCoreId][coreId] != NULL) + { + fifo_free(mpc2mpcComsFifoId[localCoreId][coreId]); + mpc2mpcComsFifoId[localCoreId][coreId] = NULL; + } + } +} + +PUBLIC t_event_params_handle cm_AllocEvent(t_nmf_fifo_arm_desc *pArmFifo) + +{ + t_uint32 retValue; + + //migration impacts the ARM-side address of the fifoDesc, + //thus translate the fifo desc adress systematically. + pArmFifo->fifoDesc = (t_nmf_fifo_desc*)cm_migration_translate(pArmFifo->dspAddressInfo.segmentType, (t_shared_addr)pArmFifo->fifoDescShadow); + + retValue = fifo_getAndAckNextElemToWritePointer(pArmFifo); + + return (t_event_params_handle)retValue; +} + +PUBLIC void cm_AcknowledgeEvent(t_nmf_fifo_arm_desc *pArmFifo) +{ + fifo_acknowledgeRead(pArmFifo); +} + +PUBLIC t_cm_error cm_PushEventTrace(t_nmf_fifo_arm_desc *pArmFifo, t_event_params_handle h, t_uint32 methodIndex, t_uint32 isTrace) +{ + t_uint32 retValue; + + retValue = fifo_getNextElemToWritePointer(mpc2mpcComsFifoId[ARM_CORE_ID][pArmFifo->poperCoreId]); + + if(retValue != 0x0) { + t_shared_field *pEvent = (t_shared_field *)retValue; + +#ifdef __DEBUG + armdspCounter++; +#endif /* __DEBUG */ + + pEvent[EVENT_ELEM_METHOD_IDX] = (t_shared_addr)methodIndex; + pEvent[EVENT_ELEM_PARAM_IDX] = pArmFifo->dspAdress + (((t_cm_logical_address)h - (t_cm_logical_address)pArmFifo->fifoDesc) >> 1); //note byte to half-word conversion + pEvent[EVENT_ELEM_EXTFIELD_IDX] = pArmFifo->fifoDesc->extendedField; + + if (isTrace) + { + cm_TRC_traceCommunication( + TRACE_COMMUNICATION_COMMAND_SEND, + ARM_CORE_ID, + pArmFifo->poperCoreId); + } + fifo_coms_acknowledgeWriteAndInterruptGeneration(mpc2mpcComsFifoId[ARM_CORE_ID][pArmFifo->poperCoreId]); + + return CM_OK; + } + + ERROR("CM_MPC_NOT_RESPONDING: FIFO COM full '%s'\n", 0, 0, 0, 0, 0, 0); + return CM_MPC_NOT_RESPONDING; +} + +PUBLIC t_cm_error cm_PushEvent(t_nmf_fifo_arm_desc *pArmFifo, t_event_params_handle h, t_uint32 methodIndex) +{ + return cm_PushEventTrace(pArmFifo,h,methodIndex,1); +} + +static void cmProcessMPCFifo(t_nmf_core_id coreId) +{ + t_shared_field *pEvent; + + while((pEvent = (t_shared_field *)fifo_getNextElemToReadPointer(mpc2mpcComsFifoId[coreId][ARM_CORE_ID])) != NULL) + { + t_event_params_handle pParamsAddr; + t_shared_field *pParamsFifoESFDesc; + + pParamsAddr = (t_event_params_handle)cm_DSP_ConvertDspAddressToHostLogicalAddress( + coreId, + pEvent[EVENT_ELEM_PARAM_IDX]); + pParamsFifoESFDesc = (t_shared_field *)pEvent[EVENT_ELEM_EXTFIELD_IDX]; +#ifdef __DEBUG + dsparmCounter++; +#endif /* __DEBUG */ + + if(pParamsFifoESFDesc[EXTENDED_FIELD_BCTHIS_OR_TOP] == (t_shared_field)NMF_INTERNAL_USERTHIS) + { + internalHostJumptable[pEvent[EVENT_ELEM_METHOD_IDX]](coreId, pParamsAddr); + } + else + { + cm_TRC_traceCommunication( + TRACE_COMMUNICATION_COMMAND_RECEIVE, + ARM_CORE_ID, + coreId); + + OSAL_PostDfc( + pParamsFifoESFDesc[EXTENDED_FIELD_BCTHIS_OR_TOP], + pEvent[EVENT_ELEM_METHOD_IDX], + pParamsAddr, + pParamsFifoESFDesc[EXTENDED_FIELD_BCDESC]); + } + + // [Pwr] mpc2hostComsFifoId value is checked to support the case where + // CM_PostCleanUpAndFlush method is called under interrupt context + // -> mpc2hostComsFifoId can be released. + if (mpc2mpcComsFifoId[coreId][ARM_CORE_ID] != NULL) + fifo_acknowledgeRead(mpc2mpcComsFifoId[coreId][ARM_CORE_ID]); + else + break; + } +} + +PUBLIC EXPORT_SHARED void CM_ProcessMpcEvent(t_nmf_core_id coreId) +{ +#ifdef __DEBUG + dsparmIrqCounter++; +#endif /* __DEBUG */ + + if (coreId != ARM_CORE_ID) + { + /* Acknowledge DSP communication interrupt */ + cm_DSP_AcknowledgeDspIrq(coreId, DSP2ARM_IRQ_0); + + cmProcessMPCFifo(coreId); + } + else + { + while((coreId = cm_HSEM_GetCoreIdFromIrqSrc()) <= LAST_MPC_ID) + cmProcessMPCFifo(coreId); + } +} + diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/bind.h b/drivers/staging/nmf-cm/cm/engine/component/inc/bind.h new file mode 100644 index 00000000000..325703e3367 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/inc/bind.h @@ -0,0 +1,443 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + * \brief Binding Factories internal API. + * + * \defgroup BF_COMMON Binding factories: Common API + * \defgroup BF_PRIMITIVE Binding Factories: Primitive API + * \defgroup BF_TRACE Binding Factories: Trace API + * \defgroup BF_ASYNCHRONOUS Binding Factories: Asynchronous API + * \defgroup BF_DISTRIBUTED Binding Factories: Distributed API + */ +#ifndef __INC_CM_BIND_H +#define __INC_CM_BIND_H + +#include <cm/engine/component/inc/introspection.h> +#include <cm/engine/communication/inc/communication.h> +#include <cm/engine/utils/inc/table.h> + +/** + * \internal + * \ingroup BF_COMMON + * + * \brief Identification number of prefedined Binding Factories + */ +typedef enum { + BF_SYNCHRONOUS, //!< Intra-DSP Synchronous Binding Factory Identifier + BF_TRACE, //!< Intra-DSP trace synchronous Binding Factory Identifier + BF_ASYNCHRONOUS, //!< Intra-DSP Asynchronous Binding Factory Identifier + BF_DSP2HOST, //!< DSP to Host Binding Factory Identifier + BF_HOST2DSP, //!< Host to DSP Binding Factory Identifier + BF_DSP2DSP, //!< DSP to DSP Binding Factory Identifier +} t_bf_info_ID; + +/*! + * \internal + * \brief Description of a provided interface + * + * \ingroup COMPONENT_INTERNAL + */ +typedef struct _t_interface_reference { + const t_component_instance *instance; //!< Component instance that provide this interface + t_uint8 provideIndex; //!< Index of the interface in the provide array + t_uint8 collectionIndex;//!< Index in the collection if provided interface is a collection + t_bf_info_ID bfInfoID; //!< Identification of BF used for creating binding + void* bfInfo; //!< Storage of the binding factory info +} t_interface_reference; + +/** + * \internal + * \ingroup BF_COMMON + * + * Make some basic sanity check for a client: + * - component stopped + * - Interface really required + * + * \param[in] client The client component instance handle. + * \param[in] requiredItfClientName The client required interface name + * \param[out] requiredItf return the required interface (avoid user searching) + */ +t_cm_error cm_checkValidClient( + const t_component_instance* client, + const char* requiredItfClientName, + t_interface_require_description *itfRequire, + t_bool *bindable); +/** + * \internal + * \ingroup BF_COMMON + * + * Make some basic sanity check for a server: + * - Interface really provided + * + * \param[in] server The server component instance handle. + * \param[in] providedItfServerName The server provided interface name + * \param[out] itf return the provided interface (avoid user searching) + */ +t_cm_error cm_checkValidServer( + const t_component_instance* server, + const char* providedItfServerName, + t_interface_provide_description *itfProvide); + +/** + * \internal + * \ingroup BF_COMMON + * + * Make some basic sanity check for a binding: + * - Sanity check for a server + * - Sanity check for a client (and potentially wait initialisation) + * - Provided and required interface matches + * + * \param[in] client The client component instance handle + * \param[in] requiredItfClientName The client required interface name + * \param[in] server The server component instance handle + * \param[in] providedItfServerName The server provided interface name + * \param[out] requiredItf return the required interface (avoid user searching) + * \param[out] itf return the provided interface (avoid user searching) + */ +t_cm_error cm_checkValidBinding( + const t_component_instance* client, + const char* requiredItfClientName, + const t_component_instance* server, + const char* requiredItfServerName, + t_interface_require_description *itfRequire, + t_interface_provide_description *itfProvide, + t_bool *bindable); + +/** + * \internal + * \ingroup BF_COMMON + * + * Make some basic sanity check for each unbinding: + * - Interface really required + * - Component stopped + * + * \param[in] client The client component instance handle + * \param[in] requiredItfClientName The client required interface name + * \param[out] itfRequire return the previously binded required interface (avoid user searching) + * \param[out] itfProvide return the previously binded provided interface (avoid user searching) + * \param[out] bfInfoID return the binding factory identifiant which done the previously bind + * \param[out] bfInfo return the binding factory information which done the previously bind + */ +t_cm_error cm_checkValidUnbinding( + const t_component_instance* client, + const char* requiredItfClientName, + t_interface_require_description *itfRequire, + t_interface_provide_description *itfProvide); + +/** + * \internal + * \ingroup BF_PRIMITIVE + * + * Create a primitive binding between a client to a server interface. + * + * \param[in] itfRequire The client required interface description + * \param[in] itfProvide The server provided interface description + */ +t_cm_error cm_bindInterface( + const t_interface_require_description *itfRequire, + const t_interface_provide_description *itfProvide); + +/** + * \internal + * \ingroup BF_PRIMITIVE + * + * Unbind a previously binded client. + * + * \param[in] itfRequire The client required interafce description + */ +void cm_unbindInterface( + const t_interface_require_description *itfRequire); + +/** + * \internal + * \ingroup BF_PRIMITIVE + * + * Get a server interface previouly binded to a client + * + * \param[in] client The client component instance handle + * \param[in] requiredItfClientName The client required interface name + * \param[out] itf The server interface + */ +t_cm_error cm_lookupInterface( + const t_interface_require_description *itfRequire, + t_interface_provide_description *itfProvide); + +/** + * \internal + * \ingroup BF_PRIMITIVE + * + * Create a void binding. + * + * \param[in] client The client component instance handle + * \param[in] requiredItfClientName The client required interface name + */ +t_cm_error cm_bindInterfaceToVoid( + const t_interface_require_description *itfRequire); + +/** + * \internal + * \ingroup BF_TRACE + * + * Trace synchronous binding factory Information + */ +typedef struct { + t_component_instance *traceInstance; //!< Trace binding component instance +} t_trace_bf_info; + +/** + * \internal + * \ingroup BF_TRACE + * + * Create a traced binding between a client to a server interface. + * + * \param[in] itfRequire The client required interface description + * \param[in] itfProvide The server provided interface description + */ +t_cm_error cm_bindInterfaceTrace( + const t_interface_require_description *itfRequire, + const t_interface_provide_description *itfProvide, + t_elfdescription *elfhandleTrace); + +/** + * \internal + * \ingroup BF_TRACE + * + * Unbind a previously binded client. + * + * \param[in] itfRequire The client required interafce description + */ +void cm_unbindInterfaceTrace( + const t_interface_require_description *itfRequire, + t_trace_bf_info *bfInfo); + + +/** + * \internal + * \ingroup BF_ASYNCHRONOUS + * + * Asynchronous binding factory Information + */ +typedef struct { + t_component_instance *eventInstance; //!< Event binding component instance + t_memory_handle dspfifoHandle; //!< Memory handle of allocated event fifo (pass to the event binding component) +} t_async_bf_info; + +/** + * \internal + * \ingroup BF_ASYNCHRONOUS + * + * Create a asynchronous binding between a client to a server interface. + * \param[in] client The client component instance handle + * \param[in] requiredItfClientName The client required interface name + * \param[in] itf The server interface + * \param[in] fifosize Number of waited event in the fifo + */ +t_cm_error cm_bindInterfaceAsynchronous( + const t_interface_require_description *itfRequire, + const t_interface_provide_description *itfProvide, + t_uint32 fifosize, + t_dsp_memory_type_id dspEventMemType, + t_elfdescription *elfhandleEvent); +/** + * \internal + * \ingroup BF_ASYNCHRONOUS + * + * Destroy a asynchronous binding between a client to a server interface. + * \param[in] itfRequire the required interface + */ +void cm_unbindInterfaceAsynchronous( + const t_interface_require_description *itfRequire, + t_async_bf_info *bfInfo); + +/** + * \internal + * \ingroup BF_DISTRIBUTED + * + * Stub information in distributed binding factory (client side) + */ +typedef struct { + t_component_instance *stubInstance; //!< Stub +} t_dspstub_bf_info; + +/** + * \internal + * \ingroup BF_DISTRIBUTED + * + * Skeleton information in distributed binding factory (server side) + */ +typedef struct { + t_component_instance *skelInstance; //!< Skeleton binding component instance + t_memory_handle dspfifoHandle; //!< Memory handle of allocated event fifo (pass to the event binding component) +} t_dspskel_bf_info; + +/** + * \internal + * \ingroup BF_DISTRIBUTED + * + * Host to DSP distributed binding factory Information + */ +typedef struct { + t_dspskel_bf_info dspskeleton; //!< Information about the DSP skeleton (server side) + t_nmf_fifo_arm_desc* fifo; //!< Handle of the fifo params + t_nmf_client_id clientId; //!< Client ID of the host client +} t_host2mpc_bf_info; + +/* + * Table of instantiated of host2mpc bindings + */ +extern t_nmf_table Host2MpcBindingTable; /**< list (table) of host2mpc bindings */ + +/** + * \internal + * \ingroup BF_DISTRIBUTED + * + * Create a Host to DSP distributed binding between a host client interface to a server interface. + * (Not manage in the same way as distributed binding since the Host programming model is not component aware). + * \param[in] itfServer The server interface + * \param[in] fifosize Number of waited event in the fifo + * \param[in] dspEventMemType The type of memory to use + * \param[in] bfInfo info structure + */ +t_cm_error cm_bindComponentFromCMCore( + const t_interface_provide_description *itfProvide, + t_uint32 fifosize, + t_dsp_memory_type_id dspEventMemType, + t_elfdescription *elfhandleSkeleton, + t_host2mpc_bf_info **bfInfo); + +/** + * \internal + * \ingroup BF_DISTRIBUTED + * + * Destroy a Host to DSP distributed binding between a host client interface to a server interface. + * \param[in] bfInfo The Host to DSP distributed binding factory information + */ +void cm_unbindComponentFromCMCore( + t_host2mpc_bf_info *bfInfo); + +/** + * \internal + * \ingroup BF_DISTRIBUTED + * + * DSP to Host distributed binding factory Information + */ +typedef struct { + t_dspstub_bf_info dspstub; //!< Information about the DSP stub (client side) + t_nmf_fifo_arm_desc* fifo; //!< Handle of the fifo params + t_uint32 context; +} t_mpc2host_bf_info; + +/** + * \internal + * \ingroup BF_DISTRIBUTED + * + * Create a DSP to Host distributed binding between a client interface to a host server interface. + * (Not manage in the same way as distributed binding since the Host programming model is not component aware). + * \param[in] client The client component instance handle + * \param[in] requiredItfClientName The client required interface name + * \param[in] itfref The host server interface to be called + * \param[in] fifosize Number of waited event in the fifo + */ +t_cm_error cm_bindComponentToCMCore( + const t_interface_require_description *itfRequire, + t_uint32 fifosize, + t_uint32 context, + t_elfdescription *elfhandleStub, + t_mpc2host_bf_info ** bfInfo); + +/** + * \internal + * \ingroup BF_DISTRIBUTED + * + * Destroy a DSP to Host distributed binding between a client interface to a server interface. + * \param[in] itfRequire The required interface + * \param[out] upLayerThis The 'THIS' context of upper layer + */ +void cm_unbindComponentToCMCore( + const t_interface_require_description *itfRequire, + t_mpc2host_bf_info *bfInfo); + +/** + * \internal + * \ingroup BF_DISTRIBUTED + * + * Asynchronous distributed binding factory Information + */ +typedef struct { + t_nmf_fifo_arm_desc* fifo; //!< Handle of the fifo params + t_dspstub_bf_info dspstub; //!< Information about the DSP stub (client side) + t_dspskel_bf_info dspskeleton; //!< Information about the DSP skeleton (server side) +} t_mpc2mpc_bf_info; + +/** + * \internal + * \ingroup BF_DISTRIBUTED + * + * Create a asynchronous distributed binding between a client interface to a server interface. + * \param[in] client The client component instance handle + * \param[in] requiredItfClientName The client required interface name + * \param[in] itf The server interface + * \param[in] fifosize Number of waited event in the fifo + */ +t_cm_error cm_bindInterfaceDistributed( + const t_interface_require_description *itfRequire, + const t_interface_provide_description *itfProvide, + t_uint32 fifosize, + t_dsp_memory_type_id dspEventMemType, + t_elfdescription *elfhandleSkeleton, + t_elfdescription *elfhandleStub); + +/** + * \internal + * \ingroup BF_DISTRIBUTED + * + * Destroy a asynchronous distributed binding between a client interface to a server interface. + * \param[in] itfRequire The required interface + */ +void cm_unbindInterfaceDistributed( + const t_interface_require_description *itfRequire, + t_mpc2mpc_bf_info *bfInfo); + +/** + * \internal + * + * Bind a static interrupt to server provide interface name. + * \param[in] coreId The core to which component is loaded + * \param[in] interruptLine Interrupt line number to use + * \param[in] server Server instance that provide interrupt service + * \param[in] providedItfServerName Interface name hat provide interrupt service + */ +t_cm_error cm_bindInterfaceStaticInterrupt( + const t_nmf_core_id coreId, + const int interruptLine, + const t_component_instance *server, + const char* providedItfServerName); + +/** + * \internal + * + * Unbind a static interrupt. + * \param[in] coreId The core to which component is loaded + * \param[in] interruptLine Interrupt line number to use + */ +t_cm_error cm_unbindInterfaceStaticInterrupt( + const t_nmf_core_id coreId, + const int interruptLine); + +void cm_destroyRequireInterface(t_component_instance* component, t_nmf_client_id clientId); +void cm_registerSingletonBinding( + t_component_instance* component, + t_interface_require_description* itfRequire, + t_interface_provide_description* itfProvide, + t_nmf_client_id clientId); +t_bool cm_unregisterSingletonBinding( + t_component_instance* component, + t_interface_require_description* itfRequire, + t_interface_provide_description* itfProvide, + t_nmf_client_id clientId); + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/component_type.h b/drivers/staging/nmf-cm/cm/engine/component/inc/component_type.h new file mode 100644 index 00000000000..35571dde06d --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/inc/component_type.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Components Component Manager API type. + * + * \defgroup COMPONENT CM Components API + * \ingroup CM_USER_API + */ + +#ifndef COMPONENT_TYPE_H_ +#define COMPONENT_TYPE_H_ + +#include <cm/inc/cm_type.h> +#include <nmf/inc/component_type.h> + +/*! + * @defgroup t_nmf_ee_priority t_nmf_ee_priority + * \brief Identification of the execution engine priority and sub priority. + * @{ + * \ingroup COMPONENT + */ +typedef t_uint32 t_nmf_ee_priority; //!< Fake enumeration type + +#define NMF_SCHED_BACKGROUND ((t_nmf_ee_priority)0) //!< Background priority +#define NMF_SCHED_NORMAL ((t_nmf_ee_priority)1) //!< Normal priority +#define NMF_SCHED_URGENT ((t_nmf_ee_priority)2) //!< Urgent priority +/* @} */ + + +/*! + * \brief Identification of host component returned during introspection + * + * \ingroup COMPONENT_INTROSPECTION + */ +#define NMF_HOST_COMPONENT ((t_cm_instance_handle)0xFFFFFFFF) + +/*! + * \brief Identification of void component returned during introspection + * + * \ingroup COMPONENT_INTROSPECTION + */ +#define NMF_VOID_COMPONENT ((t_cm_instance_handle)0xFFFFFFFE) + + +/*! + * @defgroup t_nmf_ee_priority t_nmf_ee_priority + * \brief Identification of the execution engine priority and sub priority. + * @{ + * \ingroup COMPONENT + */ +typedef t_uint8 t_cm_require_state; //!< Fake enumeration type + +#define CM_REQUIRE_STATIC ((t_cm_require_state)0) //!< Required interface is static +#define CM_REQUIRE_OPTIONAL ((t_cm_require_state)1) //!< Required interface is optional +#define CM_REQUIRE_COLLECTION ((t_cm_require_state)2) //!< Required interface is a collection + +/* @} */ + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/description.h b/drivers/staging/nmf-cm/cm/engine/component/inc/description.h new file mode 100644 index 00000000000..882dc1ea873 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/inc/description.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#ifndef __INC_CM_COMPONENT_DESCRIPTION_H +#define __INC_CM_COMPONENT_DESCRIPTION_H + +#include <cm/engine/elf/inc/memory.h> +#include <cm/engine/utils/inc/string.h> + +#include <inc/nmf-limits.h> + +/*! + * \internal + * \brief Description of an interface + * \ingroup COMPONENT_INTERNAL + */ +typedef struct _t_interface_description { + t_dup_char type; //!< Type of the interface + t_uint16 referenceCounter; //!< Number of template referencing the interface + t_uint8 methodNumber; //!< Number of method in the interfaces + struct _t_interface_description* next; + t_dup_char methodNames[1]; //!< Array of method names +} t_interface_description; + +/*! + * \internal + * \brief Description of a variable memory on a collection index + * \ingroup COMPONENT_INTERNAL + */ +typedef struct { + t_uint32 offset; //!< Offset in the memory + const t_elfmemory *memory; //!< Memory +} t_memory_reference; + +/*! + * \internal + * \brief Description of a required interface on a collection index + * \ingroup COMPONENT_INTERNAL + */ +typedef struct { + t_uint32 numberOfClient; //!< Number of interface descriptor really connected to this interface + t_memory_reference *memories; /*!< Memory where each interface reference descriptor resides + \note memories[numberOfClient] */ +} t_interface_require_index; + +/*! + * \internal + * \brief Description of a required interface + * \ingroup COMPONENT_INTERNAL + */ +typedef struct { + t_dup_char name; //!< Name of the interface + t_interface_description *interface; //!< Description of the interface + t_uint8 requireTypes; //!< Mask of t_elf_interface_require_type + t_uint8 collectionSize; //!< Size of the collection (1 if not a collection) + t_interface_require_index *indexes; /*!< Require information for each collection index + \note indexes[collectionSize] */ +} t_interface_require; + +/*! + * \internal + * \brief Description of a provided interface method on a collection index + * \ingroup COMPONENT_INTERNAL + */ +typedef struct { + t_memory_reference memory; //!< Memory of the method +} t_interface_provide_index; + +/*! + * \internal + * \brief Description of a provided interface + * \ingroup COMPONENT_INTERNAL + */ +typedef struct { + t_dup_char name; //!< Name of the interface + t_interface_description *interface; //!< Description of the interface + t_uint8 provideTypes; //!< Mask of t_elf_interface_provide_type + t_uint8 interruptLine; //!< Interrupt line if interrupt (0 if not) + t_uint8 collectionSize; //!< Size of the collection (1 if not a collection) + t_interface_provide_index **indexes; //!< Provide information for each collection index +} t_interface_provide; + +/*! + * \internal + * \brief Description of a attribute + * \ingroup COMPONENT_INTERNAL + */ +typedef struct { + t_dup_char name; //!< Name of the attribute + t_memory_reference memory; //!< Memory where the attribute reside +} t_attribute; + +/*! + * \internal + * \brief Description of a property + * \ingroup COMPONENT_INTERNAL + */ +typedef struct { + t_dup_char name; //!< Name of this attribute + t_dup_char value; //!< String of the value +} t_property; + + + + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/dspevent.h b/drivers/staging/nmf-cm/cm/engine/component/inc/dspevent.h new file mode 100644 index 00000000000..bb47363c0ae --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/inc/dspevent.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef __INC_DSP_EVENT +#define __INC_DSP_EVENT + +#include <cm/inc/cm_type.h> +#include <cm/engine/component/inc/instance.h> +#include <cm/engine/memory/inc/memory.h> + +/* value should be size of t_remote_event in mmdsp word */ +#define DSP_REMOTE_EVENT_SIZE_IN_DSPWORD 5 + +t_cm_error dspevent_createDspEventFifo( + const t_component_instance *pComp, + const char* nameOfTOP, + t_uint32 fifoNbElem, + t_uint32 fifoElemSizeInWord, + t_dsp_memory_type_id dspEventMemType, + t_memory_handle *pHandle); +void dspevent_destroyDspEventFifo(t_memory_handle handle); + +#endif /* __INC_DSP_EVENT */ diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/initializer.h b/drivers/staging/nmf-cm/cm/engine/component/inc/initializer.h new file mode 100644 index 00000000000..5ac9ec453b7 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/inc/initializer.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef __INC_NMF_INITIALIZER +#define __INC_NMF_INITIALIZER + +#include <cm/inc/cm_type.h> +#include <cm/engine/component/inc/instance.h> +#include <share/communication/inc/initializer.h> + +PUBLIC t_cm_error cm_COMP_INIT_Init(t_nmf_core_id coreId); +PUBLIC t_cm_error cm_COMP_CallService(int serviceIndex, t_component_instance *pComp, t_uint32 methodAddress); +PUBLIC void cm_COMP_Flush(t_nmf_core_id coreId); +PUBLIC void cm_COMP_INIT_Close(t_nmf_core_id coreId); +PUBLIC t_cm_error cm_COMP_UpdateStack(t_nmf_core_id coreId, t_uint32 stackSize); +PUBLIC t_cm_error cm_COMP_ULPForceWakeup(t_nmf_core_id coreId); +PUBLIC t_cm_error cm_COMP_ULPAllowSleep(t_nmf_core_id coreId); +PUBLIC t_cm_error cm_COMP_InstructionCacheLock(t_nmf_core_id coreId, t_uint32 mmdspAddr, t_uint32 mmdspSize); +PUBLIC t_cm_error cm_COMP_InstructionCacheUnlock(t_nmf_core_id coreId, t_uint32 mmdspAddr, t_uint32 mmdspSize); + + +PUBLIC void processAsyncAcknowledge(t_nmf_core_id coreId, t_event_params_handle pParam); +PUBLIC void processSyncAcknowledge(t_nmf_core_id coreId, t_event_params_handle pParam); + +#endif /* __INC_NMF_INITIALIZER */ diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/instance.h b/drivers/staging/nmf-cm/cm/engine/component/inc/instance.h new file mode 100644 index 00000000000..0a7d80e2e02 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/inc/instance.h @@ -0,0 +1,222 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Components Management internal methods - Instance API. + * + */ +#ifndef __INC_CM_INSTANCE_H +#define __INC_CM_INSTANCE_H + +#include <cm/engine/component/inc/template.h> +#include <cm/engine/repository_mgt/inc/repository_mgt.h> +#include <cm/engine/memory/inc/domain.h> +#include <cm/engine/utils/inc/table.h> +#include <cm/engine/utils/inc/string.h> + +/*---------------------------------------------------------------------------- + * Component Instance API. + *----------------------------------------------------------------------------*/ +struct _t_interface_reference; + +/*! + * \internal + * \brief Component life cycle state + * + * \ingroup COMPONENT_INTERNAL + */ +typedef enum { + STATE_NONE, + STATE_STOPPED, + STATE_RUNNABLE, + // STATE_DESTROYED identified when component remove from component list +} t_component_state; + +struct t_client_of_singleton +{ + struct t_client_of_singleton *next; + t_nmf_client_id clientId; + t_uint16 numberOfInstance; + t_uint16 numberOfStart; + t_uint16 numberOfBind; +}; + +/*! + * \internal + * \brief Description of a component instance + * + * \ingroup COMPONENT_INTERNAL + */ +typedef struct t_component_instance { + t_dup_char pathname; //!< Path Name of this component in the components architecture + + t_component_state state; //!< Component state + t_nmf_ee_priority priority; //!< Executive engine component priority + t_component_template *Template; //!< Component template + + t_uint32 thisAddress; //!< Cached value of cm_DSP_GetDspAddress(component->memories[data], &thisAddress); + + t_memory_handle memories[NUMBER_OF_MMDSP_MEMORY]; //!<Reference in different memory where datas are (YES, we fix implementation to MMDSP) + + struct _t_interface_reference **interfaceReferences; /*!< Interface references + (Share same index as template->u.p.requires) + type == targets[interface_index][collection_index] */ + + t_uint16 providedItfUsedCount; //!< Use count to reference the number of components binded to this once, ie count the number of provided interfaces in use + t_cm_instance_handle instance; //!< index of this component within the ComponentTable + t_cm_domain_id domainId; //!< Domain where the component has been installed + + struct t_client_of_singleton *clientOfSingleton; //!< Client of singleton list + t_memory_handle loadMapHandle; // handle of allocated memory for the loadMap structure and name; + void *dbgCooky; //!< pointer to OS internal data +} t_component_instance; + +t_component_template* cm_lookupTemplate(t_nmf_core_id dspId, t_dup_char str); + +/*! + * \internal + * \brief Load a component template. + * + * ... + * + * \param[in] templateName name of the template to load + * \param[in] coreId DSP where template must be loaded + * \praem[in] pRepComponent Pointer to the component entry stored in the Component Cache Repository + * \param[in, out] template reference to put the loaded template (null if first instance) + * + * \exception CM_COMPONENT_NOT_FOUND + * \exception CM_NO_MORE_MEMORY + * + * \return exception number. + * + * \warning For Component manager use only. + * \ingroup COMPONENT_INTERNAL + */ +t_cm_error cm_loadComponent( + t_dup_char templateName, + t_cm_domain_id domainId, + t_elfdescription* elfhandle, + t_component_template **reftemplate); + +/*! + * \internal + * \brief Unload a component template. + * + * ... + * + * \param[in] template template to be unloaded + * \praem[in] Private memories that has been created from component binary file + * + * \return exception number. + * + * \warning For Component manager use only. + * \ingroup COMPONENT_INTERNAL + */ +t_cm_error cm_unloadComponent( + t_component_template *reftemplate); + +/*! + * \internal + * \brief Instantiate a component. + * + * \ingroup COMPONENT_INTERNAL + */ +t_cm_error cm_instantiateComponent(const char* templateName, + t_cm_domain_id domainId, + t_nmf_ee_priority priority, + const char* pathName, + t_elfdescription *elfhandle, + t_component_instance** refcomponent); + +struct t_client_of_singleton* cm_getClientOfSingleton(t_component_instance* component, t_bool createdIfNotExist, t_nmf_client_id clientId); + +/*! + * \internal + * \brief Start a component. + * + * \ingroup COMPONENT_INTERNAL + */ +t_cm_error cm_startComponent(t_component_instance* component, t_nmf_client_id clientId); + +/*! + * \internal + * \brief Stop a component. + * + * \ingroup COMPONENT_INTERNAL + */ +t_cm_error cm_stopComponent(t_component_instance* component, t_nmf_client_id clientId); + +/*! + * \internal + */ +typedef enum { + DESTROY_NORMAL, + DESTROY_WITHOUT_CHECK, + DESTROY_WITHOUT_CHECK_CALL +} t_destroy_state; + +/*! + * \internal + * \brief Destroy a component instance. + * + * \ingroup COMPONENT_INTERNAL + */ +t_cm_error cm_destroyInstance(t_component_instance* component, t_destroy_state forceDestroy); + +/*! + * \internal + * \brief Destroy a component instance. + * + * \ingroup COMPONENT_INTERNAL + */ +t_cm_error cm_destroyInstanceForClient(t_component_instance* component, t_destroy_state forceDestroy, t_nmf_client_id clientId); + +/*! + * \internal + * \brief + * + * \ingroup COMPONENT_INTERNAL + */ +void cm_delayedDestroyComponent(t_component_instance *component); + +/*! + * \internal + * \brief + * + * \ingroup COMPONENT_INTERNAL + */ +t_component_instance *cm_lookupComponent(const t_cm_instance_handle hdl); + +/*! + * \internal + * \brief + * + * \ingroup COMPONENT_INTERNAL + */ +t_bool cm_isComponentOnCoreId(t_nmf_core_id coreId); + +/*! + * \internal + * \brief + * + * \ingroup COMPONENT_INTERNAL + */ +t_cm_error cm_COMP_Init(void); + +/*! + * \internal + * \brief + * + * \ingroup COMPONENT_INTERNAL + */ +void cm_COMP_Destroy(void); + +/* + * Table of instantiated components. + */ +extern t_nmf_table ComponentTable; /**< list (table) of components */ +#define componentEntry(i) ((t_component_instance *)ComponentTable.entries[i]) +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/introspection.h b/drivers/staging/nmf-cm/cm/engine/component/inc/introspection.h new file mode 100644 index 00000000000..cfb55c91779 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/inc/introspection.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Components Management internal methods - Introspection. + * + */ +#ifndef __INC_CM_INTROSPECTION_H +#define __INC_CM_INTROSPECTION_H + +#include <cm/engine/component/inc/instance.h> + +/*! + * \internal + * \brief Description of a required interface reference + * + * \ingroup COMPONENT_INTERNAL + */ +typedef struct { + const t_component_instance *client; //!< Component that provide this interface + t_uint8 requireIndex; //!< Index of the interface in the require array + t_uint8 collectionIndex; //!< Index in the collection if required interface is a collection + const char* origName; //!< Name of the component interface +} t_interface_require_description; + +/*! + * \internal + * \brief Description of a provided interface + * + * \ingroup COMPONENT_INTERNAL + */ +typedef struct { + const t_component_instance *server; //!< Component that provide this interface + t_uint8 provideIndex; //!< Index of the interface in the provide array + t_uint8 collectionIndex; //!< Index in the collection if provided interface is a collection + const char* origName; //!< Name of the component interface +} t_interface_provide_description; + + +/*! + * \internal + * \brief Get property of a component. + * + * \ingroup COMPONENT_INTERNAL + */ +t_cm_error cm_getComponentProperty( + const t_component_instance *component, + const char *propName, + char value[MAX_PROPERTY_VALUE_LENGTH], + t_uint32 valueLength); + + +t_dsp_address cm_getAttributeMpcAddress( + const t_component_instance *component, + const char *attrName); + +t_cm_logical_address cm_getAttributeHostAddr( + const t_component_instance *component, + const char *attrName); + +t_uint32 cm_readAttributeNoError( + const t_component_instance *component, + const char *attrName); + +t_cm_error cm_readAttribute( + const t_component_instance *component, + const char *attrName, + t_uint32 *value); + +t_cm_error cm_writeAttribute( + const t_component_instance *component, + const char *attrName, + t_uint32 value); + +/*! + * \internal + * \brief Get internal component symbol + * + * \ingroup COMPONENT_INTERNAL + */ +t_dsp_address cm_getFunction( + const t_component_instance* component, + const char* interfaceName, + const char* methodName); + +/*! + * \internal + * \brief Get interface provided by a component instance. + * + * \ingroup COMPONENT_INTERNAL + */ +t_cm_error cm_getProvidedInterface(const t_component_instance* server, + const char* itfName, + t_interface_provide_description *itfProvide); + +/*! + * \internal + * \brief Get interface required by a component instance. + * + * \ingroup COMPONENT_INTERNAL + */ +t_cm_error cm_getRequiredInterface(const t_component_instance* server, + const char* itfName, + t_interface_require_description *itfRequire); + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/nmfheaderabi.h b/drivers/staging/nmf-cm/cm/engine/component/inc/nmfheaderabi.h new file mode 100644 index 00000000000..9eae19b2f70 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/inc/nmfheaderabi.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief NMF component description ABI + * + * \defgroup NMF_HEADER NMF Component Description ABI + * The NMF component description ABI is stored in the nmf_segment in the ELF component file. + * The NMF component description section start by the t_elf_component_header structure. + * + * \warning <B>The format of this section is not fixed and is able to be changed without concerting.</B> + * \note You can use the nmfHeaderVersion to check if the format has changed. + * \note Each pointers in this section is relative to the beginning of the section and must be relocated before used. + * \ingroup NMF_ABI + */ +#ifndef __INC_CM_NMF_HEADERABI_H +#define __INC_CM_NMF_HEADERABI_H + +#include <cm/inc/cm_type.h> + +/*! + * \brief Description of a interface + * \ingroup NMF_HEADER + */ +typedef struct { + char *type; //!< Type of this Interface + t_uint8 methodNumber; //!< Number of method in the interfaces + t_uint8 reserved1, reserved2, reserved3; + char *methodNames[1]; //!< Array of method names [methodNumber] +} t_elf_interface_description; + +/*! + * \brief Description of required interface type (value could be combinated) + * \ingroup NMF_HEADER + */ +typedef enum { + COLLECTION_REQUIRE = 1, //!< Required interface is a collection + OPTIONAL_REQUIRE = 2, //!< Required interface if optional + STATIC_REQUIRE = 4, //!< Required interface is static + VIRTUAL_REQUIRE = 8, //!< Required interface is virtual (only for introspection purpose) + INTRINSEC_REQUIRE = 16 //!< Required interface is intrinsec (bind automatically done by runtime) +} t_elf_interface_require_type; + +/*! + * \brief Description of a required interface on a collection index + * \ingroup NMF_HEADER + */ +typedef struct { + t_uint32 numberOfClient; //!< Number of interface descriptor really connected to this interface + t_uint32 symbols[1]; /*!< Symbol of the real name of the attribute + \note Real type symbols[numberOfClient] + \note Use relocation in order to get symbol information */ +} t_elf_interface_require_index; + +/*! + * \brief Description of an interface required + * \ingroup NMF_HEADER + */ +typedef struct { + char *name; //!< name of the interface: offset in string segment + t_uint8 requireTypes; //!< Mask of t_elf_interface_require_type + t_uint8 collectionSize; //!< Size of the collection (1 if not a collection) + t_uint8 reserved1, reserved2; + t_elf_interface_description *interface; //!< Interface description + t_elf_interface_require_index indexes[1]; /*!< Require information for each collection index + \note Real type: indexes[collectionSize], + available only if not static interface */ +} t_elf_required_interface; + +/*! + * \brief Description of provided interface type (value could be combinated) + * \ingroup NMF_HEADER + */ +typedef enum { + COLLECTION_PROVIDE = 1, //!< Provided interface is a collection + VIRTUAL_PROVIDE = 2 //!< Provided interface is virtual (only for introspection purpose) +} t_elf_interface_provide_type; + +/*! + * \brief Description of an interface provided + * \ingroup NMF_HEADER + */ +typedef struct { + char* name; //!< name of the interface: offset in string segment + t_uint8 provideTypes; //!< Mask of t_elf_interface_provide_type + t_uint8 interruptLine; //!< Interrupt line if interrupt (0 if not) + t_uint8 collectionSize; //!< Size of the collection (1 if not a collection) + t_uint8 reserved1; + t_elf_interface_description *interface; //!< Interface description + t_uint32 methodSymbols[1]; /*!< Symbol of the real name of methods of the interface for each collection index + \note Real type: methodSymbols[collectionSize][methodNumber] + \note Use relocation in order to get symbol information*/ +} t_elf_provided_interface; + +/*! + * \brief Description of an attribute + * \ingroup NMF_HEADER + */ +typedef struct { + char* name; //!< Name of this attribute + t_uint32 symbols; /*!< Symbol of the real name of the attribute + \note Use relocation in order to get symbol information */ +} t_elf_attribute; + +/*! + * \brief Description of an property + * \ingroup NMF_HEADER + */ +typedef struct { + char* name; //!< Name of this attribute + char* value; //!< String of the value +} t_elf_property; + +#define MAGIC_COMPONENT 0x123 //!< Magic Number for a component \ingroup NMF_HEADER +#define MAGIC_SINGLETON 0x321 //!< Magic Number for a singleton component \ingroup NMF_HEADER +#define MAGIC_FIRMWARE 0x456 //!< Magic Number for Execution Engine Component \ingroup NMF_HEADER + +/*! + * \brief Description of a ELF component header + * + * The NMF component description section start by this structure. + * + * \ingroup NMF_HEADER + */ +typedef struct { + t_uint32 magic; //!< Magic Number + t_uint32 nmfVersion; //!< Version of the NMF Header + + char* templateName; //!< Name of the component template + + t_uint32 LCCConstruct; //!< Life cycle Constructor offset + t_uint32 LCCStart; //!< Life cycle Starter offset + t_uint32 LCCStop; //!< Life cycle Stopper offset + t_uint32 LCCDestroy; //!< Life cycle Destructer offset + + t_uint32 minStackSize; //!< Minimum stack size + + t_uint32 attributeNumber;//!< Number of attributes + t_elf_attribute *attributes; //!< Array of attributes (be careful, this reference must be relocated before use) + + t_uint32 propertyNumber; //!< Number of properties + t_elf_property *properties; //!< Array of properties (be careful, this reference must be relocated before use) + + t_uint32 provideNumber; //!< Number of interfaces provided + t_elf_provided_interface *provides; //!< Array of interfaces provided (be careful, this reference must be relocated before use) + + t_uint32 requireNumber; //!< Array of interfaces required + t_elf_required_interface *requires; //!< Array of interfaces required (be careful, this reference must be relocated before use) + +} t_elf_component_header; + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/template.h b/drivers/staging/nmf-cm/cm/engine/component/inc/template.h new file mode 100644 index 00000000000..2718d8ae9fb --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/inc/template.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Components Management internal methods - Template API. + * + * \defgroup COMPONENT_INTERNAL Private component instances API + */ +#ifndef __INC_CM_TEMPLATE_H +#define __INC_CM_TEMPLATE_H + +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/component/inc/description.h> +#include <cm/engine/elf/inc/elfapi.h> +#include <cm/engine/utils/inc/string.h> + + +/*! + * \internal + * \brief Class of a component + * \ingroup COMPONENT_INTERNAL + */ +typedef enum { + COMPONENT, //!< Primitive component + SINGLETON, //!< Singleton component + FIRMWARE, //!< Firmware composite component +} t_component_classe; + +/*! + * \internal + * \brief Description of delayed relocation + * \ingroup COMPONENT_INTERNAL + */ +typedef struct _t_function_relocation { + t_dup_char symbol_name; + t_uint32 type; + char *reloc_addr; + struct _t_function_relocation *next; +} t_function_relocation; + +struct t_component_instance; + +/*! + * \internal + * \brief Description of a provided interface method on a collection index ; Available only when template loaded + * \ingroup COMPONENT_INTERNAL + */ +typedef struct { + t_uint32 methodAddresses; //!< Address of each method +} t_interface_provide_index_loaded; + +/*! + * \internal + * \brief Description of a provided interface ; Available only when template loaded + * \ingroup COMPONENT_INTERNAL + */ +typedef struct { + t_interface_provide_index_loaded **indexesLoaded; //!< Provide information for each collection index +} t_interface_provide_loaded; + + +/*! + * \internal + * \brief Description of a component template + * \ingroup COMPONENT_INTERNAL + */ +typedef struct _t_component_template { + t_dup_char name; //!< Template name (a.k.a component type) + + t_component_classe classe; //!< Class of the component + //TODO, juraj, remove dspId + t_nmf_core_id dspId; //!< Reference on DSP where template is loaded + + t_uint8 numberOfInstance; //!< Number of same instance (or singleton copy) create from this template + + t_uint8 propertyNumber; //!< Number of properties in this template + t_uint8 attributeNumber; //!< Number of attributes in this template + t_uint8 provideNumber; //!< Number of interface provided by this template + t_uint8 requireNumber; //!< Number of interface required by this template + + t_uint32 LCCConstructAddress; //!< Life cycle Constructor address + t_uint32 LCCStartAddress; //!< Life cycle Starter address + t_uint32 LCCStopAddress; //!< Life cycle Stopper address + t_uint32 LCCDestroyAddress; //!< Life cycle Destructer address + + t_uint32 minStackSize; //!< Minimum stack size + + t_memory_handle memories[NUMBER_OF_MMDSP_MEMORY]; //!< Reference in different memory where datas are (YES, we fix implementation to MMDSP) + const t_elfmemory *thisMemory; //!< Memory used to determine this + const t_elfmemory *codeMemory; //!< Memory used to determine code + + t_function_relocation *delayedRelocation; //!< List of reference that can't been relocatable while appropritae binding done. + + t_property *properties; //!< Array of properties in this template + t_attribute *attributes; //!< Array of attributes in this template + t_interface_provide *provides; //!< Array of interface provided by this template + t_interface_require *requires; //!< Array of interface required by this template + + t_interface_provide_loaded *providesLoaded; //!< Array of interface provided by this template ; Available when loaded + + t_bool descriptionAssociatedWithTemplate; + + struct _t_component_template *prev, *next; + struct t_component_instance *singletonIfAvaliable; +} t_component_template; + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/binder.c b/drivers/staging/nmf-cm/cm/engine/component/src/binder.c new file mode 100644 index 00000000000..5f08713833b --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/src/binder.c @@ -0,0 +1,1313 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include "../inc/bind.h" +#include "../inc/dspevent.h" +#include <cm/engine/communication/fifo/inc/nmf_fifo_arm.h> +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> +#include <cm/engine/component/inc/introspection.h> + +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/trace/inc/xtitrace.h> + +#include <cm/engine/utils/inc/string.h> + +#define CM_IT_NAME_MAX_LENGTH 8 + +t_nmf_table Host2MpcBindingTable; /**< list (table) of host2mpc bindings */ + +static void cm_fillItName(int interruptLine, char *itName); +static t_uint16 getNumberOfBind(t_component_instance* component); + +/* + * Bind virtual interface, here we assume that: + * - client component require this interface as last one and without collection, + * - server component provide only this interface and without collection. + * Fixed in loader.c. + */ +static void cm_bindVirtualInterface( + t_component_instance* client, + const t_component_instance* server) { + t_interface_require_description itfRequire; + + if(cm_getRequiredInterface(client, "coms", &itfRequire) == CM_OK) + { + t_interface_reference* itfRef = client->interfaceReferences[itfRequire.requireIndex]; + + /* + * Memorise this reference + */ + itfRef->provideIndex = 0; + itfRef->collectionIndex = 0; + itfRef->instance = server; + itfRef->bfInfoID = (t_bf_info_ID)0; + itfRef->bfInfo = (void*)-1; // TODO + } + else + { + ERROR("Internal Error in cm_bindVirtualInterface\n", 0, 0, 0, 0, 0, 0); + } +} + +static void cm_unbindVirtualInterface( + t_component_instance* client) { + t_interface_require_description itfRequire; + + if(cm_getRequiredInterface(client, "coms", &itfRequire) == CM_OK) + { + t_interface_reference* itfRef = client->interfaceReferences[itfRequire.requireIndex]; + itfRef->instance = NULL; + } + else + { + ERROR("Internal Error in cm_unbindVirtualInterface\n", 0, 0, 0, 0, 0, 0); + } +} + +/* + * Bind component + */ +static void cm_bindLowLevelInterface( + const t_interface_require_description *itfRequire, + const t_interface_provide_description *itfLocalBC, /* On the same DSP */ + t_bf_info_ID bfInfoID, void* bfInfo) +{ + const t_component_instance* client = itfRequire->client; + t_component_instance* server = (t_component_instance*)itfLocalBC->server; + t_interface_require *require = &client->Template->requires[itfRequire->requireIndex]; + t_interface_provide* provide = &server->Template->provides[itfLocalBC->provideIndex]; + t_interface_provide_loaded* provideLoaded = &server->Template->providesLoaded[itfLocalBC->provideIndex]; + int k, j; + + if(require->indexes != NULL) + { + t_interface_require_index *requireindex = &require->indexes[itfRequire->collectionIndex]; + + for(k = 0; k < requireindex->numberOfClient; k++) { + t_uint32 *hostAddr; + + hostAddr = (t_uint32*)( + cm_DSP_GetHostLogicalAddress(client->memories[requireindex->memories[k].memory->id]) + + requireindex->memories[k].offset * requireindex->memories[k].memory->memEntSize); + + LOG_INTERNAL(2, "Fill ItfRef %s.%s mem=%s Off=%x @=%x\n", + client->pathname, require->name, + requireindex->memories[k].memory->memoryName, + requireindex->memories[k].offset, + hostAddr, 0); + + /* + * Fill the interface references. We start by This then methods in order to keep + * Unbinded panic as long as possible and not used method with wrong This. This is + * relevent only for optional since we must go in stop state before rebinding other + * required interface. + * + * Direct write to DSP memory without go through DSP abstraction since we know we are in 24bits + */ + // Write THIS reference into the Data field of the interface reference + // Write the interface methods reference + + if(((t_uint32)hostAddr & 0x7) == 0 && require->interface->methodNumber > 0) + { + // We are 64word byte aligned, combine this write with first method + *(volatile t_uint64*)hostAddr = + ((t_uint64)server->thisAddress << 0) | + ((t_uint64)provideLoaded->indexesLoaded[itfLocalBC->collectionIndex][0].methodAddresses << 32); + hostAddr += 2; + j = 1; + } + else + { + // We are not, write this which will align us + *hostAddr++ = (t_uint32)server->thisAddress; + j = 0; + } + + // Word align copy + for(; j < require->interface->methodNumber - 1; j+=2) { + *(volatile t_uint64*)hostAddr = + ((t_uint64)provideLoaded->indexesLoaded[itfLocalBC->collectionIndex][j].methodAddresses << 0) | + ((t_uint64)provideLoaded->indexesLoaded[itfLocalBC->collectionIndex][j+1].methodAddresses << 32); + hostAddr += 2; + } + + // Last word align if required + if(j < require->interface->methodNumber) + *hostAddr = provideLoaded->indexesLoaded[itfLocalBC->collectionIndex][j].methodAddresses; + } + } + else + { + t_function_relocation *reloc = client->Template->delayedRelocation; + while(reloc != NULL) { + for(j = 0; j < provide->interface->methodNumber; j++) + { + if(provide->interface->methodNames[j] == reloc->symbol_name) { + cm_ELF_performRelocation( + reloc->type, + reloc->symbol_name, + provideLoaded->indexesLoaded[itfLocalBC->collectionIndex][j].methodAddresses, + reloc->reloc_addr); + break; + } + } + + reloc = reloc -> next; + } + } + + /* + * Memorise this reference + */ + { + t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex]; + + itfRef->provideIndex = itfLocalBC->provideIndex; + itfRef->collectionIndex = itfLocalBC->collectionIndex; + itfRef->instance = itfLocalBC->server; + itfRef->bfInfoID = bfInfoID; + itfRef->bfInfo = bfInfo; + + /* + * Do not count binding from EE (ie interrupt line), as this will prevent + * cm_destroyInstance() of server to succeed (interrupt line bindings are + * destroyed after the check in cm_destroyInstance() + */ + if (client->Template->classe != FIRMWARE) + server->providedItfUsedCount++; + } +} + +static void cm_registerLowLevelInterfaceToConst( + const t_interface_require_description *itfRequire, + const t_component_instance* targetInstance) +{ + const t_component_instance* client = itfRequire->client; + + /* + * Memorise this no reference + */ + { + t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex]; + + // This is an unbind from a true component (not to void) + // Do not count bindings from EE (ie interrupt line) + if ((targetInstance == NULL) + && (client->Template->classe != FIRMWARE) + && (itfRef->instance != (t_component_instance *)NMF_VOID_COMPONENT) + && (itfRef->instance != NULL)) + { + ((t_component_instance*)itfRef->instance)->providedItfUsedCount--; + } + + itfRef->instance = targetInstance; + itfRef->bfInfoID = BF_SYNCHRONOUS; // Just to memorize no Binding component used and unbind ToVoid happy ;-). + } +} + +static void cm_bindLowLevelInterfaceToConst( + const t_interface_require_description *itfRequire, + const t_dsp_address functionAddress, + const t_component_instance* targetInstance) { + const t_component_instance* client = itfRequire->client; + t_interface_require *require = &client->Template->requires[itfRequire->requireIndex]; + int j, k; + + + // If DSP is off/panic/... -> write nothing + if( + require->indexes != NULL + && cm_DSP_GetState(client->Template->dspId)->state == MPC_STATE_BOOTED) + { + t_interface_require_index *requireindex = &require->indexes[itfRequire->collectionIndex]; + + for(k = 0; k < requireindex->numberOfClient; k++) { + t_uint32 *hostAddr; + + hostAddr = (t_uint32*)( + cm_DSP_GetHostLogicalAddress(client->memories[requireindex->memories[k].memory->id]) + + requireindex->memories[k].offset * requireindex->memories[k].memory->memEntSize); + + /* + * Fill the interface references. We start by Methods then This in order to swith to + * Unbinded panic as fast as possible and not used method with wrong This. This is + * relevent only for optional since we must go in stop state before rebinding other + * required interface. + * + * Direct write to DSP memory without go through DSP abstraction since we know we are in 24bits + */ + /* + * Write THIS reference into the Data field of the interface reference + * Hack for simplifying debug just to keep THIS reference with caller one + * (could be removed if __return_address MMDSP intrinsec provided by compiler). + */ + // Write the interface methods reference + + if(((t_uint32)hostAddr & 0x7) == 0 && require->interface->methodNumber > 0) + { + // We are 64word byte aligned, combine this write with first method + *(volatile t_uint64*)hostAddr = + ((t_uint64)client->thisAddress << 0) | + ((t_uint64)functionAddress << 32); + hostAddr += 2; + j = 1; + } + else + { + // We are not, write this which will align us + *hostAddr++ = (t_uint32)client->thisAddress; + j = 0; + } + + // Word align copy + for(; j < require->interface->methodNumber - 1; j+=2) { + *(volatile t_uint64*)hostAddr = + ((t_uint64)functionAddress << 0) | + ((t_uint64)functionAddress << 32); + hostAddr += 2; + } + + // Last word align if required + if(j < require->interface->methodNumber) + *hostAddr = functionAddress; + } + } + + cm_registerLowLevelInterfaceToConst(itfRequire, targetInstance); +} + +/* + * Bind User component though primitive binding factory + */ +t_cm_error cm_bindInterface( + const t_interface_require_description *itfRequire, + const t_interface_provide_description *itfProvide) { + + LOG_INTERNAL(1, "\n##### Bind Synchronous %s/%x.%s -> %s/%x.%s #####\n", + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, + itfProvide->server->pathname, itfProvide->server, itfProvide->origName); + + cm_bindLowLevelInterface( + itfRequire, + itfProvide, + BF_SYNCHRONOUS, NULL); + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_SYNCHRONOUS, + itfRequire->client, itfProvide->server, + itfRequire->client->Template->requires[itfRequire->requireIndex].name, + itfProvide->server->Template->provides[itfProvide->provideIndex].name); + + return CM_OK; +} + +/* + * + */ +void cm_unbindInterface( + const t_interface_require_description *itfRequire) { + + LOG_INTERNAL(1, "\n##### UnBind synchronous %s/%x.%s #####\n", + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0); + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_SYNCHRONOUS, + itfRequire->client, NULL, + itfRequire->client->Template->requires[itfRequire->requireIndex].name, + NULL); + + cm_bindLowLevelInterfaceToConst(itfRequire, + 0x0, + NULL); +} + +/* + * + */ +t_cm_error cm_bindInterfaceToVoid( + const t_interface_require_description *itfRequire) { + LOG_INTERNAL(1, "\n##### Bind %s/%x.%s -> Void #####\n", + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0); + + cm_bindLowLevelInterfaceToConst(itfRequire, + cm_EEM_getExecutiveEngine(itfRequire->client->Template->dspId)->voidAddr, + (t_component_instance*)NMF_VOID_COMPONENT); + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_SYNCHRONOUS, + itfRequire->client, NULL, + itfRequire->client->Template->requires[itfRequire->requireIndex].name, + NULL); + + return CM_OK; +} +/* + * Find the server and its interface inded to a given required interface for a given component + */ +t_cm_error cm_lookupInterface( + const t_interface_require_description *itfRequire, + t_interface_provide_description *itfProvide) { + const t_component_instance* client = itfRequire->client; + t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex]; + + if(itfRef->instance != NULL) + { + itfProvide->server = itfRef->instance; + itfProvide->provideIndex = itfRef->provideIndex; + itfProvide->collectionIndex = itfRef->collectionIndex; + + return CM_OK; + } else { + itfProvide->server = NULL; + return CM_INTERFACE_NOT_BINDED; + } +} + +/* + * + */ +t_cm_error cm_bindInterfaceTrace( + const t_interface_require_description *itfRequire, + const t_interface_provide_description *itfProvide, + t_elfdescription *elfhandleTrace) +{ + t_interface_require *require = &itfRequire->client->Template->requires[itfRequire->requireIndex]; + t_interface_require_description bcitfRequire; + t_interface_provide_description bcitfProvide; + t_trace_bf_info *bfInfo; + t_cm_error error; + + LOG_INTERNAL(1, "\n##### Bind Synchronous Trace %s/%x.%s -> %s/%x.%s #####\n", + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, + itfProvide->server->pathname, itfProvide->server, itfProvide->origName); + + /* Allocate aynchronous binding factory information */ + bfInfo = (t_trace_bf_info*)OSAL_Alloc(sizeof(t_trace_bf_info)); + if(bfInfo == 0) + return CM_NO_MORE_MEMORY; + + /* + * Instantiate related trace on dsp + */ + { + char traceTemplateName[4 + MAX_INTERFACE_TYPE_NAME_LENGTH + 1]; + + cm_StringCopy(traceTemplateName,"_tr.", sizeof(traceTemplateName)); + cm_StringConcatenate(traceTemplateName, require->interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH); + + if ((error = cm_instantiateComponent( + traceTemplateName, + itfRequire->client->domainId, + itfProvide->server->priority, + traceDup, + elfhandleTrace, + &bfInfo->traceInstance)) != CM_OK) { + OSAL_Free(bfInfo); + return (error == CM_COMPONENT_NOT_FOUND)?CM_BINDING_COMPONENT_NOT_FOUND : error; + } + } + + /* Bind event to server interface (Error must not occure) */ + CM_ASSERT(cm_getRequiredInterface(bfInfo->traceInstance, "target", &bcitfRequire) == CM_OK); + + cm_bindLowLevelInterface(&bcitfRequire, itfProvide, BF_SYNCHRONOUS, NULL); + + /* Get the event interface (Error must not occure) */ + CM_ASSERT(cm_getProvidedInterface(bfInfo->traceInstance, "target", &bcitfProvide) == CM_OK); + + /* Bind client to event (Error must not occure) */ + cm_bindLowLevelInterface(itfRequire, &bcitfProvide, BF_TRACE, bfInfo); + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_SYNCHRONOUS, + itfRequire->client, itfProvide->server, + itfRequire->client->Template->requires[itfRequire->requireIndex].name, + itfProvide->server->Template->provides[itfProvide->provideIndex].name); + + return CM_OK; +} + +void cm_unbindInterfaceTrace( + const t_interface_require_description *itfRequire, + t_trace_bf_info *bfInfo) +{ + t_interface_require_description traceitfRequire; + + LOG_INTERNAL(1, "\n##### UnBind trace synchronous %s/%x.%s #####\n", + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0); + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_SYNCHRONOUS, + itfRequire->client, NULL, + itfRequire->client->Template->requires[itfRequire->requireIndex].name, + NULL); + + /* Unbind Client from Event Binding Component */ + cm_bindLowLevelInterfaceToConst(itfRequire, 0x0, NULL); + + /* Unbind explicitly Event from Server Binding Component */ + /* This is mandatory to fix the providedItfUsedCount of the server */ + CM_ASSERT(cm_getRequiredInterface(bfInfo->traceInstance, "target", &traceitfRequire) == CM_OK); + + cm_registerLowLevelInterfaceToConst(&traceitfRequire, NULL); + + /* Destroy Event Binding Component */ + cm_destroyInstance(bfInfo->traceInstance, DESTROY_WITHOUT_CHECK); + + /* Free BF info */ + OSAL_Free(bfInfo); +} + + +/* + * + */ +t_cm_error cm_bindInterfaceAsynchronous( + const t_interface_require_description *itfRequire, + const t_interface_provide_description *itfProvide, + t_uint32 fifosize, + t_dsp_memory_type_id dspEventMemType, + t_elfdescription *elfhandleEvent) { + t_interface_require *require = &itfRequire->client->Template->requires[itfRequire->requireIndex]; + t_interface_require_description eventitfRequire; + t_interface_provide_description eventitfProvide; + t_async_bf_info *bfInfo; + t_cm_error error; + + LOG_INTERNAL(1, "\n##### Bind Asynchronous %s/%x.%s -> %s/%x.%s #####\n", + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, + itfProvide->server->pathname, itfProvide->server, itfProvide->origName); + + /* Allocate aynchronous binding factory information */ + bfInfo = (t_async_bf_info*)OSAL_Alloc(sizeof(t_async_bf_info)); + if(bfInfo == 0) + return CM_NO_MORE_MEMORY; + + /* + * Instantiate related event on dsp + */ + { + char eventTemplateName[4 + MAX_INTERFACE_TYPE_NAME_LENGTH + 1]; + + cm_StringCopy(eventTemplateName,"_ev.", sizeof(eventTemplateName)); + cm_StringConcatenate(eventTemplateName, require->interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH); + + if ((error = cm_instantiateComponent( + eventTemplateName, + itfRequire->client->domainId, + itfProvide->server->priority, + eventDup, + elfhandleEvent, + &bfInfo->eventInstance)) != CM_OK) { + OSAL_Free(bfInfo); + return (error == CM_COMPONENT_NOT_FOUND)?CM_BINDING_COMPONENT_NOT_FOUND : error; + } + } + + /* + * Initialize the event component + */ + { + unsigned int size; + + // Get fifo elem size (which was store in TOP by convention) + size = cm_readAttributeNoError(bfInfo->eventInstance, "TOP"); + LOG_INTERNAL(3, "DspEvent Fifo element size = %d\n", size, 0, 0, 0, 0, 0); + + // Allocate fifo + if ((error = dspevent_createDspEventFifo(bfInfo->eventInstance, + "TOP", + fifosize, size, + dspEventMemType, + &bfInfo->dspfifoHandle)) != CM_OK) + { + cm_destroyInstance(bfInfo->eventInstance, DESTROY_WITHOUT_CHECK); + OSAL_Free(bfInfo); + return error; + } + } + + /* Bind event to server interface (Error must not occure) */ + CM_ASSERT(cm_getRequiredInterface(bfInfo->eventInstance, "target", &eventitfRequire) == CM_OK); + + cm_bindLowLevelInterface(&eventitfRequire, itfProvide, BF_SYNCHRONOUS, NULL); + + /* Get the event interface (Error must not occure) */ + CM_ASSERT(cm_getProvidedInterface(bfInfo->eventInstance, "target", &eventitfProvide) == CM_OK); + + /* Bind client to event (Error must not occure) */ + cm_bindLowLevelInterface(itfRequire, &eventitfProvide, BF_ASYNCHRONOUS, bfInfo); + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_ASYNCHRONOUS, + itfRequire->client, itfProvide->server, + itfRequire->client->Template->requires[itfRequire->requireIndex].name, + itfProvide->server->Template->provides[itfProvide->provideIndex].name); + + return CM_OK; +} + +void cm_unbindInterfaceAsynchronous( + const t_interface_require_description *itfRequire, + t_async_bf_info *bfInfo) +{ + t_interface_require_description eventitfRequire; + + LOG_INTERNAL(1, "\n##### UnBind asynchronous %s/%x.%s #####\n", + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0); + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_ASYNCHRONOUS, + itfRequire->client, NULL, + itfRequire->client->Template->requires[itfRequire->requireIndex].name, + NULL); + + /* Unbind Client from Event Binding Component */ + cm_bindLowLevelInterfaceToConst(itfRequire, 0x0, NULL); + + /* Unbind explicitly Event from Server Binding Component */ + /* This is mandatory to fix the providedItfUsedCount of the server */ + CM_ASSERT(cm_getRequiredInterface(bfInfo->eventInstance, "target", &eventitfRequire) == CM_OK); + + cm_registerLowLevelInterfaceToConst(&eventitfRequire, NULL); + + /* Destroy Event fifo */ + dspevent_destroyDspEventFifo(bfInfo->dspfifoHandle); + + /* Destroy Event Binding Component */ + cm_destroyInstance(bfInfo->eventInstance, DESTROY_WITHOUT_CHECK); + + /* Free BF info */ + OSAL_Free(bfInfo); +} + +/*! + * Create Shared FIFO and set stub and skeleton to it + */ +PRIVATE t_cm_error cm_createParamsFifo(t_component_instance *stub, + t_component_instance *skeleton, + t_cm_domain_id domainId, + t_uint32 fifosize, + t_nmf_fifo_arm_desc **fifo, + t_uint32 *fifoElemSize, + t_uint32 bcDescSize) +{ + t_nmf_core_id stubcore = (stub != NULL) ?(stub->Template->dspId): ARM_CORE_ID; + t_nmf_core_id skelcore = (skeleton != NULL) ?(skeleton->Template->dspId) : ARM_CORE_ID; + t_component_instance *bcnotnull = (stub != NULL) ? stub : skeleton; + int _fifoelemsize; + + CM_ASSERT(bcnotnull != NULL); + + /* Get fifo param elem size (which was store in FIFO by convention) */ + _fifoelemsize = cm_readAttributeNoError(bcnotnull, "FIFO"); + LOG_INTERNAL(3, "Fifo Params element size = %d\n", _fifoelemsize, 0, 0, 0, 0, 0); + if(fifoElemSize != NULL) + *fifoElemSize = _fifoelemsize; + + /* Allocation of the fifo params */ + *fifo = fifo_alloc(stubcore, skelcore, _fifoelemsize, fifosize, 1+bcDescSize, paramsLocation, extendedFieldLocation, domainId); /* 1+nbMethods fro hostBCThis_or_TOP space */ + if(*fifo == NULL) { + ERROR("CM_NO_MORE_MEMORY: fifo_alloc() failed in cm_createParamsFifo()\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + if(stub != NULL) + { + /* Set stub FIFO attribute (Error mut not occure) */ + cm_writeAttribute(stub, "FIFO", (*fifo)->dspAdress); + + LOG_INTERNAL(2, " FIFO param %x:%x\n", *fifo, (*fifo)->dspAdress, 0, 0, 0, 0); + } + + if(skeleton != NULL) + { + /* Set Skeleton FIFO attribute (Error mut not occure) */ + cm_writeAttribute(skeleton, "FIFO", (*fifo)->dspAdress); + + LOG_INTERNAL(2, " FIFO param %x:%x\n", *fifo, (*fifo)->dspAdress, 0, 0, 0, 0); + } + + return CM_OK; +} +/** + * + */ +static void cm_destroyParamsFifo(t_nmf_fifo_arm_desc *fifo) { + fifo_free(fifo); +} + +/*! + * Create DSP skeleton + */ +PRIVATE t_cm_error cm_createDSPSkeleton( + const t_interface_provide_description *itfProvide, + t_uint32 fifosize, + t_dsp_memory_type_id dspEventMemType, //INTERNAL_XRAM24 + t_elfdescription *elfhandleSkeleton, + t_dspskel_bf_info *bfInfo) +{ + t_interface_provide *provide = &itfProvide->server->Template->provides[itfProvide->provideIndex]; + t_interface_require_description skelitfRequire; + t_cm_error error; + unsigned int fifoeventsize = 0; + + /* Instantiate related stub on dsp */ + { + char stubTemplateName[4 + MAX_INTERFACE_TYPE_NAME_LENGTH + 1]; + + cm_StringCopy(stubTemplateName,"_sk.", sizeof(stubTemplateName)); + cm_StringConcatenate(stubTemplateName, provide->interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH); + + if ((error = cm_instantiateComponent( + stubTemplateName, + itfProvide->server->domainId, + itfProvide->server->priority, + skeletonDup, + elfhandleSkeleton, + &bfInfo->skelInstance)) != CM_OK) { + return ((error == CM_COMPONENT_NOT_FOUND)?CM_BINDING_COMPONENT_NOT_FOUND:error); + } + } + + /* Get fifo elem size (which was store in TOP by convention) */ + fifoeventsize = cm_readAttributeNoError(bfInfo->skelInstance, "TOP"); + LOG_INTERNAL(3, "DspEvent Fifo element size = %d\n", fifoeventsize, 0, 0, 0, 0, 0); + + /* Allocation of the itf event dsp fifo */ + if ((error = dspevent_createDspEventFifo( + bfInfo->skelInstance, + "TOP", + fifosize, + fifoeventsize, + dspEventMemType, + &bfInfo->dspfifoHandle)) != CM_OK) + { + cm_destroyInstance(bfInfo->skelInstance, DESTROY_WITHOUT_CHECK); + return error; + } + + /* Bind stub to server component (Error must not occure) */ + CM_ASSERT(cm_getRequiredInterface(bfInfo->skelInstance, "target", &skelitfRequire) == CM_OK); + + cm_bindLowLevelInterface(&skelitfRequire, itfProvide, BF_SYNCHRONOUS, NULL); + + return CM_OK; +} + +/** + * Destroy DSP Skeleton + */ +PRIVATE t_cm_error cm_destroyDSPSkeleton(t_dspskel_bf_info *bfInfo) { + t_interface_require_description skelitfRequire; + + /* Unbind explicitly stub from server component (Error must not occure) */ + /* This is mandatory to fix the providedItfUsedCount of the server */ + CM_ASSERT(cm_getRequiredInterface(bfInfo->skelInstance, "target", &skelitfRequire) == CM_OK); + + cm_registerLowLevelInterfaceToConst(&skelitfRequire, NULL); + + /* Destroy Event fifo */ + dspevent_destroyDspEventFifo(bfInfo->dspfifoHandle); + + /* Destroy Event Binding Component */ + return cm_destroyInstance(bfInfo->skelInstance, DESTROY_WITHOUT_CHECK); +} + +/* + * + */ +t_cm_error cm_bindComponentFromCMCore( + const t_interface_provide_description *itfProvide, + t_uint32 fifosize, + t_dsp_memory_type_id dspEventMemType, + t_elfdescription *elfhandleSkeleton, + t_host2mpc_bf_info **bfInfo) { + t_interface_provide *provide = &itfProvide->server->Template->provides[itfProvide->provideIndex]; + t_dsp_offset shareVarOffset; + t_cm_error error; + + LOG_INTERNAL(1, "\n##### Bind HOST -> %s/%x.%s #####\n", + itfProvide->server->pathname, itfProvide->server, itfProvide->origName, 0, 0, 0); + + /* Allocate host2dsp binding factory information */ + *bfInfo = (t_host2mpc_bf_info*)OSAL_Alloc(sizeof(t_host2mpc_bf_info)); + if((*bfInfo) == 0) + return CM_NO_MORE_MEMORY; + + /* Create the Skeleton */ + if ((error = cm_createDSPSkeleton(itfProvide, + fifo_normalizeDepth(fifosize), /* We SHALL create DSP Skeleton before creating the Params Fifo, but we need in advance the real depth of this fifo */ + dspEventMemType, + elfhandleSkeleton, + &(*bfInfo)->dspskeleton)) != CM_OK) + { + OSAL_Free((*bfInfo)); + return error; + } + + /* Create the FIFO Params */ + if ((error = cm_createParamsFifo(NULL, + (*bfInfo)->dspskeleton.skelInstance, + itfProvide->server->domainId, + fifosize, + &(*bfInfo)->fifo, + NULL, + provide->interface->methodNumber)) != CM_OK) + { + cm_destroyDSPSkeleton(&(*bfInfo)->dspskeleton); + OSAL_Free((*bfInfo)); + return error; + } + + /* Set Target info in FIFO param to TOP */ + shareVarOffset = cm_getAttributeMpcAddress((*bfInfo)->dspskeleton.skelInstance, "TOP"); + + /* + * Set Target info in FIFO param to armThis + * Should not return any error + */ + fifo_params_setSharedField((*bfInfo)->fifo, 0, (t_shared_field)shareVarOffset /* ArmBCThis_or_TOP */); + + /* Initialise FIFO Param bcDesc with Skeleton methods */ + { + int i; + t_component_instance *skel = (*bfInfo)->dspskeleton.skelInstance; + for (i=0; i < provide->interface->methodNumber; i++) + { + /* should not return error */ + fifo_params_setSharedField( + (*bfInfo)->fifo, + 1+i, + skel->Template->providesLoaded[0].indexesLoaded[0][i].methodAddresses + ); + } + } + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_ASYNCHRONOUS, + ARM_TRACE_COMPONENT, itfProvide->server, + NULL, + itfProvide->server->Template->provides[itfProvide->provideIndex].name); + + return CM_OK; +} + +void cm_unbindComponentFromCMCore( + t_host2mpc_bf_info* bfInfo) { + t_component_instance *skel = bfInfo->dspskeleton.skelInstance; + t_interface_reference* itfProvide = &skel->interfaceReferences[0][0]; + t_interface_provide *provide = &itfProvide->instance->Template->provides[itfProvide->provideIndex]; + + LOG_INTERNAL(1, "\n##### UnBind HOST -> %s/%x.%s #####\n", + itfProvide->instance->pathname, itfProvide->instance, provide->name, 0, 0, 0); + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_ASYNCHRONOUS, + ARM_TRACE_COMPONENT, itfProvide->instance, + NULL, + itfProvide->instance->Template->provides[itfProvide->provideIndex].name); + + // Destroy FIFO params + cm_destroyParamsFifo(bfInfo->fifo); + + // Destory Skeleton + cm_destroyDSPSkeleton(&bfInfo->dspskeleton); + + // Free BF info (which contains bcDecr(==dspfct) and arm This) + OSAL_Free(bfInfo); +} + +/** + * Create DSP Stub + */ +PRIVATE t_cm_error cm_createDSPStub( + const t_interface_require_description *itfRequire, + const char* itfType, + t_dspstub_bf_info* bfInfo, + t_elfdescription *elfhandleStub, + t_interface_provide_description *itfstubProvide) { + t_cm_error error; + + /* + * Instantiate related skel on dsp + */ + { + char skelTemplateName[4 + MAX_INTERFACE_TYPE_NAME_LENGTH + 1]; + + cm_StringCopy(skelTemplateName, "_st.", sizeof(skelTemplateName)); + cm_StringConcatenate(skelTemplateName, itfType, MAX_INTERFACE_TYPE_NAME_LENGTH); + + if ((error = cm_instantiateComponent( + skelTemplateName, + itfRequire->client->domainId, + itfRequire->client->priority, + stubDup, + elfhandleStub, + &bfInfo->stubInstance)) != CM_OK) { + return (error == CM_COMPONENT_NOT_FOUND)?CM_BINDING_COMPONENT_NOT_FOUND : error; + } + } + + /* Get the internal component that serve this interface (Error must not occure) */ + (void)cm_getProvidedInterface(bfInfo->stubInstance, "source", itfstubProvide); + + return CM_OK; +} + +PRIVATE t_cm_error cm_destroyDSPStub( + const t_interface_require_description *itfRequire, + t_dspstub_bf_info* bfInfo) { + + /* Unbind Client from Event Binding Component */ + cm_bindLowLevelInterfaceToConst(itfRequire, + 0x0, + NULL); + + /* Destroy Event Binding Component */ + return cm_destroyInstance(bfInfo->stubInstance, DESTROY_WITHOUT_CHECK); +} +/* + * + */ +t_cm_error cm_bindComponentToCMCore( + const t_interface_require_description *itfRequire, + t_uint32 fifosize, + t_uint32 context, + t_elfdescription *elfhandleStub, + t_mpc2host_bf_info ** bfInfo) { + t_interface_require *require = &itfRequire->client->Template->requires[itfRequire->requireIndex]; + t_interface_provide_description itfstubProvide; + t_cm_error error; + t_uint32 fifoelemsize; + + LOG_INTERNAL(1, "\n##### Bind %s/%x.%s -> HOST #####\n", + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0); + + /* Allocate dsp2host binding factory information */ + *bfInfo = (t_mpc2host_bf_info*)OSAL_Alloc(sizeof(t_mpc2host_bf_info)); + if(*bfInfo == 0) + return CM_NO_MORE_MEMORY; + (*bfInfo)->context = context; + + if ((error = cm_createDSPStub(itfRequire, + require->interface->type, + &(*bfInfo)->dspstub, + elfhandleStub, + &itfstubProvide)) != CM_OK) + { + OSAL_Free(*bfInfo); + return error; + } + + /* Create the FIFO Params */ + if ((error = cm_createParamsFifo( + (*bfInfo)->dspstub.stubInstance, + NULL, + itfRequire->client->domainId, + fifosize, + &(*bfInfo)->fifo, + &fifoelemsize, + 1)) != CM_OK) /* 1 => we used first field as max params size */ + { + cm_destroyDSPStub(itfRequire, &(*bfInfo)->dspstub); + OSAL_Free(*bfInfo); + return error; + } + + /* Bind client to stub component (Error must not occure) */ + cm_bindLowLevelInterface(itfRequire, &itfstubProvide, BF_DSP2HOST, *bfInfo); + + /* Bind stub component to host (virtual bind) */ + cm_bindVirtualInterface((*bfInfo)->dspstub.stubInstance, (t_component_instance*)NMF_HOST_COMPONENT); + + /* + * Set Target info in FIFO param to armThis + * Initialise FIFO Param bcDesc with Jumptable + * Should not return any error + */ + fifo_params_setSharedField((*bfInfo)->fifo, 0, (t_shared_field)context /* ArmBCThis_or_TOP */); + fifo_params_setSharedField((*bfInfo)->fifo, 1, (t_shared_field)fifoelemsize * 2/* bcDescRef */); + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_ASYNCHRONOUS, + itfRequire->client, ARM_TRACE_COMPONENT, + itfRequire->client->Template->requires[itfRequire->requireIndex].name, + NULL); + + return error; +} + +void cm_unbindComponentToCMCore( + const t_interface_require_description *itfRequire, + t_mpc2host_bf_info *bfInfo) +{ + LOG_INTERNAL(1, "\n##### UnBind %s/%x.%s -> HOST #####\n", + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0); + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_ASYNCHRONOUS, + itfRequire->client, ARM_TRACE_COMPONENT, + itfRequire->client->Template->requires[itfRequire->requireIndex].name, + NULL); + + /* Unbind virtual interface coms */ + cm_unbindVirtualInterface(bfInfo->dspstub.stubInstance); + + // Destroy FIFO params + cm_destroyParamsFifo(bfInfo->fifo); + + // Destroy DSP Stub + cm_destroyDSPStub(itfRequire, &bfInfo->dspstub); + + /* Free BF info */ + OSAL_Free(bfInfo); +} + +/*! + * + */ +t_cm_error cm_bindInterfaceDistributed( + const t_interface_require_description *itfRequire, + const t_interface_provide_description *itfProvide, + t_uint32 fifosize, + t_dsp_memory_type_id dspEventMemType, + t_elfdescription *elfhandleSkeleton, + t_elfdescription *elfhandleStub) { + t_interface_require *require = &itfRequire->client->Template->requires[itfRequire->requireIndex]; + t_interface_provide_description itfstubProvide; + t_cm_error error; + t_mpc2mpc_bf_info *bfInfo; + t_dsp_offset shareVarOffset; + + LOG_INTERNAL(1, "\n##### Bind Distributed %s/%x.%s -> %s/%x.%s #####\n", + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, + itfProvide->server->pathname, itfProvide->server, itfProvide->origName); + + /* Allocate aynchronous binding factory information */ + bfInfo = (t_mpc2mpc_bf_info*)OSAL_Alloc(sizeof(t_mpc2mpc_bf_info)); + if(bfInfo == 0) + return CM_NO_MORE_MEMORY; + + /* Create the Skeleton */ + if ((error = cm_createDSPSkeleton(itfProvide, + fifo_normalizeDepth(fifosize), /* We SHALL create DSP Skeleton before creating the Params Fifo, but we need in advance the real depth of this fifo */ + dspEventMemType, + elfhandleSkeleton, + &bfInfo->dspskeleton)) != CM_OK) + { + OSAL_Free(bfInfo); + return error; + } + + // Create DSP Stub + if ((error = cm_createDSPStub(itfRequire, + require->interface->type, + &bfInfo->dspstub, + elfhandleStub, + &itfstubProvide)) != CM_OK) + { + cm_destroyDSPSkeleton(&bfInfo->dspskeleton); + OSAL_Free(bfInfo); + return error; + } + + /* Bind client to stub component (Error must not occure) */ + cm_bindLowLevelInterface(itfRequire, &itfstubProvide, BF_DSP2DSP, bfInfo); + + /* Create the FIFO Params */ + if ((error = cm_createParamsFifo( + bfInfo->dspstub.stubInstance, + bfInfo->dspskeleton.skelInstance, + itfProvide->server->domainId, + fifosize, + &bfInfo->fifo, + NULL, + require->interface->methodNumber)) != CM_OK) + { + cm_destroyDSPStub(itfRequire, &bfInfo->dspstub); + cm_destroyDSPSkeleton(&bfInfo->dspskeleton); + OSAL_Free(bfInfo); + return error; + } + + /* Bind stub component to host (virtual bind) */ + cm_bindVirtualInterface(bfInfo->dspstub.stubInstance, bfInfo->dspskeleton.skelInstance); + + /* Set Target info in FIFO param to TOP */ + shareVarOffset = cm_getAttributeMpcAddress(bfInfo->dspskeleton.skelInstance, "TOP"); + + /* + * Set Target info in FIFO param to armThis + * Should not return any error + */ + fifo_params_setSharedField(bfInfo->fifo, 0, (t_shared_field)shareVarOffset /* ArmBCThis_or_TOP */); + + /* Initialise FIFO Param bcDesc with Skeleton methods */ + { + int i; + t_component_instance *skel = bfInfo->dspskeleton.skelInstance; + for (i=0; i < require->interface->methodNumber; i++) + { + /* should not return error */ + fifo_params_setSharedField( + bfInfo->fifo, + 1+i, + skel->Template->providesLoaded[0].indexesLoaded[0][i].methodAddresses + ); + } + } + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_ASYNCHRONOUS, + itfRequire->client, itfProvide->server, + itfRequire->client->Template->requires[itfRequire->requireIndex].name, + itfProvide->server->Template->provides[itfProvide->provideIndex].name); + + return CM_OK; +} + +/*! + * + */ +void cm_unbindInterfaceDistributed( + const t_interface_require_description *itfRequire, + t_mpc2mpc_bf_info *bfInfo) +{ + LOG_INTERNAL(1, "\n##### UnBind distributed %s/%x.%s #####\n", + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0); + + cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_ASYNCHRONOUS, + itfRequire->client, NULL, + itfRequire->client->Template->requires[itfRequire->requireIndex].name, + NULL); + + /* Unbind virtual interface */ + cm_unbindVirtualInterface(bfInfo->dspstub.stubInstance); + + // Destroy FIFO params + cm_destroyParamsFifo(bfInfo->fifo); + + // Destroy DSP Stub + cm_destroyDSPStub(itfRequire, &bfInfo->dspstub); + + // Destory DSP Skeleton + cm_destroyDSPSkeleton(&bfInfo->dspskeleton); + + // Destroy BF Info + OSAL_Free(bfInfo); +} + +t_cm_error cm_bindInterfaceStaticInterrupt( + const t_nmf_core_id coreId, + const int interruptLine, + const t_component_instance *server, + const char* providedItfServerName +) +{ + char requiredItfClientName[CM_IT_NAME_MAX_LENGTH]; + t_component_instance *client = cm_EEM_getExecutiveEngine(coreId)->instance; + t_interface_require_description itfRequire; + t_interface_provide_description itfProvide; + t_cm_error error; + + //build it[%d] name + if (interruptLine < 0 || interruptLine > 255) {return CM_OUT_OF_LIMITS;} + cm_fillItName(interruptLine, requiredItfClientName); + + //do binding + if ((error = cm_getRequiredInterface(client,requiredItfClientName,&itfRequire)) != CM_OK) {return error;} + if ((error = cm_getProvidedInterface(server,providedItfServerName,&itfProvide)) != CM_OK) {return error;} + if((error = cm_bindInterface(&itfRequire, &itfProvide)) != CM_OK) {return error;} + + return CM_OK; +} + +t_cm_error cm_unbindInterfaceStaticInterrupt( + const t_nmf_core_id coreId, + const int interruptLine +) +{ + char requiredItfClientName[CM_IT_NAME_MAX_LENGTH]; + t_component_instance *client = cm_EEM_getExecutiveEngine(coreId)->instance; + t_interface_require_description itfRequire; + t_cm_error error; + + //build it[%d] name + if (interruptLine < 0 || interruptLine > 255) {return CM_OUT_OF_LIMITS;} + cm_fillItName(interruptLine, requiredItfClientName); + + //do unbinding + if ((error = cm_getRequiredInterface(client,requiredItfClientName,&itfRequire)) != CM_OK) {return error;} + cm_unbindInterface(&itfRequire); + + return CM_OK; +} + +void cm_destroyRequireInterface(t_component_instance* component, t_nmf_client_id clientId) +{ + int i, j; + + /* + * Special code for SINGLETON handling + */ + if(component->Template->classe == SINGLETON) + { + if(getNumberOfBind(component) > 0) + return; + } + + for(i = 0; i < component->Template->requireNumber; i++) + { + int nb = component->Template->requires[i].collectionSize; + for(j = 0; j < nb; j++) + { + if(component->interfaceReferences[i][j].instance != NULL) + { + t_interface_reference* itfRef = &component->interfaceReferences[i][j]; + t_interface_require_description itfRequire; + + itfRequire.client = component; + itfRequire.requireIndex = i; + itfRequire.collectionIndex = j; + itfRequire.origName = component->Template->requires[i].name; + + switch (itfRef->bfInfoID) { + case BF_SYNCHRONOUS: + /* Error ignored as it is always OK */ + cm_unbindInterface(&itfRequire); + break; + case BF_TRACE: + cm_unbindInterfaceTrace(&itfRequire, + (t_trace_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo); + break; + case BF_ASYNCHRONOUS: + cm_unbindInterfaceAsynchronous(&itfRequire, + (t_async_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo); + break; + case BF_DSP2HOST: + /* This 'mpc2host handle' is provided by the host at OS Integration level. + It must then be handled and released in OS specific part. + */ + cm_unbindComponentToCMCore(&itfRequire, + (t_mpc2host_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo); + break; + case BF_HOST2DSP: + /* These bindings are from CM Core to DSP, they are not listed + here and must be handles/freed by host at OS Integration level + */ + break; + case BF_DSP2DSP: + cm_unbindInterfaceDistributed(&itfRequire, + (t_mpc2mpc_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo); + break; + default: + break; + } + } + } + } +} + +void cm_registerSingletonBinding( + t_component_instance* component, + t_interface_require_description* itfRequire, + t_interface_provide_description* itfProvide, + t_nmf_client_id clientId) +{ + if(component->Template->classe == SINGLETON) + { + struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, FALSE, clientId); + if(cl != NULL) + cl->numberOfBind++; + + if(itfProvide != NULL) + LOG_INTERNAL(1, " -> Singleton[%d] : Register binding %s/%x.%s -> %s/%x\n", + clientId, + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, + itfProvide->server->pathname, itfProvide->server); + else + LOG_INTERNAL(1, " -> Singleton[%d] : Register binding %s/%x.%s -> ARM/VOID\n", + clientId, + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0); + } +} + +t_bool cm_unregisterSingletonBinding( + t_component_instance* component, + t_interface_require_description* itfRequire, + t_interface_provide_description* itfProvide, + t_nmf_client_id clientId) +{ + if(component->Template->classe == SINGLETON) + { + struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, FALSE, clientId); + if(cl != NULL) + cl->numberOfBind--; + + if(itfProvide->server == (t_component_instance *)NMF_VOID_COMPONENT) + LOG_INTERNAL(1, " -> Singleton[%d] : Unregister binding %s/%x.%s -> ARM/VOID\n", + clientId, + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0); + else if(itfProvide->server == NULL) + LOG_INTERNAL(1, " -> Singleton[%d] : Unregister binding %s/%x.%s -> ?? <already unbound>\n", + clientId, + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0); + else + LOG_INTERNAL(1, " -> Singleton[%d] : Unregister binding %s/%x.%s -> %s/%x\n", + clientId, + itfRequire->client->pathname, itfRequire->client, itfRequire->origName, + itfProvide->server->pathname, itfProvide->server); + + if(getNumberOfBind(component) == 0) + { + LOG_INTERNAL(1, " -> Singleton[%d] : All required of %s/%x logically unbound, perform physical unbind\n", + clientId, itfRequire->client->pathname, itfRequire->client, 0, 0, 0); + + (void)cm_EEM_ForceWakeup(component->Template->dspId); + + // This is the last binding unbind all !!! + cm_destroyRequireInterface(component, clientId); + + cm_EEM_AllowSleep(component->Template->dspId); + } + else if(itfProvide->server != NULL) + { + t_interface_require* itfReq; + itfReq = &itfRequire->client->Template->requires[itfRequire->requireIndex]; + if((itfReq->requireTypes & OPTIONAL_REQUIRE) != 0x0) + return TRUE; + } + + return FALSE; + } + + return TRUE; +} + +static t_uint16 getNumberOfBind(t_component_instance* component) +{ + t_uint16 bindNumber = 0; + struct t_client_of_singleton* cur = component->clientOfSingleton; + + for( ; cur != NULL ; cur = cur->next) + { + bindNumber += cur->numberOfBind; + } + + return bindNumber; +} + +static void cm_fillItName(int interruptLine, char *itName) +{ + int divider = 10000; + + *itName++ = 'i'; + *itName++ = 't'; + *itName++ = '['; + + // Find first significant divider + while(divider > interruptLine) + divider /= 10; + + // Compute number + do + { + *itName++ = "0123456789"[interruptLine / divider]; + interruptLine %= divider; + divider /= 10; + } while(divider != 0); + + *itName++ = ']'; + *itName++ = '\0'; +} diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/binder_check.c b/drivers/staging/nmf-cm/cm/engine/component/src/binder_check.c new file mode 100644 index 00000000000..373fea0cd47 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/src/binder_check.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include "../inc/bind.h" +#include <cm/engine/trace/inc/trace.h> + +#include <cm/engine/utils/inc/string.h> + +t_cm_error cm_checkValidClient( + const t_component_instance* client, + const char* requiredItfClientName, + t_interface_require_description *itfRequire, + t_bool *bindable) { + t_cm_error error; + + // Component LC state check + if (NULL == client) + return CM_INVALID_COMPONENT_HANDLE; + + // Check if the requiredItfClientName is required by client component + if ((error = cm_getRequiredInterface(client, requiredItfClientName, itfRequire)) != CM_OK) + return error; + + // Check required interface not already binded + { + t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex]; + + if(itfRef->instance != (t_component_instance*)NULL) + { + if(client->Template->classe == SINGLETON) + { + // Singleton is immutable thus we can't rebind it, nevertheless it's not an issue + *bindable = FALSE; + return CM_OK; + } + else + { + t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex]; + + if(itfRef->instance == (const t_component_instance*)NMF_VOID_COMPONENT) + ERROR("CM_INTERFACE_ALREADY_BINDED(): Component (%s<%s>.%s) already bound to VOID\n", + client->pathname, client->Template->name, requiredItfClientName, 0, 0, 0); + else + ERROR("CM_INTERFACE_ALREADY_BINDED(): Component (%s<%s>.%s) already bound to another server (%s<%s>.%s)\n", + client->pathname, client->Template->name, requiredItfClientName, + itfRef->instance->pathname, itfRef->instance->Template->name, itfRef->instance->Template->provides[itfRef->provideIndex].name); + return CM_INTERFACE_ALREADY_BINDED; + } + } + } + + // Delayed Component LC state check done only if not optional required interface or intrinsic one that has been solved by loader + { + t_interface_require* itfReq = &client->Template->requires[itfRequire->requireIndex]; + + if((itfReq->requireTypes & (OPTIONAL_REQUIRE | INTRINSEC_REQUIRE)) == 0) { + if(client->state == STATE_RUNNABLE) + return CM_COMPONENT_NOT_STOPPED; + } + } + + *bindable = TRUE; + + return CM_OK; +} + +t_cm_error cm_checkValidServer( + const t_component_instance* server, + const char* providedItfServerName, + t_interface_provide_description *itfProvide) { + t_cm_error error; + + // Check if the components are initialized + //if (server->state == STATE_INSTANCIATED) + // return CM_COMPONENT_NOT_INITIALIZED; + if(NULL == server) + return CM_INVALID_COMPONENT_HANDLE; + + // Check if the providedItfServerName is provided by server component + if((error = cm_getProvidedInterface(server, providedItfServerName, itfProvide)) != CM_OK) + return error; + + return CM_OK; +} + +t_cm_error cm_checkValidBinding( + const t_component_instance* client, + const char* requiredItfClientName, + const t_component_instance* server, + const char* providedItfServerName, + t_interface_require_description *itfRequire, + t_interface_provide_description *itfProvide, + t_bool *bindable) { + t_interface_require *require; + t_interface_provide *provide; + t_cm_error error; + + // Check Server + if((error = cm_checkValidServer(server, providedItfServerName, itfProvide)) != CM_OK) + return error; + + // Check Client + if((error = cm_checkValidClient(client, requiredItfClientName, itfRequire, bindable)) != CM_OK) + return error; + + // If this is a singleton which has been already bound check that next binding is at the same server + if(*bindable == FALSE + && client->Template->classe == SINGLETON) + { + t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex]; + while( itfRef->instance != server + || itfRef->provideIndex != itfProvide->provideIndex + || itfRef->collectionIndex != itfProvide->collectionIndex ) + { + if(itfRef->instance == (const t_component_instance*)NMF_VOID_COMPONENT) + { + ERROR("CM_INTERFACE_ALREADY_BINDED(): Singleton (%s<%s>.%s) already bound to VOID\n", + client->pathname, client->Template->name, requiredItfClientName, 0, 0, 0); + return CM_INTERFACE_ALREADY_BINDED; + } + else if(itfRef->bfInfoID == BF_ASYNCHRONOUS || itfRef->bfInfoID == BF_TRACE) + { + t_interface_require_description eventitfRequire; + CM_ASSERT(cm_getRequiredInterface(itfRef->instance, "target", &eventitfRequire) == CM_OK); + itfRef = &itfRef->instance->interfaceReferences[eventitfRequire.requireIndex][eventitfRequire.collectionIndex]; + + // Go to see client of event if the same + } + else + { + ERROR("CM_INTERFACE_ALREADY_BINDED(): Singleton (%s<%s>.%s) already bound to different server (%s<%s>.%s)\n", + client->pathname, client->Template->name, requiredItfClientName, + itfRef->instance->pathname, itfRef->instance->Template->name, itfRef->instance->Template->provides[itfRef->provideIndex].name); + return CM_INTERFACE_ALREADY_BINDED; + } + } + } + + // Check if provided and required type matches + require = &client->Template->requires[itfRequire->requireIndex]; + provide = &server->Template->provides[itfProvide->provideIndex]; + if(require->interface != provide->interface) + { + ERROR("CM_ILLEGAL_BINDING(%s, %s)\n", require->interface->type, provide->interface->type, 0, 0, 0, 0); + return CM_ILLEGAL_BINDING; + } + + // Check if static required interface binded to singleton component + if((require->requireTypes & STATIC_REQUIRE) && + (server->Template->classe != SINGLETON)) + { + ERROR("CM_ILLEGAL_BINDING(): Can't bind static required interface to not singleton component\n", + 0, 0, 0, 0, 0, 0); + return CM_ILLEGAL_BINDING; + } + + return CM_OK; +} + +t_cm_error cm_checkValidUnbinding( + const t_component_instance* client, + const char* requiredItfClientName, + t_interface_require_description *itfRequire, + t_interface_provide_description *itfProvide) { + t_cm_error error; + t_interface_require* itfReq; + + // Component LC state check + if (NULL == client) + return CM_INVALID_COMPONENT_HANDLE; + + // Check if the requiredItfClientName is required by client component + if ((error = cm_getRequiredInterface(client, requiredItfClientName, itfRequire)) != CM_OK) + return error; + + itfReq = &client->Template->requires[itfRequire->requireIndex]; + + // Check if the requiredItfClientName is required by client component + if ((error = cm_lookupInterface(itfRequire, itfProvide)) != CM_OK) + { + // We allow to unbind optional required of singleton even if not binded, since it could have been unbound previously but we don't + // want to break bind singleton reference counter + if((client->Template->classe == SINGLETON) && + (itfReq->requireTypes & OPTIONAL_REQUIRE) != 0x0) + return CM_OK; + + return error; + } + + // Singleton is immutable, don't unbind it + if(client->Template->classe == SINGLETON) + return CM_OK; + + /* if interface is optionnal then allow unbinding even if not stop */ + if((itfReq->requireTypes & OPTIONAL_REQUIRE) == 0x0) + { + if(client->state == STATE_RUNNABLE) + return CM_COMPONENT_NOT_STOPPED; + } + + return CM_OK; +} + diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/component_wrapper.c b/drivers/staging/nmf-cm/cm/engine/component/src/component_wrapper.c new file mode 100644 index 00000000000..88e6b4749ec --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/src/component_wrapper.c @@ -0,0 +1,1298 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/api/component_engine.h> +#include <cm/engine/api/communication_engine.h> + +#include <cm/engine/component/inc/bind.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/component/inc/introspection.h> +#include <cm/engine/utils/inc/string.h> +#include <cm/engine/memory/inc/domain.h> + +#include <cm/engine/configuration/inc/configuration.h> +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> + +/* + * Component mangement wrapping. + */ +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_InstantiateComponent( + const char* templateName, + t_cm_domain_id domainId, + t_nmf_client_id clientId, + t_nmf_ee_priority priority, + const char localName[MAX_COMPONENT_NAME_LENGTH], + const char *dataFile, + t_cm_instance_handle *instance) { + t_cm_error error; + t_nmf_core_id coreId; + t_component_instance *comp; + t_elfdescription *elfhandle = NULL; + + OSAL_LOCK_API(); + + /* + * Load Elf File + */ + if(dataFile != NULL && + (error = cm_ELF_CheckFile( + dataFile, + TRUE, + &elfhandle)) != CM_OK) + goto out; + + //only allow instantiation in non-scratch domains (ie. DOMAIN_NORMAL)! + if ((error = cm_DM_CheckDomainWithClient(domainId, DOMAIN_NORMAL, clientId)) != CM_OK) + goto out; + + coreId = cm_DM_GetDomainCoreId(domainId); + + if(coreId < FIRST_MPC_ID || coreId > LAST_CORE_ID) + { + error = CM_INVALID_PARAMETER; + goto out; + } + + if ((error = cm_CFG_CheckMpcStatus(coreId)) != CM_OK) + goto out; + + if ((error = cm_EEM_ForceWakeup(coreId)) != CM_OK) + goto out; + + error = cm_instantiateComponent( + templateName, + domainId, + priority, + localName, + elfhandle, + &comp); + if(error == CM_OK) + *instance = comp->instance; + + cm_EEM_AllowSleep(coreId); + +out: + cm_ELF_CloseFile(TRUE, elfhandle); + + OSAL_UNLOCK_API(); + + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_StartComponent( + t_cm_instance_handle instance, + t_nmf_client_id clientId) { + t_cm_error error; + t_component_instance *component; + + OSAL_LOCK_API(); + + component = cm_lookupComponent(instance); + if (NULL == component) + error = CM_INVALID_COMPONENT_HANDLE; + else + { + error = cm_startComponent(component, clientId); + } + + OSAL_UNLOCK_API(); + + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_StopComponent( + t_cm_instance_handle instance, + t_nmf_client_id clientId) { + t_cm_error error; + t_component_instance *component; + + OSAL_LOCK_API(); + + component = cm_lookupComponent(instance); + if (NULL == component) + error = CM_INVALID_COMPONENT_HANDLE; + else + { + error = cm_stopComponent(component, clientId); + } + + OSAL_UNLOCK_API(); + + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_DestroyComponent( + t_cm_instance_handle instance, + t_nmf_client_id clientId) +{ + t_cm_error error; + t_component_instance *component; + + OSAL_LOCK_API(); + + component = cm_lookupComponent(instance); + if (NULL == component) + { + error = CM_INVALID_COMPONENT_HANDLE; + } + else + { + t_nmf_core_id coreId = component->Template->dspId; + + (void)cm_EEM_ForceWakeup(coreId); + + error = cm_destroyInstanceForClient(component, DESTROY_NORMAL, clientId); + + cm_CFG_ReleaseMpc(coreId); + + cm_EEM_AllowSleep(coreId); + } + + OSAL_UNLOCK_API(); + + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_FlushComponents(t_nmf_client_id clientId) +{ + t_cm_error error = CM_OK; + t_component_instance *instance; + t_uint32 i; + + if (clientId == 0) + return CM_INVALID_PARAMETER; + + OSAL_LOCK_API(); + + // We don't know exactly where components will be, wake up everybody !! + (void)cm_EEM_ForceWakeup(SVA_CORE_ID); + (void)cm_EEM_ForceWakeup(SIA_CORE_ID); + + /* Destroy all host2mpc bindings */ + OSAL_LOCK_COM(); + for (i=0; i<Host2MpcBindingTable.idxMax; i++) + { + t_host2mpc_bf_info* bfInfo; + bfInfo = Host2MpcBindingTable.entries[i]; + if ((bfInfo != NULL) && (bfInfo->clientId == clientId)) { + cm_delEntry(&Host2MpcBindingTable, i); + OSAL_UNLOCK_COM(); + cm_unbindComponentFromCMCore(bfInfo); + OSAL_LOCK_COM(); + } + } + OSAL_UNLOCK_COM(); + + /* First, stop all remaining components for this client */ + for (i=0; i<ComponentTable.idxMax; i++) + { + if ((instance = componentEntry(i)) == NULL) + continue; + if (/* skip EE */ + (instance->Template->classe == FIRMWARE) || + /* Skip all binding components */ + (cm_StringCompare(instance->Template->name, "_ev.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_st.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_sk.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_tr.", 4) == 0)) + continue; + + /* + * Special code for SINGLETON handling + */ + if(instance->Template->classe == SINGLETON) + { + struct t_client_of_singleton* cl = cm_getClientOfSingleton(instance, FALSE, clientId); + if(cl == NULL) + continue; + + cl->numberOfStart = 1; // == 1 since it will go to 0 in cm_stopComponent + cl->numberOfInstance = 1; // == 1 since it will go to 0 in cm_destroyInstanceForClient + cl->numberOfBind = 0; // == 0 since we don't want anymore binding for this component + } + else if(domainDesc[instance->domainId].client != clientId) + /* Skip all components not belonging to our client */ + continue; + + // Stop the component + error = cm_stopComponent(instance, clientId); + if (error != CM_OK && error != CM_COMPONENT_NOT_STARTED) + LOG_INTERNAL(0, "Error stopping component %s/%x (%s, error=%d, client=%u)\n", instance->pathname, instance, instance->Template->name, error, clientId, 0); + + // Destroy dependencies + cm_destroyRequireInterface(instance, clientId); + } + + /* Destroy all remaining components for this client */ + for (i=0; i<ComponentTable.idxMax; i++) + { + if ((instance = componentEntry(i)) == NULL) + continue; + if (/* skip EE */ + (instance->Template->classe == FIRMWARE) || + /* Skip all binding components */ + (cm_StringCompare(instance->Template->name, "_ev.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_st.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_sk.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_tr.", 4) == 0)) { + continue; + } + + + /* + * Special code for SINGLETON handling + */ + if(instance->Template->classe == SINGLETON) + { + struct t_client_of_singleton* cl = cm_getClientOfSingleton(instance, FALSE, clientId); + if(cl == NULL) + continue; + } + else if(domainDesc[instance->domainId].client != clientId) + /* Skip all components not belonging to our client */ + continue; + + + // Destroy the component + error = cm_destroyInstanceForClient(instance, DESTROY_WITHOUT_CHECK, clientId); + + if (error != CM_OK) + { + /* FIXME : add component name instance in log message but need to make a copy before cm_flushComponent() + * because it's no more available after. + */ + LOG_INTERNAL(0, "Error flushing component (error=%d, client=%u)\n", error, clientId, 0, 0, 0, 0); + } + } + + cm_CFG_ReleaseMpc(SVA_CORE_ID); + cm_CFG_ReleaseMpc(SIA_CORE_ID); + + cm_EEM_AllowSleep(SVA_CORE_ID); + cm_EEM_AllowSleep(SIA_CORE_ID); + + OSAL_UNLOCK_API(); + + return error; +} + +/* + * Component binding wrapping. + */ +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_BindComponent( + const t_cm_instance_handle clientInstance, + const char* requiredItfClientName, + const t_cm_instance_handle serverInstance, + const char* providedItfServerName, + t_bool traced, + t_nmf_client_id clientId, + const char *dataFileTrace) { + t_interface_require_description itfRequire; + t_interface_provide_description itfProvide; + t_bool bindable; + t_cm_error error; + t_component_instance *client, *server; + t_elfdescription *elfhandleTrace = NULL; + + OSAL_LOCK_API(); + + /* + * Load Elf File + */ + if(dataFileTrace != NULL && + (error = cm_ELF_CheckFile( + dataFileTrace, + TRUE, + &elfhandleTrace)) != CM_OK) + goto out; + + client = cm_lookupComponent(clientInstance); + server = cm_lookupComponent(serverInstance); + // Sanity check + if((error = cm_checkValidBinding(client, requiredItfClientName, + server, providedItfServerName, + &itfRequire, &itfProvide, &bindable)) != CM_OK) + goto out; + + // Check that client and server component run on same DSP + if (itfRequire.client->Template->dspId != itfProvide.server->Template->dspId) + { + error = CM_ILLEGAL_BINDING; + goto out; + } + + // Check if we really need to bind + if(bindable) + { + if ((error = cm_EEM_ForceWakeup(itfRequire.client->Template->dspId)) != CM_OK) + goto out; + + /* + * Synchronous binding, so no binding component + */ + if(traced) + error = cm_bindInterfaceTrace(&itfRequire, &itfProvide, elfhandleTrace); + else + error = cm_bindInterface(&itfRequire, &itfProvide); + + cm_EEM_AllowSleep(itfRequire.client->Template->dspId); + } + + cm_registerSingletonBinding(client, &itfRequire, &itfProvide, clientId); + +out: + cm_ELF_CloseFile(TRUE, elfhandleTrace); + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_UnbindComponent( + const t_cm_instance_handle clientInstance, + const char* requiredItfClientName, + t_nmf_client_id clientId) { + t_interface_require_description itfRequire; + t_interface_provide_description itfProvide; + t_bf_info_ID bfInfoID; + t_cm_error error; + t_component_instance *client; + + OSAL_LOCK_API(); + + client = cm_lookupComponent(clientInstance); + // Sanity check + if((error = cm_checkValidUnbinding(client, requiredItfClientName, + &itfRequire, &itfProvide)) != CM_OK) + goto out; + + // Check if this is a Primitive binding + bfInfoID = itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfoID; + if(bfInfoID != BF_SYNCHRONOUS && bfInfoID != BF_TRACE) + { + error = CM_ILLEGAL_UNBINDING; + goto out; + } + + // Check if we really need to unbind + if(cm_unregisterSingletonBinding(client, &itfRequire, &itfProvide, clientId)) + { + (void)cm_EEM_ForceWakeup(itfRequire.client->Template->dspId); + + if(bfInfoID == BF_SYNCHRONOUS) + cm_unbindInterface(&itfRequire); + else + cm_unbindInterfaceTrace( + &itfRequire, + (t_trace_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo); + + cm_EEM_AllowSleep(itfRequire.client->Template->dspId); + + error = CM_OK; + } + +out: + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_BindComponentToVoid( + const t_cm_instance_handle clientInstance, + const char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH], + t_nmf_client_id clientId) +{ + t_interface_require_description itfRequire; + t_bool bindable; + t_cm_error error; + t_component_instance *client; + + OSAL_LOCK_API(); + + client = cm_lookupComponent(clientInstance); + // Check invalid binding + if((error = cm_checkValidClient(client, requiredItfClientName, + &itfRequire, &bindable)) != CM_OK) + goto out; + + // Check if we really need to bind + if(bindable) + { + if ((error = cm_EEM_ForceWakeup(itfRequire.client->Template->dspId)) != CM_OK) + goto out; + + error = cm_bindInterfaceToVoid(&itfRequire); + + cm_EEM_AllowSleep(itfRequire.client->Template->dspId); + } + + cm_registerSingletonBinding(client, &itfRequire, NULL, clientId); + +out: + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_BindComponentAsynchronous( + const t_cm_instance_handle clientInstance, + const char* requiredItfClientName, + const t_cm_instance_handle serverInstance, + const char* providedItfServerName, + t_uint32 fifosize, + t_cm_mpc_memory_type eventMemType, + t_nmf_client_id clientId, + const char *dataFileSkeletonOrEvent, + const char *dataFileStub) { + t_interface_require_description itfRequire; + t_interface_provide_description itfProvide; + t_dsp_memory_type_id dspEventMemType; + t_bool bindable; + t_cm_error error; + t_component_instance *client, *server; + t_elfdescription *elfhandleSkeletonOrEvent = NULL; + t_elfdescription *elfhandleStub = NULL; + + OSAL_LOCK_API(); + + /* + * Load Elf File + */ + if(dataFileSkeletonOrEvent != NULL && + (error = cm_ELF_CheckFile( + dataFileSkeletonOrEvent, + TRUE, + &elfhandleSkeletonOrEvent)) != CM_OK) + goto out; + if(dataFileStub != NULL && + (error = cm_ELF_CheckFile( + dataFileStub, + TRUE, + &elfhandleStub)) != CM_OK) + goto out; + + client = cm_lookupComponent(clientInstance); + server = cm_lookupComponent(serverInstance); + // Check invalid binding + if((error = cm_checkValidBinding(client, requiredItfClientName, + server, providedItfServerName, + &itfRequire, &itfProvide, &bindable)) != CM_OK) + goto out; + + switch(eventMemType) + { + case CM_MM_MPC_TCM24_X: + dspEventMemType = INTERNAL_XRAM24; + break; + case CM_MM_MPC_ESRAM24: + dspEventMemType = ESRAM_EXT24; + break; + case CM_MM_MPC_SDRAM24: + dspEventMemType = SDRAM_EXT24; + break; + default: + error = CM_INVALID_PARAMETER; + goto out; + } + + // Check if we really need to bind + if(bindable) + { + // Create the binding and bind it to the client (or all sub-components clients ....) + if (itfRequire.client->Template->dspId != itfProvide.server->Template->dspId) + { + if ((error = cm_EEM_ForceWakeup(itfRequire.client->Template->dspId)) != CM_OK) + goto out; + if ((error = cm_EEM_ForceWakeup(itfProvide.server->Template->dspId)) != CM_OK) + { + cm_EEM_AllowSleep(itfRequire.client->Template->dspId); + goto out; + } + + // This is a distribute communication + error = cm_bindInterfaceDistributed( + &itfRequire, + &itfProvide, + fifosize, + dspEventMemType, + elfhandleSkeletonOrEvent, + elfhandleStub); + + cm_EEM_AllowSleep(itfRequire.client->Template->dspId); + cm_EEM_AllowSleep(itfProvide.server->Template->dspId); + } + else + { + if ((error = cm_EEM_ForceWakeup(itfRequire.client->Template->dspId)) != CM_OK) + goto out; + + // This is a acynchronous communication + error = cm_bindInterfaceAsynchronous( + &itfRequire, + &itfProvide, + fifosize, + dspEventMemType, + elfhandleSkeletonOrEvent); + + cm_EEM_AllowSleep(itfRequire.client->Template->dspId); + } + } + + cm_registerSingletonBinding(client, &itfRequire, &itfProvide, clientId); + +out: + cm_ELF_CloseFile(TRUE, elfhandleSkeletonOrEvent); + cm_ELF_CloseFile(TRUE, elfhandleStub); + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentAsynchronous( + const t_cm_instance_handle instance, + const char* requiredItfClientName, + t_nmf_client_id clientId) { + t_interface_require_description itfRequire; + t_interface_provide_description itfProvide; + t_bf_info_ID bfInfoID; + t_cm_error error; + t_component_instance *client; + + OSAL_LOCK_API(); + + client = cm_lookupComponent(instance); + // Sanity check + if((error = cm_checkValidUnbinding(client, requiredItfClientName, + &itfRequire, &itfProvide)) != CM_OK) + goto out; + + bfInfoID = itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfoID; + + // Check if we really need to unbind + if(cm_unregisterSingletonBinding(client, &itfRequire, &itfProvide, clientId)) + { + // Check if this is a Asynchronous binding + if(bfInfoID == BF_DSP2DSP) + { + t_nmf_core_id clientDsp = itfRequire.client->Template->dspId; + t_nmf_core_id serverDsp = itfProvide.server->Template->dspId; + + (void)cm_EEM_ForceWakeup(clientDsp); + (void)cm_EEM_ForceWakeup(serverDsp); + + cm_unbindInterfaceDistributed( + &itfRequire, + (t_mpc2mpc_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo); + + cm_EEM_AllowSleep(clientDsp); + cm_EEM_AllowSleep(serverDsp); + + error = CM_OK; + } + else if(bfInfoID == BF_ASYNCHRONOUS) + { + t_nmf_core_id clientDsp = itfRequire.client->Template->dspId; + + (void)cm_EEM_ForceWakeup(clientDsp); + + cm_unbindInterfaceAsynchronous( + &itfRequire, + (t_async_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo); + + cm_EEM_AllowSleep(clientDsp); + + error = CM_OK; + } + else + error = CM_ILLEGAL_UNBINDING; + } + + out: + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_BindComponentFromCMCore( + const t_cm_instance_handle server, + const char* providedItfServerName, + t_uint32 fifosize, + t_cm_mpc_memory_type eventMemType, + t_cm_bf_host2mpc_handle *bfHost2mpcHdl, + t_nmf_client_id clientId, + const char *dataFileSkeleton) { + t_interface_provide_description itfProvide; + t_dsp_memory_type_id dspEventMemType; + t_cm_error error; + t_component_instance* component; + t_host2mpc_bf_info *bfInfo; + t_elfdescription *elfhandleSkeleton = NULL; + + OSAL_LOCK_API(); + + /* + * Load Elf File + */ + if(dataFileSkeleton != NULL && + (error = cm_ELF_CheckFile( + dataFileSkeleton, + TRUE, + &elfhandleSkeleton)) != CM_OK) + goto out; + + component = cm_lookupComponent(server); + // Check server validity + if((error = cm_checkValidServer(component, providedItfServerName, + &itfProvide)) != CM_OK) + goto out; + + if ((error = cm_EEM_ForceWakeup(itfProvide.server->Template->dspId)) != CM_OK) + goto out; + + switch(eventMemType) + { + case CM_MM_MPC_TCM24_X: + dspEventMemType = INTERNAL_XRAM24; + break; + case CM_MM_MPC_ESRAM24: + dspEventMemType = ESRAM_EXT24; + break; + case CM_MM_MPC_SDRAM24: + dspEventMemType = SDRAM_EXT24; + break; + default: + goto out; + } + + error = cm_bindComponentFromCMCore(&itfProvide, + fifosize, + dspEventMemType, + elfhandleSkeleton, + &bfInfo); + + cm_EEM_AllowSleep(itfProvide.server->Template->dspId); + +out: + cm_ELF_CloseFile(TRUE, elfhandleSkeleton); + OSAL_UNLOCK_API(); + + if (error == CM_OK) { + bfInfo->clientId = clientId; + OSAL_LOCK_COM(); + *bfHost2mpcHdl = cm_addEntry(&Host2MpcBindingTable, bfInfo); + if (*bfHost2mpcHdl == 0) + error = CM_NO_MORE_MEMORY; + OSAL_UNLOCK_COM(); + + if (error != CM_OK) { + OSAL_LOCK_API(); + (void)cm_EEM_ForceWakeup(itfProvide.server->Template->dspId); + cm_unbindComponentFromCMCore(bfInfo); + cm_EEM_AllowSleep(itfProvide.server->Template->dspId); + OSAL_UNLOCK_API(); + } + } + + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentFromCMCore( + t_cm_bf_host2mpc_handle bfHost2mpcId) { + t_host2mpc_bf_info* bfInfo; + t_nmf_core_id coreId; + + OSAL_LOCK_COM(); + bfInfo = cm_lookupEntry(&Host2MpcBindingTable, bfHost2mpcId); + if (bfInfo) + cm_delEntry(&Host2MpcBindingTable, bfHost2mpcId & INDEX_MASK); + OSAL_UNLOCK_COM(); + if (NULL == bfInfo) + return CM_INVALID_PARAMETER; + + OSAL_LOCK_API(); + + // Check if this is a DSP to Host binding + //if(bfInfo->id != BF_HOST2DSP) + // return CM_ILLEGAL_UNBINDING; + coreId = bfInfo->dspskeleton.skelInstance->Template->dspId; + + (void)cm_EEM_ForceWakeup(coreId); + + cm_unbindComponentFromCMCore(bfInfo); + + cm_EEM_AllowSleep(coreId); + + OSAL_UNLOCK_API(); + return CM_OK; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_BindComponentToCMCore( + const t_cm_instance_handle instance, + const char *requiredItfClientName, + t_uint32 fifosize, + t_nmf_mpc2host_handle upLayerThis, + const char *dataFileStub, + t_cm_bf_mpc2host_handle *mpc2hostId, + t_nmf_client_id clientId) { + t_interface_require_description itfRequire; + t_bool bindable; + t_cm_error error; + t_component_instance* client; + t_elfdescription *elfhandleStub = NULL; + + OSAL_LOCK_API(); + + /* + * Load Elf File + */ + if(dataFileStub != NULL && + (error = cm_ELF_CheckFile( + dataFileStub, + TRUE, + &elfhandleStub)) != CM_OK) + goto out; + + client = cm_lookupComponent(instance); + // Check invalid binding + if((error = cm_checkValidClient(client, requiredItfClientName, + &itfRequire, &bindable)) != CM_OK) + goto out; + + // Check if we really need to bind + if(bindable) + { + if ((error = cm_EEM_ForceWakeup(itfRequire.client->Template->dspId)) != CM_OK) + goto out; + + error = cm_bindComponentToCMCore( + &itfRequire, + fifosize, + upLayerThis, + elfhandleStub, + (t_mpc2host_bf_info**)mpc2hostId); + + cm_EEM_AllowSleep(itfRequire.client->Template->dspId); + } + + cm_registerSingletonBinding(client, &itfRequire, NULL, clientId); + +out: + cm_ELF_CloseFile(TRUE, elfhandleStub); + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentToCMCore( + const t_cm_instance_handle instance, + const char *requiredItfClientName, + t_nmf_mpc2host_handle *upLayerThis, + t_nmf_client_id clientId) { + t_interface_require_description itfRequire; + t_interface_provide_description itfProvide; + t_cm_error error; + t_mpc2host_bf_info *bfInfo; + t_component_instance* client; + + OSAL_LOCK_API(); + + client = cm_lookupComponent(instance); + // Sanity check + if((error = cm_checkValidUnbinding(client, requiredItfClientName, + &itfRequire, &itfProvide)) != CM_OK) + goto out; + + // Check if this is a DSP to Host binding + if(itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfoID != BF_DSP2HOST) + { + error = CM_ILLEGAL_UNBINDING; + goto out; + } + + bfInfo = (t_mpc2host_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo; + + // Get client information + *upLayerThis = bfInfo->context; + + // Check if we really need to unbind + if(cm_unregisterSingletonBinding(client, &itfRequire, &itfProvide, clientId)) + { + (void)cm_EEM_ForceWakeup(itfRequire.client->Template->dspId); + + cm_unbindComponentToCMCore(&itfRequire, bfInfo); + + cm_EEM_AllowSleep(itfRequire.client->Template->dspId); + + error = CM_OK; + } +out: + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_event_params_handle CM_ENGINE_AllocEvent(t_cm_bf_host2mpc_handle host2mpcId) { + t_host2mpc_bf_info* bfInfo; + t_event_params_handle eventHandle; + + OSAL_LOCK_COM(); + bfInfo = cm_lookupEntry(&Host2MpcBindingTable, host2mpcId); + if (NULL == bfInfo) { + OSAL_UNLOCK_COM(); + return NULL; + } + + if(bfInfo->dspskeleton.skelInstance->interfaceReferences[0][0].instance->state != STATE_RUNNABLE) { + ERROR("CM_COMPONENT_NOT_STARTED: Call interface before start component %s<%s>\n", + bfInfo->dspskeleton.skelInstance->pathname, + bfInfo->dspskeleton.skelInstance->Template->name, 0, 0, 0, 0); + } + + eventHandle = cm_AllocEvent(bfInfo->fifo); + + OSAL_UNLOCK_COM(); + + return eventHandle; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_PushEvent(t_cm_bf_host2mpc_handle host2mpcId, t_event_params_handle h, t_uint32 methodIndex) { + t_host2mpc_bf_info* bfInfo; + t_cm_error error; + + OSAL_LOCK_COM(); + bfInfo = cm_lookupEntry(&Host2MpcBindingTable, host2mpcId); + if (NULL == bfInfo) { + OSAL_UNLOCK_COM(); + return CM_INVALID_PARAMETER; + } + error = cm_PushEvent(bfInfo->fifo, h, methodIndex); + OSAL_UNLOCK_COM(); + + return error; +} + +PUBLIC EXPORT_SHARED void CM_ENGINE_AcknowledgeEvent(t_cm_bf_mpc2host_handle mpc2hostId) { + t_mpc2host_bf_info* bfInfo = (t_mpc2host_bf_info*)mpc2hostId; + + //t_dsp2host_bf_info* bfInfo = (t_host2mpc_bf_info*)mpc2hostId; + OSAL_LOCK_COM(); + cm_AcknowledgeEvent(bfInfo->fifo); + OSAL_UNLOCK_COM(); +} + +/* + * Get a reference on a given attribute of a given component + */ +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_ReadComponentAttribute( + const t_cm_instance_handle instance, + const char* attrName, + t_uint24 *attrValue) +{ + t_cm_error error; + t_component_instance* component; + + OSAL_LOCK_API(); + + component = cm_lookupComponent(instance); + if (NULL == component) + error = CM_INVALID_COMPONENT_HANDLE; + else + { + if ((error = cm_EEM_ForceWakeup(component->Template->dspId)) != CM_OK) + goto out; + + // t_uint24 -> t_uint32 possible since we know it same size + error = cm_readAttribute(component, attrName, (t_uint32*)attrValue); + + cm_EEM_AllowSleep(component->Template->dspId); + } + +out: + OSAL_UNLOCK_API(); + return error; +} + +/*=============================================================================== + * Introspection API + *===============================================================================*/ +/* + * Component + */ +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentListHeader( + const t_nmf_client_id client, + t_cm_instance_handle *headerComponent) { + t_uint32 i; + + OSAL_LOCK_API(); + + *headerComponent = 0; + for (i=0; i < ComponentTable.idxMax; i++) { + if ((componentEntry(i) != NULL) && + (componentEntry(i)->Template->classe != FIRMWARE) && + (domainDesc[componentEntry(i)->domainId].client == client)) { + *headerComponent = ENTRY2HANDLE(componentEntry(i), i);; + break; + } + } + + OSAL_UNLOCK_API(); + + return CM_OK; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentListNext( + const t_nmf_client_id client, + const t_cm_instance_handle prevComponent, + t_cm_instance_handle *nextComponent){ + t_cm_error error; + t_uint32 i = prevComponent & INDEX_MASK; + + OSAL_LOCK_API(); + + // Sanity check + if ((i >= ComponentTable.idxMax) + || (((unsigned int)componentEntry(i) << INDEX_SHIFT) != (prevComponent & ~INDEX_MASK))) + error = CM_INVALID_COMPONENT_HANDLE; + else { + *nextComponent = 0; + for (i++; i < ComponentTable.idxMax; i++) { + if ((componentEntry(i) != NULL) && + (componentEntry(i)->Template->classe != FIRMWARE) && + (domainDesc[componentEntry(i)->domainId].client == client)) { + *nextComponent = ENTRY2HANDLE(componentEntry(i), i);; + break; + } + } + + error = CM_OK; + } + + OSAL_UNLOCK_API(); + + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentDescription( + const t_cm_instance_handle instance, + char *templateName, + t_uint32 templateNameLength, + t_nmf_core_id *coreId, + char *localName, + t_uint32 localNameLength, + t_nmf_ee_priority *priority) { + t_component_instance *comp; + t_cm_error error; + + OSAL_LOCK_API(); + + comp = cm_lookupComponent(instance); + // Sanity check + if (NULL == comp) { + error = CM_INVALID_COMPONENT_HANDLE; + } else { + cm_StringCopy( + templateName, + comp->Template->name, + templateNameLength); + *coreId = comp->Template->dspId; + cm_StringCopy( + localName, + comp->pathname, + localNameLength); + if (priority) + *priority = comp->priority; + error = CM_OK; + } + + OSAL_UNLOCK_API(); + + return error; +} + +/* + * Require interface + */ +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterfaceNumber( + const t_cm_instance_handle instance, + t_uint8 *numberRequiredInterfaces) { + t_component_instance *comp; + t_cm_error error; + + OSAL_LOCK_API(); + + comp = cm_lookupComponent(instance); + // Sanity check + if (NULL == comp) { + error = CM_INVALID_COMPONENT_HANDLE; + } else { + *numberRequiredInterfaces = comp->Template->requireNumber; + + error = CM_OK; + } + + OSAL_UNLOCK_API(); + + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterface( + const t_cm_instance_handle instance, + const t_uint8 index, + char *itfName, + t_uint32 itfNameLength, + char *itfType, + t_uint32 itfTypeLength, + t_cm_require_state *requireState, + t_sint16 *collectionSize) { + t_component_instance *comp; + t_cm_error error; + + OSAL_LOCK_API(); + + comp = cm_lookupComponent(instance); + // Sanity check + if (NULL == comp) { + error = CM_INVALID_COMPONENT_HANDLE; + } else if(index >= comp->Template->requireNumber) { + error = CM_NO_SUCH_REQUIRED_INTERFACE; + } else { + cm_StringCopy( + itfName, + comp->Template->requires[index].name, + itfNameLength); + cm_StringCopy( + itfType, + comp->Template->requires[index].interface->type, + itfTypeLength); + if(comp->Template->requires[index].requireTypes & COLLECTION_REQUIRE) + *collectionSize = comp->Template->requires[index].collectionSize; + else + *collectionSize = -1; + + if(requireState != NULL) { + *requireState = 0; + if(comp->Template->requires[index].requireTypes & COLLECTION_REQUIRE) + *requireState |= CM_REQUIRE_COLLECTION; + if(comp->Template->requires[index].requireTypes & OPTIONAL_REQUIRE) + *requireState |= CM_REQUIRE_OPTIONAL; + if(comp->Template->requires[index].requireTypes & STATIC_REQUIRE) + *requireState |= CM_REQUIRE_STATIC; + } + + error = CM_OK; + } + + OSAL_UNLOCK_API(); + + return error; +} +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterfaceBinding( + const t_cm_instance_handle instance, + const char *itfName, + t_cm_instance_handle *server, + char *serverItfName, + t_uint32 serverItfNameLength) { + t_component_instance *comp; + t_interface_require_description itfRequire; + t_interface_provide_description itfProvide; + t_cm_error error; + + OSAL_LOCK_API(); + + comp = cm_lookupComponent(instance); + // Sanity check + if(NULL == comp) { + error = CM_INVALID_COMPONENT_HANDLE; + } else if ((error = cm_getRequiredInterface(comp, itfName, &itfRequire)) != CM_OK) { + // Check if the requiredItfClientName is required by client component + } else if ((error = cm_lookupInterface(&itfRequire, &itfProvide)) != CM_OK) { + // Check if the requiredItfClientName is required by client component + } else { + if ((t_cm_instance_handle)itfProvide.server == NMF_HOST_COMPONENT + || (t_cm_instance_handle)itfProvide.server == NMF_VOID_COMPONENT) + *server = (t_cm_instance_handle)itfProvide.server; + else + *server = itfProvide.server->instance; + if(*server == NMF_HOST_COMPONENT) { + cm_StringCopy( + serverItfName, + "unknown", + serverItfNameLength); + } else if(*server == NMF_VOID_COMPONENT) { + cm_StringCopy( + serverItfName, + "void", + serverItfNameLength); + } else if(*server != 0) { + cm_StringCopy( + serverItfName, + itfProvide.server->Template->provides[itfProvide.provideIndex].name, + serverItfNameLength); + if(itfProvide.server->Template->provides[itfProvide.provideIndex].provideTypes & COLLECTION_PROVIDE) { + int len = cm_StringLength(serverItfName, serverItfNameLength); + serverItfName[len++] = '['; + if(itfProvide.collectionIndex >= 100) + serverItfName[len++] = '0' + (itfProvide.collectionIndex / 100); + if(itfProvide.collectionIndex >= 10) + serverItfName[len++] = '0' + ((itfProvide.collectionIndex % 100) / 10); + serverItfName[len++] = '0' + (itfProvide.collectionIndex % 10); + serverItfName[len++] = ']'; + serverItfName[len] = 0; + } + } + + error = CM_OK; + } + + OSAL_UNLOCK_API(); + + return error; +} + +/* + * Provide interface + */ +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentProvidedInterfaceNumber( + const t_cm_instance_handle instance, + t_uint8 *numberProvidedInterfaces) { + t_component_instance *comp; + t_cm_error error; + + OSAL_LOCK_API(); + + comp = cm_lookupComponent(instance); + // Sanity check + if (NULL == comp) { + error = CM_INVALID_COMPONENT_HANDLE; + } else { + *numberProvidedInterfaces = comp->Template->provideNumber; + + error = CM_OK; + } + + OSAL_UNLOCK_API(); + + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentProvidedInterface( + const t_cm_instance_handle instance, + const t_uint8 index, + char *itfName, + t_uint32 itfNameLength, + char *itfType, + t_uint32 itfTypeLength, + t_sint16 *collectionSize) { + t_component_instance *comp; + t_cm_error error; + + OSAL_LOCK_API(); + + comp = cm_lookupComponent(instance); + // Sanity check + if (NULL == comp) { + error = CM_INVALID_COMPONENT_HANDLE; + } else if(index >= comp->Template->provideNumber) { + error = CM_NO_SUCH_PROVIDED_INTERFACE; + } else { + cm_StringCopy( + itfName, + comp->Template->provides[index].name, + itfNameLength); + cm_StringCopy( + itfType, + comp->Template->provides[index].interface->type, + itfTypeLength); + if(comp->Template->provides[index].provideTypes & COLLECTION_PROVIDE) + *collectionSize = comp->Template->provides[index].collectionSize; + else + *collectionSize = -1; + + error = CM_OK; + } + + OSAL_UNLOCK_API(); + + return error; +} + +/* + * Component Property + */ +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyNumber( + const t_cm_instance_handle instance, + t_uint8 *numberProperties) { + t_component_instance *comp; + t_cm_error error; + + OSAL_LOCK_API(); + + comp = cm_lookupComponent(instance); + // Sanity check + if (NULL == comp) { + error = CM_INVALID_COMPONENT_HANDLE; + } else { + *numberProperties = comp->Template->propertyNumber; + + error = CM_OK; + } + + OSAL_UNLOCK_API(); + + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyName( + const t_cm_instance_handle instance, + const t_uint8 index, + char *propertyName, + t_uint32 propertyNameLength) { + t_component_instance *comp; + t_cm_error error; + + OSAL_LOCK_API(); + + comp = cm_lookupComponent(instance); + // Sanity check + if (NULL == comp) { + error = CM_INVALID_COMPONENT_HANDLE; + } else if(index >= comp->Template->propertyNumber) { + error = CM_NO_SUCH_PROPERTY; + } else { + cm_StringCopy( + propertyName, + comp->Template->properties[index].name, + propertyNameLength); + + error = CM_OK; + } + + OSAL_UNLOCK_API(); + + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyValue( + const t_cm_instance_handle instance, + const char *propertyName, + char *propertyValue, + t_uint32 propertyValueLength) +{ + t_component_instance *comp; + t_cm_error error; + + OSAL_LOCK_API(); + + comp = cm_lookupComponent(instance); + if (NULL == comp) + error = CM_INVALID_COMPONENT_HANDLE; + else + { + error = cm_getComponentProperty( + comp, + propertyName, + propertyValue, + propertyValueLength); + + if(error == CM_NO_SUCH_PROPERTY) + ERROR("CM_NO_SUCH_PROPERTY(%s, %s)\n", comp->pathname, propertyName, 0, 0, 0, 0); + } + + OSAL_UNLOCK_API(); + + return error; +} diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/dspevent.c b/drivers/staging/nmf-cm/cm/engine/component/src/dspevent.c new file mode 100644 index 00000000000..0d5e89e0515 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/src/dspevent.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/inc/cm_type.h> +#include <cm/engine/component/inc/introspection.h> +#include <cm/engine/communication/inc/communication.h> +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h> +#include <cm/engine/trace/inc/trace.h> +#include "../inc/dspevent.h" + + +#define DSP_REMOTE_EVENT_SIZE_IN_BYTE (4*DSP_REMOTE_EVENT_SIZE_IN_DSPWORD) +#define DSP_REMOTE_EVENT_NEXT_FIELD_OFFSET 0 +#define DSP_REMOTE_EVENT_REACTION_FIELD_OFFSET 1 +#define DSP_REMOTE_EVENT_THIS_FIELD_OFFSET 2 +#define DSP_REMOTE_EVENT_PRIORITY_FIELD_OFFSET 3 +#define DSP_REMOTE_EVENT_DATA_FIELD_OFFSET 4 + +t_cm_error dspevent_createDspEventFifo( + const t_component_instance *pComp, + const char* nameOfTOP, + t_uint32 fifoNbElem, + t_uint32 fifoElemSizeInWord, + t_dsp_memory_type_id dspEventMemType, + t_memory_handle *pHandle) +{ + t_uint32 dspElementAddr; + t_uint32 *elemAddr32; + int i; + + // Allocate fifo + *pHandle = cm_DM_Alloc(pComp->domainId, dspEventMemType, fifoNbElem*fifoElemSizeInWord, CM_MM_ALIGN_2WORDS, TRUE); + if(*pHandle == INVALID_MEMORY_HANDLE) { + ERROR("CM_NO_MORE_MEMORY: dspevent_createDspEventFifo()\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + cm_DSP_GetDspAddress(*pHandle, &dspElementAddr); + + elemAddr32 = (t_uint32*)cm_DSP_GetHostLogicalAddress(*pHandle); + + LOG_INTERNAL(2, "\n##### FIFO (dsp event): ARM=0x%x DSP=0x%x\n", elemAddr32, dspElementAddr, 0, 0, 0, 0); + + // Read attribute addr (we assume that variable in XRAM) + cm_writeAttribute(pComp, nameOfTOP, dspElementAddr); + + // Initialise the linked list (next...) + for (i = 0; i < fifoNbElem - 1; i++) + { + dspElementAddr += fifoElemSizeInWord; + + /* Write next field */ + *elemAddr32 = dspElementAddr; + /* Write THIS field & priority field */ + *(volatile t_uint64*)&elemAddr32[DSP_REMOTE_EVENT_THIS_FIELD_OFFSET] = + ((t_uint64)pComp->thisAddress | (((t_uint64)pComp->priority) << 32)); + + elemAddr32 += fifoElemSizeInWord; + } + + /* Last element: Write next field */ + *elemAddr32 = 0x0 /* NULL */; + /* Last element: Write THIS field & priority field */ + *(volatile t_uint64*)&elemAddr32[DSP_REMOTE_EVENT_THIS_FIELD_OFFSET] = + ((t_uint64)pComp->thisAddress | (((t_uint64)pComp->priority) << 32)); + + return CM_OK; +} + + + +void dspevent_destroyDspEventFifo(t_memory_handle handle) +{ + (void)cm_DM_Free(handle, TRUE); +} diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/initializer.c b/drivers/staging/nmf-cm/cm/engine/component/src/initializer.c new file mode 100644 index 00000000000..7f99b710401 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/src/initializer.c @@ -0,0 +1,383 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/inc/cm_type.h> +#include <cm/engine/component/inc/introspection.h> +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> +#include <cm/engine/communication/inc/communication.h> +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h> + +#include <cm/engine/power_mgt/inc/power.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> + +#include <cm/engine/trace/inc/trace.h> + +#include "../inc/dspevent.h" +#include "../inc/initializer.h" + +// Since now due to semaphore use call is synchrone so we only need a fifo size of three +// (due to updateStack + (InstructionCacheLock or InstructionCacheUnlock)) +#define DEFAULT_INITIALIZER_FIFO_SIZE 3 + +/* private prototype */ +PRIVATE t_cm_error cm_COMP_generic(t_nmf_core_id coreId, t_event_params_handle paramArray, t_uint32 paramNumber, t_uint32 serviceIndex); +PRIVATE void cm_COMP_generatePanic(t_nmf_core_id coreId); + +/* + * This module is tightly coupled with cm_DSP_components one (communication/initializer) + */ +static struct { + t_nmf_fifo_arm_desc* downlinkFifo; + t_nmf_fifo_arm_desc* uplinkFifo; + t_memory_handle dspfifoHandle; + t_nmf_osal_sem_handle fifoSemHandle; + t_uint32 servicePending; // TODO : Use sem counter instead of defining such variable (need to create new OSAL) +} initializerDesc[NB_CORE_IDS]; + +PUBLIC t_cm_error cm_COMP_INIT_Init(t_nmf_core_id coreId) +{ + t_uint32 i; + t_cm_error error; + t_component_instance *ee; + t_dsp_offset sharedVarOffset; + t_interface_provide_description itfProvide; + t_interface_provide* provide; + t_interface_provide_loaded* provideLoaded; + + ee = cm_EEM_getExecutiveEngine(coreId)->instance; + + // Get interface description + if((error = cm_getProvidedInterface(ee, "service", &itfProvide)) != CM_OK) + return error; + provide = &ee->Template->provides[itfProvide.provideIndex]; + provideLoaded = &ee->Template->providesLoaded[itfProvide.provideIndex]; + + + if ((error = dspevent_createDspEventFifo( + ee, "comms/TOP", + DEFAULT_INITIALIZER_FIFO_SIZE, + DSP_REMOTE_EVENT_SIZE_IN_DSPWORD, + INTERNAL_XRAM24, + &initializerDesc[coreId].dspfifoHandle)) != CM_OK) + return error; + + /* create fifo semaphore */ + initializerDesc[coreId].servicePending = 0; + initializerDesc[coreId].fifoSemHandle = OSAL_CreateSemaphore(DEFAULT_INITIALIZER_FIFO_SIZE); + if (initializerDesc[coreId].fifoSemHandle == 0) { + dspevent_destroyDspEventFifo(initializerDesc[coreId].dspfifoHandle); + return CM_NO_MORE_MEMORY; + } + + /* static armTHis initialisation */ + /* + * In the two next fifo_alloc call (1+n) means that we want to manage the hostThis_or_TOP and one method for each params fifos */ + initializerDesc[coreId].downlinkFifo = + fifo_alloc(ARM_CORE_ID, coreId, + INIT_COMPONENT_CMD_SIZE, DEFAULT_INITIALIZER_FIFO_SIZE, + (1+provide->interface->methodNumber), paramsLocation, extendedFieldLocation, cm_DSP_GetState(coreId)->domainEE); + if (initializerDesc[coreId].downlinkFifo == NULL) + { + OSAL_DestroySemaphore(initializerDesc[coreId].fifoSemHandle); + dspevent_destroyDspEventFifo(initializerDesc[coreId].dspfifoHandle); + ERROR("CM_NO_MORE_MEMORY: fifo_alloc() failed in cm_COMP_INIT_Init()\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + initializerDesc[coreId].uplinkFifo = + fifo_alloc(coreId, ARM_CORE_ID, + INIT_COMPONENT_ACK_SIZE, DEFAULT_INITIALIZER_FIFO_SIZE, + (1), paramsLocation, extendedFieldLocation, cm_DSP_GetState(coreId)->domainEE); /* 1 is mandatory to compute internally the indexMask */ + /* this statement is acceptable only written by skilled man ;) */ + /* We don't used bcDescRef, since we assume that we don't need params size */ + if (initializerDesc[coreId].uplinkFifo == NULL) + { + OSAL_DestroySemaphore(initializerDesc[coreId].fifoSemHandle); + fifo_free(initializerDesc[coreId].downlinkFifo); + dspevent_destroyDspEventFifo(initializerDesc[coreId].dspfifoHandle); + ERROR("CM_NO_MORE_MEMORY: fifo_alloc() failed in cm_COMP_INIT_Init()\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + cm_writeAttribute(ee, "comms/FIFOcmd", initializerDesc[coreId].downlinkFifo->dspAdress); + + cm_writeAttribute(ee, "comms/FIFOack", initializerDesc[coreId].uplinkFifo->dspAdress); + + sharedVarOffset = cm_getAttributeMpcAddress(ee, "comms/TOP"); + + /* HOST->DSP ParamsFifo extended fields initialisation */ + fifo_params_setSharedField( + initializerDesc[coreId].downlinkFifo, + 0, + (t_shared_field)sharedVarOffset /* TOP DSP Address */ + ); + for(i=0; i<provide->interface->methodNumber; i++) + { + fifo_params_setSharedField( + initializerDesc[coreId].downlinkFifo, + i + 1, + provideLoaded->indexesLoaded[itfProvide.collectionIndex][i].methodAddresses); + } + + /* DSP->HOST ParamsFifo extended fields initialisation */ + fifo_params_setSharedField( + initializerDesc[coreId].uplinkFifo, + 0, + (t_shared_field)NMF_INTERNAL_USERTHIS + ); + + return CM_OK; +} + + +PUBLIC t_cm_error cm_COMP_CallService( + int serviceIndex, + t_component_instance *pComp, + t_uint32 methodAddress) { + t_cm_error error; + t_uint16 params[INIT_COMPONENT_CMD_SIZE]; + t_bool isSynchronous = (serviceIndex == NMF_CONSTRUCT_SYNC_INDEX || + serviceIndex == NMF_START_SYNC_INDEX || + serviceIndex == NMF_STOP_SYNC_INDEX || + serviceIndex == NMF_DESTROY_INDEX)?TRUE:FALSE; + + params[INIT_COMPONENT_CMD_HANDLE_INDEX] = (t_uint16)((unsigned int)pComp & 0xFFFF); + params[INIT_COMPONENT_CMD_HANDLE_INDEX+1] = (t_uint16)((unsigned int)pComp >> 16); + params[INIT_COMPONENT_CMD_THIS_INDEX] = (t_uint16)(pComp->thisAddress & 0xFFFF); + params[INIT_COMPONENT_CMD_THIS_INDEX+1] = (t_uint16)(pComp->thisAddress >> 16); + params[INIT_COMPONENT_CMD_METHOD_INDEX] = (t_uint16)(methodAddress & 0xFFFF); + params[INIT_COMPONENT_CMD_METHOD_INDEX+1] = (t_uint16)(methodAddress >> 16); + + error = cm_COMP_generic(pComp->Template->dspId, params, sizeof(params) / sizeof(t_uint16), serviceIndex); + + if (isSynchronous == TRUE && error == CM_OK) { + if (OSAL_SEMAPHORE_WAIT_TIMEOUT(semHandle) != SYNC_OK) { + cm_COMP_generatePanic(pComp->Template->dspId); + error = CM_MPC_NOT_RESPONDING; + } + } + + return error; +} + +PUBLIC void cm_COMP_Flush(t_nmf_core_id coreId) { + + if(initializerDesc[coreId].servicePending > 0) + { + t_uint16 params[INIT_COMPONENT_CMD_SIZE]; + t_uint32 methodAddress = cm_EEM_getExecutiveEngine(coreId)->voidAddr; + + // If service still pending on MMDSP side, send a flush command (today, we reuse Destroy to not create new empty service) + // When we receive the result, this mean that we have flushed all previous request. + + params[INIT_COMPONENT_CMD_HANDLE_INDEX] = (t_uint16)(0x0 & 0xFFFF); + params[INIT_COMPONENT_CMD_HANDLE_INDEX+1] = (t_uint16)(0x0 >> 16); + params[INIT_COMPONENT_CMD_THIS_INDEX] = (t_uint16)(0x0 & 0xFFFF); + params[INIT_COMPONENT_CMD_THIS_INDEX+1] = (t_uint16)(0x0 >> 16); + params[INIT_COMPONENT_CMD_METHOD_INDEX] = (t_uint16)(methodAddress & 0xFFFF); + params[INIT_COMPONENT_CMD_METHOD_INDEX+1] = (t_uint16)(methodAddress >> 16); + + if (cm_COMP_generic(coreId, params, sizeof(params) / sizeof(t_uint16), NMF_DESTROY_INDEX) != CM_OK || + OSAL_SEMAPHORE_WAIT_TIMEOUT(semHandle) != SYNC_OK) + { + cm_COMP_generatePanic(coreId); + ERROR("CM_MPC_NOT_RESPONDING: can't call flush service\n", 0, 0, 0, 0, 0, 0); + } + } +} + +PUBLIC void cm_COMP_INIT_Close(t_nmf_core_id coreId) +{ + unsigned int i; + + /* wait for semaphore to be sure it would not be touch later on */ + /* in case of timeout we break and try to clean everythink */ + for(i = 0; i < DEFAULT_INITIALIZER_FIFO_SIZE; i++) { + if (OSAL_SEMAPHORE_WAIT_TIMEOUT(initializerDesc[coreId].fifoSemHandle) != SYNC_OK) + break; + } + + /* destroy semaphore */ + OSAL_DestroySemaphore(initializerDesc[coreId].fifoSemHandle); + + /* Unallocate initializerDesc[index].uplinkFifo */ + /* (who is used in this particular case to store dummy (with no data space (only descriptor)) DSP->HOST params fifo */ + fifo_free(initializerDesc[coreId].uplinkFifo); + + /* Unallocate initializerDesc[index].downlinkFifo */ + fifo_free(initializerDesc[coreId].downlinkFifo); + + /* Unallocate initializerDesc[index].dspfifoHandle */ + dspevent_destroyDspEventFifo(initializerDesc[coreId].dspfifoHandle); +} + +PUBLIC void processAsyncAcknowledge(t_nmf_core_id coreId, t_event_params_handle pParam) +{ + cm_AcknowledgeEvent(initializerDesc[coreId].uplinkFifo); + + initializerDesc[coreId].servicePending--; + OSAL_SemaphorePost(initializerDesc[coreId].fifoSemHandle,1); +} + +PUBLIC void processSyncAcknowledge(t_nmf_core_id coreId, t_event_params_handle pParam) +{ + cm_AcknowledgeEvent(initializerDesc[coreId].uplinkFifo); + + initializerDesc[coreId].servicePending--; + OSAL_SemaphorePost(initializerDesc[coreId].fifoSemHandle,1); + OSAL_SemaphorePost(semHandle,1); +} + +PUBLIC t_cm_error cm_COMP_UpdateStack( + t_nmf_core_id coreId, + t_uint32 stackSize +) +{ + t_uint16 params[2]; + + // Marshall parameter + params[0] = (t_uint16)((unsigned int)stackSize & 0xFFFF); + params[1] = (t_uint16)((unsigned int)stackSize >> 16); + + return cm_COMP_generic(coreId, params, sizeof(params) / sizeof(t_uint16), NMF_UPDATE_STACK); +} + +PUBLIC t_cm_error cm_COMP_ULPForceWakeup( + t_nmf_core_id coreId +) +{ + t_cm_error error; + + error = cm_COMP_generic(coreId, NULL, 0, NMF_ULP_FORCEWAKEUP); + + if (error == CM_OK) { + if (OSAL_SEMAPHORE_WAIT_TIMEOUT(semHandle) != SYNC_OK) { + cm_COMP_generatePanic(coreId); + error = CM_MPC_NOT_RESPONDING; + } + } + + return error; +} + +PUBLIC t_cm_error cm_COMP_ULPAllowSleep( + t_nmf_core_id coreId +) +{ + return cm_COMP_generic(coreId, NULL, 0, NMF_ULP_ALLOWSLEEP); +} + +PUBLIC t_cm_error cm_COMP_InstructionCacheLock( + t_nmf_core_id coreId, + t_uint32 mmdspAddr, + t_uint32 mmdspSize +) +{ + t_uint16 params[4]; + t_uint32 startAddr = cm_DSP_GetState(coreId)->locked_offset; + int way; + + for(way = 1; startAddr < mmdspAddr + mmdspSize; startAddr += MMDSP_CODE_CACHE_WAY_SIZE, way++) + { + if(mmdspAddr < startAddr + MMDSP_CODE_CACHE_WAY_SIZE) + { + t_cm_error error; + + // Marshall parameter + params[0] = (t_uint16)((unsigned int)startAddr & 0xFFFF); + params[1] = (t_uint16)((unsigned int)startAddr >> 16); + params[2] = (t_uint16)((unsigned int)way & 0xFFFF); + params[3] = (t_uint16)((unsigned int)way >> 16); + + if((error = cm_COMP_generic(coreId, params, sizeof(params) / sizeof(t_uint16), NMF_LOCK_CACHE)) != CM_OK) + return error; + } + } + + return CM_OK; +} + +PUBLIC t_cm_error cm_COMP_InstructionCacheUnlock( + t_nmf_core_id coreId, + t_uint32 mmdspAddr, + t_uint32 mmdspSize +) +{ + t_uint16 params[2]; + t_uint32 startAddr = cm_DSP_GetState(coreId)->locked_offset; + int way; + + for(way = 1; startAddr < mmdspAddr + mmdspSize; startAddr += MMDSP_CODE_CACHE_WAY_SIZE, way++) + { + if(mmdspAddr < startAddr + MMDSP_CODE_CACHE_WAY_SIZE) + { + t_cm_error error; + + // Marshall parameter + params[0] = (t_uint16)((unsigned int)way & 0xFFFF); + params[1] = (t_uint16)((unsigned int)way >> 16); + + if((error = cm_COMP_generic(coreId, params, sizeof(params) / sizeof(t_uint16), NMF_UNLOCK_CACHE)) != CM_OK) + return error; + } + } + + return CM_OK; +} + +/* private method */ +PRIVATE t_cm_error cm_COMP_generic( + t_nmf_core_id coreId, + t_event_params_handle paramArray, + t_uint32 paramNumber, + t_uint32 serviceIndex +) +{ + t_event_params_handle _xyuv_data; + t_cm_error error; + t_uint32 i; + + // wait for an event in fifo + if (OSAL_SEMAPHORE_WAIT_TIMEOUT(initializerDesc[coreId].fifoSemHandle) != SYNC_OK) { + cm_COMP_generatePanic(coreId); + return CM_MPC_NOT_RESPONDING; + } + + + // AllocEvent + if((_xyuv_data = cm_AllocEvent(initializerDesc[coreId].downlinkFifo)) == NULL) + { + ERROR("CM_INTERNAL_FIFO_OVERFLOW: service FIFO full\n", 0, 0, 0, 0, 0, 0); + error = CM_INTERNAL_FIFO_OVERFLOW; + goto unlock; + } + + // Copy param + for(i=0;i<paramNumber;i++) + _xyuv_data[i] = paramArray[i]; + + OSAL_LOCK_COM(); + + // Send Command + error = cm_PushEventTrace(initializerDesc[coreId].downlinkFifo, _xyuv_data, serviceIndex,0); + if(error == CM_OK) + initializerDesc[coreId].servicePending++; + +unlock: + OSAL_UNLOCK_COM(); + + return error; +} + +PRIVATE void cm_COMP_generatePanic(t_nmf_core_id coreId) +{ + const t_dsp_desc* pDspDesc = cm_DSP_GetState(coreId); + + if (pDspDesc->state != MPC_STATE_PANIC) { + cm_DSP_SetStatePanic(coreId); + OSAL_GeneratePanic(coreId, 0); + } +} diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/instantiater.c b/drivers/staging/nmf-cm/cm/engine/component/src/instantiater.c new file mode 100644 index 00000000000..92c28b63171 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/src/instantiater.c @@ -0,0 +1,829 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/component/inc/instance.h> +#include <cm/engine/component/inc/bind.h> +#include <cm/engine/component/inc/initializer.h> + +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> +#include <cm/engine/configuration/inc/configuration_status.h> + +#include <cm/engine/dsp/inc/dsp.h> + +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/trace/inc/xtitrace.h> + +#include <cm/engine/memory/inc/domain.h> + +#include <cm/engine/utils/inc/string.h> +#include <cm/engine/utils/inc/mem.h> +#include <cm/engine/utils/inc/convert.h> + +#include <cm/engine/power_mgt/inc/power.h> + + +t_nmf_table ComponentTable; /**< list (table) of components */ + +static t_uint32 cm_getMaxStackValue(t_component_instance *pComponent); +static t_uint16 getNumberOfInstance(t_component_instance* component); +static t_uint16 getNumberOfStart(t_component_instance* component); + + +t_cm_error cm_COMP_Init(void) { + t_cm_error error; + error = cm_initTable(&ComponentTable); + if (error == CM_OK) + error = cm_initTable(&Host2MpcBindingTable); + return error; +} + +void cm_COMP_Destroy(void) { + cm_destroyTable(&ComponentTable); + cm_destroyTable(&Host2MpcBindingTable); +} + +/** cm_addComponent - Add an internal handler to the list + * + * 1. Increase the size of the list if it's full + * 2. Search an empty entry + * 3. Add the element to the list + * 4. Compute and return the "user handle" (= t_cm_instance_handle) + */ +static t_cm_instance_handle cm_addComponent(t_component_instance *comp) +{ + OSAL_DisableServiceMessages(); + comp->instance = cm_addEntry(&ComponentTable, comp); + OSAL_EnableServiceMessages(); + + return comp->instance; +} + +/** cm_delComponent - remove the given component from the list + * + * 1. Check if the handle is valid + * 2. Search the entry and free it + */ +static void cm_delComponent(t_component_instance *comp) +{ + if (comp == NULL) + return; + + OSAL_DisableServiceMessages(); + cm_delEntry(&ComponentTable, comp->instance & INDEX_MASK); + OSAL_EnableServiceMessages(); +} + +/** cm_lookupComponent - search the component corresponding to + * the component instance. + * + * 1. Check if the instance is valid + * 2. Return a pointer to the component + */ +t_component_instance *cm_lookupComponent(const t_cm_instance_handle hdl) +{ + return cm_lookupEntry(&ComponentTable, hdl); +} + +static void cm_DestroyComponentMemory(t_component_instance *component) +{ + int i; + + /* + * Remove instance from list + */ + cm_delComponent(component); + + /* + * Destroy instance + */ + { + struct t_client_of_singleton* cur = component->clientOfSingleton; + + for( ; cur != NULL ; ) + { + struct t_client_of_singleton* tmp = cur; + cur = cur->next; + + OSAL_Free(tmp); + } + } + + for(i = 0; i < component->Template->requireNumber; i++) + { + OSAL_Free(component->interfaceReferences[i]); + } + + cm_StringRelease(component->pathname); + + cm_ELF_FreeInstance(component->Template->dspId, component->Template->memories, component->memories); + + cm_unloadComponent(component->Template); + OSAL_Free(component); +} + +/** + * Non-Require: + * - MMDSP could be sleep (Since we access it only through HSEM) + */ +void cm_delayedDestroyComponent(t_component_instance *component) { + int i; + + if (osal_debug_ops.component_destroy) + osal_debug_ops.component_destroy(component); + + /* + * Remove component from load map here + */ + cm_DSPABI_RemoveLoadMap( + component->domainId, + component->Template->name, + component->memories, + component->pathname, + component); + + // Generate XTI/STM trace + cm_TRC_traceLoadMap(TRACE_COMPONENT_COMMAND_REMOVE, component); + + /* + * disconnect interrupt handler if needed + */ + for(i = 0; i < component->Template->provideNumber; i++) + { + if(component->Template->provides[i].interruptLine) + { + cm_unbindInterfaceStaticInterrupt(component->Template->dspId, component->Template->provides[i].interruptLine); + } + } + + /* + * Update dsp stack size if needed + */ + if (component->Template->minStackSize > MIN_STACK_SIZE) + { + if (cm_EEM_isStackUpdateNeed(component->Template->dspId, component->priority, FALSE, component->Template->minStackSize)) + { + t_uint32 newStackValue; + t_uint32 maxComponentStackSize; + + maxComponentStackSize = cm_getMaxStackValue(component); + cm_EEM_UpdateStack(component->Template->dspId, component->priority, maxComponentStackSize, &newStackValue); + if (cm_DSP_GetState(component->Template->dspId)->state == MPC_STATE_BOOTED) + cm_COMP_UpdateStack(component->Template->dspId, newStackValue); + } + } + + cm_DestroyComponentMemory(component); +} + +/** + * Pre-Require: + * - MMDSP wakeup (when loading in TCM) + */ +t_cm_error cm_instantiateComponent(const char* templateName, + t_cm_domain_id domainId, + t_nmf_ee_priority priority, + const char* pathName, + t_elfdescription *elfhandle, + t_component_instance** refcomponent) +{ + t_nmf_core_id coreId = cm_DM_GetDomainCoreId(domainId); + t_dup_char templateNameDup; + t_component_template* template; + t_component_instance *component; + /* coverity[var_decl] */ + t_cm_error error; + int i, j, k; + + *refcomponent = NULL; + + templateNameDup = cm_StringDuplicate(templateName); + if(templateNameDup == NULL) + return CM_NO_MORE_MEMORY; + + /* + * Lookup in template list + */ + template = cm_lookupTemplate(coreId, templateNameDup); + if(template != NULL) + { + if(template->classe == SINGLETON) + { + // Return same handle for singleton component + struct t_client_of_singleton* cl; + + cm_StringRelease(templateNameDup); + + cl = cm_getClientOfSingleton(template->singletonIfAvaliable, TRUE, domainDesc[domainId].client); + if(cl == NULL) + return CM_NO_MORE_MEMORY; + cl->numberOfInstance++; + + *refcomponent = template->singletonIfAvaliable; + LOG_INTERNAL(1, "##### Singleton : New handle of %s/%x component on %s provItf=%d#####\n", + template->singletonIfAvaliable->pathname, template->singletonIfAvaliable, cm_getDspName(coreId), + template->singletonIfAvaliable->providedItfUsedCount, 0, 0); + return CM_OK; + } + } + + // Get the dataFile (identity if already pass as parameter) + if((elfhandle = cm_REP_getComponentFile(templateNameDup, elfhandle)) == NULL) + { + cm_StringRelease(templateNameDup); + return CM_COMPONENT_NOT_FOUND; + } + + // Load template + if((error = cm_loadComponent(templateNameDup, domainId, elfhandle, &template)) != CM_OK) + { + cm_StringRelease(templateNameDup); + return error; + } + + // templateNameDup no more used, release it + cm_StringRelease(templateNameDup); + + // Allocated component + component = (t_component_instance*)OSAL_Alloc_Zero( + sizeof(t_component_instance) + + sizeof(t_interface_reference*) * template->requireNumber); + if(component == NULL) + { + cm_unloadComponent(template); + return CM_NO_MORE_MEMORY; + } + + component->interfaceReferences = (t_interface_reference**)((char*)component + sizeof(t_component_instance)); + component->Template = template; + + /* + * Update linked list + */ + if (cm_addComponent(component) == 0) { + cm_unloadComponent(template); + OSAL_Free(component); + return CM_NO_MORE_MEMORY; + } + + // NOTE: From here use cm_DestroyComponentMemory + + component->pathname = pathName ? cm_StringDuplicate(pathName) : cm_StringReference(anonymousDup); + if(component->pathname == NULL) + { + cm_DestroyComponentMemory(component); + return CM_NO_MORE_MEMORY; + } + + LOG_INTERNAL(1, "\n##### Instantiate %s/%x (%s) component on %s at priority %d #####\n", component->pathname, component, template->name, cm_getDspName(coreId), priority, 0); + + if((error = cm_ELF_LoadInstance(domainId, elfhandle, template->memories, component->memories, template->classe == SINGLETON)) != CM_OK) + { + cm_DestroyComponentMemory(component); + return error; + } + + if((error = cm_ELF_relocatePrivateSegments( + component->memories, + elfhandle, + template)) != CM_OK) + { + cm_DestroyComponentMemory(component); + return error; + } + + cm_ELF_FlushInstance(coreId, template->memories, component->memories); + + /* + * Create a new component instance + */ + component->priority = priority; + component->thisAddress = 0xFFFFFFFF; + component->state = STATE_NONE; + + if(component->Template->classe == SINGLETON) + { // Return same handle for singleton component + struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, TRUE, domainDesc[domainId].client); + if(cl == NULL) + { + cm_DestroyComponentMemory(component); + return CM_NO_MORE_MEMORY; + } + + cl->numberOfInstance = 1; + template->singletonIfAvaliable = component; + if (cm_DM_GetDomainCoreId(domainId) == SVA_CORE_ID) + component->domainId = DEFAULT_SVA_DOMAIN; + else + component->domainId = DEFAULT_SIA_DOMAIN; + } else { + component->domainId = domainId; + } + + if(component->memories[template->thisMemory->id] != INVALID_MEMORY_HANDLE) + cm_DSP_GetDspAddress(component->memories[template->thisMemory->id], &component->thisAddress); + else { + // In case of singleton or component without data + component->thisAddress = 0; + } + + /* + * Create empty required interfaces array and set method interface to Panic + */ + for(i = 0; i < template->requireNumber; i++) // For all required interface + { + component->interfaceReferences[i] = + (t_interface_reference*)OSAL_Alloc_Zero(sizeof(t_interface_reference) * template->requires[i].collectionSize); + if(component->interfaceReferences[i] == NULL) + { + cm_DestroyComponentMemory(component); + return CM_NO_MORE_MEMORY; + } + + for(j = 0; j < template->requires[i].collectionSize; j++) // ... and for each index in collection (set THIS&method for each client) + { + component->interfaceReferences[i][j].instance = NULL; + component->interfaceReferences[i][j].bfInfoID = BF_SYNCHRONOUS; // Just to memorize no Binding component used and unbind ToVoid happy ;-). + + if(template->classe == COMPONENT && template->requires[i].indexes != NULL) + { + // If component, fill THIS to itself to detect UNBINDED panic with rigth DSP + t_interface_require_index *requireindex = &template->requires[i].indexes[j]; + for(k = 0; k < requireindex->numberOfClient; k++) + { + t_uint32 *hostAddr; + + hostAddr = (t_uint32*)( + cm_DSP_GetHostLogicalAddress( + component->memories[requireindex->memories[k].memory->id]) + + requireindex->memories[k].offset * requireindex->memories[k].memory->memEntSize); + *hostAddr++ = (t_uint32)component->thisAddress; + } + } + } + } + + /* + * Inform debugger about new component + */ + if ((error = cm_DSPABI_AddLoadMap( + domainId, + template->name, + component->pathname, + component->memories, + component)) != CM_OK) + { + cm_DestroyComponentMemory(component); + return error; + } + + // Generate XTI/STM trace + cm_TRC_traceLoadMap(TRACE_COMPONENT_COMMAND_ADD, component); + + // NOTE: From here use cm_delayedDestroyComponent + + /* + * Relocate interrupt if this is an interrupt + */ + for(i = 0; i < template->provideNumber; i++) + { + if(template->provides[i].interruptLine) + { + if ((error = cm_bindInterfaceStaticInterrupt(coreId, + template->provides[i].interruptLine, + component, + template->provides[i].name)) != CM_OK) + { + cm_delayedDestroyComponent(component); + return error; + } + } + } + + /* + * For first instance of a component; Update ee stack size if needed + */ + if(template->classe != FIRMWARE && template->numberOfInstance == 1 && template->minStackSize > MIN_STACK_SIZE) + { + t_uint32 newStackValue; + + if (cm_EEM_isStackUpdateNeed(template->dspId, priority, TRUE, template->minStackSize)) + { + error = cm_EEM_UpdateStack(template->dspId, priority, template->minStackSize, &newStackValue); + if (error != CM_OK) + { + cm_delayedDestroyComponent(component); + return error; + } + cm_COMP_UpdateStack(template->dspId, newStackValue); + } + } + + + /* + * For component or first instance + */ + if(template->classe == SINGLETON || template->classe == COMPONENT) + { + /* + * Call init function generated by the compiler (one per .elf) + */ + LOG_INTERNAL(2, "constructor call(s) <%s>\n", template->name, 0, 0, 0, 0, 0); + if (cm_DSP_GetState(template->dspId)->state != MPC_STATE_BOOTED) + { + cm_delayedDestroyComponent(component); + return CM_MPC_NOT_RESPONDING; + } + else if ((error = cm_COMP_CallService( + (priority > cm_EEM_getExecutiveEngine(coreId)->instance->priority)?NMF_CONSTRUCT_SYNC_INDEX:NMF_CONSTRUCT_INDEX, + component, + template->LCCConstructAddress)) != CM_OK) + { + if (error == CM_MPC_NOT_RESPONDING) + ERROR("CM_MPC_NOT_RESPONDING: can't call constructor '%s'\n", component->pathname, 0, 0, 0, 0, 0); + cm_delayedDestroyComponent(component); + return error; + } + } + else + { + /* be sure everything is write into memory, not required elsewhere since will be done by cm_COMP_CallService */ + OSAL_mb(); + } + + // For firmware; Directly switch to STARTED state, don't need to start it + if (template->classe == FIRMWARE) + component->state = STATE_RUNNABLE; + else + component->state = STATE_STOPPED; + + if (osal_debug_ops.component_create) + osal_debug_ops.component_create(component); + + *refcomponent = component; + return CM_OK; +} + +struct t_client_of_singleton* cm_getClientOfSingleton(t_component_instance* component, t_bool createdIfNotExist, t_nmf_client_id clientId) +{ + struct t_client_of_singleton* cur = component->clientOfSingleton; + + for( ; cur != NULL ; cur = cur->next) + { + if(cur->clientId == clientId) + { + return cur; + } + } + + //if(createdIfNotExist) + { + cur = OSAL_Alloc(sizeof(struct t_client_of_singleton)); + if(cur != NULL) + { + cur->clientId = clientId; + cur->next = component->clientOfSingleton; + cur->numberOfBind = 0; + cur->numberOfInstance= 0; + cur->numberOfStart = 0; + component->clientOfSingleton = cur; + } + } + return cur; +} + +/** + * Non-Require: + * - MMDSP could be sleep (Since we access it only through HSEM) + */ +t_cm_error cm_startComponent(t_component_instance* component, t_nmf_client_id clientId) +{ + t_cm_error error; + char value[MAX_PROPERTY_VALUE_LENGTH]; + int i; + + /* + * Special code for SINGLETON handling + */ + if(component->Template->classe == SINGLETON) + { + struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, FALSE, clientId); + if(cl != NULL) + cl->numberOfStart++; + // A singleton could be started twice, thus start it only if first client starter + if(getNumberOfStart(component) > 1) + return CM_OK; + + // Fall through and start really the singleton. + } + + if(component->state == STATE_RUNNABLE) + return CM_COMPONENT_NOT_STOPPED; + + // CM_ASSERT component->state == STATE_STOPPED + + /* + * Check that all required binding have been binded! + */ + for(i = 0; i < component->Template->requireNumber; i++) + { + int nb = component->Template->requires[i].collectionSize, j; + for(j = 0; j < nb; j++) + { + if(component->interfaceReferences[i][j].instance == NULL && + (component->Template->requires[i].requireTypes & (OPTIONAL_REQUIRE | INTRINSEC_REQUIRE)) == 0) + { + ERROR("CM_REQUIRE_INTERFACE_UNBINDED: Required interface '%s'.'%s' binded\n", component->pathname, component->Template->requires[i].name, 0, 0, 0, 0); + return CM_REQUIRE_INTERFACE_UNBINDED; + } + } + } + + component->state = STATE_RUNNABLE; + + /* + * Power on, HW resources if required + */ + if(cm_getComponentProperty( + component, + "hardware", + value, + sizeof(value)) == CM_OK) + { + error = cm_PWR_EnableMPC(MPC_PWR_HWIP, component->Template->dspId); + if(error != CM_OK) + return error; + } + + /* + * Call starter if available + */ + if(component->Template->LCCStartAddress != 0) + { + if (cm_DSP_GetState(component->Template->dspId)->state != MPC_STATE_BOOTED) + { + return CM_MPC_NOT_RESPONDING; + } + else if ((error = cm_COMP_CallService( + (component->priority > cm_EEM_getExecutiveEngine(component->Template->dspId)->instance->priority)?NMF_START_SYNC_INDEX:NMF_START_INDEX, + component, + component->Template->LCCStartAddress)) != CM_OK) + { + if (error == CM_MPC_NOT_RESPONDING) + ERROR("CM_MPC_NOT_RESPONDING: can't call starter '%s'\n", component->pathname, 0, 0, 0, 0, 0); + return error; + } + } + + return CM_OK; +} + +/** + * Non-Require: + * - MMDSP could be sleep (Since we access it only through HSEM) + */ +t_cm_error cm_stopComponent(t_component_instance* component, t_nmf_client_id clientId) +{ + char value[MAX_PROPERTY_VALUE_LENGTH]; + t_cm_error error = CM_OK; + t_bool isHwProperty; + + /* + * Special code for SINGLETON handling + */ + if(component->Template->classe == SINGLETON) + { + struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, FALSE, clientId); + if(cl != NULL) + cl->numberOfStart--; + // A singleton could be started twice, thus stop it only if no more client starter + if(getNumberOfStart(component) > 0) + return CM_OK; + + // Fall through and stop really the singleton. + } + + /* + * Component life cycle sanity check + */ + if(component->state == STATE_STOPPED) + return CM_COMPONENT_NOT_STARTED; + + // CM_ASSERT component->state == STATE_RUNNABLE + component->state = STATE_STOPPED; + + isHwProperty = (cm_getComponentProperty( + component, + "hardware", + value, + sizeof(value)) == CM_OK); + + if (cm_DSP_GetState(component->Template->dspId)->state != MPC_STATE_BOOTED) + { + error = CM_MPC_NOT_RESPONDING; + } + else + { + /* + * Call stopper if available + */ + if(component->Template->LCCStopAddress != 0) + { + if ((error = cm_COMP_CallService( + isHwProperty ? NMF_STOP_SYNC_INDEX : NMF_STOP_INDEX, + component, + component->Template->LCCStopAddress)) != CM_OK) + { + if (error == CM_MPC_NOT_RESPONDING) + ERROR("CM_MPC_NOT_RESPONDING: can't call stopper '%s'\n", component->pathname, 0, 0, 0, 0, 0); + } + } + } + + /* + * Power on, HW resources if required + */ + if(isHwProperty) + { + cm_PWR_DisableMPC(MPC_PWR_HWIP, component->Template->dspId); + } + + return error; +} + +t_cm_error cm_destroyInstance(t_component_instance* component, t_destroy_state forceDestroy) +{ + int i, j; + + LOG_INTERNAL(1, "\n##### Destroy %s/%x (%s) component on %s #####\n", + component->pathname, component, component->Template->name, cm_getDspName(component->Template->dspId), 0, 0); + + /* + * Component life cycle sanity check; do it only when destroying last reference. + */ + if(forceDestroy == DESTROY_NORMAL) + { + if (component->state == STATE_RUNNABLE) + return CM_COMPONENT_NOT_STOPPED; + + // CM_ASSERT component->state == STATE_STOPPED + + // Check that all required binding have been unbound! + for(i = 0; i < component->Template->requireNumber; i++) + { + int nb = component->Template->requires[i].collectionSize; + for(j = 0; j < nb; j++) + { + if(component->interfaceReferences[i][j].instance != NULL) + { + ERROR("CM_COMPONENT_NOT_UNBINDED: Required interface %s/%x.%s still binded\n", + component->pathname, component, component->Template->requires[i].name, 0, 0, 0); + return CM_COMPONENT_NOT_UNBINDED; + } + } + } + + // Check that all provided bindings have been unbound! + if (component->providedItfUsedCount != 0) + { + unsigned idx; + + ERROR("CM_COMPONENT_NOT_UNBINDED: Still %d binding to %s/%x provided interface\n", + component->providedItfUsedCount, component->pathname, component, 0, 0, 0); + + /* Find which interface is still bound to gracefully print an error message */ + for (idx=0; idx<ComponentTable.idxMax; idx++) + { + if ((componentEntry(idx) == NULL) || (componentEntry(idx) == component)) + continue; + for (i = 0; i < componentEntry(idx)->Template->requireNumber; i++) + { + for (j = 0; j < componentEntry(idx)->Template->requires[i].collectionSize; j++) + { + if(componentEntry(idx)->interfaceReferences[i][j].instance == component + && component->Template->provides[componentEntry(idx)->interfaceReferences[i][j].provideIndex].interruptLine == 0) + { + ERROR(" -> %s/%x.%s still used by %s/%x.%s\n", + component->pathname, component, + component->Template->provides[componentEntry(idx)->interfaceReferences[i][j].provideIndex].name, + componentEntry(idx)->pathname, + componentEntry(idx), + componentEntry(idx)->Template->requires[i].name); + } + } + } + } + + return CM_COMPONENT_NOT_UNBINDED; + } + } + + // Sanity check finished, here, we will do the JOB whatever error + + if (cm_DSP_GetState(component->Template->dspId)->state == MPC_STATE_BOOTED) + { + /* + * Call destroy if available + */ + /* Call the destructor only if we don't want to force the destruction */ + if(forceDestroy != DESTROY_WITHOUT_CHECK_CALL && component->Template->LCCDestroyAddress != 0) + { + if (cm_COMP_CallService( + NMF_DESTROY_INDEX, + component, + component->Template->LCCDestroyAddress) != CM_OK) + { + ERROR("CM_MPC_NOT_RESPONDING: can't call destroy '%s'\n", component->pathname, 0, 0, 0, 0, 0); + } + } + else + { + cm_COMP_Flush(component->Template->dspId); + } + } + + cm_delayedDestroyComponent(component); + + return CM_OK; +} + +/** + * Pre-Require: + * - MMDSP wakeup (when accessing loadmap) + */ +t_cm_error cm_destroyInstanceForClient(t_component_instance* component, t_destroy_state forceDestroy, t_nmf_client_id clientId) +{ + /* + * Special code for SINGLETON handling + */ + if(component->Template->classe == SINGLETON) + { + struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, FALSE, clientId); + int nbinstance; + if(cl != NULL) + cl->numberOfInstance--; + + // A singleton could be instantiate twice, thus destroy it only if no more client constructor + nbinstance = getNumberOfInstance(component); + if(nbinstance > 0) + { + LOG_INTERNAL(1, "##### Singleton : Delete handle of %s/%x (%s) component on %s [%d] provItf=%d #####\n", + component->pathname, component, component->Template->name, cm_getDspName(component->Template->dspId), + nbinstance, component->providedItfUsedCount); + return CM_OK; + } + + // Fall through + } + + return cm_destroyInstance(component, forceDestroy); +} + + +static t_uint32 cm_getMaxStackValue(t_component_instance *pComponent) +{ + t_nmf_executive_engine_id executiveEngineId = cm_EEM_getExecutiveEngine(pComponent->Template->dspId)->executiveEngineId; + t_uint32 res = MIN_STACK_SIZE; + unsigned int i; + + for (i=0; i<ComponentTable.idxMax; i++) + { + if ((componentEntry(i) != NULL) && + (componentEntry(i) != pComponent) && + (pComponent->Template->dspId == componentEntry(i)->Template->dspId) && + (executiveEngineId == SYNCHRONOUS_EXECUTIVE_ENGINE || componentEntry(i)->priority == pComponent->priority)) + { + if (componentEntry(i)->Template->minStackSize > res) + res = componentEntry(i)->Template->minStackSize; + } + } + + return res; +} + +static t_uint16 getNumberOfInstance(t_component_instance* component) +{ + t_uint16 instanceNumber = 0; + struct t_client_of_singleton* cur = component->clientOfSingleton; + + for( ; cur != NULL ; cur = cur->next) + { + instanceNumber += cur->numberOfInstance; + } + + return instanceNumber; +} + +static t_uint16 getNumberOfStart(t_component_instance* component) +{ + t_uint16 startNumber = 0; + struct t_client_of_singleton* cur = component->clientOfSingleton; + + for( ; cur != NULL ; cur = cur->next) + { + startNumber += cur->numberOfStart; + } + + return startNumber; +} diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/introspection.c b/drivers/staging/nmf-cm/cm/engine/component/src/introspection.c new file mode 100644 index 00000000000..4aaf8dff889 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/src/introspection.c @@ -0,0 +1,327 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/component/inc/introspection.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/utils/inc/string.h> + +/* + * + */ +t_cm_error cm_getComponentProperty( + const t_component_instance *component, + const char *propName, + char value[MAX_PROPERTY_VALUE_LENGTH], + t_uint32 valueLength){ + t_component_template* template = component->Template; + int i; + + for(i = 0; i < template->propertyNumber; i++) { + if(cm_StringCompare(template->properties[i].name, propName, MAX_PROPERTY_NAME_LENGTH) == 0) { + cm_StringCopy( + value, + template->properties[i].value, + valueLength); + return CM_OK; + } + } + + return CM_NO_SUCH_PROPERTY; +} + +/** + * + */ +static t_attribute* cm_getAttributeDescriptor( + const t_component_instance *component, + const char *attrName) +{ + int i; + + for(i = 0; i < component->Template->attributeNumber; i++) + { + if(cm_StringCompare(component->Template->attributes[i].name, attrName, MAX_ATTRIBUTE_NAME_LENGTH) == 0) + { + return &component->Template->attributes[i]; + } + } + + return NULL; +} + +t_dsp_address cm_getAttributeMpcAddress( + const t_component_instance *component, + const char *attrName) +{ + t_attribute* attribute; + t_uint32 dspAddress; + + if((attribute = cm_getAttributeDescriptor(component, attrName)) == NULL) + return 0x0; + + cm_DSP_GetDspAddress(component->memories[attribute->memory.memory->id], &dspAddress); + + return (dspAddress + + attribute->memory.offset); +} + +t_cm_logical_address cm_getAttributeHostAddr( + const t_component_instance *component, + const char *attrName) +{ + t_attribute* attribute; + + if((attribute = cm_getAttributeDescriptor(component, attrName)) == NULL) + return 0x0; + + // TODO JPF: component->Template->attributes[i].memory.offset could be converted in byte during load + return cm_DSP_GetHostLogicalAddress(component->memories[attribute->memory.memory->id]) + + attribute->memory.offset * attribute->memory.memory->memEntSize; +} + + +t_cm_error cm_readAttribute( + const t_component_instance* component, + const char* attrName, + t_uint32* value) +{ + t_attribute* attribute; + t_cm_logical_address hostAddr; + + if((attribute = cm_getAttributeDescriptor(component, attrName)) == NULL) + { + ERROR("CM_NO_SUCH_ATTRIBUTE(%s, %s)\n", component->pathname, attrName, 0, 0, 0, 0); + return CM_NO_SUCH_ATTRIBUTE; + } + + // TODO JPF: component->Template->attributes[i].memory.offset could be converted in byte during load + hostAddr = cm_DSP_GetHostLogicalAddress(component->memories[attribute->memory.memory->id]) + + attribute->memory.offset * attribute->memory.memory->memEntSize; + + if(attribute->memory.memory->memEntSize != 2) + *value = *((t_uint32 *)hostAddr) & ~MASK_BYTE3; + else + *value = *((t_uint16 *)hostAddr); + + LOG_INTERNAL(3, "cm_readAttribute: [%s:%s, %x]=%x\n", + component->pathname, attrName, hostAddr, *value, 0, 0); + + return CM_OK; +} + +t_uint32 cm_readAttributeNoError( + const t_component_instance* component, + const char* attrName) +{ + t_uint32 value; + + if(cm_readAttribute(component, attrName, &value) != CM_OK) + value = 0; + + return value; +} + +t_cm_error cm_writeAttribute( + const t_component_instance* component, + const char* attrName, + t_uint32 value) +{ + t_attribute* attribute; + t_cm_logical_address hostAddr; + + if((attribute = cm_getAttributeDescriptor(component, attrName)) == NULL) + { + ERROR("CM_NO_SUCH_ATTRIBUTE(%s, %s)\n", component->pathname, attrName, 0, 0, 0, 0); + return CM_NO_SUCH_ATTRIBUTE; + } + + // TODO JPF: component->Template->attributes[i].memory.offset could be converted in byte during load + hostAddr = cm_DSP_GetHostLogicalAddress(component->memories[attribute->memory.memory->id]) + + attribute->memory.offset * attribute->memory.memory->memEntSize; + + if(attribute->memory.memory->memEntSize != 2) + *((t_uint32 *)hostAddr) = value & ~MASK_BYTE3; + else + *((t_uint16 *)hostAddr) = value; + + /* be sure attribute is write into memory */ + OSAL_mb(); + + LOG_INTERNAL(3, "cm_writeAttribute: [%s:%s, %x]=%x\n", + component->pathname, attrName, hostAddr, value, 0, 0); + + return CM_OK; +} + + +/** + * + */ +t_dsp_address cm_getFunction( + const t_component_instance* component, + const char* interfaceName, + const char* methodName) +{ + t_interface_provide_description itfProvide; + t_interface_provide* provide; + t_interface_provide_loaded* provideLoaded; + t_cm_error error; + int i; + + // Get interface description + if((error = cm_getProvidedInterface(component, interfaceName, &itfProvide)) != CM_OK) + return error; + + provide = &component->Template->provides[itfProvide.provideIndex]; + provideLoaded = &component->Template->providesLoaded[itfProvide.provideIndex]; + + for(i = 0; i < provide->interface->methodNumber; i++) + { + if(cm_StringCompare(provide->interface->methodNames[i], methodName, MAX_INTERFACE_METHOD_NAME_LENGTH) == 0) + { + return provideLoaded->indexesLoaded[itfProvide.collectionIndex][i].methodAddresses; + } + } + + return 0x0; +} + +/** + * + */ +PRIVATE t_uint8 compareItfName(const char* simplename, const char* complexname, int *collectionIndex) { + int i; + + // Search if simplename is a prefix of complexname ?? + for(i = 0; simplename[i] != 0; i++) { + if(simplename[i] != complexname[i]) + return 1; // NO + } + + // YES + if(complexname[i] == '[') { + // This is a collection + int value = 0; + i++; + if(complexname[i] < '0' || complexname[i] > '9') { + return 1; + } + for(; complexname[i] >= '0' && complexname[i] <= '9'; i++) { + value = value * 10 + (complexname[i] - '0'); + } + if(complexname[i++] != ']') + return 1; + *collectionIndex = value; + } else + *collectionIndex = -1; + + if(complexname[i] != 0) { + // Complexe name has not been fully parsed -> different name + return 1; + } + + return 0; +} + + +/** + * + */ +PUBLIC t_cm_error cm_getProvidedInterface(const t_component_instance* server, + const char* itfName, + t_interface_provide_description *itfProvide){ + int i; + + for(i = 0; i < server->Template->provideNumber; i++) + { + int collectionIndex; + if(compareItfName(server->Template->provides[i].name, itfName, &collectionIndex) == 0) + { + t_interface_provide *provide = &server->Template->provides[i]; + if(collectionIndex >= 0) + { + if(! (provide->provideTypes & COLLECTION_PROVIDE)) { + ERROR("CM_NO_SUCH_PROVIDED_INTERFACE(%s, %s)\n", + server->pathname, itfName, 0, 0, 0, 0); + goto out; + } + if(collectionIndex >= provide->collectionSize) { + ERROR("CM_NO_SUCH_PROVIDED_INTERFACE(%s, %s): out of range [0..%d[\n", + server->pathname, itfName, provide->collectionSize, + 0, 0, 0); + goto out; + } + } + else + { + if(provide->provideTypes & COLLECTION_PROVIDE) { + ERROR("CM_NO_SUCH_PROVIDED_INTERFACE(%s, %s): interface is a collection [0..%d[\n", + server->pathname, itfName, provide->collectionSize, + 0, 0, 0); + goto out; + } + collectionIndex = 0; + } + itfProvide->provideIndex = i; + itfProvide->server = server; + itfProvide->collectionIndex = collectionIndex; + itfProvide->origName = itfName; + return CM_OK; + } + } + + ERROR("CM_NO_SUCH_PROVIDED_INTERFACE(%s, %s)\n", server->pathname, itfName, 0, 0, 0, 0); +out: + itfProvide->provideIndex = 0; + itfProvide->server = NULL; + itfProvide->collectionIndex = 0; + itfProvide->origName = NULL; + return CM_NO_SUCH_PROVIDED_INTERFACE; +} + +/** + * + */ +t_cm_error cm_getRequiredInterface(const t_component_instance* client, + const char* itfName, + t_interface_require_description *itfRequire){ + int i; + + for(i = 0; i < client->Template->requireNumber; i++) { + int collectionIndex; + if(compareItfName(client->Template->requires[i].name, itfName, &collectionIndex) == 0) { + t_interface_require *require = &client->Template->requires[i]; + if(collectionIndex >= 0) { + if(! (require->requireTypes & COLLECTION_REQUIRE)) { + ERROR("CM_NO_SUCH_REQUIRED_INTERFACE(%s, %s)\n", + client->pathname, itfName, 0, 0, 0, 0); + return CM_NO_SUCH_REQUIRED_INTERFACE; + } + if(collectionIndex >= require->collectionSize) { + ERROR("CM_NO_SUCH_REQUIRED_INTERFACE(%s, %s): out of range [0..%d[\n", + client->pathname, itfName, require->collectionSize, + 0, 0, 0); + return CM_NO_SUCH_REQUIRED_INTERFACE; + } + } else { + if(require->requireTypes & COLLECTION_REQUIRE) { + ERROR("CM_NO_SUCH_REQUIRED_INTERFACE(%s, %s): interface is a collection [0..%d[\n", + client->pathname, itfName, require->collectionSize, + 0, 0, 0); + return CM_NO_SUCH_REQUIRED_INTERFACE; + } + collectionIndex = 0; + } + itfRequire->client = client; + itfRequire->requireIndex = i; + itfRequire->collectionIndex = collectionIndex; + itfRequire->origName = itfName; + return CM_OK; + } + } + + ERROR("CM_NO_SUCH_REQUIRED_INTERFACE(%s, %s)\n", client->pathname, itfName, 0, 0, 0, 0); + return CM_NO_SUCH_REQUIRED_INTERFACE; +} diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/loader.c b/drivers/staging/nmf-cm/cm/engine/component/src/loader.c new file mode 100644 index 00000000000..3d81e8308f9 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/component/src/loader.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/component/inc/instance.h> +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> +#include <cm/engine/component/inc/bind.h> + +#include <cm/engine/utils/inc/string.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/utils/inc/convert.h> + +void START(void); +void END(char*); + +#undef NHASH +#define NHASH 79 //Use a prime number! +#define MULT 17 + +static t_component_template *templates[NB_CORE_IDS][NHASH]; + +static unsigned int templateHash(const char *str) +{ + unsigned int h = 0; + for(; *str; str++) + h = MULT * h + *str; + return h % NHASH; +} + +static void templateAdd(t_component_template *template) +{ + unsigned int h = templateHash(template->name); + + if(templates[template->dspId][h] != NULL) + templates[template->dspId][h]->prev = template; + template->next = templates[template->dspId][h]; + template->prev = NULL; + templates[template->dspId][h] = template; +} + +static void templateRemove(t_component_template *template) +{ + unsigned int h = templateHash(template->name); + + if(template->prev != NULL) + template->prev->next = template->next; + if(template->next != NULL) + template->next->prev = template->prev; + if(template == templates[template->dspId][h]) + templates[template->dspId][h] = template->next; +} + + +t_component_template* cm_lookupTemplate(t_nmf_core_id dspId, t_dup_char str) +{ + t_component_template *template; + + for(template = templates[dspId][templateHash(str)]; template != NULL; template = template->next) + { + if(str == template->name) + return template; + } + + return NULL; +} + +t_bool cm_isComponentOnCoreId(t_nmf_core_id coreId) { + t_uint32 i; + + for(i = 0; i < NHASH; i++) + { + if ((templates[coreId][i] != NULL) + && (templates[coreId][i]->classe != FIRMWARE)) // Skip firmware + return TRUE; + } + + return FALSE; +} + + +static t_dsp_address MemoryToDspAdress(t_component_template *template, t_memory_reference *memory) +{ + if(memory->memory == NULL) + return (t_dsp_address)memory->offset; + else + { + t_dsp_address address; + + cm_DSP_GetDspAddress(template->memories[memory->memory->id], &address); + + return (t_dsp_address)(address + memory->offset); + } +} + +/* + * Method callback + */ +t_uint32 cm_resolvSymbol( + void* context, + t_uint32 type, + t_dup_char symbolName, + char* reloc_addr) +{ + t_component_template *template = (t_component_template*)context; + t_component_instance* ee = cm_EEM_getExecutiveEngine(template->dspId)->instance; + int i, j; + + // Search if this method is provided by EE and resolve it directly + for(i = 0; i < ee->Template->provideNumber; i++) + { + t_interface_provide* provide = &ee->Template->provides[i]; + t_interface_provide_loaded* provideLoaded = &ee->Template->providesLoaded[i]; + + for(j = 0; j < provide->interface->methodNumber; j++) + { + if(provide->interface->methodNames[j] == symbolName) + { + return provideLoaded->indexesLoaded[0][j].methodAddresses; // Here we assume no collection provided !! + } + } + } + + // Lookup if the method is statically required, ands delay relocation when bind occur + for(i = 0; i < template->requireNumber; i++) + { + if((template->requires[i].requireTypes & STATIC_REQUIRE) == 0) + continue; + + for(j = 0; j < template->requires[i].interface->methodNumber; j++) + { + if(template->requires[i].interface->methodNames[j] == symbolName) + { + t_function_relocation* delayedRelocation = (t_function_relocation*)OSAL_Alloc(sizeof(t_function_relocation)); + if(delayedRelocation == NULL) + return 0xFFFFFFFE; + + delayedRelocation->type = type; + delayedRelocation->symbol_name = cm_StringReference(symbolName); + delayedRelocation->reloc_addr = reloc_addr; + delayedRelocation->next = template->delayedRelocation; + template->delayedRelocation = delayedRelocation; + + return 0xFFFFFFFF; + } + } + } + + //Symbol not found + return 0x0; +} + +/* + * Template Management + */ +t_cm_error cm_loadComponent( + t_dup_char templateName, + t_cm_domain_id domainId, + t_elfdescription* elfhandle, + t_component_template **reftemplate) +{ + t_nmf_core_id coreId = cm_DM_GetDomainCoreId(domainId); + t_cm_error error; + int i, j, k; + + /* + * Allocate new component template if first instance + */ + if(*reftemplate == NULL) + { + t_component_template *template; + + LOG_INTERNAL(1, "\n##### Load template %s on %s #####\n", templateName, cm_getDspName(coreId), 0, 0, 0, 0); + + /* + * Sanity check + */ + if(elfhandle->foundedTemplateName != templateName) + { + ERROR("CM_INVALID_ELF_FILE: template name %s != %s\n", templateName, elfhandle->foundedTemplateName, 0, 0, 0, 0); + return CM_INVALID_ELF_FILE; + } + + // Alloc & Reset variable in order to use unloadComponent either with partial constructed template + *reftemplate = template = (t_component_template*)OSAL_Alloc_Zero(sizeof(t_component_template)); + if(template == NULL) + return CM_NO_MORE_MEMORY; + template->name = cm_StringReference(elfhandle->foundedTemplateName); + + // Get information from elfhandle + template->descriptionAssociatedWithTemplate = elfhandle->temporaryDescription; + template->requireNumber = elfhandle->requireNumber; + template->requires = elfhandle->requires; + template->attributeNumber = elfhandle->attributeNumber; + template->attributes = elfhandle->attributes; + template->propertyNumber = elfhandle->propertyNumber; + template->properties = elfhandle->properties; + template->provideNumber = elfhandle->provideNumber; + template->provides = elfhandle->provides; + if(template->descriptionAssociatedWithTemplate) + { + elfhandle->requires = NULL; + elfhandle->attributes = NULL; + elfhandle->properties = NULL; + elfhandle->provides = NULL; + } + + // Compute simple information + template->numberOfInstance = 1; + template->dspId = coreId; + LOG_INTERNAL(3, "load<%x> = %s\n", (int)template, template->name, 0, 0, 0, 0); + switch(elfhandle->magicNumber) { + case MAGIC_COMPONENT: + template->classe = COMPONENT; + break; + case MAGIC_SINGLETON: + template->classe = SINGLETON; + break; + case MAGIC_FIRMWARE: + template->classe = FIRMWARE; + break; + } + template->minStackSize = elfhandle->minStackSize; + + /* + * Load shared memory from file + */ + // START(); + if((error = cm_ELF_LoadTemplate(domainId, elfhandle, template->memories, template->classe == SINGLETON)) != CM_OK) + goto out; + MMDSP_serializeMemories(elfhandle->instanceProperty, &template->codeMemory, &template->thisMemory); + // END("cm_ELF_LoadTemplate"); + + /* + * Copy LCC functions information + * Since MMDSP require Constructor & Destructor (for cache flush and debug purpose) to be called + * either if not provided by user for allowing defered breakpoint, we use Void method if not provided. + */ + template->LCCConstructAddress = MemoryToDspAdress(template, &elfhandle->memoryForConstruct); + template->LCCStartAddress = MemoryToDspAdress(template, &elfhandle->memoryForStart); + template->LCCStopAddress = MemoryToDspAdress(template, &elfhandle->memoryForStop); + template->LCCDestroyAddress = MemoryToDspAdress(template, &elfhandle->memoryForDestroy); + if(template->LCCConstructAddress == 0 && template->classe != FIRMWARE) + template->LCCConstructAddress = cm_EEM_getExecutiveEngine(coreId)->voidAddr; + + // Compute provide methodIndex + if(template->provideNumber != 0) + { + template->providesLoaded = + (t_interface_provide_loaded*)OSAL_Alloc_Zero(sizeof(t_interface_provide_loaded) * template->provideNumber); + if(template->providesLoaded == NULL) + goto oom; + + for(i = 0; i < template->provideNumber; i++) + { + template->providesLoaded[i].indexesLoaded = (t_interface_provide_index_loaded**)OSAL_Alloc_Zero( + sizeof(t_interface_provide_index_loaded*) * template->provides[i].collectionSize); + if(template->providesLoaded[i].indexesLoaded == NULL) + goto oom; + + if(template->provides[i].interface->methodNumber != 0) + { + for(j = 0; j < template->provides[i].collectionSize; j++) + { + template->providesLoaded[i].indexesLoaded[j] = (t_interface_provide_index_loaded*)OSAL_Alloc( + sizeof(t_interface_provide_index_loaded) * template->provides[i].interface->methodNumber); + if(template->providesLoaded[i].indexesLoaded[j] == NULL) + goto oom; + + for(k = 0; k < template->provides[i].interface->methodNumber; k++) + { + template->providesLoaded[i].indexesLoaded[j][k].methodAddresses = + MemoryToDspAdress(template, &template->provides[i].indexes[j][k].memory); + + LOG_INTERNAL(2, " [%d, %d] method '%s' @ %x\n", + j, k, template->provides[i].interface->methodNames[k], + template->providesLoaded[i].indexesLoaded[j][k].methodAddresses, 0, 0); + } + + } + } + } + } + + /* + * TODO + + if((error = elfhandle->errorOccured) != CM_OK) + goto out; + */ + + // START(); + if(template->classe != FIRMWARE) + { + if((error = cm_ELF_relocateSharedSegments( + template->memories, + elfhandle, + template)) != CM_OK) + goto out; + } + // END("cm_ELF_relocateSharedSegments"); + + cm_ELF_FlushTemplate(coreId, template->memories); + + templateAdd(template); + + return CM_OK; + oom: + error = CM_NO_MORE_MEMORY; + out: + cm_unloadComponent(template); + return error; + } + else + { + (*reftemplate)->numberOfInstance++; + } + + return CM_OK; +} + +PUBLIC t_cm_error cm_unloadComponent( + t_component_template *template) +{ + /* + * Destroy template if last instance + */ + if(--template->numberOfInstance == 0) { + t_function_relocation* reloc; + + LOG_INTERNAL(3, "unload<%s>\n", template->name, 0, 0, 0, 0, 0); + + templateRemove(template); + + // Free delayedRelocation + reloc = template->delayedRelocation; + while(reloc != NULL) + { + t_function_relocation *tofree = reloc; + reloc = reloc->next; + cm_StringRelease(tofree->symbol_name); + OSAL_Free(tofree); + } + + if(template->providesLoaded != NULL) + { + int i, j; + + for(i = 0; i < template->provideNumber; i++) + { + if(template->providesLoaded[i].indexesLoaded != NULL) + { + for(j = 0; j < template->provides[i].collectionSize; j++) + { + OSAL_Free(template->providesLoaded[i].indexesLoaded[j]); + } + OSAL_Free(template->providesLoaded[i].indexesLoaded); + } + } + + OSAL_Free(template->providesLoaded); + } + + if(template->descriptionAssociatedWithTemplate) + { + cm_ELF_ReleaseDescription( + template->requireNumber, template->requires, + template->attributeNumber, template->attributes, + template->propertyNumber, template->properties, + template->provideNumber, template->provides); + } + + // Free shared memories + cm_ELF_FreeTemplate(template->dspId, template->memories); + + cm_StringRelease(template->name); + + OSAL_Free(template); + } + + return CM_OK; +} + diff --git a/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration.h b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration.h new file mode 100644 index 00000000000..98d22bba743 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef __INC_CONFIGURATION_H_ +#define __INC_CONFIGURATION_H_ + +#include <cm/engine/api/control/configuration_engine.h> +#include <cm/engine/memory/inc/memory.h> +#include <inc/nmf-limits.h> +#include <cm/engine/dsp/inc/dsp.h> + +/******************************************************************************/ +/************************ FUNCTIONS PROTOTYPES ********************************/ +/******************************************************************************/ + +PUBLIC t_cm_error cm_CFG_ConfigureMediaProcessorCore(t_nmf_core_id coreId, + t_nmf_executive_engine_id executiveEngineId, + t_nmf_semaphore_type_id semaphoreTypeId, t_uint8 nbYramBanks, + const t_cm_system_address *mediaProcessorMappingBaseAddr, + const t_cm_domain_id eeDomain, + t_dsp_allocator_desc* sdramCodeAllocId, + t_dsp_allocator_desc* sdramDataAllocId + ); + +PUBLIC t_cm_error cm_CFG_AddMpcSdramSegment(const t_nmf_memory_segment *pDesc, + const char *memoryname, t_dsp_allocator_desc **allocDesc); + +PUBLIC t_cm_error cm_CFG_CheckMpcStatus(t_nmf_core_id coreId); + +void cm_CFG_ReleaseMpc(t_nmf_core_id coreId); + +#endif /* __INC_CONFIGURATION_H_ */ diff --git a/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_status.h b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_status.h new file mode 100644 index 00000000000..0c75b9c49b0 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_status.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef __INC_CONFIGSTATUS_H_ +#define __INC_CONFIGSTATUS_H + +#include <cm/inc/cm_type.h> +#include <cm/engine/utils/inc/string.h> + +/* + * Variable to active intensive check + * + * \ingroup CM_CONFIGURATION_API + */ +extern t_sint32 cmIntensiveCheckState; + +/* + * Variable to active trace level + * + * \ingroup CM_CONFIGURATION_API + */ +extern t_sint32 cm_debug_level; + +/* + * Variable to active error break + * + * \ingroup CM_CONFIGURATION_API + */ +extern t_sint32 cm_error_break; + +/* + * Variable to activate Ulp + * + * \ingroup CM_CONFIGURATION_API + */ +extern t_bool cmUlpEnable; + +extern t_dup_char anonymousDup, eventDup, skeletonDup, stubDup, traceDup; + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_type.h b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_type.h new file mode 100644 index 00000000000..abfe452cf78 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_type.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Configuration Component Manager API type. + */ +#ifndef CONFIGURATION_TYPE_H +#define CONFIGURATION_TYPE_H + +#include <cm/inc/cm_type.h> + +/*! + * @defgroup t_cm_cmd_id t_cm_cmd_id + * \brief Definition of the command ID + * \ingroup CM_CONFIGURATION_API + * + * CM_CMD_XXX designates the command ID used by the \ref CM_SetMode routine. + * + * \remarks Other command IDs are not yet implemented. + */ + +typedef t_uint32 t_cm_cmd_id; //!< Fake enumeration type \ingroup t_cm_cmd_id +#define CM_CMD_SYNC ((t_cm_cmd_id)0x01) //!< Synchronize on-going operations (no parameter) \ingroup t_cm_cmd_id + +#define CM_CMD_WARM_RESET ((t_cm_cmd_id)0x02) //!< Reset a part of the CM-engine (parameter indicates the part which must be reseted) \ingroup t_cm_cmd_id + +#define CM_CMD_PWR_MGR ((t_cm_cmd_id)0x10) //!< Enable/Disable the internal power management module (0=Disable, 1=Enable) \ingroup t_cm_cmd_id + +#define CM_CMD_DBG_MODE ((t_cm_cmd_id)0x40) //!< Enable/Disable DEBUG mode, Pwr Mgr is also disabled (0=Disable, 1=Enable) \ingroup t_cm_cmd_id + +#define CM_CMD_TRACE_ON ((t_cm_cmd_id)0x41) //!< Enable STM/XTI tracing and force network resetting and dumping \note Since MPC trace will be usable, you can enable them if not \ingroup t_cm_cmd_id +#define CM_CMD_TRACE_OFF ((t_cm_cmd_id)0x42) //!< Disable STM/XTI tracing \note Since MPC trace will not be usable, you can also disable them \ingroup t_cm_cmd_id + +#define CM_CMD_MPC_TRACE_ON ((t_cm_cmd_id)0x50) //!< Enable MPC STM/XTI tracing (param == coreId). \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id +#define CM_CMD_MPC_TRACE_OFF ((t_cm_cmd_id)0x51) //!< Disable MPC STM/XTI tracing (param == coreId) This is the default configuration. \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id + +#define CM_CMD_MPC_PRINT_OFF ((t_cm_cmd_id)0x52) //!< Set to OFF the level of MPC traces (param == coreId) \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id +#define CM_CMD_MPC_PRINT_ERROR ((t_cm_cmd_id)0x53) //!< Set to ERROR the level of MPC traces param == coreId) \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id +#define CM_CMD_MPC_PRINT_WARNING ((t_cm_cmd_id)0x54) //!< Set to WARNING the level of MPC traces param == coreId) \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id +#define CM_CMD_MPC_PRINT_INFO ((t_cm_cmd_id)0x55) //!< Set to INFO the level of MPC traces (param == coreId) \note This command is not execute if execution engine not started on the coreId This is the default configuration. \ingroup t_cm_cmd_id +#define CM_CMD_MPC_PRINT_VERBOSE ((t_cm_cmd_id)0x56) //!< Set to VERBOSE the level of MPC traces param == coreId) \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id + +/*! + * \brief Define the level of internal CM log traces + * + * Define the level of internal CM log traces (-1 to 3) + * -# <b>-1 </b> all internal LOG/ERROR traces are disabled + * -# <b> 0 </b> all internal LOG traces are disabled (<b>default/reset value</b>) + * -# <b> 1, 2, 3 </b> Most and most + * + * \ingroup t_cm_cmd_id + */ +#define CM_CMD_TRACE_LEVEL ((t_cm_cmd_id)0x80) + +/*! + * \brief Enable/Disable intensive internal check + * + * Enable/Disable intensive internal check (0=Disable, 1=Enable): + * - Component handle checking + * + * Must be used during the integration phase (additional process is time consuming). + * + * \ingroup t_cm_cmd_id + */ +#define CM_CMD_INTENSIVE_CHECK ((t_cm_cmd_id)0x100) + +/*! + * \brief Enable/Disable ulp mode + * + * Enable/Disable Ultra Low Power mode. + * + * \ingroup t_cm_cmd_id + */ +#define CM_CMD_ULP_MODE_ON ((t_cm_cmd_id)0x111) //!< Enable ULP mode \ingroup t_cm_cmd_id +#define CM_CMD_ULP_MODE_OFF ((t_cm_cmd_id)0x110) //!< Deprecated (must be removed in 2.10) !!! + +#endif /* CONFIGURATION_TYPE_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration.c b/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration.c new file mode 100644 index 00000000000..f092c7061b4 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/configuration/inc/configuration.h> +#include <cm/engine/component/inc/initializer.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> +#include <cm/engine/semaphores/inc/semaphores.h> +#include <cm/engine/communication/inc/communication.h> +#include <cm/engine/utils/inc/string.h> +#include <cm/engine/repository_mgt/inc/repository_mgt.h> +#include <inc/nmf-limits.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/memory/inc/domain.h> + +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/utils/inc/convert.h> + +#include <cm/engine/power_mgt/inc/power.h> + +t_sint32 cmIntensiveCheckState = 0; +t_sint32 cm_debug_level = 1; +t_sint32 cm_error_break = 0; +t_bool cmUlpEnable = FALSE; + + +#define MAX_EE_NAME_LENGTH 32 +typedef struct { + char eeName[MAX_EE_NAME_LENGTH]; + t_nmf_executive_engine_id executiveEngineId; + t_uint32 EEmemoryCount; +} t_cfg_mpc_desc; + +static t_cfg_mpc_desc cfgMpcDescArray[NB_CORE_IDS]; + +PUBLIC t_cm_error cm_CFG_ConfigureMediaProcessorCore( + t_nmf_core_id coreId, + t_nmf_executive_engine_id executiveEngineId, + t_nmf_semaphore_type_id semaphoreTypeId, + t_uint8 nbYramBanks, + const t_cm_system_address *mediaProcessorMappingBaseAddr, + const t_cm_domain_id eeDomain, + t_dsp_allocator_desc *sdramCodeAllocDesc, + t_dsp_allocator_desc *sdramDataAllocDesc) +{ + /* Process requested configuration (save it) */ + cfgMpcDescArray[coreId].EEmemoryCount = 0; + cfgMpcDescArray[coreId].executiveEngineId = executiveEngineId; + /* Build Executive Engine Name */ + switch(executiveEngineId) + { + case SYNCHRONOUS_EXECUTIVE_ENGINE: + cm_StringCopy(cfgMpcDescArray[coreId].eeName, "synchronous_", MAX_EE_NAME_LENGTH); + break; + case HYBRID_EXECUTIVE_ENGINE: + cm_StringCopy(cfgMpcDescArray[coreId].eeName, "hybrid_", MAX_EE_NAME_LENGTH); + break; + } + + switch(semaphoreTypeId) + { + case LOCAL_SEMAPHORES: + cm_StringConcatenate(cfgMpcDescArray[coreId].eeName, "lsem", MAX_EE_NAME_LENGTH); + break; + case SYSTEM_SEMAPHORES: + cm_StringConcatenate(cfgMpcDescArray[coreId].eeName, "hsem", MAX_EE_NAME_LENGTH); + break; + } + + cm_SEM_InitMpc(coreId, semaphoreTypeId); + + return cm_DSP_Add(coreId, nbYramBanks, mediaProcessorMappingBaseAddr, eeDomain, sdramCodeAllocDesc, sdramDataAllocDesc); +} + +// TODO JPF: Move in dsp.c +PUBLIC t_cm_error cm_CFG_AddMpcSdramSegment(const t_nmf_memory_segment *pDesc, const char* memoryname, t_dsp_allocator_desc **allocDesc) +{ + t_dsp_allocator_desc *desc; + if ( (pDesc == NULL) || + ((pDesc->systemAddr.logical & CM_MM_ALIGN_64BYTES) != 0) ) + return CM_INVALID_PARAMETER; + + //TODO, juraj, the right place and way to do this? + desc = (t_dsp_allocator_desc*)OSAL_Alloc(sizeof (t_dsp_allocator_desc)); + if (desc == 0) + return CM_NO_MORE_MEMORY; + + desc->allocDesc = cm_MM_CreateAllocator(pDesc->size, 0, memoryname); + if (desc->allocDesc == 0) { + OSAL_Free(desc); + return CM_NO_MORE_MEMORY; + } + desc->baseAddress = pDesc->systemAddr; + desc->referenceCounter = 0; + + *allocDesc = desc; + + return CM_OK; +} + +PUBLIC t_cm_error cm_CFG_CheckMpcStatus(t_nmf_core_id coreId) +{ + t_cm_error error; + + if (cm_DSP_GetState(coreId)->state == MPC_STATE_BOOTABLE) + { + /* Allocate coms fifo for a given MPC */ + if ((error = cm_COM_AllocateMpc(coreId)) != CM_OK) + return error; + + /* Launch EE */ + if ((error = cm_EEM_Init(coreId, + cfgMpcDescArray[coreId].eeName, + cfgMpcDescArray[coreId].executiveEngineId)) != CM_OK) + { + cm_COM_FreeMpc(coreId); + return error; + } + + /* Initialize coms fifo for a given MPC */ + cm_COM_InitMpc(coreId); + + /* Initialisation of the dedicated communication channel for component initialization */ + if((error = cm_COMP_INIT_Init(coreId)) != CM_OK) + { + cm_EEM_Close(coreId); + cm_COM_FreeMpc(coreId); + return error; + } + + cfgMpcDescArray[coreId].EEmemoryCount = cm_PWR_GetMPCMemoryCount(coreId); + + if(cmUlpEnable) + { + // We have finish boot, allow MMDSP to go in auto idle + cm_EEM_AllowSleep(coreId); + } + } + + if (cm_DSP_GetState(coreId)->state != MPC_STATE_BOOTED) + return CM_MPC_NOT_INITIALIZED; + + return CM_OK; +} + +void cm_CFG_ReleaseMpc(t_nmf_core_id coreId) +{ + t_uint32 memoryCount = cm_PWR_GetMPCMemoryCount(coreId); + + // If No more memory and no more component (to avoid switch off in case of component using no memory) + if( + cm_PWR_GetMode() == NORMAL_PWR_MODE && + memoryCount != 0 /* Just to see if there is something */ && + memoryCount == cfgMpcDescArray[coreId].EEmemoryCount && + cm_isComponentOnCoreId(coreId) == FALSE) + { + LOG_INTERNAL(1, "\n##### Shutdown %s #####\n", cm_getDspName(coreId), 0, 0, 0, 0, 0); + + (void)cm_EEM_ForceWakeup(coreId); + + /* remove ee from load map here */ + cm_COMP_INIT_Close(coreId); + cm_EEM_Close(coreId); + cm_COM_FreeMpc(coreId); + + cfgMpcDescArray[coreId].EEmemoryCount = 0; // For debug purpose + } +} diff --git a/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration_wrapper.c b/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration_wrapper.c new file mode 100644 index 00000000000..bc3952e63b4 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration_wrapper.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/api/configuration_engine.h> +#include <cm/engine/communication/inc/communication.h> +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/memory/inc/chunk_mgr.h> +#include <cm/engine/repository_mgt/inc/repository_mgt.h> +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/semaphores/inc/semaphores.h> +#include <cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> +#include <cm/engine/configuration/inc/configuration.h> +#include <cm/engine/power_mgt/inc/power.h> +#include <cm/engine/utils/inc/string.h> +#include <cm/engine/component/inc/introspection.h> +#include <cm/engine/component/inc/bind.h> +#include <cm/engine/memory/inc/domain.h> +#include <cm/engine/api/executive_engine_mgt_engine.h> + +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/trace/inc/xtitrace.h> + +t_dup_char anonymousDup, eventDup, skeletonDup, stubDup, traceDup; + +PUBLIC t_cm_error CM_ENGINE_Init( + const t_nmf_hw_mapping_desc *pNmfHwMappingDesc, + const t_nmf_config_desc *pNmfConfigDesc + ) +{ + t_cm_error error; + + // The purpose of that is just to not free/unfree some String frequently used + anonymousDup = cm_StringDuplicate("anonymous"); + eventDup = cm_StringDuplicate("event"); + skeletonDup = cm_StringDuplicate("skeleton"); + stubDup = cm_StringDuplicate("stub"); + traceDup = cm_StringDuplicate("trace"); + + if (( + error = cm_OSAL_Init() + ) != CM_OK) { return error; } + + if (( + error = cm_COMP_Init() + ) != CM_OK) { return error; } + + if (( + error = cm_PWR_Init() + ) != CM_OK) { return error; } + + cm_TRC_traceReset(); + + if (( + error = cm_DM_Init() + ) != CM_OK) {return error; } + + if (( + error = cm_SEM_Init(&pNmfHwMappingDesc->hwSemaphoresMappingBaseAddr) + ) != CM_OK) { return error; } + + if ((error = cm_COM_Init(pNmfConfigDesc->comsLocation)) != CM_OK) + return error; + + cm_DSP_Init(&pNmfHwMappingDesc->esramDesc); + + return CM_OK; +} + +PUBLIC void CM_ENGINE_Destroy(void) +{ + t_component_instance *instance; + t_cm_error error; + t_uint32 i; + + /* PP: Well, on Linux (and probably on Symbian too), this is called when driver is removed + * => the module (driver) can't be removed if there are some pending clients + * => all remaining components should have been destroyed in CM_ENGINE_FlushClient() + * => So, if we found some components here, we are in BIG trouble ... + */ + /* First, stop all remaining components */ + for (i=0; i<ComponentTable.idxMax; i++) + { + t_nmf_client_id clientId; + + if ((instance = componentEntry(i)) == NULL) + continue; + clientId = domainDesc[instance->domainId].client; + LOG_INTERNAL(0, "Found a remaining component %s (%s) when destroying the CM !!!\n", instance->pathname, instance->Template->name, 0, 0, 0, 0); + if (/* skip EE */ + (instance->Template->classe == FIRMWARE) || + /* Skip all binding components */ + (cm_StringCompare(instance->Template->name, "_ev.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_st.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_sk.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_tr.", 4) == 0)) + continue; + + /* + * Special code for SINGLETON handling + */ + if(instance->Template->classe == SINGLETON) + { + struct t_client_of_singleton* cl = instance->clientOfSingleton; + + clientId = instance->clientOfSingleton->clientId; + for( ; cl != NULL ; cl = cl->next) + { + if(cl == instance->clientOfSingleton) + { + cl->numberOfStart = 1; // == 1 since it will go to 0 in cm_stopComponent + cl->numberOfInstance = 1; // == 1 since it will go to 0 in cm_destroyInstanceForClient + } + else + { + cl->numberOfStart = 0; + cl->numberOfInstance = 0; + } + cl->numberOfBind = 0; + } + } + + // Stop the component + error = cm_stopComponent(instance, clientId); + if (error != CM_OK && error != CM_COMPONENT_NOT_STARTED) + LOG_INTERNAL(0, "Error stopping component %s/%x (%s, error=%d, client=%u)\n", instance->pathname, instance, instance->Template->name, error, clientId, 0); + + // Destroy dependencies + cm_destroyRequireInterface(instance, clientId); + } + + /* Destroy all remaining components */ + for (i=0; i<ComponentTable.idxMax; i++) + { + t_nmf_client_id clientId; + + if ((instance = componentEntry(i)) == NULL) + continue; + clientId = domainDesc[instance->domainId].client; + + if (/* skip EE */ + (instance->Template->classe == FIRMWARE) || + /* Skip all binding components */ + (cm_StringCompare(instance->Template->name, "_ev.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_st.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_sk.", 4) == 0) || + (cm_StringCompare(instance->Template->name, "_tr.", 4) == 0)) { + continue; + } + + if(instance->Template->classe == SINGLETON) + { + clientId = instance->clientOfSingleton->clientId; + } + + // Destroy the component + error = cm_destroyInstanceForClient(instance, DESTROY_WITHOUT_CHECK, clientId); + + if (error != CM_OK) + { + /* FIXME : add component name instance in log message but need to make a copy before cm_flushComponent() + * because it's no more available after. + */ + LOG_INTERNAL(0, "Error flushing component (error=%d, client=%u)\n", error, clientId, 0, 0, 0, 0); + } + } + + /* This will power off all ressources and destroy EE */ + cm_PWR_SetMode(NORMAL_PWR_MODE); + cm_DSP_Destroy(); + cm_DM_Destroy(); + /* Nothing to do about SEM */ + //cm_MM_Destroy(); + cm_REP_Destroy(); + cm_COMP_Destroy(); + cm_OSAL_Destroy(); + + cm_StringRelease(traceDup); + cm_StringRelease(stubDup); + cm_StringRelease(skeletonDup); + cm_StringRelease(eventDup); + cm_StringRelease(anonymousDup); +} + +PUBLIC t_cm_error CM_ENGINE_ConfigureMediaProcessorCore( + t_nmf_core_id coreId, + t_nmf_executive_engine_id executiveEngineId, + t_nmf_semaphore_type_id semaphoreTypeId, + t_uint8 nbYramBanks, + const t_cm_system_address *mediaProcessorMappingBaseAddr, + const t_cm_domain_id eeDomain, + const t_cfg_allocator_id sdramCodeAllocId, + const t_cfg_allocator_id sdramDataAllocId + ) +{ + return cm_CFG_ConfigureMediaProcessorCore( + coreId, + executiveEngineId, + semaphoreTypeId, + nbYramBanks, + mediaProcessorMappingBaseAddr, + eeDomain, + (t_dsp_allocator_desc*)sdramCodeAllocId, + (t_dsp_allocator_desc*)sdramDataAllocId + ); +} + +PUBLIC t_cm_error CM_ENGINE_AddMpcSdramSegment( + const t_nmf_memory_segment *pDesc, + t_cfg_allocator_id *id, + const char *memoryname + ) +{ + return cm_CFG_AddMpcSdramSegment(pDesc, memoryname == NULL ? "" : memoryname, (t_dsp_allocator_desc**)id); +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_SetMode(t_cm_cmd_id aCmdID, t_sint32 aParam) +{ + t_cm_error error = CM_OK; + int i; + + OSAL_LOCK_API(); + + switch(aCmdID) { + case CM_CMD_DBG_MODE: + cm_PWR_SetMode(( aParam==1 ) ? DISABLE_PWR_MODE : NORMAL_PWR_MODE); + switch(cm_PWR_GetMode()) + { + case NORMAL_PWR_MODE: + // Release the MPC (which will switch it off if no more used) + for (i=FIRST_MPC_ID; i<NB_CORE_IDS; i++) + { + cm_CFG_ReleaseMpc(i); + } + break; + case DISABLE_PWR_MODE: + // Force the load of the EE if not already done. + for (i=FIRST_MPC_ID; i<NB_CORE_IDS;i++) + { + if((error = cm_CFG_CheckMpcStatus(i)) != CM_OK) + break; + } + break; + } + break; + case CM_CMD_TRACE_LEVEL: + if (aParam<-1) cm_debug_level = -1; + else cm_debug_level = aParam; + break; + case CM_CMD_INTENSIVE_CHECK: + cmIntensiveCheckState = aParam; + break; + + case CM_CMD_TRACE_ON: + cm_trace_enabled = TRUE; + cm_TRC_Dump(); + break; + case CM_CMD_TRACE_OFF: + cm_trace_enabled = FALSE; + break; + + case CM_CMD_MPC_TRACE_ON: + cm_EEM_setTraceMode((t_nmf_core_id)aParam, 1); + break; + case CM_CMD_MPC_TRACE_OFF: + cm_EEM_setTraceMode((t_nmf_core_id)aParam, 0); + break; + + case CM_CMD_MPC_PRINT_OFF: + cm_EEM_setPrintLevel((t_nmf_core_id)aParam, 0); + break; + case CM_CMD_MPC_PRINT_ERROR: + cm_EEM_setPrintLevel((t_nmf_core_id)aParam, 1); + break; + case CM_CMD_MPC_PRINT_WARNING: + cm_EEM_setPrintLevel((t_nmf_core_id)aParam, 2); + break; + case CM_CMD_MPC_PRINT_INFO: + cm_EEM_setPrintLevel((t_nmf_core_id)aParam, 3); + break; + case CM_CMD_MPC_PRINT_VERBOSE: + cm_EEM_setPrintLevel((t_nmf_core_id)aParam, 4); + break; + + case CM_CMD_ULP_MODE_ON: + cmUlpEnable = TRUE; + break; + + default: + error = CM_INVALID_PARAMETER; + break; + } + + OSAL_UNLOCK_API(); + + return error; +} + diff --git a/drivers/staging/nmf-cm/cm/engine/dsp/inc/dsp.h b/drivers/staging/nmf-cm/cm/engine/dsp/inc/dsp.h new file mode 100644 index 00000000000..439deac115f --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/dsp/inc/dsp.h @@ -0,0 +1,453 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief DSP abstraction layer + * + * \defgroup DSP_INTERNAL Private DSP Abstraction Layer API. + * + */ +#ifndef __INC_CM_DSP_H +#define __INC_CM_DSP_H + +#include <cm/inc/cm_type.h> +#include <share/inc/nmf.h> +#include <cm/engine/memory/inc/domain_type.h> +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/memory/inc/remote_allocator.h> + + +#define SxA_NB_BLOCK_RAM 8 /*32kworks (24-bit) */ + +#define SxA_LOCKED_WAY 1 + +/* + * Type defintion to handle dsp offset in word + */ +typedef t_uint32 t_dsp_offset; + +typedef t_uint32 t_dsp_address; + +typedef enum { + DSP2ARM_IRQ_0, + DSP2ARM_IRQ_1 +} t_mpc2host_irq_num; + +typedef enum { + ARM2DSP_IRQ_0, + ARM2DSP_IRQ_1, + ARM2DSP_IRQ_2, + ARM2DSP_IRQ_3 +} t_host2mpc_irq_num; + +typedef enum { + INTERNAL_XRAM24 = 0, /* 24-bit XRAM */ + INTERNAL_XRAM16 = 1, /* 16-bit XRAM */ + INTERNAL_YRAM24 = 2, /* 24-bit YRAM */ + INTERNAL_YRAM16 = 3, /* 16-bit YRAM */ + SDRAM_EXT24 = 4, /* 24-bit external "X" memory */ + SDRAM_EXT16 = 5, /* 16-bit external "X" memory */ + ESRAM_EXT24 = 6, /* ESRAM24 */ + ESRAM_EXT16 = 7, /* ESRAM16 */ + SDRAM_CODE = 8, /* Program memory */ + ESRAM_CODE = 9, /* ESRAM code */ + LOCKED_CODE = 10, /* For way locking */ + NB_DSP_MEMORY_TYPE, + DEFAULT_DSP_MEM_TYPE = MASK_ALL16 +} t_dsp_memory_type_id; + +typedef struct { + t_cm_allocator_desc *allocDesc; + t_cm_system_address baseAddress; + t_uint32 referenceCounter; +} t_dsp_allocator_desc; + +typedef struct { + t_cm_system_address base; + t_uint32 size; +} t_dsp_segment; + +typedef enum { +#if defined(__STN_8500) && (__STN_8500 > 10) + SDRAM_CODE_EE, + SDRAM_CODE_USER, + SDRAM_DATA_EE, + SDRAM_DATA_USER, + NB_MIGRATION_SEGMENT, + ESRAM_CODE_EE = NB_MIGRATION_SEGMENT, + ESRAM_CODE_USER, + ESRAM_DATA_EE, + ESRAM_DATA_USER, +#else + SDRAM_CODE_EE, + SDRAM_DATA_EE, + ESRAM_CODE_EE, + ESRAM_DATA_EE, +#endif + NB_DSP_SEGMENT_TYPE +} t_dsp_segment_type; + +typedef struct { + t_dsp_segment_type segmentType; + t_uint32 baseOffset; +} t_dsp_address_info; + +typedef enum { + MPC_STATE_UNCONFIGURED, + MPC_STATE_BOOTABLE, + MPC_STATE_BOOTED, + MPC_STATE_PANIC, +} t_dsp_state; + +typedef struct { + t_dsp_state state; + t_uint8 nbYramBank; + t_cm_domain_id domainEE; + t_dsp_allocator_desc *allocator[NB_DSP_MEMORY_TYPE]; + t_dsp_segment segments[NB_DSP_SEGMENT_TYPE]; + t_uint32 yram_offset; + t_uint32 yram_size; + t_uint32 locked_offset; + t_uint32 locked_size; +} t_dsp_desc; + +typedef struct { + t_nmf_core_id coreId; + t_dsp_memory_type_id memType; // Index in MPC desc allocator + t_cm_allocator_desc *alloc; +} t_dsp_chunk_info; + +PUBLIC const t_dsp_desc* cm_DSP_GetState(t_nmf_core_id coreId); +PUBLIC void cm_DSP_SetStatePanic(t_nmf_core_id coreId); + +PUBLIC void cm_DSP_Init(const t_nmf_memory_segment *pEsramDesc); +PUBLIC void cm_DSP_Destroy(void); + +/*! + * \brief Initialize the memory segments management of a given MPC + * + * \param[in] coreId Identifier of the DSP to initialize + * \param[in] pDspMapDesc DSP mapping into host space + * \param[in] memConf configuration of the DSP memories (standalone or shared) + * + * \retval t_cm_error + * + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_error cm_DSP_Add(t_nmf_core_id coreId, + t_uint8 nbYramBanks, + const t_cm_system_address *pDspMapDesc, + const t_cm_domain_id eeDomain, + t_dsp_allocator_desc *sdramCodeAllocDesc, + t_dsp_allocator_desc *sdramDataAllocDesc); + + + +/*! + * \brief Configure a given Media Processor Core + * + * This routine programs the configuration (caches, ahb wrapper, ...) registers of a given MPC. + * + * \param[in] coreId Identifier of the DSP to initialize + * + * \retval t_cm_error + * + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_error cm_DSP_Boot(t_nmf_core_id coreId); + +/*! + * \brief Boot a given DSP + * + * This routine allows after having initialized and loaded the EE into a given DSP to start it (boot it) + * + * \param[in] coreId identifier of the DSP to boot + * \param[in] panicReasonOffset offset of panic reason which will pass to NONE_PANIC when DSP booted. + * + * \retval t_cm_error + * + * \ingroup DSP_INTERNAL + */ +PUBLIC void cm_DSP_ConfigureAfterBoot(t_nmf_core_id coreId); + +PUBLIC void cm_DSP_Start(t_nmf_core_id coreId); + +PUBLIC void cm_DSP_Stop(t_nmf_core_id coreId); + +/*! + * \brief Shutdown a given DSP + * + * This routine allows to stop and shutdown a given DSP + * + * \param[in] coreId identifier of the DSP to shutdown + * + * \retval t_cm_error + * + * \ingroup DSP_INTERNAL + */ +PUBLIC void cm_DSP_Shutdown(t_nmf_core_id coreId); + +PUBLIC t_uint32 cm_DSP_ReadXRamWord(t_nmf_core_id coreId, t_uint32 dspOffset); +PUBLIC void cm_DSP_WriteXRamWord(t_nmf_core_id coreId, t_uint32 dspOffset, t_uint32 value); + +/*! + * \brief Convert a Dsp address (offset inside a given DSP memory segment) into the host address (logical) + * + * \param[in] coreId identifier of the given DSP + * \param[in] dspAddress dsp address to be converted + * \param[in] memType memory type identifier + * + * \retval t_cm_logical_address + * + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_logical_address cm_DSP_ConvertDspAddressToHostLogicalAddress(t_nmf_core_id coreId, t_shared_addr dspAddress); + +/*! + * \brief Acknowledge the local interrupt of a given DSP (when not using HW semaphore mechanisms) + * + * \param[in] coreId identifier of the given DSP + * \param[in] irqNum irq identifier + * + * \retval void + * + * \ingroup DSP_INTERNAL + */ +PUBLIC void cm_DSP_AcknowledgeDspIrq(t_nmf_core_id coreId, t_mpc2host_irq_num irqNum); + + +/* + * Memory Management API routines + */ + +/*! + * \brief Retrieve DSP information for a memory chunk. + * + * This function retrieves information stored in user-data of the allocated chunk. + * See also \ref{t_dsp_chunk_info}. + * + * \param[in] memHandle Handle to the allocated chunk. + * \param[out] info Dsp information structure. + * + * \ingroup DSP_INTERNAL + */ +PUBLIC void cm_DSP_GetDspChunkInfo(t_memory_handle memHandle, t_dsp_chunk_info *info); + +/*! + * \brief Get memory allocator for a given memory type on a DSP. + * + * \param[in] coreId Dsp identifier. + * \param[in] memType Memory type identifier. + * + * \retval reference to the allocator descriptor (or null) + * + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_allocator_desc* cm_DSP_GetAllocator(t_nmf_core_id coreId, t_dsp_memory_type_id memType); + +/*! + * \brief Get DSP internal memory (TCM) information for allocation. + * + * For DSP-internal memories (TCMX, Y 16/24), return the offset and size of the allocation zone (for domain + * mechanism) and the allocation memory type. + * + * \param[in] coreId Dsp identifier. + * \param[in] memType Memory type identifier. + * \param[out] mem_info Memory information structure. + * + * \retval CM_OK + * + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_error cm_DSP_GetInternalMemoriesInfo(t_cm_domain_id domainId, t_dsp_memory_type_id memType, + t_uint32 *offset, t_uint32 *size); + + +/*! + * \brief Convert word size to byte size. + * + * \param[in] memType Memory type identifier. + * \param[in] wordSize Word size to be converted. + * + * \retval Byte size. + * + * \ingroup DSP_INTERNAL + */ +PUBLIC t_uint32 cm_DSP_ConvertSize(t_dsp_memory_type_id memType, t_uint32 wordSize); + +/*! + * \brief Provide the Memory status of a given memory type for a given DSP + * + * \param[in] coreId dsp identifier. + * \param[in] memType Type of memory. + * \param[out] pStatus requested memory status + * + * \retval t_cm_error + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_error cm_DSP_GetAllocatorStatus(t_nmf_core_id coreId, t_dsp_memory_type_id memType, t_uint32 offset, t_uint32 size, t_cm_allocator_status *pStatus); + +/*! + * \brief Provide DSP memory host shared address + * + * \param[in] memHandle Allocated block handle + * \param[out] pAddr Returned system address. + * + * \retval t_cm_error + * \ingroup DSP_INTERNAL + */ +PUBLIC void cm_DSP_GetHostSystemAddress( t_memory_handle memHandle, t_cm_system_address *pAddr); + +/*! + * \brief Get physical address of a memory chunk. + * + * \param[in] memHandle Memory handle. + * + * \retval Physical address. + * + * \ingroup DSP_INTERNAL + */ +PUBLIC t_physical_address cm_DSP_GetPhysicalAdress(t_memory_handle memHandle); + +/*! + * \brief Return Logical Address of an allocated memory chunk. + * + * \param[in] memHandle Allocated chunk handle + * \retval t_cm_error + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_logical_address cm_DSP_GetHostLogicalAddress(t_memory_handle memHandle); + +/*! + * \brief Provide DSP memory DSP address (offset inside a given DSP memory segment) + * + * \param[in] memHandle Allocated block handle + * \param[out] dspAddress allocated block address seen by the given DSP + * + * \retval t_cm_error + * \ingroup DSP_INTERNAL + */ +PUBLIC void cm_DSP_GetDspAddress(t_memory_handle handle, t_uint32 *pDspAddress); + +/*! + * \brief Return the adress of the DSP base associated to the memory type. + * Caution, this information is valid only in normal state (not when migrated). + * + * \param[in] coreId DSP Identifier. + * \param[in] memType Type of memory. + * \param[out] pAddr Base address. + * + * \retval t_cm_error + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_error cm_DSP_GetDspBaseAddress(t_nmf_core_id coreId, t_dsp_memory_type_id memType, t_cm_system_address *pAddr); + +/*! + * \brief Return DSP memory handle offset (offset inside a given DSP memory) + * + * \param[in] coreId dsp identifier. + * \param[in] memType Type of memory. + * \param[in] memHandle Allocated block handle + * + * \retval t_uint32: Offset of memory handle inside memory + * \ingroup DSP_INTERNAL + */ +PUBLIC t_uint32 cm_DSP_GetDspMemoryHandleOffset( + t_nmf_core_id coreId, + t_dsp_memory_type_id dspMemType, + t_memory_handle memHandle); + +/*! + * \brief Provide DSP memory handle size + * + * \param[in] memHandle Allocated block handle + * \param[out] pDspSize Size of the given memory handle + + * + * \retval t_cm_error + * \ingroup DSP_INTERNAL + */ +PUBLIC void cm_DSP_GetDspMemoryHandleSize(t_memory_handle memHandle, t_uint32 *pDspSize); + +/*! + * \brief Resize xram allocator to reserve spave for stack. + * + * \param[in] coreId dsp identifier. + * \param[in] newStackSize New required stack size. + + * + * \retval t_cm_error + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_error cm_DSP_setStackSize(t_nmf_core_id coreId, t_uint32 newStackSize); + +/*! + * \brief Allow to know if nbYramBanks parameter is valid for coreId. This api is need since use of nbYramBanks + * is deferred. + * + * \param[in] coreId dsp identifier. + * \param[in] nbYramBanks number of yramBanks to use. + * + * \retval t_cm_error + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_error cm_DSP_IsNbYramBanksValid(t_nmf_core_id coreId, t_uint8 nbYramBanks); + +/*! + * \brief Allow to know stack base address according to coreId and nbYramBanks use. + * + * \param[in] coreId dsp identifier. + * \param[in] nbYramBanks number of yramBanks to use. + * + * \retval t_uint32 return stack address + * \ingroup DSP_INTERNAL + */ +PUBLIC t_uint32 cm_DSP_getStackAddr(t_nmf_core_id coreId); + +/*! + * \brief For a give dsp adress return the offset from the hardware base that the adress is relative to. + * + * \param[in] coreId DSP identifier. + * \param[in] adr DSP address. + * \param[out] info Info structure containing (hw base id, offset) + * + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_error cm_DSP_GetDspDataAddressInfo(t_nmf_core_id coreId, t_uint32 adr, t_dsp_address_info *info); + +/*! + * \brief Modify the mapping of a code hardware base. Used for memory migration. + * + * The function calculates the new hardware base so that in the DSP address-space, + * the source address will be mapped to the destination address. + * + * \param[in] coreId DSP Identifier. + * \param[in] hwSegment Identifier of the hardware segment (thus hardware base). + * \param[in] src Source address + * \param[in] dst Destination address + * + * \retval t_cm_error + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_error cm_DSP_updateCodeBase(t_nmf_core_id coreId, t_dsp_segment_type hwSegment, t_cm_system_address src, t_cm_system_address dst); + +/*! + * \brief Modify the mapping of a data hardware base. Used for memory migration. + * + * The function calculates the new hardware base so that in the DSP address-space, + * the source address will be mapped to the destination address. + * + * \param[in] coreId DSP Identifier. + * \param[in] hwSegment Identifier of the hardware segment (thus hardware base). + * \param[in] src Source address + * \param[in] dst Destination address + * + * \retval t_cm_error + * \ingroup DSP_INTERNAL + */ +PUBLIC t_cm_error cm_DSP_updateDataBase(t_nmf_core_id coreId, t_dsp_segment_type hwSegment, t_cm_system_address src, t_cm_system_address dst); + +#endif /* __INC_CM_DSP_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/dsp/inc/semaphores_dsp.h b/drivers/staging/nmf-cm/cm/engine/dsp/inc/semaphores_dsp.h new file mode 100644 index 00000000000..1bb1c34cced --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/dsp/inc/semaphores_dsp.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef __INC_CM_SEMAPHORES_DSP_H +#define __INC_CM_SEMAPHORES_DSP_H + +#include <share/semaphores/inc/semaphores.h> +#include <cm/engine/dsp/inc/dsp.h> + +PUBLIC void cm_DSP_SEM_Take(t_nmf_core_id coreId, t_semaphore_id semId); +PUBLIC void cm_DSP_SEM_Give(t_nmf_core_id coreId, t_semaphore_id semId); +PUBLIC void cm_DSP_SEM_GenerateIrq(t_nmf_core_id coreId, t_semaphore_id semId); +PUBLIC void cm_DSP_AssertDspIrq(t_nmf_core_id coreId, t_host2mpc_irq_num irqNum); + +PUBLIC void cm_DSP_AcknowledgeDspIrq(t_nmf_core_id coreId, t_mpc2host_irq_num irqNum); + +#endif /* __INC_CM_SEMAPHORES_DSP_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h b/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h new file mode 100644 index 00000000000..0ddc71d2c4f --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h @@ -0,0 +1,959 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef __INC_MMDSP_HWP_H +#define __INC_MMDSP_HWP_H + +#include <cm/inc/cm_type.h> + +#define MMDSP_NB_BLOCK_RAM 8 +#define MMDSP_RAM_BLOCK_SIZE 4096 /* 0x1000 */ +#define MMDSP_NB_TIMER 3 +#define MMDSP_NB_BIT_SEM 8 +#define MMDSP_NB_DMA_IF 8 +#define MMDSP_NB_DMA_CTRL 4 +#define MMDSP_NB_ITREMAP_REG 32 + +#define MMDSP_INSTRUCTION_WORD_SIZE (sizeof(t_uint64)) +#define MMDSP_ICACHE_LINE_SIZE_IN_INST (4) +#define MMDSP_ICACHE_LINE_SIZE (MMDSP_ICACHE_LINE_SIZE_IN_INST * MMDSP_INSTRUCTION_WORD_SIZE) + +#define MMDSP_DATA_WORD_SIZE (3) +#define MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE (sizeof(t_uint32)) +#define MMDSP_DATA_WORD_SIZE_IN_EXT24 (sizeof(t_uint32)) +#define MMDSP_DATA_WORD_SIZE_IN_EXT16 (sizeof(t_uint16)) +#define MMDSP_DCACHE_LINE_SIZE_IN_WORDS (8) +#define MMDSP_DCACHE_LINE_SIZE (MMDSP_DCACHE_LINE_SIZE_IN_WORDS * sizeof(t_uint32)) + +#define MMDSP_NB_IO 16 + +#define MMDSP_CODE_CACHE_WAY_SIZE 256 + +//#define MMDSP_ESRAM_DSP_BASE_ADDR 0xE0000 /* 64-bit words */ +//#define MMDSP_DATA24_DSP_BASE_ADDR 0x10000 +//#define MMDSP_DATA16_DSP_BASE_ADDR 0x800000 +//#define MMDSP_MMIO_DSP_BASE_ADDR 0xF80000 + +/* Specified according MMDSP & ELF convention */ +/* Note: Here we assume that ESRAM is less than 2MB */ +#define SDRAMTEXT_BASE_ADDR 0x00000000 +#define ESRAMTEXT_BASE_ADDR 0x000E0000 + +#define SDRAMMEM24_BASE_ADDR 0x00010000 +#define ESRAMMEM24_BASE_ADDR 0x00600000 /* ELF == 0x00400000 TODO: Update it in MMDSP ELF compiler */ +#define SDRAMMEM16_BASE_ADDR 0x00800000 +#define ESRAMMEM16_BASE_ADDR 0x00D80000 /* ELF == 0x00BC0000 TODO: Update it in MMDSP ELF compiler */ + +#define MMIO_BASE_ADDR 0x00F80000 + +/* + * Definition of indirect host registers + */ +#define IHOST_ICACHE_FLUSH_REG 0x0 +#define IHOST_ICACHE_FLUSH_CMD_ENABLE (t_uint64)MASK_BIT0 +#define IHOST_ICACHE_FLUSH_ALL_ENTRIES_CMD (t_uint64)0x0 +#if 0 +#define IHOST_ICACHE_INVALID_ALL_UNLOCKED_L2_LINES_CMD (t_uint64)0x8 +#define IHOST_ICACHE_INVALID_ALL_LOCKED_L2_LINES_CMD (t_uint64)0xA +#define IHOST_ICACHE_UNLOCK_ALL_LOCKED_L2_LINES_CMD (t_uint64)0xC +#define IHOST_ICACHE_LOCK_ALL_WAYS_LESSER_THAN_LOCK_V_CMD (t_uint64)0xE +#else +#define IHOST_ICACHE_INVALID_ALL_UNLOCKED_L2_LINES_CMD (t_uint64)0x10 +#define IHOST_ICACHE_INVALID_ALL_LOCKED_L2_LINES_CMD (t_uint64)0x12 +#define IHOST_ICACHE_UNLOCK_ALL_LOCKED_L2_LINES_CMD (t_uint64)0x14 +#define IHOST_ICACHE_LOCK_ALL_WAYS_LESSER_THAN_LOCK_V_CMD (t_uint64)0x16 +#define IHOST_ICACHE_FLUSH_BY_SERVICE (t_uint64)0x18 +#define IHOST_ICACHE_FLUSH_OUTSIDE_RANGE (t_uint64)0x1A +#endif + +#define IHOST_ICACHE_LOCK_V_REG 0x1 + +#define IHOST_ICACHE_MODE_REG 0x2 +#define IHOST_ICACHE_MODE_PERFMETER_ON (t_uint64)MASK_BIT0 +#define IHOST_ICACHE_MODE_PERFMETER_OFF (t_uint64)0x0 +#define IHOST_ICACHE_MODE_L2_CACHE_ON (t_uint64)MASK_BIT1 +#define IHOST_ICACHE_MODE_L2_CACHE_OFF (t_uint64)0x0 +#define IHOST_ICACHE_MODE_L1_CACHE_ON (t_uint64)MASK_BIT2 +#define IHOST_ICACHE_MODE_L1_CACHE_OFF (t_uint64)0x0 +#define IHOST_ICACHE_MODE_FILL_MODE_ON (t_uint64)MASK_BIT3 +#define IHOST_ICACHE_MODE_FILL_MODE_OFF (t_uint64)0x0 + +#define IHOST_CLEAR_PERFMETER_REG 0x3 +#define IHOST_CLEAR_PERFMETER_ON (t_uint64)0x1 +#define IHOST_CLEAR_PERFMETER_OFF (t_uint64)0x0 + +#define IHOST_PERF_HIT_STATUS_REG 0x4 + +#define IHOST_PERF_MISS_STATUS_REG 0x5 + +#define IHOST_FILL_START_WAY_REG 0x6 +#define IHOST_FILL_START_ADDR_VALUE_SHIFT 0U +#define IHOST_FILL_WAY_NUMBER_SHIFT 20U + +#define IHOST_PRG_BASE_ADDR_REG 0x7 +#define IHOST_PRG_BASE1_ADDR_SHIFT 0 +#define IHOST_PRG_BASE2_ADDR_SHIFT 32 + +#if defined(__STN_8500) && (__STN_8500>10) +#define IHOST_PRG_BASE_34_ADDR_REG 0x1A +#define IHOST_PRG_BASE3_ADDR_SHIFT 0 +#define IHOST_PRG_BASE4_ADDR_SHIFT 32 +#endif + +#if defined(__STN_8815) /* __STN_8815 */ +#define IHOST_PRG_AHB_CONF_REG 0x8 +#define IHOST_PRG_AHB_LOCKED_SHIFT 0U +#define IHOST_PRG_AHB_PROT_SHIFT 1U + +#define AHB_LOCKED_ON (t_uint64)1 +#define AHB_LOCKED_OFF (t_uint64)0 + +#define AHB_PROT_USER (t_uint64)0 +#define AHB_PROT_PRIVILEGED (t_uint64)MASK_BIT0 +#define AHB_PROT_NONBUFFERABLE (t_uint64)0 +#define AHB_PROT_BUFFERABLE (t_uint64)MASK_BIT1 +#define AHB_PROT_NONCACHEABLE (t_uint64)0 +#define AHB_PROT_CACHEABLE (t_uint64)MASK_BIT2 + + +#define IHOST_DATA_AHB_CONF_REG 0x9 +#define IHOST_DATA_AHB_LOCKED_SHIFT 0U +#define IHOST_DATA_AHB_PROT_SHIFT 1U +#else /* def __STN_8820 or __STN_8500 */ +#define IHOST_STBUS_ID_CONF_REG 0x8 +#define SAA_STBUS_ID 176 /* = 0xB0 */ +#define SVA_STBUS_ID 4 /* = 0x4 */ +#define SIA_STBUS_ID 180 /* = 0xB4 */ + +#define IHOST_STBUF_CONF_REG 0x9 /* RESERVED */ +#endif /* __STN_8820 or __STN_8500 */ + +#define IHOST_DATA_EXT_BUS_BASE_REG 0xA +#define IHOST_DATA_EXT_BUS_BASE_16_SHIFT 32ULL +#define IHOST_DATA_EXT_BUS_BASE_24_SHIFT 0ULL + +#define IHOST_EXT_MMIO_BASE_DATA_EXT_BUS_TOP_REG 0xB +#define IHOST_EXT_MMIO_DATA_EXT_BUS_TOP_SHIFT 0ULL +#define IHOST_EXT_MMIO_BASE_ADDR_SHIFT 32ULL + +#define IHOST_DATA_EXT_BUS_BASE2_REG 0xC +#define IHOST_DATA_EXT_BUS_BASE2_16_SHIFT 32ULL +#define IHOST_DATA_EXT_BUS_BASE2_24_SHIFT 0ULL + +#if defined(__STN_8500) && (__STN_8500>10) + +#define IHOST_DATA_EXT_BUS_BASE3_REG 0x1B +#define IHOST_DATA_EXT_BUS_BASE3_16_SHIFT 32ULL +#define IHOST_DATA_EXT_BUS_BASE3_24_SHIFT 0ULL + +#define IHOST_DATA_EXT_BUS_BASE4_REG 0x1C +#define IHOST_DATA_EXT_BUS_BASE4_16_SHIFT 32ULL +#define IHOST_DATA_EXT_BUS_BASE4_24_SHIFT 0ULL + +#endif + +#define IHOST_ICACHE_STATE_REG 0xD +#define IHOST_ICACHE_STATE_RESET 0x0 +#define IHOST_ICACHE_STATE_INITAGL2 0x1 +#define IHOST_ICACHE_STATE_READY_TO_START 0x2 +#define IHOST_ICACHE_STATE_WAIT_FOR_MISS 0x3 +#define IHOST_ICACHE_STATE_FILLDATARAM0 0x4 +#define IHOST_ICACHE_STATE_FILLDATARAM1 0x5 +#define IHOST_ICACHE_STATE_FILLDATARAM2 0x6 +#define IHOST_ICACHE_STATE_FILLDATARAM3 0x7 +#define IHOST_ICACHE_STATE_FLUSH 0x8 +#define IHOST_ICACHE_STATE_FILL_INIT 0x9 +#define IHOST_ICACHE_STATE_FILL_LOOP 0xA +#define IHOST_ICACHE_STATE_FILL_LOOP0 0xB +#define IHOST_ICACHE_STATE_FILL_LOOP1 0xC +#define IHOST_ICACHE_STATE_FILL_LOOP2 0xD +#define IHOST_ICACHE_STATE_FILL_LOOP3 0xE +#define IHOST_ICACHE_STATE_FILL_END 0xF +#define IHOST_ICACHE_STATE_SPECIFIC_FLUSH_R 0x10 +#define IHOST_ICACHE_STATE_SPECIFIC_FLUSH_W 0x11 +#define IHOST_ICACHE_STATE_SPECIFIC_FLUSH_END 0x12 +#define IHOST_ICACHE_STATE_OTHERS 0x1F + +#define IHOST_EN_EXT_BUS_TIMEOUT_REG 0xE +#define IHOST_TIMEOUT_ENABLE 1ULL +#define IHOST_TIMEOUT_DISABLE 0ULL + +#define IHOST_DATA2_1624_XA_BASE_REG 0xF +#define IHOST_DATA2_24_XA_BASE_SHIFT 0ULL +#define IHOST_DATA2_16_XA_BASE_SHIFT 32ULL +#if defined(__STN_8500) && (__STN_8500>10) +#define IHOST_DATA3_24_XA_BASE_SHIFT 8ULL +#define IHOST_DATA3_16_XA_BASE_SHIFT 40ULL +#define IHOST_DATA4_24_XA_BASE_SHIFT 16ULL +#define IHOST_DATA4_16_XA_BASE_SHIFT 48ULL +#endif + +#define IHOST_PERFMETERS_MODE_REG 0x10 + +#if defined(__STN_8815) /* __STN_8815 */ +#define IHOST_EXT_MMIO_AHB_CONF_REG 0x11 +#define IHOST_EXT_MMIO_AHB_LOCKED_SHIFT 0U +#define IHOST_EXT_MMIO_AHB_PROT_SHIFT 1U +#else /* def __STN_8820 or __STN_8500 */ +#define IHOST_EXT_MMIO_STBS_CONF_REG 0x11 /* RESERVED */ +#endif /* __STN_8820 or __STN_8500 */ + +#define IHOST_PRG_BASE_SEL_REG 0x12 +#define IHOST_PRG_BASE_SEL_OFF (t_uint64)0 +#define IHOST_PRG_BASE_SEL_ON (t_uint64)1 + +#define IHOST_PRG_BASE2_ACTIV_REG 0x13 +#define IHOST_PRG_BASE2_ACTIV_OFF (t_uint64)0 +#if defined(__STN_8500) && (__STN_8500>10) +/* TODO : for the moment just divide mmdsp in fix 4 spaces */ + #define IHOST_PRG_BASE2_ACTIV_ON (t_uint64)((((t_uint64)0xf0000>>10)<<48) | (((t_uint64)0xe0000>>10)<<32) | (((t_uint64)0x70000>>10)<<16) | 1) +#else + #define IHOST_PRG_BASE2_ACTIV_ON (t_uint64)1 +#endif + +#define IHOST_DATA_EXT_BUS_TOP_16_24_REG 0x14 +#define IHOST_DATA_EXT_BUS_TOP_24_SHIFT 0ULL +#define IHOST_DATA_EXT_BUS_TOP_16_SHIFT 32ULL + +#define IHOST_DATA_TOP_16_24_CHK_REG 0x16 +#define IHOST_DATA_TOP_16_24_CHK_OFF (t_uint64)0 +#define IHOST_DATA_TOP_16_24_CHK_ON (t_uint64)1 + +#define IHOST_EXT_BUS_TOP2_16_24_REG 0x15 +#define IHOST_DATA_EXT_BUS_TOP2_24_SHIFT 0ULL +#define IHOST_DATA_EXT_BUS_TOP2_16_SHIFT 32ULL + +#if defined(__STN_8500) && (__STN_8500>10) + +#define IHOST_EXT_BUS_TOP3_16_24_REG 0x1D +#define IHOST_DATA_EXT_BUS_TOP3_24_SHIFT 0ULL +#define IHOST_DATA_EXT_BUS_TOP3_16_SHIFT 32ULL + +#define IHOST_EXT_BUS_TOP4_16_24_REG 0x1E +#define IHOST_DATA_EXT_BUS_TOP4_24_SHIFT 0ULL +#define IHOST_DATA_EXT_BUS_TOP4_16_SHIFT 32ULL + +#endif + +#define IHOST_DATA_BASE2_ACTIV_REG 0x17 +#define IHOST_DATA_BASE2_ACTIV_OFF (t_uint64)0 +#define IHOST_DATA_BASE2_ACTIV_ON (t_uint64)1 + +#define IHOST_INST_BURST_SZ_REG 0x18 +#define IHOST_INST_BURST_SZ_ALWAYS_1_LINE (t_uint64)0x0 +#define IHOST_INST_BURST_SZ_ALWAYS_2_LINES (t_uint64)0x1 +#define IHOST_INST_BURST_SZ_AUTO (t_uint64)0x2 /* 2 lines for SDRAM [0, 0xE0000[, 1 line for ESRAM [0xE0000, 0xFFFFF] */ + +#define IHOST_ICACHE_END_CLEAR_REG 0x19 +#define IHOST_ICACHE_START_CLEAR_REG IHOST_FILL_START_WAY_REG + +/* + * Definition of value of the ucmd register + */ +#define MMDSP_UCMD_WRITE 0 +#define MMDSP_UCMD_READ 4 +#define MMDSP_UCMD_CTRL_STATUS_ACCESS 0x10 // (MASK_BIT4 | !MASK_BIT3 | !MASK_BIT0) +#define MMDSP_UCMD_DECREMENT_ADDR MASK_BIT5 +#define MMDSP_UCMD_INCREMENT_ADDR MASK_BIT1 + +/* + * Definition of value of the ubkcmd register + */ +#define MMDSP_UBKCMD_EXT_CODE_MEM_ACCESS_ENABLE MASK_BIT3 +#define MMDSP_UBKCMD_EXT_CODE_MEM_ACCESS_DISABLE 0 + +/* + * Definition of value of the clockcmd register + */ +#define MMDSP_CLOCKCMD_STOP_CLOCK MASK_BIT0 +#define MMDSP_CLOCKCMD_START_CLOCK 0 + +/* + * Definition of macros used to access indirect addressed host register + */ +#define WRITE_INDIRECT_HOST_REG(pRegs, addr, value64) \ +{ \ + (pRegs)->host_reg.emul_uaddrl = addr; \ + (pRegs)->host_reg.emul_uaddrm = 0; \ + (pRegs)->host_reg.emul_uaddrh = 0; \ + (pRegs)->host_reg.emul_udata[0] = ((value64 >> 0ULL) & MASK_BYTE0); \ + (pRegs)->host_reg.emul_udata[1] = ((value64 >> 8ULL) & MASK_BYTE0); \ + (pRegs)->host_reg.emul_udata[2] = ((value64 >> 16ULL) & MASK_BYTE0); \ + (pRegs)->host_reg.emul_udata[3] = ((value64 >> 24ULL) & MASK_BYTE0); \ + (pRegs)->host_reg.emul_udata[4] = ((value64 >> 32ULL) & MASK_BYTE0); \ + (pRegs)->host_reg.emul_udata[5] = ((value64 >> 40ULL) & MASK_BYTE0); \ + (pRegs)->host_reg.emul_udata[6] = ((value64 >> 48ULL) & MASK_BYTE0); \ + (pRegs)->host_reg.emul_udata[7] = ((value64 >> 56ULL) & MASK_BYTE0); \ + (pRegs)->host_reg.emul_ucmd = (MMDSP_UCMD_CTRL_STATUS_ACCESS | MMDSP_UCMD_WRITE); \ +} + +#define READ_INDIRECT_HOST_REG(pRegs, addr, value64) \ +{ \ + (pRegs)->host_reg.emul_udata[0] = 0; \ + (pRegs)->host_reg.emul_udata[1] = 0; \ + (pRegs)->host_reg.emul_udata[2] = 0; \ + (pRegs)->host_reg.emul_udata[3] = 0; \ + (pRegs)->host_reg.emul_udata[4] = 0; \ + (pRegs)->host_reg.emul_udata[5] = 0; \ + (pRegs)->host_reg.emul_udata[6] = 0; \ + (pRegs)->host_reg.emul_udata[7] = 0; \ + (pRegs)->host_reg.emul_uaddrl = addr; \ + (pRegs)->host_reg.emul_uaddrm = 0; \ + (pRegs)->host_reg.emul_uaddrh = 0; \ + (pRegs)->host_reg.emul_ucmd = (MMDSP_UCMD_CTRL_STATUS_ACCESS | MMDSP_UCMD_READ); \ + value64 = (((t_uint64)((pRegs)->host_reg.emul_udata[0])) << 0ULL) | \ + (((t_uint64)((pRegs)->host_reg.emul_udata[1])) << 8ULL) | \ + (((t_uint64)((pRegs)->host_reg.emul_udata[2])) << 16ULL) | \ + (((t_uint64)((pRegs)->host_reg.emul_udata[3])) << 24ULL) | \ + (((t_uint64)((pRegs)->host_reg.emul_udata[4])) << 32ULL) | \ + (((t_uint64)((pRegs)->host_reg.emul_udata[5])) << 40ULL) | \ + (((t_uint64)((pRegs)->host_reg.emul_udata[6])) << 48ULL) | \ + (((t_uint64)((pRegs)->host_reg.emul_udata[7])) << 56ULL); \ +} + +/* Common type to handle 64-bit modulo field in 32-bit mode */ +typedef struct { + t_uint32 value; + t_uint32 dummy; +} t_mmdsp_field_32; + +typedef struct { + t_uint16 value; + t_uint16 dummy; +} t_mmdsp_field_16; + +/* DCache registers */ +#define DCACHE_MODE_ENABLE MASK_BIT0 +#define DCACHE_MODE_DISABLE 0 +#define DCACHE_MODE_DIVIDE_PER_2 MASK_BIT1 +#define DCACHE_MODE_DIVIDE_PER_4 MASK_BIT2 +#define DCACHE_MODE_CHECK_TAG_ENABLE MASK_BIT3 +#define DCACHE_MODE_CHECK_TAG_DISABLE 0 +#define DCACHE_MODE_FORCE_LOCK_MODE MASK_BIT4 +#define DCACHE_MODE_LOCK_BIT MASK_BIT5 + +#define DCACHE_CONTROL_PREFETCH_LINE MASK_BIT0 +#define DCACHE_CONTROL_NON_BLOCKING_REFILL 0 +#define DCACHE_CONTROL_FAST_READ_DISABLE MASK_BIT1 +#define DCACHE_CONTROL_FAST_READ_ENABLE 0 +#define DCACHE_CONTROL_ON_FLY_FILL_ACCESS_OFF MASK_BIT2 +#define DCACHE_CONTROL_ON_FLY_FILL_ACCESS_ON 0 +#define DCACHE_CONTROL_BURST_1_WRAP8 MASK_BIT3 +#define DCACHE_CONTROL_BURST_2_WRAP4 0 +#define DCACHE_CONTROL_NOT_USE_DATA_BUFFER MASK_BIT4 +#define DCACHE_CONTROL_USE_DATA_BUFFER 0 +#define DCACHE_CONTROL_WRITE_POSTING_ENABLE MASK_BIT5 +#define DCACHE_CONTROL_WRITE_POSTING_DISABLE 0 + +#define DCACHE_CMD_NOP 0 +#define DCACHE_CMD_DISCARD_WAY 2 //see Dcache_way reg +#define DCACHE_CMD_DISCARD_LINE 3 //see Dcache_line reg +#define DCACHE_CMD_FREE_WAY 4 //see Dcache_way reg +#define DCACHE_CMD_FREE_LINE 5 //see Dchache_line reg +#define DCACHE_CMD_FLUSH 7 + +#define DCACHE_STATUS_CURRENT_WAY_MASK (MASK_BIT2 | MASK_BIT1 | MASK_BIT0) +#define DCACHE_STATUS_TAG_HIT_MASK MASK_BIT3 +#define DCACHE_STATUS_TAG_LOCKED_MASK MASK_BIT4 +#define DCACHE_STATUS_PROTECTION_ERROR_MASK MASK_BIT5 + +#define DCACHE_CPTRSEL_COUNTER_1_MASK (MASK_BIT3 | MASK_BIT2 | MASK_BIT1 | MASK_BIT0) +#define DCACHE_CPTRSEL_COUNTER_1_SHIFT 0 +#define DCACHE_CPTRSEL_COUNTER_2_MASK (MASK_BIT7 | MASK_BIT6 | MASK_BIT5 | MASK_BIT4) +#define DCACHE_CPTRSEL_COUNTER_2_SHIFT 4 +#define DCACHE_CPTRSEL_COUNTER_3_MASK (MASK_BIT11 | MASK_BIT10 | MASK_BIT9 | MASK_BIT8) +#define DCACHE_CPTRSEL_COUNTER_3_SHIFT 8 +#define DCACHE_CPTRSEL_XBUS_ACCESS_TO_CACHE_RAM 1 +#define DCACHE_CPTRSEL_CACHE_HIT 2 +#define DCACHE_CPTRSEL_LINE_MATCH 3 +#define DCACHE_CPTRSEL_XBUS_WS 4 +#define DCACHE_CPTRSEL_EXTMEM_WS 5 +#define DCACHE_CPTRSEL_CACHE_READ 6 +#define DCACHE_CPTRSEL_CACHE_WRITE 7 +#define DCACHE_CPTRSEL_TAG_HIT_READ 8 +#define DCACHE_CPTRSEL_TAG_LOCKED_ACCESS 9 +#define DCACHE_CPTRSEL_TAG_MEM_READ_CYCLE 10 +#define DCACHE_CPTRSEL_TAG_MEM_WRITE_CYCLE 11 + + +typedef volatile struct { + t_uint16 padding_1[5]; + t_uint16 mode; + t_uint16 control; + t_uint16 way; + t_uint16 line; + t_uint16 command; + t_uint16 status; + t_uint16 cptr1l; + t_uint16 cptr1h; + t_uint16 cptr2l; + t_uint16 cptr2h; + t_uint16 cptr3l; + t_uint16 cptr3h; + t_uint16 cptrsel; + t_uint16 flush_base_lsb; /* only on STn8820 and STn8500 */ + t_uint16 flush_base_msb; /* only on STn8820 and STn8500 */ + t_uint16 flush_top_lsb; /* only on STn8820 and STn8500 */ + t_uint16 flush_top_msb; /* only on STn8820 and STn8500 */ + t_uint16 padding_2[10]; +} t_mmdsp_dcache_regs_16; + +typedef volatile struct { + t_uint32 padding_1[5]; + t_uint32 mode; + t_uint32 control; + t_uint32 way; + t_uint32 line; + t_uint32 command; + t_uint32 status; + t_uint32 cptr1l; + t_uint32 cptr1h; + t_uint32 cptr2l; + t_uint32 cptr2h; + t_uint32 cptr3l; + t_uint32 cptr3h; + t_uint32 cptrsel; + t_uint32 flush_base_lsb; /* only on STn8820 and STn8500 */ + t_uint32 flush_base_msb; /* only on STn8820 and STn8500 */ + t_uint32 flush_top_lsb; /* only on STn8820 and STn8500 */ + t_uint32 flush_top_msb; /* only on STn8820 and STn8500 */ + t_uint32 padding_2[10]; +} t_mmdsp_dcache_regs_32; + +/* TIMER Registers */ +typedef volatile struct { + t_mmdsp_field_16 timer_msb; + t_mmdsp_field_16 timer_lsb; +} t_mmdsp_timer_regs_16; + +typedef volatile struct { + t_mmdsp_field_32 timer_msb; + t_mmdsp_field_32 timer_lsb; +} t_mmdsp_timer_regs_32; + + +/* DMA interface Registers */ +typedef volatile struct { + t_uint16 arm_dma_sreq; /* dma0: 5e800, dma1: +0x20 ...*/ + t_uint16 arm_dma_breq; /* ... 5e802 */ + t_uint16 arm_dma_lsreq; /* ... 5e804 */ + t_uint16 arm_dma_lbreq; + t_uint16 arm_dma_maskit; + t_uint16 arm_dma_it; + t_uint16 arm_dma_auto; + t_uint16 arm_dma_lauto; + t_uint16 dma_reserved[8]; +} t_mmdsp_dma_if_regs_16; + +typedef volatile struct { + t_uint32 arm_dma_sreq; /* dma0: 3a800, dma1: +0x40 ...*/ + t_uint32 arm_dma_breq; /* ... 3a804 */ + t_uint32 arm_dma_lsreq; /* ... 3a808 */ + t_uint32 arm_dma_lbreq; + t_uint32 arm_dma_maskit; + t_uint32 arm_dma_it; + t_uint32 arm_dma_auto; + t_uint32 arm_dma_lauto; + t_uint32 dma_reserved[8]; +} t_mmdsp_dma_if_regs_32; + +/* MMDSP DMA controller Registers */ +typedef volatile struct { + t_uint16 dma_ctrl; /* dma0: 0x5d400, dma1: +0x10 ... */ + t_uint16 dma_int_base; /* ... 0x5d402 */ + t_uint16 dma_int_length; /* ... 0x5d404 */ + t_uint16 dma_ext_baseh; + t_uint16 dma_ext_basel; + t_uint16 dma_count; + t_uint16 dma_ext_length; + t_uint16 dma_it_status; +} t_mmdsp_dma_ctrl_regs_16; + +typedef volatile struct { + t_uint32 dma_ctrl; /* dma0: 0x3a800, dma1: +0x20 ... */ + t_uint32 dma_int_base; /* ... 0x3a804 */ + t_uint32 dma_int_length; /* ... 0x3a808 */ + t_uint32 dma_ext_baseh; + t_uint32 dma_ext_basel; + t_uint32 dma_count; + t_uint32 dma_ext_length; + t_uint32 dma_it_status; +} t_mmdsp_dma_ctrl_regs_32; + +/* IO registers */ +typedef volatile struct { + t_mmdsp_field_16 io_bit[MMDSP_NB_IO]; + t_mmdsp_field_16 io_lsb; + t_mmdsp_field_16 io_msb; + t_mmdsp_field_16 io_all; + t_mmdsp_field_16 io_en; +} t_mmdsp_io_regs_16; + +typedef volatile struct { + t_mmdsp_field_32 io_bit[MMDSP_NB_IO]; + t_mmdsp_field_32 io_lsb; + t_mmdsp_field_32 io_msb; + t_mmdsp_field_32 io_all; + t_mmdsp_field_32 io_en; +} t_mmdsp_io_regs_32; + +/* HOST Registers bit mapping */ +#define HOST_GATEDCLK_ITREMAP MASK_BIT0 +#define HOST_GATEDCLK_SYSDMA MASK_BIT1 +#define HOST_GATEDCLK_INTEG_REGS MASK_BIT2 +#define HOST_GATEDCLK_TIMER_GPIO MASK_BIT3 +#define HOST_GATEDCLK_XBUSDMA MASK_BIT4 +#define HOST_GATEDCLK_STACKCTRL MASK_BIT5 +#define HOST_GATEDCLK_ITC MASK_BIT6 + +/* Only for STn8820 and STn8500 */ +#define HOST_PWR_DBG_MODE MASK_BIT0 +#define HOST_PWR_DC_STATUS (MASK_BIT1 | MASK_BIT2 | MASK_BIT3 | MASK_BIT4 | MASK_BIT5) +#define HOST_PWR_DE_STATUS MASK_BIT6 +#define HOST_PWR_STOV_STATUS MASK_BIT7 + +/* HOST Registers */ +typedef volatile struct { + t_uint16 ident; /*0x...60000*/ + t_uint16 identx[4]; /*0x...60002..8*/ + t_uint16 r5; /*0x...6000a*/ + t_uint16 r6; /*0x...6000c*/ + t_uint16 inte[2]; /*0x...6000e..10*/ + t_uint16 intx[2]; /*0x...60012..14*/ + t_uint16 int_ris[2]; /*0x...60016..18*/ + t_uint16 intpol; /*0x...6001a*/ + t_uint16 pwr; /*0x...6001c*/ /* only on STn8820 and STn8500 */ + t_uint16 gatedclk; /*0x...6001e*/ + t_uint16 softreset; /*0x...60020*/ + t_uint16 int_icr[2]; /*0x...60022..24*/ + t_uint16 cmd[4]; /*0x...60026..2c*/ + t_uint16 RESERVED4; + t_uint16 int_mis0; /*0x...60030*/ + t_uint16 RESERVED5; + t_uint16 RESERVED6; + t_uint16 RESERVED7; + t_uint16 i2cdiv; /*0x...60038*/ + t_uint16 int_mis1; /*0x...6003a*/ + t_uint16 RESERVED8; + t_uint16 RESERVED9; + t_uint16 emul_udata[8]; /*0x...60040..4e*/ + t_uint16 emul_uaddrl; /*0x...60050*/ + t_uint16 emul_uaddrm; /*0x...60052*/ + t_uint16 emul_ucmd; /*0x...60054*/ + t_uint16 emul_ubkcmd; /*0x...60056*/ + t_uint16 emul_bk2addl; /*0x...60058*/ + t_uint16 emul_bk2addm; /*0x...6005a*/ + t_uint16 emul_bk2addh; /*0x...6005c*/ + t_uint16 emul_mdata[3]; /*0x...6005e..62*/ + t_uint16 emul_maddl; /*0x...60064*/ + t_uint16 emul_maddm; /*0x...60066*/ + t_uint16 emul_mcmd; /*0x...60068*/ + t_uint16 emul_maddh; /*0x...6006a*/ + t_uint16 emul_uaddrh; /*0x...6006c*/ + t_uint16 emul_bk_eql; /*0x...6006e*/ + t_uint16 emul_bk_eqh; /*0x...60070*/ + t_uint16 emul_bk_combi; /*0x...60072*/ + t_uint16 emul_clockcmd; /*0x...60074*/ + t_uint16 emul_stepcmd; /*0x...60076*/ + t_uint16 emul_scanreg; /*0x...60078*/ + t_uint16 emul_breakcountl; /*0x...6007a*/ + t_uint16 emul_breakcounth; /*0x...6007c*/ + t_uint16 emul_forcescan; /*0x...6007e*/ + t_uint16 user_area[(0x200 - 0x80)>>1]; +} t_mmdsp_host_regs_16; + +typedef volatile struct { + t_uint32 ident; /*0x...60000*/ + t_uint32 identx[4]; /*0x...60004..10*/ + t_uint32 r5; /*0x...60014*/ + t_uint32 r6; /*0x...60018*/ + t_uint32 inte[2]; /*0x...6001c..20*/ + t_uint32 intx[2]; /*0x...60024..28*/ + t_uint32 int_ris[2]; /*0x...6002c..30*/ + t_uint32 intpol; /*0x...60034*/ + t_uint32 pwr; /*0x...60038*/ /* only on STn8820 and STn8500 */ + t_uint32 gatedclk; /*0x...6003c*/ + t_uint32 softreset; /*0x...60040*/ + t_uint32 int_icr[2]; /*0x...60044..48*/ + t_uint32 cmd[4]; /*0x...6004c..58*/ + t_uint32 RESERVED4; + t_uint32 int_mis0; /*0x...60060*/ + t_uint32 RESERVED5; + t_uint32 RESERVED6; + t_uint32 RESERVED7; + t_uint32 i2cdiv; /*0x...60070*/ + t_uint32 int_mis1; /*0x...60074*/ + t_uint32 RESERVED8; + t_uint32 RESERVED9; + t_uint32 emul_udata[8]; /*0x...60080..9c*/ + t_uint32 emul_uaddrl; /*0x...600a0*/ + t_uint32 emul_uaddrm; /*0x...600a4*/ + t_uint32 emul_ucmd; /*0x...600a8*/ + t_uint32 emul_ubkcmd; /*0x...600ac*/ + t_uint32 emul_bk2addl; /*0x...600b0*/ + t_uint32 emul_bk2addm; /*0x...600b4*/ + t_uint32 emul_bk2addh; /*0x...600b8*/ + t_uint32 emul_mdata[3]; /*0x...600bc..c4*/ + t_uint32 emul_maddl; /*0x...600c8*/ + t_uint32 emul_maddm; /*0x...600cc*/ + t_uint32 emul_mcmd; /*0x...600d0*/ + t_uint32 emul_maddh; /*0x...600d4*/ + t_uint32 emul_uaddrh; /*0x...600d8*/ + t_uint32 emul_bk_eql; /*0x...600dc*/ + t_uint32 emul_bk_eqh; /*0x...600e0*/ + t_uint32 emul_bk_combi; /*0x...600e4*/ + t_uint32 emul_clockcmd; /*0x...600e8*/ + t_uint32 emul_stepcmd; /*0x...600ec*/ + t_uint32 emul_scanreg; /*0x...600f0*/ + t_uint32 emul_breakcountl; /*0x...600f4*/ + t_uint32 emul_breakcounth; /*0x...600f8*/ + t_uint32 emul_forcescan; /*0x...600fc*/ + t_uint32 user_area[(0x400 - 0x100)>>2]; +} t_mmdsp_host_regs_32; + +/* MMIO blocks */ +#if defined(__STN_8820) || defined(__STN_8500) +typedef volatile struct { + t_uint16 RESERVED1[(0xD400-0x8000)>>1]; + + t_mmdsp_dma_ctrl_regs_16 dma_ctrl[MMDSP_NB_DMA_CTRL]; + + t_uint16 RESERVED2[(0xD800-0xD440)>>1]; + + t_mmdsp_dcache_regs_16 dcache; + + t_uint16 RESERVED3[(0xE000-0xD840)>>1]; + + t_mmdsp_io_regs_16 io; + + t_uint16 RESERVED4[(0x60-0x50)>>1]; + + t_mmdsp_timer_regs_16 timer[MMDSP_NB_TIMER]; + + t_uint16 RESERVED5[(0x410-0x78)>>1]; + + t_mmdsp_field_16 sem[MMDSP_NB_BIT_SEM]; + + t_uint16 RESERVED6[(0x450-0x430)>>1]; + + t_mmdsp_field_16 ipen; + t_uint16 itip_0; + t_uint16 itip_1; + t_uint16 itip_2; + t_uint16 itip_3; + t_uint16 itop_0; + t_uint16 itop_1; + t_uint16 itop_2; + t_uint16 itop_3; + t_uint16 RESERVED7[(0x8a-0x64)>>1]; + t_uint16 itip_4; + t_uint16 itop_4; + + t_uint16 RESERVED8[(0x7e0-0x48e)>>1]; + + t_mmdsp_field_16 id[4]; + t_mmdsp_field_16 idp[4]; + + t_mmdsp_dma_if_regs_16 dma_if[MMDSP_NB_DMA_IF]; + + t_uint16 RESERVED9[(0xC00-0x900)>>1]; + + t_mmdsp_field_16 emu_unit_maskit; + t_mmdsp_field_16 RESERVED[3]; + t_mmdsp_field_16 config_data_mem; + t_mmdsp_field_16 compatibility; + + t_uint16 RESERVED10[(0xF000-0xEC18)>>1]; + + t_uint16 stbus_if_config; + t_uint16 stbus_if_mode; + t_uint16 stbus_if_status; + t_uint16 stbus_if_security; + t_uint16 stbus_if_flush; + t_uint16 stbus_reserved; + t_uint16 stbus_if_priority; + t_uint16 stbus_msb_attribut; + + t_uint16 RESERVED11[(0xFC00-0xF010)>>1]; + + t_mmdsp_field_16 itremap_reg[MMDSP_NB_ITREMAP_REG]; + t_mmdsp_field_16 itmsk_l_reg; + t_mmdsp_field_16 itmsk_h_reg; + + t_uint16 RESERVED12[(0xfc9c - 0xfc88)>>1]; + + t_mmdsp_field_16 itmemo_l_reg; + t_mmdsp_field_16 itmeme_h_reg; + + t_uint16 RESERVED13[(0xfd00 - 0xfca4)>>1]; + + t_mmdsp_field_16 itremap1_reg[MMDSP_NB_ITREMAP_REG]; + + t_uint16 RESERVED14[(0x60000 - 0x5fd80)>>1]; +} t_mmdsp_mmio_regs_16; + + +typedef volatile struct { + t_uint32 RESERVED1[(0xa800)>>2]; + + t_mmdsp_dma_ctrl_regs_32 dma_ctrl[MMDSP_NB_DMA_CTRL]; + + t_uint32 RESERVED2[(0xb000-0xa880)>>2]; + + t_mmdsp_dcache_regs_32 dcache; + + t_uint32 RESERVED3[(0xc000-0xb080)>>2]; + + t_mmdsp_io_regs_32 io; + + t_uint32 RESERVED4[(0xc0-0xa0)>>2]; + + t_mmdsp_timer_regs_32 timer[MMDSP_NB_TIMER]; + + t_uint32 RESERVED5[(0x820-0x0f0)>>2]; + + t_mmdsp_field_32 sem[MMDSP_NB_BIT_SEM]; + + t_uint32 RESERVED6[(0x8a0-0x860)>>2]; + + t_mmdsp_field_32 ipen; + t_uint32 itip_0; + t_uint32 itip_1; + t_uint32 itip_2; + t_uint32 itip_3; + t_uint32 itop_0; + t_uint32 itop_1; + t_uint32 itop_2; + t_uint32 itop_3; + t_uint32 RESERVED7[(0x914-0x8c8)>>2]; + t_uint32 itip_4; + t_uint32 itop_4; + + t_uint32 RESERVED8[(0xcfc0-0xc91c)>>2]; + + t_mmdsp_field_32 id[4]; + t_mmdsp_field_32 idp[4]; + + t_mmdsp_dma_if_regs_32 dma_if[MMDSP_NB_DMA_IF]; + + t_uint32 RESERVED9[(0x800-0x200)>>2]; + + t_mmdsp_field_32 emu_unit_maskit; + t_mmdsp_field_32 RESERVED[3]; + t_mmdsp_field_32 config_data_mem; + t_mmdsp_field_32 compatibility; + + t_uint32 RESERVED10[(0xE000-0xD830)>>2]; + + t_uint32 stbus_if_config; + t_uint32 stbus_if_mode; + t_uint32 stbus_if_status; + t_uint32 stbus_if_security; + t_uint32 stbus_if_flush; + t_uint32 stbus_reserved; + t_uint32 stbus_if_priority; + t_uint32 stbus_msb_attribut; + + t_uint32 RESERVED11[(0xF800-0xE020)>>2]; + + t_mmdsp_field_32 itremap_reg[MMDSP_NB_ITREMAP_REG]; + t_mmdsp_field_32 itmsk_l_reg; + t_mmdsp_field_32 itmsk_h_reg; + + t_uint32 RESERVED12[(0xf938 - 0xf910)>>2]; + + t_mmdsp_field_32 itmemo_l_reg; + t_mmdsp_field_32 itmeme_h_reg; + + t_uint32 RESERVED13[(0xfa00 - 0xf948)>>2]; + + t_mmdsp_field_32 itremap1_reg[MMDSP_NB_ITREMAP_REG]; + + t_uint32 RESERVED14[(0x40000 - 0x3fb00)>>2]; +} t_mmdsp_mmio_regs_32; +#endif /* __STN_8820 or __STN_8500 */ + +#ifdef __STN_8815 +typedef volatile struct { + t_uint16 RESERVED1[(0xD400-0x8000)>>1]; + + t_mmdsp_dma_ctrl_regs_16 dma_ctrl[MMDSP_NB_DMA_CTRL]; + + t_uint16 RESERVED2[(0xD800-0xD440)>>1]; + + t_mmdsp_dcache_regs_16 dcache; + + t_uint16 RESERVED3[(0xE000-0xD840)>>1]; + + t_mmdsp_io_regs_16 io; + + t_uint16 RESERVED4[(0x60-0x50)>>1]; + + t_mmdsp_timer_regs_16 timer[MMDSP_NB_TIMER]; + + t_uint16 RESERVED5[(0x410-0x78)>>1]; + + t_mmdsp_field_16 sem[MMDSP_NB_BIT_SEM]; + + t_uint16 RESERVED6[(0x450-0x430)>>1]; + + t_mmdsp_field_16 ipen; + t_uint16 itip_0; + t_uint16 itip_1; + t_uint16 itip_2; + t_uint16 itip_3; + t_uint16 itop_0; + t_uint16 itop_1; + t_uint16 itop_2; + t_uint16 itop_3; + t_uint16 RESERVED7[(0x8a-0x64)>>1]; + t_uint16 itip_4; + t_uint16 itop_4; + + t_uint16 RESERVED8[(0x7e0-0x48e)>>1]; + + t_mmdsp_field_16 id[4]; + t_mmdsp_field_16 idp[4]; + + t_mmdsp_dma_if_regs_16 dma_if[MMDSP_NB_DMA_IF]; + + t_uint16 RESERVED9[(0xC00-0x900)>>1]; + + t_mmdsp_field_16 emu_unit_maskit; + t_mmdsp_field_16 RESERVED[3]; + t_mmdsp_field_16 config_data_mem; + t_mmdsp_field_16 compatibility; + + t_uint16 RESERVED10[(0xF000-0xEC18)>>1]; + + t_uint16 ahb_if_config; + t_uint16 ahb_if_mode; + t_uint16 ahb_if_status; + t_uint16 ahb_if_security; + t_uint16 ahb_if_flush; + + t_uint16 RESERVED11[(0xFC00-0xF00A)>>1]; + + t_mmdsp_field_16 itremap_reg[MMDSP_NB_ITREMAP_REG]; + t_mmdsp_field_16 itmsk_l_reg; + t_mmdsp_field_16 itmsk_h_reg; + + t_uint16 RESERVED12[(0xfc9c - 0xfc88)>>1]; + + t_mmdsp_field_16 itmemo_l_reg; + t_mmdsp_field_16 itmeme_h_reg; + + t_uint16 RESERVED13[(0xfd00 - 0xfca4)>>1]; + + t_mmdsp_field_16 itremap1_reg[MMDSP_NB_ITREMAP_REG]; + + t_uint16 RESERVED14[(0x60000 - 0x5fd80)>>1]; +} t_mmdsp_mmio_regs_16; + + +typedef volatile struct { + t_uint32 RESERVED1[(0xa800)>>2]; + + t_mmdsp_dma_ctrl_regs_32 dma_ctrl[MMDSP_NB_DMA_CTRL]; + + t_uint32 RESERVED2[(0xb000-0xa880)>>2]; + + t_mmdsp_dcache_regs_32 dcache; + + t_uint32 RESERVED3[(0xc000-0xb080)>>2]; + + t_mmdsp_io_regs_32 io; + + t_uint32 RESERVED4[(0xc0-0xa0)>>2]; + + t_mmdsp_timer_regs_32 timer[MMDSP_NB_TIMER]; + + t_uint32 RESERVED5[(0x820-0x0f0)>>2]; + + t_mmdsp_field_32 sem[MMDSP_NB_BIT_SEM]; + + t_uint32 RESERVED6[(0x8a0-0x860)>>2]; + + t_mmdsp_field_32 ipen; + t_uint32 itip_0; + t_uint32 itip_1; + t_uint32 itip_2; + t_uint32 itip_3; + t_uint32 itop_0; + t_uint32 itop_1; + t_uint32 itop_2; + t_uint32 itop_3; + t_uint32 RESERVED7[(0x914-0x8c8)>>2]; + t_uint32 itip_4; + t_uint32 itop_4; + + t_uint32 RESERVED8[(0xcfc0-0xc91c)>>2]; + + t_mmdsp_field_32 id[4]; + t_mmdsp_field_32 idp[4]; + + t_mmdsp_dma_if_regs_32 dma_if[MMDSP_NB_DMA_IF]; + + t_uint32 RESERVED9[(0x800-0x200)>>2]; + + t_mmdsp_field_32 emu_unit_maskit; + t_mmdsp_field_32 RESERVED[3]; + t_mmdsp_field_32 config_data_mem; + t_mmdsp_field_32 compatibility; + + t_uint32 RESERVED10[(0xE000-0xD830)>>2]; + + t_uint32 ahb_if_config; + t_uint32 ahb_if_mode; + t_uint32 ahb_if_status; + t_uint32 ahb_if_security; + t_uint32 ahb_if_flush; + + t_uint32 RESERVED11[(0xF800-0xE014)>>2]; + + t_mmdsp_field_32 itremap_reg[MMDSP_NB_ITREMAP_REG]; + t_mmdsp_field_32 itmsk_l_reg; + t_mmdsp_field_32 itmsk_h_reg; + + t_uint32 RESERVED12[(0xf938 - 0xf910)>>2]; + + t_mmdsp_field_32 itmemo_l_reg; + t_mmdsp_field_32 itmeme_h_reg; + + t_uint32 RESERVED13[(0xfa00 - 0xf948)>>2]; + + t_mmdsp_field_32 itremap1_reg[MMDSP_NB_ITREMAP_REG]; + + t_uint32 RESERVED14[(0x40000 - 0x3fb00)>>2]; +} t_mmdsp_mmio_regs_32; +#endif /* __STN_8815 */ + +/* Smart xx Accelerator memory map */ +typedef volatile struct { + t_uint32 mem24[MMDSP_NB_BLOCK_RAM*MMDSP_RAM_BLOCK_SIZE]; /* 0x0000 -> 0x20000 */ + + t_uint32 RESERVED1[(0x30000 - 0x20000)>>2]; + + t_mmdsp_mmio_regs_32 mmio_32; + + t_uint16 mem16[MMDSP_NB_BLOCK_RAM*MMDSP_RAM_BLOCK_SIZE]; /* 0x40000 -> 0x50000 */ + + t_uint32 RESERVED2[(0x58000 - 0x50000)>>2]; + + t_mmdsp_mmio_regs_16 mmio_16; + + t_mmdsp_host_regs_16 host_reg; + /* + union host_reg { + t_mmdsp_host_regs_16 reg16; + t_mmdsp_host_regs_32 reg32; + }; + */ +} t_mmdsp_hw_regs; + +#endif // __INC_MMDSP_HWP_H diff --git a/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_macros.h b/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_macros.h new file mode 100644 index 00000000000..b8911d27609 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_macros.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef __INC_MMDSP_DSP_MACROS +#define __INC_MMDSP_DSP_MACROS + +#include <cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h> + +#define MMDSP_ENABLE_WRITE_POSTING(pRegs) \ +{ \ + (pRegs)->mmio_16.dcache.control |= DCACHE_CONTROL_WRITE_POSTING_ENABLE; \ +} + +#define MMDSP_FLUSH_DCACHE(pRegs) \ +{ /* Today, only full cache flush (clear all the ways) */ \ + (pRegs)->mmio_16.dcache.command = DCACHE_CMD_FLUSH; \ +} + +#define MMDSP_FLUSH_DCACHE_BY_SERVICE(pRegs, startAddr, endAddr) + +#define MMDSP_FLUSH_ICACHE(pRegs) \ +{ /* Flush the Instruction cache */ \ + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_ICACHE_FLUSH_REG, (IHOST_ICACHE_FLUSH_ALL_ENTRIES_CMD | IHOST_ICACHE_FLUSH_CMD_ENABLE)); \ +} + +#ifndef __STN_8810 +#define MMDSP_FLUSH_ICACHE_BY_SERVICE(pRegs, startAddr, endAddr) \ +{ /* Flush the Instruction cache by service */ \ + /*t_uint64 start_clear_addr = startAddr & ~(MMDSP_ICACHE_LINE_SIZE_IN_INST - 1);*/ \ + t_uint64 start_clear_addr = (startAddr)>>2; \ + t_uint64 end_clear_addr = ((endAddr) + MMDSP_ICACHE_LINE_SIZE_IN_INST) & ~(MMDSP_ICACHE_LINE_SIZE_IN_INST - 1); \ + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_ICACHE_START_CLEAR_REG, start_clear_addr); \ + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_ICACHE_END_CLEAR_REG, end_clear_addr); \ + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_ICACHE_FLUSH_REG, (IHOST_ICACHE_FLUSH_BY_SERVICE | IHOST_ICACHE_FLUSH_CMD_ENABLE)); \ +} +#else +#define MMDSP_FLUSH_ICACHE_BY_SERVICE(pRegs, startAddr, endAddr) {(void)pRegs; (void)startAddr; (void)endAddr; } +#endif + +#define MMDSP_RESET_CORE(pRegs) \ +{ /* Assert DSP core soft reset */ \ + (pRegs)->host_reg.softreset = 1; \ +} + +#define MMDSP_START_CORE(pRegs) \ +{ \ + /* Enable external memory access (set bit 3 of ubkcmd) */ \ + (pRegs)->host_reg.emul_ubkcmd |= MMDSP_UBKCMD_EXT_CODE_MEM_ACCESS_ENABLE; \ + \ + /* Start core clock */ \ + (pRegs)->host_reg.emul_clockcmd = MMDSP_CLOCKCMD_START_CLOCK; \ +} + +#define MMDSP_STOP_CORE(pRegs) \ +{ \ + /* Disable external memory access (reset bit 3 of ubkcmd) */ \ + (pRegs)->host_reg.emul_ubkcmd = MMDSP_UBKCMD_EXT_CODE_MEM_ACCESS_DISABLE; \ + \ + /* Stop core clock */ \ + (pRegs)->host_reg.emul_clockcmd = MMDSP_CLOCKCMD_STOP_CLOCK; \ +} + +#define MMDSP_ASSERT_IRQ(pRegs, irqNum) \ +{ \ + (pRegs)->host_reg.cmd[irqNum] = 1; \ +} + +#define MMDSP_ACKNOWLEDGE_IRQ(pRegs, irqNum) \ +{ \ + volatile t_uint16 dummy; \ + dummy =(pRegs)->host_reg.intx[irqNum]; \ +} + +#define MMDSP_WRITE_XWORD(pRegs, offset, value) \ +{ \ + (pRegs)->mem24[offset] = value; \ +} + +#define MMDSP_READ_XWORD(pRegs, offset) (pRegs)->mem24[offset] + +#endif /* __INC_MMDSP_DSP_MACROS */ diff --git a/drivers/staging/nmf-cm/cm/engine/dsp/src/dsp.c b/drivers/staging/nmf-cm/cm/engine/dsp/src/dsp.c new file mode 100644 index 00000000000..7646f6301de --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/dsp/src/dsp.c @@ -0,0 +1,1081 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/dsp/mmdsp/inc/mmdsp_macros.h> + +#include <cm/engine/memory/inc/domain.h> +#include <cm/engine/semaphores/inc/semaphores.h> +#include <cm/engine/power_mgt/inc/power.h> +#include <cm/engine/memory/inc/migration.h> +#include <cm/engine/trace/inc/trace.h> + +#include <share/inc/nomadik_mapping.h> + +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> +#include <cm/engine/component/inc/component_type.h> + +static t_dsp_allocator_desc esramDesc; +static t_dsp_desc mpcDesc[NB_CORE_IDS]; +static t_mmdsp_hw_regs *pMmdspRegs[NB_CORE_IDS]; + +struct s_base_descr +{ + t_uint32 startAddress[2 /* DSP16 = 0, DSP24 = 1*/]; + t_dsp_segment_type segmentType; +}; + +#if defined(__STN_8500) && (__STN_8500 > 10) + +#define DATA_BASE_NUMBER 4 + +// In bytes +#define SDRAM_CODE_SPACE_SPLIT 0x8000 +#define ESRAM_CODE_SPACE_SPLIT 0x4000 +#define SDRAM_DATA_SPACE_SPLIT 0x40000 // This is the modulo constraint of mmdsp +#define ESRAM_DATA_SPACE_SPLIT 0x40000 + +// In MMDSP word +static const struct s_base_descr DATA_ADDRESS_BASE[DATA_BASE_NUMBER + 1 /* For guard */] = { + {{SDRAMMEM16_BASE_ADDR, SDRAMMEM24_BASE_ADDR}, SDRAM_DATA_EE}, + {{SDRAMMEM16_BASE_ADDR + (SDRAM_DATA_SPACE_SPLIT / 2), SDRAMMEM24_BASE_ADDR + (SDRAM_DATA_SPACE_SPLIT / 4)}, SDRAM_DATA_USER}, + {{ESRAMMEM16_BASE_ADDR, ESRAMMEM24_BASE_ADDR}, ESRAM_DATA_EE}, + {{ESRAMMEM16_BASE_ADDR + (ESRAM_DATA_SPACE_SPLIT / 2), ESRAMMEM24_BASE_ADDR + (ESRAM_DATA_SPACE_SPLIT / 4)}, ESRAM_DATA_USER}, + {{MMIO_BASE_ADDR, SDRAMMEM16_BASE_ADDR}, NB_DSP_SEGMENT_TYPE /* Not used*/} +}; + +#else + +#define DATA_BASE_NUMBER 2 + +// In MMDSP word +static const struct s_base_descr DATA_ADDRESS_BASE[DATA_BASE_NUMBER + 1 /* For guard */] = { + {{SDRAMMEM16_BASE_ADDR, SDRAMMEM24_BASE_ADDR}, SDRAM_DATA_EE}, + {{ESRAMMEM16_BASE_ADDR, ESRAMMEM24_BASE_ADDR}, ESRAM_DATA_EE}, + {{MMIO_BASE_ADDR, SDRAMMEM16_BASE_ADDR}, NB_DSP_SEGMENT_TYPE /* Not used*/} +}; + +#endif + +#if defined(__STN_8500) && (__STN_8500 > 10) +// In word +static const t_uint32 CODE_ADDRESS_BASE[4] = { + SDRAMTEXT_BASE_ADDR, + SDRAMTEXT_BASE_ADDR + (SDRAM_CODE_SPACE_SPLIT / 8), + ESRAMTEXT_BASE_ADDR, + ESRAMTEXT_BASE_ADDR + (ESRAM_CODE_SPACE_SPLIT / 8) +}; +#endif + +static void arm_Init(void); +static t_cm_error mmdsp_Init(const t_cm_system_address *dspSystemAddr, + t_uint8 nbXramBlocks, t_uint8 nbYramBlocks, + t_dsp_allocator_desc *sdramCodeDesc, + t_dsp_allocator_desc *sdramDataDesc, + t_cm_domain_id eeDomain, + t_dsp_desc *pDspDesc, + t_mmdsp_hw_regs **pRegs); +static t_cm_error mmdsp_Configure(t_nmf_core_id coreId, t_mmdsp_hw_regs *pRegs, const t_dsp_desc *pDspDesc); +static t_cm_error mmdsp_ConfigureAfterBoot(t_nmf_core_id coreId, t_uint8 nbXramBlocks, t_uint8 nbYramBlocks); +static void cm_DSP_SEM_Init(t_nmf_core_id coreId); + +PUBLIC const t_dsp_desc* cm_DSP_GetState(t_nmf_core_id coreId) +{ + return &mpcDesc[coreId]; +} +PUBLIC void cm_DSP_SetStatePanic(t_nmf_core_id coreId) +{ + mpcDesc[coreId].state = MPC_STATE_PANIC; +} + +PUBLIC void cm_DSP_Init(const t_nmf_memory_segment *pEsramDesc) +{ + t_nmf_core_id coreId; + int i; + + /* Create esram desc */ + esramDesc.allocDesc = cm_MM_CreateAllocator(pEsramDesc->size, 0, "esram"); + esramDesc.baseAddress = pEsramDesc->systemAddr; + esramDesc.referenceCounter = 1; // Don't free it with destroy mechanism + + /* Create ARM */ + arm_Init(); + + mpcDesc[ARM_CORE_ID].state = MPC_STATE_BOOTED; + + /* Reset MPC configuration */ + for (coreId = FIRST_MPC_ID; coreId <= LAST_CORE_ID; coreId++) + { + mpcDesc[coreId].state = MPC_STATE_UNCONFIGURED; + + for(i = 0; i < NB_DSP_MEMORY_TYPE; i++) + mpcDesc[coreId].allocator[i] = NULL; + } + +} + +PUBLIC void cm_DSP_Destroy(void) +{ + t_nmf_core_id coreId; + int i; + + for (coreId = ARM_CORE_ID; coreId <= LAST_CORE_ID; coreId++) + { + for(i = 0; i < NB_DSP_MEMORY_TYPE; i++) + { + if (mpcDesc[coreId].allocator[i] != NULL) + { + if(--mpcDesc[coreId].allocator[i]->referenceCounter == 0) + { + cm_MM_DeleteAllocator(mpcDesc[coreId].allocator[i]->allocDesc); + + OSAL_Free(mpcDesc[coreId].allocator[i]); + } + } + } + } + + cm_MM_DeleteAllocator(esramDesc.allocDesc); +} + + +PUBLIC t_cm_error cm_DSP_Add(t_nmf_core_id coreId, + t_uint8 nbYramBanks, + const t_cm_system_address *pDspMapDesc, + const t_cm_domain_id eeDomain, + t_dsp_allocator_desc *sdramCodeAllocDesc, + t_dsp_allocator_desc *sdramDataAllocDesc) +{ + t_cm_error error; + + /* checking nbYramBanks is valid */ + if (nbYramBanks >= SxA_NB_BLOCK_RAM) + return CM_MPC_INVALID_CONFIGURATION; + + if((error = cm_DM_CheckDomain(eeDomain, DOMAIN_NORMAL)) != CM_OK) + return error; + + mpcDesc[coreId].domainEE = eeDomain; + mpcDesc[coreId].nbYramBank = nbYramBanks; + mpcDesc[coreId].state = MPC_STATE_BOOTABLE; + + return mmdsp_Init( + pDspMapDesc, + SxA_NB_BLOCK_RAM, /* nb of data tcm bank minus one (reserved for cache) */ + nbYramBanks, + sdramCodeAllocDesc, + sdramDataAllocDesc, + eeDomain, + &mpcDesc[coreId], + &pMmdspRegs[coreId] + ); +} + +PUBLIC t_cm_error cm_DSP_Boot(t_nmf_core_id coreId) +{ + t_cm_error error; + + // Enable the associated power domain + if((error = cm_PWR_EnableMPC(MPC_PWR_CLOCK, coreId)) != CM_OK) + return error; + + cm_SEM_PowerOn[coreId](coreId); + + if((error = mmdsp_Configure( + coreId, + pMmdspRegs[coreId], + &mpcDesc[coreId])) != CM_OK) + { + cm_PWR_DisableMPC(MPC_PWR_CLOCK, coreId); + } + + // Put it in auto idle mode ; it's the default in Step 2 of power implementation + if((error = cm_PWR_EnableMPC(MPC_PWR_AUTOIDLE, coreId)) != CM_OK) + return error; + + return error; +} + +/* + * This method is required since MMDSP C bootstrap set some value that must be set differently !!! + */ +PUBLIC void cm_DSP_ConfigureAfterBoot(t_nmf_core_id coreId) +{ + mpcDesc[coreId].state = MPC_STATE_BOOTED; + + mmdsp_ConfigureAfterBoot(coreId, SxA_NB_BLOCK_RAM, mpcDesc[coreId].nbYramBank); + + cm_DSP_SEM_Init(coreId); +} + +PUBLIC void cm_DSP_Stop(t_nmf_core_id coreId) +{ + MMDSP_STOP_CORE(pMmdspRegs[coreId]); + + { + volatile t_uint32 loopme = 0xfff; + while(loopme--) ; + } +} + +PUBLIC void cm_DSP_Start(t_nmf_core_id coreId) +{ + MMDSP_START_CORE(pMmdspRegs[coreId]); + + { + volatile t_uint32 loopme = 0xfff; + while(loopme--) ; + } +} + +PUBLIC void cm_DSP_Shutdown(t_nmf_core_id coreId) +{ + MMDSP_FLUSH_DCACHE(pMmdspRegs[coreId]); + MMDSP_FLUSH_ICACHE(pMmdspRegs[coreId]); + + // Due to a hardware bug that breaks MTU when DSP are powered off, don't do that + // on mop500_ed for now +#if !defined(__STN_8500) || (__STN_8500 > 10) + MMDSP_RESET_CORE(pMmdspRegs[coreId]); + { + volatile t_uint32 loopme = 0xfff; + while(loopme--) ; + } + MMDSP_STOP_CORE(pMmdspRegs[coreId]); + { + volatile t_uint32 loopme = 0xfff; + while(loopme--) ; + } +#endif + + mpcDesc[coreId].state = MPC_STATE_BOOTABLE; + + cm_SEM_PowerOff[coreId](coreId); + + cm_PWR_DisableMPC(MPC_PWR_AUTOIDLE, coreId); + cm_PWR_DisableMPC(MPC_PWR_CLOCK, coreId); +} + +PUBLIC t_uint32 cm_DSP_ReadXRamWord(t_nmf_core_id coreId, t_uint32 dspOffset) +{ + t_uint32 value; + + value = pMmdspRegs[coreId]->mem24[dspOffset]; + + LOG_INTERNAL(3, "cm_DSP_ReadXRamWord: [%x]=%x\n", + dspOffset, value, + 0, 0, 0, 0); + + return value; +} + + +PUBLIC void cm_DSP_WriteXRamWord(t_nmf_core_id coreId, t_uint32 dspOffset, t_uint32 value) +{ + LOG_INTERNAL(3, "cm_DSP_WriteXRamWord: [%x]<-%x\n", + dspOffset, value, + 0, 0, 0, 0); + + pMmdspRegs[coreId]->mem24[dspOffset] = value; +} + +static void cm_DSP_SEM_Init(t_nmf_core_id coreId) +{ + pMmdspRegs[coreId]->mmio_16.sem[1].value = 1; +} + +PUBLIC void cm_DSP_SEM_Take(t_nmf_core_id coreId, t_semaphore_id semId) +{ + /* take semaphore */ + while(pMmdspRegs[coreId]->mmio_16.sem[1].value) ; +} + +PUBLIC void cm_DSP_SEM_Give(t_nmf_core_id coreId, t_semaphore_id semId) +{ + /* release semaphore */ + pMmdspRegs[coreId]->mmio_16.sem[1].value = 1; +} + +PUBLIC void cm_DSP_SEM_GenerateIrq(t_nmf_core_id coreId, t_semaphore_id semId) +{ + MMDSP_ASSERT_IRQ(pMmdspRegs[coreId], ARM2DSP_IRQ_0); +} + + +PUBLIC void cm_DSP_AssertDspIrq(t_nmf_core_id coreId, t_host2mpc_irq_num irqNum) +{ + MMDSP_ASSERT_IRQ(pMmdspRegs[coreId], irqNum); + return; +} + +PUBLIC void cm_DSP_AcknowledgeDspIrq(t_nmf_core_id coreId, t_mpc2host_irq_num irqNum) +{ + MMDSP_ACKNOWLEDGE_IRQ(pMmdspRegs[coreId], irqNum); + return; +} + +//TODO, juraj, cleanup INTERNAL_XRAM vs INTERNAL_XRAM16/24 +static const t_uint32 dspMemoryTypeId2OffsetShifter[NB_DSP_MEMORY_TYPE] = +{ + 2, /* INTERNAL_XRAM24: Internal X memory but seen by host as 32-bit memory */ + 2, /* INTERNAL_XRAM16: Internal X memory but seen by host as 16-bit memory */ + 2, /* INTERNAL_YRAM24: Internal Y memory but seen by host as 32-bit memory */ + 2, /* INTERNAL_YRAM16: Internal Y memory but seen by host as 16-bit memory */ + 2, /* SDRAM_EXT24: 24-bit external "X" memory */ + 1, /* SDRAM_EXT16: 16-bit external "X" memory */ + 2, /* ESRAM_EXT24: ESRAM24 */ + 1, /* ESRAM_EXT16: ESRAM16 */ + 3, /* SDRAM_CODE: Program memory */ + 3, /* ESRAM_CODE: ESRAM code */ + 3, /* LOCKED_CODE: ESRAM code */ +}; + +//TODO, juraj, use these values in mmdsp_Configure +static const t_uint32 dspMemoryTypeId2DspAddressOffset[NB_DSP_MEMORY_TYPE] = +{ + 0, /* INTERNAL_XRAM24 */ + 0, /* INTERNAL_XRAM16 */ + 0, /* INTERNAL_YRAM24 */ + 0, /* INTERNAL_YRAM16 */ + SDRAMMEM24_BASE_ADDR, /* SDRAM_EXT24: 24-bit external "X" memory */ + SDRAMMEM16_BASE_ADDR, /* SDRAM_EXT16: 16-bit external "X" memory */ + ESRAMMEM24_BASE_ADDR, /* ESRAM_EXT24: ESRAM24 */ + ESRAMMEM16_BASE_ADDR, /* ESRAM_EXT16: ESRAM16 */ + SDRAMTEXT_BASE_ADDR, /* SDRAM_CODE: Program memory */ + ESRAMTEXT_BASE_ADDR, /* ESRAM_CODE: ESRAM code */ + SDRAMTEXT_BASE_ADDR, /* ESRAM_CODE: ESRAM code */ +}; + +PUBLIC t_cm_allocator_desc* cm_DSP_GetAllocator(t_nmf_core_id coreId, t_dsp_memory_type_id memType) +{ + return mpcDesc[coreId].allocator[memType] ? mpcDesc[coreId].allocator[memType]->allocDesc : NULL; +} + +PUBLIC void cm_DSP_GetDspChunkInfo(t_memory_handle memHandle, t_dsp_chunk_info *info) +{ + t_uint16 userData; + + cm_MM_GetMemoryHandleUserData(memHandle, &userData, &info->alloc); + + info->coreId = (t_nmf_core_id) ((userData >> SHIFT_BYTE1) & MASK_BYTE0); + info->memType = (t_dsp_memory_type_id)((userData >> SHIFT_BYTE0) & MASK_BYTE0); +} + +PUBLIC t_cm_error cm_DSP_GetInternalMemoriesInfo(t_cm_domain_id domainId, t_dsp_memory_type_id memType, + t_uint32 *offset, t_uint32 *size) +{ + t_nmf_core_id coreId = domainDesc[domainId].domain.coreId; + + switch(memType) + { + case INTERNAL_XRAM24: + case INTERNAL_XRAM16: + *offset = 0; + *size = mpcDesc[coreId].yram_offset; + break; + case INTERNAL_YRAM24: + case INTERNAL_YRAM16: + *offset = mpcDesc[coreId].yram_offset; + *size = mpcDesc[coreId].yram_size; + break; + case LOCKED_CODE: + *offset = mpcDesc[coreId].locked_offset; + *size = mpcDesc[coreId].locked_size; + break; + case SDRAM_EXT24: + case SDRAM_EXT16: + *offset = domainDesc[domainId].domain.sdramData.offset; + *size = domainDesc[domainId].domain.sdramData.size; + break; + case ESRAM_EXT24: + case ESRAM_EXT16: + *offset = domainDesc[domainId].domain.esramData.offset; + *size = domainDesc[domainId].domain.esramData.size; + break; + case SDRAM_CODE: + *offset = domainDesc[domainId].domain.sdramCode.offset; + *size = domainDesc[domainId].domain.sdramCode.size; + + // update domain size to take into account .locked section + if(*offset + *size > mpcDesc[coreId].locked_offset) + *size = mpcDesc[coreId].locked_offset - *offset; + break; + case ESRAM_CODE: + *offset = domainDesc[domainId].domain.esramCode.offset; + *size = domainDesc[domainId].domain.esramCode.size; + break; + default: + //return CM_INVALID_PARAMETER; + //params are checked at the level above, so this should never occur + ERROR("Invalid memType\n",0,0,0,0,0,0); + *offset = 0; + *size = 0; + CM_ASSERT(0); + } + + return CM_OK; +} + + +PUBLIC t_uint32 cm_DSP_ConvertSize(t_dsp_memory_type_id memType, t_uint32 wordSize) +{ + return wordSize << dspMemoryTypeId2OffsetShifter[memType]; +} + +PUBLIC t_cm_logical_address cm_DSP_ConvertDspAddressToHostLogicalAddress(t_nmf_core_id coreId, t_shared_addr dspAddress) +{ + t_dsp_address_info info; + cm_DSP_GetDspDataAddressInfo(coreId, dspAddress, &info); + return mpcDesc[coreId].segments[info.segmentType].base.logical + info.baseOffset; +} + +PUBLIC t_cm_error cm_DSP_GetAllocatorStatus(t_nmf_core_id coreId, t_dsp_memory_type_id dspMemType, t_uint32 offset, t_uint32 size, t_cm_allocator_status *pStatus) +{ + t_cm_error error; + + if(mpcDesc[coreId].allocator[dspMemType] == NULL) + return CM_UNKNOWN_MEMORY_HANDLE; + + error = cm_MM_GetAllocatorStatus(cm_DSP_GetAllocator(coreId, dspMemType), offset, size, pStatus); + if (error != CM_OK) + return error; + + // complete status with stack sizes, for all dsps + //NOTE, well, surely this isn't very clean, as dsp and memory allocator are different things .. + { + t_uint8 i; + for (i = 0; i < NB_CORE_IDS; i++) { + //*(pStatus->stack[i].sizes) = *(eeState[i].currentStackSize); + pStatus->stack[i].sizes[0] = eeState[i].currentStackSize[0]; + pStatus->stack[i].sizes[1] = eeState[i].currentStackSize[1]; + pStatus->stack[i].sizes[2] = eeState[i].currentStackSize[2]; + } + } + + // Change bytes to words + pStatus->global.accumulate_free_memory = pStatus->global.accumulate_free_memory >> dspMemoryTypeId2OffsetShifter[dspMemType]; + pStatus->global.accumulate_used_memory = pStatus->global.accumulate_used_memory >> dspMemoryTypeId2OffsetShifter[dspMemType]; + pStatus->global.maximum_free_size = pStatus->global.maximum_free_size >> dspMemoryTypeId2OffsetShifter[dspMemType]; + pStatus->global.minimum_free_size = pStatus->global.minimum_free_size >> dspMemoryTypeId2OffsetShifter[dspMemType]; + + return error; +} + +PUBLIC void cm_DSP_GetHostSystemAddress(t_memory_handle memHandle, t_cm_system_address *pAddr) +{ + t_dsp_chunk_info chunk_info; + t_uint32 offset; //in bytes + + cm_DSP_GetDspChunkInfo(memHandle, &chunk_info); + + offset = cm_MM_GetOffset(memHandle); + + /* MMDSP mem16 array is very specific to host access, so .... */ + /* We compute by hand the Host System address to take into account the specifities of the mmdsp mem16 array */ + /* 1 dsp word = 2 host bytes AND mem16 array is "exported" by MMDSP External Bus wrapper at the 0x40000 offet */ + if (chunk_info.memType == INTERNAL_XRAM16 || chunk_info.memType == INTERNAL_YRAM16) { + offset = (offset >> 1) + FIELD_OFFSET(t_mmdsp_hw_regs, mem16); + } + + //TODO, juraj, calculate correct value here - based on segments desc etc.. + pAddr->logical = mpcDesc[chunk_info.coreId].allocator[chunk_info.memType]->baseAddress.logical + offset; + pAddr->physical = mpcDesc[chunk_info.coreId].allocator[chunk_info.memType]->baseAddress.physical + offset; +} + + +PUBLIC t_physical_address cm_DSP_GetPhysicalAdress(t_memory_handle memHandle) +{ + t_cm_system_address addr; + cm_DSP_GetHostSystemAddress(memHandle, &addr); + return addr.physical; +} + +PUBLIC t_cm_logical_address cm_DSP_GetHostLogicalAddress(t_memory_handle memHandle) +{ + t_cm_system_address addr; + cm_DSP_GetHostSystemAddress(memHandle, &addr); + return addr.logical; +} + +PUBLIC void cm_DSP_GetDspAddress(t_memory_handle memHandle, t_uint32 *pDspAddress) +{ + t_dsp_chunk_info chunk_info; + + cm_DSP_GetDspChunkInfo(memHandle, &chunk_info); + + *pDspAddress = + (cm_MM_GetOffset(memHandle) >> dspMemoryTypeId2OffsetShifter[chunk_info.memType]) + + dspMemoryTypeId2DspAddressOffset[chunk_info.memType]; +} + +PUBLIC t_cm_error cm_DSP_GetDspBaseAddress(t_nmf_core_id coreId, t_dsp_memory_type_id memType, t_cm_system_address *pAddr) +{ + cm_migration_check_state(coreId, STATE_NORMAL); + *pAddr = mpcDesc[coreId].allocator[memType]->baseAddress; + return CM_OK; +} + +PUBLIC void cm_DSP_GetDspMemoryHandleSize(t_memory_handle memHandle, t_uint32 *pDspSize) +{ + t_dsp_chunk_info chunk_info; + cm_DSP_GetDspChunkInfo(memHandle, &chunk_info); + *pDspSize = cm_MM_GetSize(memHandle) >> dspMemoryTypeId2OffsetShifter[chunk_info.memType]; +} + +PUBLIC t_cm_error cm_DSP_setStackSize(t_nmf_core_id coreId, t_uint32 newStackSize) +{ + t_uint8 nbXramBanks; + t_uint32 xramSize; + t_cm_error error; + + /* compute size of xram allocator */ + nbXramBanks = SxA_NB_BLOCK_RAM - mpcDesc[coreId].nbYramBank; + + /* check first that required stack size is less then xram memory ....*/ + if (newStackSize >= nbXramBanks * 4 * ONE_KB) { + ERROR("CM_NO_MORE_MEMORY: cm_DSP_setStackSize(), required stack size doesn't fit in XRAM.\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + /* compute new xram allocator size */ + xramSize = nbXramBanks * 4 * ONE_KB - newStackSize; + + /* try to resize it */ + if ((error = cm_MM_ResizeAllocator(cm_DSP_GetAllocator(coreId, INTERNAL_XRAM24), + xramSize << dspMemoryTypeId2OffsetShifter[INTERNAL_XRAM24])) == CM_NO_MORE_MEMORY) { + ERROR("CM_NO_MORE_MEMORY: Couldn't resize stack in cm_DSP_setStackSize()\n", 0, 0, 0, 0, 0, 0); + } + + return error; +} + +PUBLIC t_cm_error cm_DSP_IsNbYramBanksValid(t_nmf_core_id coreId, t_uint8 nbYramBanks) +{ + /* we use one bank for cache */ + t_uint8 nbOfRamBanksWithCacheReserved = SxA_NB_BLOCK_RAM; + + /* we want to keep at least one bank of xram */ + if (nbYramBanks < nbOfRamBanksWithCacheReserved) {return CM_OK;} + else {return CM_MPC_INVALID_CONFIGURATION;} +} + +PUBLIC t_uint32 cm_DSP_getStackAddr(t_nmf_core_id coreId) +{ + /* we use one bank for cache */ + //t_uint8 nbOfRamBanksWithCacheReserved = SxA_NB_BLOCK_RAM; + /* */ + //return ((nbOfRamBanksWithCacheReserved * MMDSP_RAM_BLOCK_SIZE * MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE) - mpcDesc[coreId].yram_offset); + return mpcDesc[coreId].yram_offset / MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE; +} + +static void arm_Init(void) +{ + mpcDesc[ARM_CORE_ID].allocator[INTERNAL_XRAM24] = 0; + mpcDesc[ARM_CORE_ID].allocator[INTERNAL_XRAM16] = 0; + + mpcDesc[ARM_CORE_ID].allocator[INTERNAL_YRAM24] = 0; + mpcDesc[ARM_CORE_ID].allocator[INTERNAL_YRAM16] = 0; + + mpcDesc[ARM_CORE_ID].allocator[SDRAM_CODE] = 0; + mpcDesc[ARM_CORE_ID].allocator[ESRAM_CODE] = 0; + + mpcDesc[ARM_CORE_ID].allocator[SDRAM_EXT16] = 0; + mpcDesc[ARM_CORE_ID].allocator[SDRAM_EXT24] = 0; + + mpcDesc[ARM_CORE_ID].allocator[ESRAM_EXT16] = &esramDesc; + mpcDesc[ARM_CORE_ID].allocator[ESRAM_EXT16]->referenceCounter++; + mpcDesc[ARM_CORE_ID].allocator[ESRAM_EXT24] = &esramDesc; + mpcDesc[ARM_CORE_ID].allocator[ESRAM_EXT24]->referenceCounter++; +} + +static void _init_Segment( + t_dsp_segment *seg, + const t_cm_system_address base, const t_uint32 arm_offset, + const t_uint32 size) +{ + seg->base.logical = base.logical + arm_offset; + seg->base.physical = base.physical + arm_offset; + seg->size = size; +} + +static t_cm_error mmdsp_Init( + const t_cm_system_address *dspSystemAddr, + t_uint8 nbXramBlocks, t_uint8 nbYramBlocks, + t_dsp_allocator_desc *sdramCodeDesc, + t_dsp_allocator_desc *sdramDataDesc, + t_cm_domain_id eeDomain, + t_dsp_desc *pDspDesc, + t_mmdsp_hw_regs **pRegs) +{ + t_cm_system_address xramSysAddr; + t_uint32 sizeInBytes; + + /* Initialize reference on hw ressources */ + *pRegs = (t_mmdsp_hw_regs *) dspSystemAddr->logical; + + /* Initialize memory segments management */ + xramSysAddr.logical = (t_cm_logical_address)(((t_mmdsp_hw_regs *)dspSystemAddr->logical)->mem24); + xramSysAddr.physical = (t_cm_physical_address)(((t_mmdsp_hw_regs *)dspSystemAddr->physical)->mem24); + + /* The last (x)ram block will be used by cache, so ... */ + /* And the NB_YRAM_BLOCKS last available block(s) will be used as YRAM */ + + /* XRAM*/ + pDspDesc->allocator[INTERNAL_XRAM16] = pDspDesc->allocator[INTERNAL_XRAM24] = (t_dsp_allocator_desc*)OSAL_Alloc(sizeof (t_dsp_allocator_desc)); + if (pDspDesc->allocator[INTERNAL_XRAM24] == NULL) + return CM_NO_MORE_MEMORY; + + pDspDesc->allocator[INTERNAL_XRAM24]->allocDesc = cm_MM_CreateAllocator( + ((nbXramBlocks-nbYramBlocks)*MMDSP_RAM_BLOCK_SIZE)*MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE, + 0, + "XRAM"); + pDspDesc->allocator[INTERNAL_XRAM24]->baseAddress = xramSysAddr; + pDspDesc->allocator[INTERNAL_XRAM24]->referenceCounter = 2; + + /* YRAM */ + pDspDesc->allocator[INTERNAL_YRAM16] = pDspDesc->allocator[INTERNAL_YRAM24] = (t_dsp_allocator_desc*)OSAL_Alloc(sizeof (t_dsp_allocator_desc)); + if (pDspDesc->allocator[INTERNAL_YRAM24] == 0) { + OSAL_Free(pDspDesc->allocator[INTERNAL_XRAM24]); + return CM_NO_MORE_MEMORY; + } + + pDspDesc->allocator[INTERNAL_YRAM24]->allocDesc = cm_MM_CreateAllocator( + (nbYramBlocks*MMDSP_RAM_BLOCK_SIZE)*MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE, + ((nbXramBlocks-nbYramBlocks)*MMDSP_RAM_BLOCK_SIZE)*MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE, + "YRAM"); + pDspDesc->allocator[INTERNAL_YRAM24]->baseAddress = xramSysAddr; /* use xram base address but offset is not null */ + pDspDesc->allocator[INTERNAL_YRAM24]->referenceCounter = 2; + + pDspDesc->yram_offset = ((nbXramBlocks-nbYramBlocks)*MMDSP_RAM_BLOCK_SIZE)*MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE; + pDspDesc->yram_size = (nbYramBlocks*MMDSP_RAM_BLOCK_SIZE)*MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE; + + /* SDRAM & ESRAM */ + pDspDesc->allocator[SDRAM_CODE] = sdramCodeDesc; + pDspDesc->allocator[SDRAM_CODE]->referenceCounter++; + pDspDesc->allocator[ESRAM_CODE] = &esramDesc; + pDspDesc->allocator[ESRAM_CODE]->referenceCounter++; + + /* LOCKED CODE at end of SDRAM code*/ + pDspDesc->allocator[LOCKED_CODE] = sdramCodeDesc; + pDspDesc->allocator[LOCKED_CODE]->referenceCounter++; + + pDspDesc->locked_offset = cm_MM_GetAllocatorSize(pDspDesc->allocator[SDRAM_CODE]->allocDesc) - MMDSP_CODE_CACHE_WAY_SIZE * 8 * SxA_LOCKED_WAY; + pDspDesc->locked_size = MMDSP_CODE_CACHE_WAY_SIZE * 8 * SxA_LOCKED_WAY; + + /* Data_16/24 memory management */ + pDspDesc->allocator[SDRAM_EXT16] = sdramDataDesc; + pDspDesc->allocator[SDRAM_EXT16]->referenceCounter++; + pDspDesc->allocator[SDRAM_EXT24] = sdramDataDesc; + pDspDesc->allocator[SDRAM_EXT24]->referenceCounter++; + + pDspDesc->allocator[ESRAM_EXT16] = &esramDesc; + pDspDesc->allocator[ESRAM_EXT16]->referenceCounter++; + pDspDesc->allocator[ESRAM_EXT24] = &esramDesc; + pDspDesc->allocator[ESRAM_EXT24]->referenceCounter++; + + sizeInBytes = cm_MM_GetAllocatorSize(pDspDesc->allocator[SDRAM_CODE]->allocDesc); +#if defined(__STN_8500) && (__STN_8500 > 10) + _init_Segment(&pDspDesc->segments[SDRAM_CODE_EE], + pDspDesc->allocator[SDRAM_CODE]->baseAddress, + domainDesc[eeDomain].domain.sdramCode.offset, + domainDesc[eeDomain].domain.sdramCode.size); + _init_Segment(&pDspDesc->segments[SDRAM_CODE_USER], + pDspDesc->allocator[SDRAM_CODE]->baseAddress, + domainDesc[eeDomain].domain.sdramCode.offset + domainDesc[eeDomain].domain.sdramCode.size, + sizeInBytes - domainDesc[eeDomain].domain.sdramCode.size); +#else + _init_Segment(&pDspDesc->segments[SDRAM_CODE_EE], + pDspDesc->allocator[SDRAM_CODE]->baseAddress, + 0x0, + sizeInBytes); +#endif + + sizeInBytes = cm_MM_GetAllocatorSize(pDspDesc->allocator[ESRAM_CODE]->allocDesc); +#if defined(__STN_8500) && (__STN_8500 > 10) + _init_Segment(&pDspDesc->segments[ESRAM_CODE_EE], + pDspDesc->allocator[ESRAM_CODE]->baseAddress, + domainDesc[eeDomain].domain.esramCode.offset, + domainDesc[eeDomain].domain.esramCode.size); + _init_Segment(&pDspDesc->segments[ESRAM_CODE_USER], + pDspDesc->allocator[ESRAM_CODE]->baseAddress, + domainDesc[eeDomain].domain.esramCode.offset + domainDesc[eeDomain].domain.esramCode.size, + sizeInBytes - domainDesc[eeDomain].domain.esramCode.size); +#else + _init_Segment(&pDspDesc->segments[ESRAM_CODE_EE], + pDspDesc->allocator[ESRAM_CODE]->baseAddress, + 0x0, + sizeInBytes); +#endif + + //the difference in the following code is the segment size used to calculate the top!! + sizeInBytes = cm_MM_GetAllocatorSize(pDspDesc->allocator[SDRAM_EXT16]->allocDesc); +#if defined(__STN_8500) && (__STN_8500 > 10) + _init_Segment(&pDspDesc->segments[SDRAM_DATA_EE], + pDspDesc->allocator[SDRAM_EXT16]->baseAddress, + domainDesc[eeDomain].domain.sdramData.offset, + domainDesc[eeDomain].domain.sdramData.size); + _init_Segment(&pDspDesc->segments[SDRAM_DATA_USER], + pDspDesc->allocator[SDRAM_EXT16]->baseAddress, + domainDesc[eeDomain].domain.sdramData.offset + domainDesc[eeDomain].domain.sdramData.size, + sizeInBytes - domainDesc[eeDomain].domain.sdramData.size); +#else + _init_Segment(&pDspDesc->segments[SDRAM_DATA_EE], + pDspDesc->allocator[SDRAM_EXT16]->baseAddress, + 0x0, + sizeInBytes); +#endif + + sizeInBytes = cm_MM_GetAllocatorSize(pDspDesc->allocator[ESRAM_EXT16]->allocDesc); +#if defined(__STN_8500) && (__STN_8500 > 10) + _init_Segment(&pDspDesc->segments[ESRAM_DATA_EE], + pDspDesc->allocator[ESRAM_EXT16]->baseAddress, + domainDesc[eeDomain].domain.esramData.offset, + domainDesc[eeDomain].domain.esramData.size); + _init_Segment(&pDspDesc->segments[ESRAM_DATA_USER], + pDspDesc->allocator[ESRAM_EXT16]->baseAddress, + domainDesc[eeDomain].domain.esramData.offset + domainDesc[eeDomain].domain.esramData.size, + sizeInBytes - domainDesc[eeDomain].domain.esramData.size); +#else + _init_Segment(&pDspDesc->segments[ESRAM_DATA_EE], + pDspDesc->allocator[ESRAM_EXT16]->baseAddress, + 0x0, + sizeInBytes); +#endif + + return CM_OK; +} + +//TODO, juraj, reuse cm_DSP_UpdateBase functions +static t_cm_error mmdsp_Configure(t_nmf_core_id coreId, t_mmdsp_hw_regs *pRegs, const t_dsp_desc *pDspDesc) +{ + t_uint64 regValue; + static const t_uint64 coreId2stbusId[NB_CORE_IDS] = + { + 0, /* ARM_CORE_ID no meaning */ + SVA_STBUS_ID, /* SVA_CORE_ID */ + SIA_STBUS_ID /* SIA_CORE_ID */ + }; + + //t_cm_system_address sysAddr; + //t_cm_size sizeInBytes; + + /* Stop core (stop clock) */ + MMDSP_RESET_CORE(pRegs); + { + volatile t_uint32 loopme = 0xfff; + while(loopme--) ; + } + MMDSP_STOP_CORE(pRegs); + { + volatile t_uint32 loopme = 0xfff; + while(loopme--) ; + } + +#if 0 + /* Reset DSP internal memory (xram) */ + { + t_uint32 *pSrc = (t_uint32 *)(pRegs->mem24); + t_uint32 tcmSize; + int i; + cm_MM_GetAllocatorSize(pDspDesc->allocator[INTERNAL_XRAM], &sizeInBytes); + tcmSize = sizeInBytes; + cm_MM_GetAllocatorSize(pDspDesc->allocator[INTERNAL_YRAM], &sizeInBytes); + tcmSize += sizeInBytes; + for (i = 0; i < (tcmSize/sizeof(t_uint32)); i++) + *(pSrc++) = 0; + } +#endif + + /* Configure all blocks as X only, except the Y ones (MOVED TO mmdsp_InitAfterBoot()) */ + + /* __STN_8815 --> __STN_8820 or __STN_8500 */ + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_STBUS_ID_CONF_REG, coreId2stbusId[coreId]); + + /* Configure External Bus timeout reg */ + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EN_EXT_BUS_TIMEOUT_REG, IHOST_TIMEOUT_ENABLE); + + /* Program memory management */ +#if defined(__STN_8500) && (__STN_8500 > 10) + { + const t_uint32 r0 = CODE_ADDRESS_BASE[1] >> 10; + const t_uint32 r1 = CODE_ADDRESS_BASE[2] >> 10; + const t_uint32 r2 = CODE_ADDRESS_BASE[3] >> 10; + const t_uint32 sdram0 = pDspDesc->segments[SDRAM_CODE_EE].base.physical; + const t_uint32 sdram1 = pDspDesc->segments[SDRAM_CODE_USER].base.physical; + const t_uint32 esram0 = pDspDesc->segments[ESRAM_CODE_EE].base.physical; + const t_uint32 esram1 = pDspDesc->segments[ESRAM_CODE_USER].base.physical; + + /* Bases for first two segments, going to sdram */ + regValue = ((t_uint64)(sdram1) << IHOST_PRG_BASE2_ADDR_SHIFT) + (t_uint64)sdram0; + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_PRG_BASE_ADDR_REG, regValue); + + /* Bases for second two segments, going to esram */ + regValue = ((t_uint64)(esram1) << IHOST_PRG_BASE4_ADDR_SHIFT) + (t_uint64)esram0; + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_PRG_BASE_34_ADDR_REG, regValue); + + /* Split mmdsp program adress-space and activate the mechanism */ + regValue = (t_uint64)((t_uint64)(r2) << 48 | (t_uint64)(r1) <<32 | (t_uint64)(r0) << 16 | 1); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_PRG_BASE2_ACTIV_REG, regValue); + } +#else + { + const t_uint32 sdram0 = pDspDesc->segments[SDRAM_CODE_EE].base.physical; + const t_uint32 esram0 = pDspDesc->segments[ESRAM_CODE_EE].base.physical; + + regValue = (t_uint64)sdram0 | ( ((t_uint64)esram0) << IHOST_PRG_BASE2_ADDR_SHIFT ); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_PRG_BASE_ADDR_REG, regValue); + + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_PRG_BASE2_ACTIV_REG, IHOST_PRG_BASE2_ACTIV_ON); + } +#endif + + /* Data_16/24 memory management */ +#if defined(__STN_8500) && (__STN_8500 > 10) + /* Segments 1 and 2 for 16/24 map to sdram continuously */ + /* Base 1 */ + regValue = (((t_uint64)pDspDesc->segments[SDRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE_24_SHIFT) | + (((t_uint64)pDspDesc->segments[SDRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE_REG, regValue); + /* Top 1 */ + regValue = (((t_uint64)(pDspDesc->segments[SDRAM_DATA_EE].base.physical + pDspDesc->segments[SDRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP_24_SHIFT) | + (((t_uint64)(pDspDesc->segments[SDRAM_DATA_EE].base.physical + pDspDesc->segments[SDRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_TOP_16_24_REG, regValue); + + /* Base 2 */ + regValue = (((t_uint64)pDspDesc->segments[SDRAM_DATA_USER].base.physical) << IHOST_DATA_EXT_BUS_BASE2_24_SHIFT) | + (((t_uint64)pDspDesc->segments[SDRAM_DATA_USER].base.physical) << IHOST_DATA_EXT_BUS_BASE2_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE2_REG, regValue); + /* Top 2 */ + regValue = (((t_uint64)(pDspDesc->segments[SDRAM_DATA_USER].base.physical + pDspDesc->segments[SDRAM_DATA_USER].size - 1)) << IHOST_DATA_EXT_BUS_TOP2_24_SHIFT) | + (((t_uint64)(pDspDesc->segments[SDRAM_DATA_USER].base.physical + pDspDesc->segments[SDRAM_DATA_USER].size - 1)) << IHOST_DATA_EXT_BUS_TOP2_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EXT_BUS_TOP2_16_24_REG, regValue); + + /* Segments 3 and 4 for 16/24 map to esram continuously */ + /* Base 3 */ + regValue = (((t_uint64)pDspDesc->segments[ESRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE3_24_SHIFT) | + (((t_uint64)pDspDesc->segments[ESRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE3_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE3_REG, regValue); + /* Top 3 */ + regValue = (((t_uint64)(pDspDesc->segments[ESRAM_DATA_EE].base.physical + pDspDesc->segments[ESRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP3_24_SHIFT) | + (((t_uint64)(pDspDesc->segments[ESRAM_DATA_EE].base.physical + pDspDesc->segments[ESRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP3_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EXT_BUS_TOP3_16_24_REG, regValue); + + /* Base 4 */ + regValue = (((t_uint64)pDspDesc->segments[ESRAM_DATA_USER].base.physical) << IHOST_DATA_EXT_BUS_BASE4_24_SHIFT) | + (((t_uint64)pDspDesc->segments[ESRAM_DATA_USER].base.physical) << IHOST_DATA_EXT_BUS_BASE4_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE4_REG, regValue); + /* Top 4 */ + regValue = (((t_uint64)(pDspDesc->segments[ESRAM_DATA_USER].base.physical + pDspDesc->segments[ESRAM_DATA_USER].size - 1)) << IHOST_DATA_EXT_BUS_TOP4_24_SHIFT) | + (((t_uint64)(pDspDesc->segments[ESRAM_DATA_USER].base.physical + pDspDesc->segments[ESRAM_DATA_USER].size - 1)) << IHOST_DATA_EXT_BUS_TOP4_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EXT_BUS_TOP4_16_24_REG, regValue); + + /* Define base 2 thresholds/offset (1MB for each up segment) */ + regValue = ((t_uint64)DATA_ADDRESS_BASE[1].startAddress[1]>>SHIFT_HALFWORD1)<< IHOST_DATA2_24_XA_BASE_SHIFT; + regValue |= ((t_uint64)DATA_ADDRESS_BASE[1].startAddress[0]>>SHIFT_HALFWORD1)<< IHOST_DATA2_16_XA_BASE_SHIFT; + + /* Define base 3 thresholds/offset (1MB for each up segment) */ + regValue |= ((t_uint64)DATA_ADDRESS_BASE[2].startAddress[1]>>SHIFT_HALFWORD1)<< IHOST_DATA3_24_XA_BASE_SHIFT; + regValue |= ((t_uint64)DATA_ADDRESS_BASE[2].startAddress[0]>>SHIFT_HALFWORD1)<< IHOST_DATA3_16_XA_BASE_SHIFT; + + /* Define base 4 thresholds/offset (1MB for each up segment) */ + regValue |= ((t_uint64)DATA_ADDRESS_BASE[3].startAddress[1]>>SHIFT_HALFWORD1)<< IHOST_DATA4_24_XA_BASE_SHIFT; + regValue |= ((t_uint64)DATA_ADDRESS_BASE[3].startAddress[0]>>SHIFT_HALFWORD1)<< IHOST_DATA4_16_XA_BASE_SHIFT; + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA2_1624_XA_BASE_REG, regValue); + +#else + /* Program data24/16 base 1 */ + regValue = (((t_uint64)pDspDesc->segments[SDRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE_24_SHIFT) | + (((t_uint64)pDspDesc->segments[SDRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE_REG, regValue); + + /* Program data24/16 top 1 */ + regValue = (((t_uint64)(pDspDesc->segments[SDRAM_DATA_EE].base.physical + pDspDesc->segments[SDRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP_24_SHIFT) | + (((t_uint64)(pDspDesc->segments[SDRAM_DATA_EE].base.physical + pDspDesc->segments[SDRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_TOP_16_24_REG, regValue); + + /* Program data24/16 base 2 */ + regValue = (((t_uint64)pDspDesc->segments[ESRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE2_24_SHIFT) | + (((t_uint64)pDspDesc->segments[ESRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE2_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE2_REG, regValue); + + /* Program data24/16 top 2 */ + regValue = (((t_uint64)(pDspDesc->segments[ESRAM_DATA_EE].base.physical + pDspDesc->segments[ESRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP2_24_SHIFT) | + (((t_uint64)(pDspDesc->segments[ESRAM_DATA_EE].base.physical + pDspDesc->segments[ESRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP2_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EXT_BUS_TOP2_16_24_REG, regValue); + + /* Define base 2 thresholds/offset (1MB for each up segment) */ + regValue = ((t_uint64)(DATA_ADDRESS_BASE[1].startAddress[1]>>SHIFT_HALFWORD1))<< IHOST_DATA2_24_XA_BASE_SHIFT; // Top address minus ONE_MB => 256KW (24/32-bit) + regValue |= ((t_uint64)(DATA_ADDRESS_BASE[1].startAddress[0]>>SHIFT_HALFWORD1))<< IHOST_DATA2_16_XA_BASE_SHIFT; // Top address minus ONE_MB => 512KW (16-bit) + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA2_1624_XA_BASE_REG, regValue); +#endif + + /* Enable top check */ + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_TOP_16_24_CHK_REG, IHOST_DATA_TOP_16_24_CHK_ON); + + /* Enable both bases */ + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_BASE2_ACTIV_REG, IHOST_DATA_BASE2_ACTIV_ON); + + /* MMIO management */ + regValue = (((t_uint64)STM_BASE_ADDR) << IHOST_EXT_MMIO_BASE_ADDR_SHIFT) | + (((t_uint64)DMA_CTRL_END_ADDR) << IHOST_EXT_MMIO_DATA_EXT_BUS_TOP_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EXT_MMIO_BASE_DATA_EXT_BUS_TOP_REG, regValue); + + /* Configure Icache */ + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_INST_BURST_SZ_REG, IHOST_INST_BURST_SZ_AUTO); + + regValue = (t_uint64)(IHOST_ICACHE_MODE_PERFMETER_OFF | IHOST_ICACHE_MODE_L2_CACHE_ON | + IHOST_ICACHE_MODE_L1_CACHE_ON | IHOST_ICACHE_MODE_FILL_MODE_OFF); + WRITE_INDIRECT_HOST_REG(pRegs, IHOST_ICACHE_MODE_REG, regValue); + + return CM_OK; +} + +PUBLIC t_cm_error cm_DSP_updateCodeBase( + t_nmf_core_id coreId, + t_dsp_segment_type hwSegment, + t_cm_system_address src, + t_cm_system_address dst + ) +{ +#if defined(__STN_8500) && (__STN_8500 > 10) + t_mmdsp_hw_regs *pRegs = pMmdspRegs[coreId]; + t_uint32 offset = src.physical - mpcDesc[coreId].segments[hwSegment].base.physical; + t_cm_system_address base; + t_uint32 altBase = 0; + t_uint64 regValue = 0; + t_uint8 reg = 0; + + base.physical = dst.physical - offset; + base.logical = dst.logical - offset; + + switch(hwSegment) { + case SDRAM_CODE_EE: + altBase = mpcDesc[coreId].segments[SDRAM_CODE_USER].base.physical; + regValue = ((t_uint64)(altBase) << IHOST_PRG_BASE2_ADDR_SHIFT) + (t_uint64)base.physical; + reg = IHOST_PRG_BASE_ADDR_REG; + break; + case SDRAM_CODE_USER: + altBase = mpcDesc[coreId].segments[SDRAM_CODE_EE].base.physical; + regValue = ((t_uint64)(base.physical) << IHOST_PRG_BASE2_ADDR_SHIFT) + (t_uint64)altBase; + reg = IHOST_PRG_BASE_ADDR_REG; + break; + case ESRAM_CODE_EE: + altBase = mpcDesc[coreId].segments[ESRAM_CODE_USER].base.physical; + regValue = ((t_uint64)(altBase) << IHOST_PRG_BASE4_ADDR_SHIFT) + (t_uint64)base.physical; + reg = IHOST_PRG_BASE_34_ADDR_REG; + break; + case ESRAM_CODE_USER: + altBase = mpcDesc[coreId].segments[ESRAM_CODE_EE].base.physical; + regValue = ((t_uint64)(base.physical) << IHOST_PRG_BASE4_ADDR_SHIFT) + (t_uint64)altBase; + reg = IHOST_PRG_BASE_34_ADDR_REG; + break; + default: + CM_ASSERT(0); + } + + LOG_INTERNAL(1, "##### DSP Code Base Update [%d]: 0x%x -> 0x%x (0x%x)\n", + hwSegment, mpcDesc[coreId].segments[hwSegment].base.physical, base.physical, base.logical, 0, 0); + + WRITE_INDIRECT_HOST_REG(pRegs, reg, regValue); + + mpcDesc[coreId].segments[hwSegment].base = base; +#endif + return CM_OK; +} + +PUBLIC t_cm_error cm_DSP_updateDataBase( + t_nmf_core_id coreId, + t_dsp_segment_type hwSegment, + t_cm_system_address src, + t_cm_system_address dst + ) +{ +#if defined(__STN_8500) && (__STN_8500 > 10) + t_mmdsp_hw_regs *pRegs = pMmdspRegs[coreId]; + t_uint32 offset = src.physical - mpcDesc[coreId].segments[hwSegment].base.physical; + t_cm_system_address base; + t_uint32 size = mpcDesc[coreId].segments[hwSegment].size; //in bytes + t_uint64 regValue; + t_uint8 reg = 0; + t_uint8 top = 0; + + base.physical = dst.physical - offset; + base.logical = dst.logical - offset; + + switch(hwSegment) { + case SDRAM_DATA_EE: + reg = IHOST_DATA_EXT_BUS_BASE_REG; + top = IHOST_DATA_EXT_BUS_TOP_16_24_REG; + break; + case SDRAM_DATA_USER: + reg = IHOST_DATA_EXT_BUS_BASE2_REG; + top = IHOST_EXT_BUS_TOP2_16_24_REG; + break; + case ESRAM_DATA_EE: + reg = IHOST_DATA_EXT_BUS_BASE3_REG; + top = IHOST_EXT_BUS_TOP3_16_24_REG; + break; + case ESRAM_DATA_USER: + reg = IHOST_DATA_EXT_BUS_BASE4_REG; + top = IHOST_EXT_BUS_TOP4_16_24_REG; + break; + default: + CM_ASSERT(0); + } + + LOG_INTERNAL(1, "##### DSP Data Base Update [%d]: 0x%x -> 0x%x (0x%x)\n", + hwSegment, mpcDesc[coreId].segments[hwSegment].base.physical, base.physical, base.logical, 0, 0); + + /* Program data24/16 base */ + regValue = (((t_uint64)(base.physical)) << IHOST_DATA_EXT_BUS_BASE2_24_SHIFT) | + (((t_uint64)(base.physical)) << IHOST_DATA_EXT_BUS_BASE2_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, reg, regValue); + + /* Program data24/16 top */ + regValue = (((t_uint64)(base.physical + size - 1)) << IHOST_DATA_EXT_BUS_TOP2_24_SHIFT) | + (((t_uint64)(base.physical + size - 1)) << IHOST_DATA_EXT_BUS_TOP2_16_SHIFT); + WRITE_INDIRECT_HOST_REG(pRegs, top, regValue); + + mpcDesc[coreId].segments[hwSegment].base = base; +#endif + return CM_OK; +} + +PUBLIC t_cm_error cm_DSP_GetDspDataAddressInfo(t_nmf_core_id coreId, t_uint32 addr, t_dsp_address_info *info) +{ + t_uint32 i, j; + + for(j = 0; j < 2; j++) + { + for(i = 0; i < DATA_BASE_NUMBER; i++) + { + if(DATA_ADDRESS_BASE[i].startAddress[j] <= addr && addr < DATA_ADDRESS_BASE[i + 1].startAddress[j]) + { + info->segmentType = DATA_ADDRESS_BASE[i].segmentType; + info->baseOffset = (addr - DATA_ADDRESS_BASE[i].startAddress[j]) * (2 + j * 2); + + return CM_OK; + } + } + } + + CM_ASSERT(0); + //return CM_INVALID_PARAMETER; +} + +static t_cm_error mmdsp_ConfigureAfterBoot(t_nmf_core_id coreId, t_uint8 nbXramBlocks, t_uint8 nbYramBlocks) +{ + /* Configure all blocks as X only, except the Y ones */ + pMmdspRegs[coreId]->mmio_16.config_data_mem.value = (t_uint16)(~(((1U << nbYramBlocks) - 1) << (nbXramBlocks-nbYramBlocks))); + +#if defined(__STN_8500) && (__STN_8500 > 10) + /* enable write posting */ + MMDSP_ENABLE_WRITE_POSTING(pMmdspRegs[coreId]); +#endif + + return CM_OK; +} + + diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/bfd.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/bfd.h new file mode 100644 index 00000000000..2bccf9c073b --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/bfd.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Elf bfd relocation. + * + * \defgroup ELFLOADER MMDSP ELF loader. + */ +#ifndef __INC_CM_ELF_BFD_H +#define __INC_CM_ELF_BFD_H + +#include <cm/inc/cm_type.h> + +/* + * Relocation spcification + */ +enum complain_overflow +{ + /* Do not complain on overflow. */ + complain_overflow_dont, + + /* Complain if the bitfield overflows, whether it is considered + as signed or unsigned. */ + complain_overflow_bitfield, + + /* Complain if the value overflows when considered as signed + number. */ + complain_overflow_signed, + + /* Complain if the value overflows when considered as an + unsigned number. */ + complain_overflow_unsigned +}; + +struct reloc_howto_struct +{ + /* The type field has mainly a documentary use - the back end can + do what it wants with it, though normally the back end's + external idea of what a reloc number is stored + in this field. For example, a PC relative word relocation + in a coff environment has the type 023 - because that's + what the outside world calls a R_PCRWORD reloc. */ + unsigned int type; + + /* The value the final relocation is shifted right by. This drops + unwanted data from the relocation. */ + unsigned int rightshift; + + /* The size of the item to be relocated. This is *not* a + power-of-two measure. To get the number of bytes operated + on by a type of relocation, use bfd_get_reloc_size. */ + int size; + + /* The number of bits in the item to be relocated. This is used + when doing overflow checking. */ + unsigned int bitsize; + + /* Notes that the relocation is relative to the location in the + data section of the addend. The relocation function will + subtract from the relocation value the address of the location + being relocated. */ + t_uint64 pc_relative; + + /* The bit position of the reloc value in the destination. + The relocated value is left shifted by this amount. */ + unsigned int bitpos; + + /* What type of overflow error should be checked for when + relocating. */ + enum complain_overflow complain_on_overflow; + + void (*special_function)(void); + + /* The textual name of the relocation type. */ + char *name; + + /* Some formats record a relocation addend in the section contents + rather than with the relocation. For ELF formats this is the + distinction between USE_REL and USE_RELA (though the code checks + for USE_REL == 1/0). The value of this field is TRUE if the + addend is recorded with the section contents; when performing a + partial link (ld -r) the section contents (the data) will be + modified. The value of this field is FALSE if addends are + recorded with the relocation (in arelent.addend); when performing + a partial link the relocation will be modified. + All relocations for all ELF USE_RELA targets should set this field + to FALSE (values of TRUE should be looked on with suspicion). + However, the converse is not true: not all relocations of all ELF + USE_REL targets set this field to TRUE. Why this is so is peculiar + to each particular target. For relocs that aren't used in partial + links (e.g. GOT stuff) it doesn't matter what this is set to. */ + char partial_inplace; + + /* src_mask selects the part of the instruction (or data) to be used + in the relocation sum. If the target relocations don't have an + addend in the reloc, eg. ELF USE_REL, src_mask will normally equal + dst_mask to extract the addend from the section contents. If + relocations do have an addend in the reloc, eg. ELF USE_RELA, this + field should be zero. Non-zero values for ELF USE_RELA targets are + bogus as in those cases the value in the dst_mask part of the + section contents should be treated as garbage. */ + t_uint64 src_mask; + + /* dst_mask selects which parts of the instruction (or data) are + replaced with a relocated value. */ + t_uint64 dst_mask; + + /* When some formats create PC relative instructions, they leave + the value of the pc of the place being relocated in the offset + slot of the instruction, so that a PC relative relocation can + be made just by adding in an ordinary offset (e.g., sun3 a.out). + Some formats leave the displacement part of an instruction + empty (e.g., m88k bcs); this flag signals the fact. */ + char pcrel_offset; +}; + +#define HOWTO(C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC) \ + { (unsigned) C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC } + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/common.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/common.h new file mode 100644 index 00000000000..c51845d5f96 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/common.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Elf common definition. + */ +#ifndef __INC_CM_ELF_COMMON_H +#define __INC_CM_ELF_COMMON_H + +#include <cm/engine/component/inc/nmfheaderabi.h> +#include <cm/engine/elf/inc/elfabi.h> +#include <cm/engine/elf/inc/reloc.h> +#include <cm/engine/memory/inc/domain.h> +#include <cm/engine/component/inc/description.h> +#include <cm/engine/utils/inc/string.h> + + +#define MAX_SEGMENT 20 // Just in order to not allocate them dynamically + +struct XXElf; + +/** + * \brief Structure used as database of pushed component. + */ +typedef struct { + t_instance_property instanceProperty; + t_uint32 magicNumber; //!< Magic Number + t_dup_char foundedTemplateName; + t_uint32 minStackSize; //!< Minimum stack size + + struct XXElf *ELF; + + t_elfSegment segments[NUMBER_OF_MMDSP_MEMORY]; + + t_bool temporaryDescription; + + t_memory_reference memoryForConstruct; + t_memory_reference memoryForStart; + t_memory_reference memoryForStop; + t_memory_reference memoryForDestroy; + + t_uint8 requireNumber; //!< Number of interface required by this template + t_uint8 attributeNumber; //!< Number of attributes in this template + t_uint8 propertyNumber; //!< Number of properties in this template + t_uint8 provideNumber; //!< Number of interface provided by this template + + t_interface_require *requires; //!< Array of interface required by this template + t_attribute *attributes; //!< Array of attributes in this template + t_property *properties; //!< Array of properties in this template + t_interface_provide *provides; //!< Array of interface provided by this template + +} t_elfdescription; + +/** + * \brief Temporary structure used as database when pushing component. + */ +typedef struct +{ + const char *elfdata; + const char *sectionData[50]; // YES it must be dynamic, but i'm tired. + + t_bool isExecutable; + + t_sint32 nmfSectionIndex; + const void *relaNmfSegment, *relaNmfSegmentEnd; + const void *relaNmfSegmentSymbols; + const char *relaNmfSegmentStrings; + + const t_elf_component_header*elfheader; + + +} t_tmp_elfdescription; + + +t_cm_error ELF64_LoadComponent( + t_uint16 e_machine, + const char *elfdata, + t_elfdescription **elfhandlePtr, + t_tmp_elfdescription *elftmp); +t_cm_error ELF64_ComputeSegment( + t_elfdescription *elfhandle, + t_tmp_elfdescription *elftmp); + +void ELF64_UnloadComponent( + t_elfdescription *elfhandle); + +t_cm_error ELF64_loadSegment( + t_elfdescription *elfhandle, + t_memory_handle *memory, + t_memory_property property); +t_cm_error ELF64_relocateSegments( + t_memory_handle *memories, + t_elfdescription *elf, + t_memory_property property, + void *cbContext); +t_cm_error ELF64_getRelocationMemory( + t_elfdescription *elfhandle, + t_tmp_elfdescription *elftmp, + t_uint32 offsetInNmf, + t_memory_reference *memory); + +const t_elfmemory* MMDSP_getMappingById(t_memory_id memId); +const t_elfmemory* MMDSP_getMappingByName(const char* sectionName, t_instance_property property); +void MMDSP_serializeMemories(t_instance_property property, + const t_elfmemory** codeMemory, const t_elfmemory** thisMemory); +void MMDSP_copySection(t_uint32 origAddr, t_uint32 remoteAddr, t_uint32 sizeInByte); +void MMDSP_bzeroSection(t_uint32 remoteAddr, t_uint32 sizeInByte); +void MMDSP_loadedSection(t_nmf_core_id coreId, t_memory_id memId, t_memory_handle handle); +void MMDSP_unloadedSection(t_nmf_core_id coreId, t_memory_id memId, t_memory_handle handle); + +void MMDSP_copyCode(t_uint64 * remoteAddr64, const char* origAddr, int nb); +void MMDSP_copyData24(t_uint32 * remoteAddr32, const char* origAddr, int nb); +void MMDSP_copyData16(t_uint16 * remoteAddr16, const char* origAddr, int nb); + +t_uint32 cm_resolvSymbol( + void* context, + t_uint32 type, + t_dup_char symbolName, + char* reloc_addr); + + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/elfabi.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/elfabi.h new file mode 100644 index 00000000000..cbcc6db3b9b --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/elfabi.h @@ -0,0 +1,539 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#ifndef _CM_ELF_H +#define _CM_ELF_H 1 + +typedef t_uint16 Elf32_Half; +typedef t_uint16 Elf64_Half; + +typedef t_uint32 Elf32_Word; +typedef t_sint32 Elf32_Sword; +typedef t_uint32 Elf64_Word; +typedef t_sint32 Elf64_Sword; + +typedef t_uint64 Elf32_Xword; +typedef t_sint64 Elf32_Sxword; +typedef t_uint64 Elf64_Xword; +typedef t_sint64 Elf64_Sxword; + +typedef t_uint32 Elf32_Addr; +typedef t_uint64 Elf64_Addr; + +typedef t_uint32 Elf32_Off; +typedef t_uint64 Elf64_Off; + +typedef t_uint16 Elf32_Section; +typedef t_uint16 Elf64_Section; + +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/********************************************* + * Header + *********************************************/ +#define EI_NIDENT (16) //!< Size of e_ident[] + +#define EI_MAG0 0 //!< File identification +#define ELFMAG0 0x7f + +#define EI_MAG1 1 //!< File identification +#define ELFMAG1 'E' + +#define EI_MAG2 2 //!< File identification +#define ELFMAG2 'L' + +#define EI_MAG3 3 //!< File identification +#define ELFMAG3 'F' + +#define EI_CLASS 4 //!< File class +#define ELFCLASSNONE 0 //!< Invalid class +#define ELFCLASS32 1 //!< 32-bit objects +#define ELFCLASS64 2 //!< 64-bit objects + +#define EI_DATA 5 //!< Data encoding +#define ELFDATANONE 0 //!< Invalid data encoding +#define ELFDATA2LSB 1 //!< 2's complement, little endian +#define ELFDATA2MSB 2 //!< 2's complement, big endian + +#define EI_VERSION 6 //!< File version + +#define EI_OSABI 7 //!< OS ABI identification +#define ELFOSABI_NONE 0 //!< No extension +#define ELFOSABI_HPUX 1 //!< HP-UX +#define ELFOSABI_NETBSD 2 //!< NetBSD +#define ELFOSABI_LINUX 3 //!< Linux +#define ELFOSABI_SOLARIS 6 //!< Sun Solaris +#define ELFOSABI_AIX 7 //!< AIX +#define ELFOSABI_IRIX 8 //!< IRIX +#define ELFOSABI_FREEBSD 9 //!< FreeBSD +#define ELFOSABI_TRU64 10 //!< Compaq TRU64 UNIX +#define ELFOSABI_MODESTO 11 //!< Novell Modesto +#define ELFOSABI_OPENBSD 12 //!< Open BSD +#define ELFOSABI_OPENVMS 13 //!< Open VMS +#define ELFOSABI_NSK 14 //!< HP Non-Stop-Kernel + +#define EI_ABIVERSION 8 //!< ABI version + +#define EI_PAD 9 //!< Start of padding byte + + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; //!< The initial bytes mark the file as an object file and provide machine-independent data with which to decode and interpret the file's contents + Elf32_Half e_type; //!< This member identifies the object file type + Elf32_Half e_machine; //!< This member's value specifies the required architecture for an individual file + Elf32_Word e_version; //!< This member identifies the object file version + Elf32_Addr e_entry; //!< This member gives the virtual address to which the system first transfers control, thus starting the process + Elf32_Off e_phoff; //!< This member holds the program header table's file offset in bytes + Elf32_Off e_shoff; //!< This member holds the section header table's file offset in bytes + Elf32_Word e_flags; //!< This member holds processor-specific flags associated with the file + Elf32_Half e_ehsize; //!< This member holds the ELF header's size in bytes + Elf32_Half e_phentsize; //!< This member holds the size in bytes of one entry in the file's program header table; all entries are the same size + Elf32_Half e_phnum; //!< This member holds the number of entries in the program header table + Elf32_Half e_shentsize; //!< This member holds a section header's size in bytes + Elf32_Half e_shnum; //!< This member holds the number of entries in the section header table + Elf32_Half e_shstrndx; //!< This member holds the section header table index of the entry associated with the section name string table +} Elf32_Ehdr; //!< 32bit Entry Header + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; //!< The initial bytes mark the file as an object file and provide machine-independent data with which to decode and interpret the file's contents + Elf64_Half e_type; //!< This member identifies the object file type + Elf64_Half e_machine; //!< This member's value specifies the required architecture for an individual file + Elf64_Word e_version; //!< This member identifies the object file version + Elf64_Addr e_entry; //!< This member gives the virtual address to which the system first transfers control, thus starting the process + Elf64_Off e_phoff; //!< This member holds the program header table's file offset in bytes + Elf64_Off e_shoff; //!< This member holds the section header table's file offset in bytes + Elf64_Word e_flags; //!< This member holds processor-specific flags associated with the file + Elf64_Half e_ehsize; //!< This member holds the ELF header's size in bytes + Elf64_Half e_phentsize; //!< This member holds the size in bytes of one entry in the file's program header table; all entries are the same size + Elf64_Half e_phnum; //!< This member holds the number of entries in the program header table + Elf64_Half e_shentsize; //!< This member holds a section header's size in bytes + Elf64_Half e_shnum; //!< This member holds the number of entries in the section header table + Elf64_Half e_shstrndx; //!< This member holds the section header table index of the entry associated with the section name string table +} Elf64_Ehdr; //!< 64bit Entry Header + +/* + * e_type + */ +#define ET_NONE 0 //!< No file type +#define ET_REL 1 //!< Relocatable file +#define ET_EXEC 2 //!< Executable file +#define ET_DYN 3 //!< Shared object file +#define ET_CORE 4 //!< Core file +#define ET_LOOS 0xfe00 //!< Operating system-specific +#define ET_HIOS 0xfeff //!< Operating system-specific +#define ET_LOPROC 0xff00 //!< Processor-specific +#define ET_HIPROC 0xffff //!< Processor-specific + +/* + * e_machine + */ +#define EM_NONE 0 //!< No machine +#define EM_M32 1 //!< AT&T WE 32100 +#define EM_SPARC 2 //!< SUN SPARC +#define EM_386 3 //!< Intel 80386 +#define EM_68K 4 //!< Motorola 68000 +#define EM_88K 5 //!< Motorola 88000 +#define EM_860 7 //!< Intel 80860 +#define EM_MIPS 8 //!< MIPS I architecture +#define EM_S370 9 //!< IBM System/370 +#define EM_MIPS_RS3_LE 10 //!< MIPS R3000 little-endian +#define EM_PARISC 15 //!< HPPA +#define EM_VPP500 17 //!< Fujitsu VPP500 +#define EM_SPARC32PLUS 18 //!< Enhanced instruction set SPARC +#define EM_960 19 //!< Intel 80960 +#define EM_PPC 20 //!< PowerPC +#define EM_PPC64 21 //!< 64-bit PowerPC +#define EM_S390 22 //!< IBM System/390 Processor +#define EM_V800 36 //!< NEC V800 +#define EM_FR20 37 //!< Fujitsu FR20 +#define EM_RH32 38 //!< TRW RH-32 +#define EM_RCE 39 //!< Motorola RCE +#define EM_ARM 40 //!< Advanced RISC Machines ARM +#define EM_FAKE_ALPHA 41 //!< Digital Alpha +#define EM_SH 42 //!< Hitachi SH +#define EM_SPARCV9 43 //!< SPARC Version 9 +#define EM_TRICORE 44 //!< Siemens TriCore embedded processor +#define EM_ARC 45 //!< Argonaut RISC Core, Argonaut Technologies Inc +#define EM_H8_300 46 //!< Hitachi H8/300 +#define EM_H8_300H 47 //!< Hitachi H8/300H +#define EM_H8S 48 //!< Hitachi H8S +#define EM_H8_500 49 //!< Hitachi H8/500 +#define EM_IA_64 50 //!< Intel IA-64 processor architecture +#define EM_MIPS_X 51 //!< Stanford MIPS-X +#define EM_COLDFIRE 52 //!< Motorola ColdFire +#define EM_68HC12 53 //!< Motorola M68HC12 +#define EM_MMA 54 //!< Fujitsu MMA Multimedia Accelerator +#define EM_PCP 55 //!< Siemens PCP +#define EM_NCPU 56 //!< Sony nCPU embedded RISC processor +#define EM_NDR1 57 //!< Denso NDR1 microprocessor +#define EM_STARCORE 58 //!< Motorola Start*Core processor +#define EM_ME16 59 //!< Toyota ME16 processor +#define EM_ST100 60 //!< STMicroelectronics ST100 processor +#define EM_TINYJ 61 //!< Advanced Logic Corp. TinyJ embedded processor family +#define EM_X86_64 62 //!< AMD x86-64 architecture +#define EM_PDSP 63 //!< Sony DSP Processor +#define EM_PDP10 64 //!< Digital Equipment Corp. PDP-10 +#define EM_PDP11 65 //!< Digital Equipment Corp. PDP-11 +#define EM_FX66 66 //!< Siemens FX66 microcontroller +#define EM_ST9PLUS 67 //!< STMicroelectronics ST9+ 8/16 bit microcontroller +#define EM_ST7 68 //!< STMicroelectronics ST7 8-bit microcontroller +#define EM_68HC16 69 //!< Motorola MC68HC16 Microcontroller +#define EM_68HC11 70 //!< Motorola MC68HC11 Microcontroller +#define EM_68HC08 71 //!< Motorola MC68HC08 Microcontroller +#define EM_68HC05 72 //!< Motorola MC68HC05 Microcontroller +#define EM_SVX 73 //!< Silicon Graphics SVx +#define EM_ST19 74 //!< STMicroelectronics ST19 8-bit microcontroller +#define EM_VAX 75 //!< Digital VAX +#define EM_CRIS 76 //!< Axis Communications 32-bit embedded processor +#define EM_JAVELIN 77 //!< Infifineon Technologies 32-bit embedded processor +#define EM_FIREPATH 78 //!< Element 14 64-bit DSP Processor +#define EM_ZSP 79 //!< LSI Logic 16-bit DSP Processor +#define EM_MMIX 80 //!< Donald Knuth's educational 64-bit processor +#define EM_HUANY 81 //!< Harvard University machine-independent object files +#define EM_PRISM 82 //!< SiTera Prism +#define EM_AVR 83 //!< Atmel AVR 8-bit microcontroller +#define EM_FR30 84 //!< Fujitsu FR30 +#define EM_D10V 85 //!< Mitsubishi D10V +#define EM_D30V 86 //!< Mitsubishi D30V +#define EM_V850 87 //!< NEC v850 +#define EM_M32R 88 //!< Mitsubishi M32R +#define EM_MN10300 89 //!< Matsushita MN10300 +#define EM_MN10200 90 //!< Matsushita MN10200 +#define EM_PJ 91 //!< picoJava +#define EM_OPENRISC 92 //!< OpenRISC 32-bit embedded processor +#define EM_ARC_A5 93 //!< ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5) +#define EM_XTENSA 94 //!< Tensilica Xtensa Architecture +#define EM_VIDEOCORE 95 //!< Alphamosaic VideoCore processor +#define EM_TMM_GPP 96 //!< Thompson Multimedia General Purpose Processor +#define EM_NS32K 97 //!< National Semiconductor 32000 series +#define EM_TPC 98 //!< Tenor Network TPC processor +#define EM_SNP1K 99 //!< Trebia SNP 1000 processor +#define EM_ST200 100 //!< STMicroelectronics (www.st.com) ST200 microcontroller +#define EM_IP2K 101 //!< Ubicom IP2xxx microcontroller family +#define EM_MAX 102 //!< MAX Processor +#define EM_CR 103 //!< National Semiconductor CompactRISC microprocessor +#define EM_F2MC16 104 //!< Fujitsu F2MC16 +#define EM_MSP430 105 //!< Texaxas Instruments embedded microcontroller msp430 +#define EM_BLACKFIN 106 //!< Analog Devices Blackfin (DSP) processor +#define EM_SE_C33 107 //!< S1C33 Family of Seiko Epspson processors +#define EM_SEP 108 //!< Sharp embedded microprocessor +#define EM_ARCA 109 //!< Arca RISC Microprocessor +#define EM_UNICORE 110 //!< Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University +#define EM_EXCESS 111 //!< eXcess: 16/32/64-bit configurable embedded CPU +#define EM_DXP 112 //!< Icera Semiconductor Inc. Deep Execution Processor +#define EM_ALTERA_NIOS2 113 //!< Altera Nios II soft-core processor +#define EM_CRX 114 //!< National Semiconductor CompactRISC CRX microprocessor +#define EM_XGATE 115 //!< Motorola XGATE embedded processor +#define EM_C166 116 //!< Infifineon C16x/XC16x processor +#define EM_M16C 117 //!< Renesas M16C series microprocessors +#define EM_DSPIC30F 118 //!< Microchip Technology dsPIC30F Digital Signal Controller +#define EM_CE 119 //!< Freescale Communication Engine RISC core +#define EM_M32C 120 //!< Renesas M32C series microprocessors +#define EM_TSK3000 131 //!< Altium TSK3000 core +#define EM_RS08 132 //!< Freescale RS08 embedded processor +#define EM_ECOG2 134 //!< Cyan Technology eCOG2 microprocessor +#define EM_SCORE7 135 //!< Sunplus S+core7 RISC processor +#define EM_DSP24 136 //!< New Japan Radio (NJR) 24-bit DSP Processor +#define EM_VIDEOCORE3 137 //!< Broadcom VideoCore III processor +#define EM_LATTICEMICO32 138 //!< RISC processor for Lattice FPGA architecture +#define EM_SE_C17 139 //!< Seiko Epspson C17 family +#define EM_TI_C6000 140 //!< The Texaxas Instruments TMS320C6000 DSP family +#define EM_TI_C2000 141 //!< The Texaxas Instruments TMS320C2000 DSP family +#define EM_TI_C5500 142 //!< The Texaxas Instruments TMS320C55x DSP family +#define EM_MMDSP_PLUS 160 //!< STMicroelectronics 64bit VLIW Data Signal Processor +#define EM_CYPRESS_M8C 161 //!< Cypress M8C microprocessor +#define EM_R32C 162 //!< Renesas R32C series microprocessors +#define EM_TRIMEDIA 163 //!< NXP Semiconductors TriMedia architecture family +#define EM_QDSP6 164 //!< QUALCOMM DSP6 Processor +#define EM_8051 165 //!< Intel 8051 and variants +#define EM_STXP7X 166 //!< STMicroelectronics STxP7x family of configurable and extensible RISC processors +#define EM_NDS32 167 //!< Andes Technology compact code size embedded RISC processor family +#define EM_ECOG1 168 //!< Cyan Technology eCOG1X family +#define EM_ECOG1X 168 //!< Cyan Technology eCOG1X family +#define EM_MAXQ30 169 //!< Dallas Semiconductor MAXQ30 Core Micro-controllers +#define EM_XIMO16 170 //!< New Japan Radio (NJR) 16-bit DSP Processor +#define EM_MANIK 171 //!< M2000 Reconfigurable RISC Microprocessor +#define EM_CRAYNV2 172 //!< Cray Inc. NV2 vector architecture +#define EM_RX 173 //!< Renesas RX family +#define EM_METAG 174 //!< Imagination Technologies META processor architecture +#define EM_MCST_ELBRUS 175 //!< MCST Elbrus general purpose hardware architecture +#define EM_ECOG16 176 //!< Cyan Technology eCOG16 family +#define EM_CR16 177 //!< National Semiconductor CompactRISC CR16 16-bit microprocessor +#define EM_ETPU 178 //!< Freescale Extended Time Processing Unit +#define EM_SLE9X 179 //!< Infifineon Technologies SLE9X core +#define EM_AVR32 185 //!< Atmel Corporation 32-bit microprocessor family +#define EM_STM8 186 //!< STMicroeletronics STM8 8-bit microcontroller +#define EM_TILE64 187 //!< Tilera TILE64 multicore architecture family +#define EM_TILEPRO 188 //!< Tilera TILEPro multicore architecture family +#define EM_MICROBLAZE 189 //!< Xilinx MicroBlaze 32-bit RISC soft processor core +#define EM_CUDA 190 //!< NVIDIA CUDA architecture +#define EM_TILEGX 191 //!< Tilera TILE-Gx multicore architecture family + +/* + * e_version (version) + */ +#define EV_NONE 0 //!< Invalid version +#define EV_CURRENT 1 //!< Current version + + +/********************************************* + * Section + *********************************************/ +typedef struct +{ + Elf32_Word sh_name; //!< This member specifies the name of the section + Elf32_Word sh_type; //!< This member categorizes the section's contents and semantics + Elf32_Word sh_flags; //!< Sections support 1-bit flags that describe miscellaneous attributes + Elf32_Addr sh_addr; //!< If the section will appear in the memory image of a process, this member gives the address at which the section's first byte should reside + Elf32_Off sh_offset; //!< This member's value gives the byte offset from the beginning of the file to the first byte in the section + Elf32_Word sh_size; //!< This member gives the section's size in bytes + Elf32_Word sh_link; //!< This member holds a section header table index link, whose interpretation depends on the section type + Elf32_Word sh_info; //!< This member holds extra information, whose interpretation depends on the section type + Elf32_Word sh_addralign; //!< Some sections have address alignment constraints + Elf32_Word sh_entsize; //!< Some sections hold a table of fixed-size entries, such as a symbol table +} Elf32_Shdr; //!< 32bit Section header + +typedef struct +{ + Elf64_Word sh_name; //!< This member specifies the name of the section + Elf64_Word sh_type; //!< This member categorizes the section's contents and semantics + Elf64_Xword sh_flags; //!< Sections support 1-bit flags that describe miscellaneous attributes + Elf64_Addr sh_addr; //!< If the section will appear in the memory image of a process, this member gives the address at which the section's first byte should reside + Elf64_Off sh_offset; //!< This member's value gives the byte offset from the beginning of the file to the first byte in the section + Elf64_Xword sh_size; //!< This member gives the section's size in bytes + Elf64_Word sh_link; //!< This member holds a section header table index link, whose interpretation depends on the section type + Elf64_Word sh_info; //!< This member holds extra information, whose interpretation depends on the section type + Elf64_Xword sh_addralign; //!< Some sections have address alignment constraints + Elf64_Xword sh_entsize; //!< Some sections hold a table of fixed-size entries, such as a symbol table +} Elf64_Shdr; //!< 64bit Section header + +/* + * Special Section Indexes + */ +#define SHN_UNDEF 0 //!< This value marks an undefined, missing, irrelevant, or otherwise meaningless section reference +#define SHN_LORESERVE 0xff00 //!< This value specifies the lower bound of the range of reserved indexes +#define SHN_LOPROC 0xff00 //!< Values in this inclusive range are reserved for processor-specific semantics +#define SHN_HIPROC 0xff1f //!< Values in this inclusive range are reserved for processor-specific semantics +#define SHN_LOOS 0xff20 //!< Values in this inclusive range are reserved for operating system-specific semantics +#define SHN_HIOS 0xff3f //!< Values in this inclusive range are reserved for operating system-specific semantics +#define SHN_ABS 0xfff1 //!< This value specifies absolute values for the corresponding reference +#define SHN_COMMON 0xfff2 //!< Symbols defined relative to this section are common symbols +#define SHN_XINDEX 0xffff //!< This value is an escape value +#define SHN_HIRESERVE 0xffff //!< This value specifies the upper bound of the range of reserved indexes + +/* + * sh_type + */ +#define SHT_NULL 0 //!< This value marks the section header as inactive +#define SHT_PROGBITS 1 //!< The section holds information defined by the program +#define SHT_SYMTAB 2 //!< These sections hold a symbol table +#define SHT_STRTAB 3 //!< The section holds a string table +#define SHT_RELA 4 //!< The section holds relocation entries with explicit addends, such as type Elf32_Rela for the 32-bit class of object files or type Elf64_Rela for the 64-bit class of object files +#define SHT_HASH 5 //!< The section holds a symbol hash table +#define SHT_DYNAMIC 6 //!< The section holds information for dynamic linking +#define SHT_NOTE 7 //!< The section holds information that marks the file in some way +#define SHT_NOBITS 8 //!< A section of this type occupies no space in the file but otherwise resembles SHT_PROGBITS +#define SHT_REL 9 //!< The section holds relocation entries without explicit addends, such as type Elf32_Rel for the 32-bit class of object files or type Elf64_Rel for the 64-bit class of object files +#define SHT_SHLIB 10 //!< This section type is reserved but has unspecified semantics +#define SHT_DYNSYM 11 //!< +#define SHT_INIT_ARRAY 14 //!< This section contains an array of pointers to initialization functions +#define SHT_FINI_ARRAY 15 //!< This section contains an array of pointers to termination functions +#define SHT_PREINIT_ARRAY 16 //!< This section contains an array of pointers to functions that are invoked before all other initialization functions +#define SHT_GROUP 17 //!< This section defines a section group +#define SHT_SYMTAB_SHNDX 18 //!< This section is associated with a section of type SHT_SYMTAB and is required if any of the section header indexes referenced by that symbol table contain the escape value SHN_XINDEX +#define SHT_LOOS 0x60000000 //!< Values in this inclusive range are reserved for operating system-specific semantics +#define SHT_HIOS 0x6fffffff //!< Values in this inclusive range are reserved for operating system-specific semantics +#define SHT_LOPROC 0x70000000 //!< Values in this inclusive range are reserved for processor-specific semantics +#define SHT_HIPROC 0x7fffffff //!< Values in this inclusive range are reserved for processor-specific semantics +#define SHT_LOUSER 0x80000000 //!< This value specifies the upper bound of the range of indexes reserved for application programs +#define SHT_HIUSER 0x8fffffff //!< This value specifies the upper bound of the range of indexes reserved for application programs + +/* + * sh_flags + */ +#define SHF_WRITE 0x1 //!< The section contains data that should be writable during process execution +#define SHF_ALLOC 0x2 //!< The section occupies memory during process execution +#define SHF_EXECINSTR 0x4 //!< The section contains executable machine instructions +#define SHF_MERGE 0x10 //!< The data in the section may be merged to eliminate duplication +#define SHF_STRINGS 0x20 //!< The data elements in the section consist of null-terminated character strings +#define SHF_INFO_LINK 0x40 //!< The sh_info field of this section header holds a section header table index +#define SHF_LINK_ORDER 0x80 //!< This flag adds special ordering requirements for link editors +#define SHF_OS_NONCONFORMING 0x100 //!< This section requires special OS-specific processing (beyond the standard linking rules) to avoid incorrect behavior +#define SHF_GROUP 0x200 //!< This section is a member (perhaps the only one) of a section group +#define SHF_TLS 0x400 //!< This section holds Thread-Local Storage, meaning that each separate execution flow has its own distinct instance of this data +#define SHF_MASKOS 0x0ff00000 //!< All bits included in this mask are reserved for operating system-specific semantics +#define SHF_MASKPROC 0xf0000000 //!< All bits included in this mask are reserved for processor-specific semantics + + +/********************************************* + * Symbol + *********************************************/ +typedef struct +{ + Elf32_Word st_name; //!< This member holds an index into the object file's symbol string table, which holds the character representations of the symbol names + Elf32_Addr st_value; //!< This member gives the value of the associated symbol + Elf32_Word st_size; //!< Many symbols have associated sizes + unsigned char st_info; //!< This member specifies the symbol's type and binding attributes + unsigned char st_other; //!< This member currently specifies a symbol's visibility + Elf32_Section st_shndx; //!< Every symbol table entry is defined in relation to some section +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; //!< This member holds an index into the object file's symbol string table, which holds the character representations of the symbol names + unsigned char st_info; //!< This member specifies the symbol's type and binding attributes + unsigned char st_other; //!< This member currently specifies a symbol's visibility + Elf64_Section st_shndx; //!< Every symbol table entry is defined in relation to some section + Elf64_Addr st_value; //!< This member gives the value of the associated symbol + Elf64_Xword st_size; //!< Many symbols have associated sizes +} Elf64_Sym; + +/* + * st_info + */ +#define ELF32_ST_BIND(i) ((i)>>4) +#define ELF32_ST_TYPE(i) ((i)&0xf) +#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) + +#define ELF64_ST_BIND(i) ((i)>>4) +#define ELF64_ST_TYPE(i) ((i)&0xf) +#define ELF64_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) + + +/* st_info (symbol binding) */ +#define STB_LOCAL 0 //!< Local symbols are not visible outside the object file containing their definition +#define STB_GLOBAL 1 //!< Global symbols are visible to all object files being combined +#define STB_WEAK 2 //!< Weak symbols resemble global symbols, but their definitions have lower precedence +#define STB_LOOS 10 //!< Values in this inclusive range are reserved for operating system-specific semantics +#define STB_HIOS 12 //!< Values in this inclusive range are reserved for operating system-specific semantics +#define STB_LOPROC 13 //!< Values in this inclusive range are reserved for processor-specific semantics +#define STB_HIPROC 15 //!< Values in this inclusive range are reserved for processor-specific semantics + +/* st_info (symbol type) */ +#define STT_NOTYPE 0 //!< The symbol's type is not specified +#define STT_OBJECT 1 //!< The symbol is associated with a data object, such as a variable, an array, and so on +#define STT_FUNC 2 //!< The symbol is associated with a function or other executable code +#define STT_SECTION 3 //!< The symbol is associated with a section +#define STT_FILE 4 //!< Conventionally, the symbol's name gives the name of the source file associated with the object file +#define STT_COMMON 5 //!< The symbol labels an uninitialized common block +#define STT_TLS 6 //!< The symbol specifies a Thread-Local Storage entity +#define STT_LOOS 10 //!< Values in this inclusive range are reserved for operating system-specific semantics +#define STT_HIOS 12 //!< Values in this inclusive range are reserved for operating system-specific semantics +#define STT_LOPROC 13 //!< Values in this inclusive range are reserved for processor-specific semantics +#define STT_HIPROC 15 //!< Values in this inclusive range are reserved for processor-specific semantics + +/* + * st_other + */ +#define ELF32_ST_VISIBILITY(o) ((o)&0x3) +#define ELF64_ST_VISIBILITY(o) ((o)&0x3) + + +#define STV_DEFAULT 0 //!< The visibility of symbols with the STV_DEFAULT attribute is as specified by the symbol's binding type +#define STV_INTERNAL 1 //!< A symbol defined in the current component is protected if it is visible in other components but not preemptable, meaning that any reference to such a symbol from within the defining component must be resolved to the definition in that component, even if there is a definition in another component that would preempt by the default rules +#define STV_HIDDEN 2 //!< A symbol defined in the current component is hidden if its name is not visible to other components +#define STV_PROTECTED 3 //!< The meaning of this visibility attribute may be defined by processor supplements to further constrain hidden symbols + + +/********************************************* + * Relocation + *********************************************/ +typedef struct +{ + Elf32_Addr r_offset; //!< This member gives the location at which to apply the relocation action + Elf32_Word r_info; //!< This member gives both the symbol table index with respect to which the relocation must be made, and the type of relocation to apply +} Elf32_Rel; //!< 32bits Relocation Entries + +typedef struct +{ + Elf64_Addr r_offset; //!< This member gives the location at which to apply the relocation action + Elf64_Xword r_info; //!< This member gives both the symbol table index with respect to which the relocation must be made, and the type of relocation to apply +} Elf64_Rel; //!< 32bits Relocation Entries + +typedef struct +{ + Elf32_Addr r_offset; //!< This member gives the location at which to apply the relocation action + Elf32_Word r_info; //!< This member gives both the symbol table index with respect to which the relocation must be made, and the type of relocation to apply + Elf32_Sword r_addend; //!< This member specifies a constant addend used to compute the value to be stored into the relocatable field +} Elf32_Rela; //!< 32bits Relocation Addend Entries + +typedef struct +{ + Elf64_Addr r_offset; //!< This member gives the location at which to apply the relocation action + Elf64_Xword r_info; //!< This member gives both the symbol table index with respect to which the relocation must be made, and the type of relocation to apply + Elf64_Sxword r_addend; //!< This member specifies a constant addend used to compute the value to be stored into the relocatable field +} Elf64_Rela; //!< 32bits Relocation Addend Entries + + +/* + * r_info + */ +#define ELF32_R_SYM(i) ((i)>>8) +#define ELF32_R_TYPE(i) ((unsigned char)(i)) +#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t)) + +#define ELF64_R_SYM(i) ((i)>>32) +#define ELF64_R_TYPE(i) ((i)&0xffffffffL) +#define ELF64_R_INFO(s,t) (((s)<<32)+((t)&0xffffffffL)) + + + +/********************************************* + * Program + *********************************************/ +typedef struct +{ + Elf32_Word p_type; //!< This member tells what kind of segment this array element describes or how to interpret the array element's information + Elf32_Off p_offset; //!< This member gives the offset from the beginning of the file at which the first byte of the segment resides + Elf32_Addr p_vaddr; //!< This member gives the virtual address at which the first byte of the segment resides in memory + Elf32_Addr p_paddr; //!< On systems for which physical addressing is relevant, this member is reserved for the segment's physical address + Elf32_Word p_filesz; //!< This member gives the number of bytes in the file image of the segment; it may be zero + Elf32_Word p_memsz; //!< This member gives the number of bytes in the memory image of the segment; it may be zero + Elf32_Word p_flags; //!< This member gives flags relevant to the segment + Elf32_Word p_align; //!< As ``Program Loading'' describes in this chapter of the processor supplement, loadable process segments must have congruent values for p_vaddr and p_offset, modulo the page size +} Elf32_Phdr; //!< 32bits Program header + +typedef struct +{ + Elf64_Word p_type; //!< This member tells what kind of segment this array element describes or how to interpret the array element's information + Elf64_Word p_flags; //!< This member gives flags relevant to the segment + Elf64_Off p_offset; //!< This member gives the offset from the beginning of the file at which the first byte of the segment resides + Elf64_Addr p_vaddr; //!< This member gives the virtual address at which the first byte of the segment resides in memory + Elf64_Addr p_paddr; //!< On systems for which physical addressing is relevant, this member is reserved for the segment's physical address + Elf64_Xword p_filesz; //!< This member gives the number of bytes in the file image of the segment; it may be zero + Elf64_Xword p_memsz; //!< This member gives the number of bytes in the memory image of the segment; it may be zero + Elf64_Xword p_align; //!< As ``Program Loading'' describes in this chapter of the processor supplement, loadable process segments must have congruent values for p_vaddr and p_offset, modulo the page size +} Elf64_Phdr; //!< 64bits Program header + +/* + * p_type + */ +#define PT_NULL 0 //!< The array element is unused; other members' values are undefined +#define PT_LOAD 1 //!< The array element specifies a loadable segment, described by p_filesz and p_memsz +#define PT_DYNAMIC 2 //!< The array element specifies dynamic linking information +#define PT_INTERP 3 //!< The array element specifies the location and size of a null-terminated path name to invoke as an interpreter +#define PT_NOTE 4 //!< The array element specifies the location and size of auxiliary information +#define PT_SHLIB 5 //!< This segment type is reserved but has unspecified semantics +#define PT_PHDR 6 //!< The array element, if present, specifies the location and size of the program header table itself, both in the file and in the memory image of the program +#define PT_TLS 7 //!< The array element specifies the Thread-Local Storage template +#define PT_LOOS 0x60000000 //!< Values in this inclusive range are reserved for operating system-specific semantics +#define PT_HIOS 0x6fffffff //!< Values in this inclusive range are reserved for operating system-specific semantics +#define PT_LOPROC 0x70000000 //!< Values in this inclusive range are reserved for processor-specific semantics +#define PT_HIPROC 0x7fffffff //!< Values in this inclusive range are reserved for processor-specific semantics + +/* + * p_flags + */ +#define PF_X (1 << 0) //!< Execute +#define PF_W (1 << 1) //!< Write +#define PF_R (1 << 2) //!< Read +#define PF_MASKOS 0x0ff00000 //!< Unspecified +#define PF_MASKPROC 0xf0000000 //!< Unspecified + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/elfapi.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/elfapi.h new file mode 100644 index 00000000000..cce6d158b4e --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/elfapi.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Elf loder internal methods. + * + * \defgroup ELFLOADER MMDSP ELF loader. + */ +#ifndef __INC_CM_ELFLOADER_H +#define __INC_CM_ELFLOADER_H + +#include <cm/engine/elf/inc/common.h> + +/*! + * \internal + * \brief ELF Parsing & checking + * \ingroup ELFLOADER + */ +t_cm_error cm_ELF_CheckFile( + const char *elfdata, + t_bool temporaryDescription, + t_elfdescription **elfhandlePtr); + +void cm_ELF_ReleaseDescription( + t_uint32 requireNumber, t_interface_require *requires, + t_uint32 attributeNumber, t_attribute *attributes, + t_uint32 propertyNumber, t_property *properties, + t_uint32 provideNumber, t_interface_provide *provides); + +/*! + * \internal + * \brief ELF closing + * \ingroup ELFLOADER + */ +void cm_ELF_CloseFile( + t_bool temporaryDescription, + t_elfdescription *elfhandle); + +/*! + * \internal + * \brief Load a component template shared memories. + * + * \note In case of error, part of memory could have been allocated and must be free by calling cm_DSPABI_FreeTemplate. + */ +t_cm_error cm_ELF_LoadTemplate( + t_cm_domain_id domainId, + t_elfdescription *elfhandle, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY], + t_bool isSingleton); + +/*! + * \internal + * \brief Clean cache memory of a component template shared code. + */ +void cm_ELF_FlushTemplate( + t_nmf_core_id coreId, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY]); + +void cm_ELF_FlushInstance( + t_nmf_core_id coreId, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY], + t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY]); + +/*! + * \internal + * \brief Load a component instance private memories. + * + * \note In case of error, part of memory could have been allocated and must be free by calling cm_DSPABI_FreeInstance. + */ +t_cm_error cm_ELF_LoadInstance( + t_cm_domain_id domainId, + t_elfdescription *elfhandle, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY], + t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY], + t_bool isSingleton); + +void cm_ELF_FreeInstance( + t_nmf_core_id coreId, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY], + t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY]); +void cm_ELF_FreeTemplate( + t_nmf_core_id coreId, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY]); + + +t_cm_error cm_ELF_relocateSharedSegments( + t_memory_handle *memories, + t_elfdescription *elfhandle, + void *cbContext); +t_cm_error cm_ELF_relocatePrivateSegments( + t_memory_handle *memories, + t_elfdescription *elfhandle, + void *cbContext); +void cm_ELF_performRelocation( + t_uint32 type, + const char *symbol_name, + t_uint32 symbol_addr, + char *reloc_addr); +t_cm_error cm_ELF_GetMemory( + t_elfdescription *elf, + t_tmp_elfdescription *elftmp, + t_uint32 address, + t_memory_purpose purpose, + t_memory_reference *memory); + + +#include <cm/engine/component/inc/component_type.h> + +t_cm_error cm_DSPABI_AddLoadMap( + t_cm_domain_id domainId, + const char* templateName, + const char* localname, + t_memory_handle *memories, + void *componentHandle); +t_cm_error cm_DSPABI_RemoveLoadMap( + t_cm_domain_id domainId, + const char* templateName, + t_memory_handle *memories, + const char* localname, + void *componentHandle); + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/memory.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/memory.h new file mode 100644 index 00000000000..9eab94f173c --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/memory.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Elf memory. + */ +#ifndef __INC_CM_ELF_MEMORY_H +#define __INC_CM_ELF_MEMORY_H + +#include <cm/engine/dsp/inc/dsp.h> + +/** + * \brief Memory identifier + */ +typedef t_uint8 t_memory_id; + +/** + * \brief Memory property + */ +typedef enum { + MEM_FOR_MULTIINSTANCE, + MEM_FOR_SINGLETON, + MEM_FOR_LAST +} t_instance_property; + +/** + * \brief Memory prupose (for processor with different address space for code and data/ + */ +typedef enum { + MEM_CODE, + MEM_DATA +} t_memory_purpose; + +/** + * \brief Memory property + */ +typedef enum { + MEM_PRIVATE, + MEM_SHARABLE, +} t_memory_property; + +/** + * \brief Elf memory mapping description + */ +typedef struct +{ + t_memory_id id; + t_dsp_memory_type_id dspMemType; + t_uint32 startAddr; + t_cm_memory_alignment memAlignement; + t_memory_property property; + t_memory_purpose purpose; + t_uint8 fileEntSize; + t_uint8 memEntSize; + char* memoryName; +} t_elfmemory; + +#define NUMBER_OF_MMDSP_MEMORY 15 + +/* + * \brief Elf segment description + */ +typedef struct { + // Data in Bytes + t_uint32 sumSize; + t_bool sumSizeSetted; + t_cm_logical_address hostAddr; // Valid only if section Load in memory + t_uint32 maxAlign; + // Data in word + t_uint32 mpcAddr; // Valid only if section Load in memory +} t_elfSegment; + + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp-loadmap.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp-loadmap.h new file mode 100644 index 00000000000..bb65c0b1244 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp-loadmap.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Elf writer internal methods. + * + * \defgroup LOADMAP MMDSP ELF writer (a linker in fact). + */ +#ifndef __INC_CM_LOADMAP_H +#define __INC_CM_LOADMAP_H + +#include <cm/inc/cm_type.h> + +/* + * Align with loadmap : + * https://codex.cro.st.com/wiki/index.php?pagename=Specification%2FLoadmap%2Fv1.2&group_id=310 + */ +#define LOADMAP_MAGIC_NUMBER 0xFBBF + +#define LOADMAP_VERSION_MSB 1 +#define LOADMAP_VERSION_LSB 2 + +struct LoadMapItem +{ + const char* pSolibFilename; // Filename of shared library object + void* pAddrProg; // Load address of program section + void* pAddrEmbProg; // Load address of embedded program section + void* pThis; // Data base address of component instance + void* pARMThis; // ARM component debug ID + const char* pComponentName; // Pretty name of the component instance, NULL if none. + struct LoadMapItem* pNextItem;// Pointer on the next list item, NULL if last one. + void* pXROM; // Start address of XROM + void* pYROM; // Start address of YROM +}; + +struct LoadMapHdr +{ + t_uint16 nMagicNumber; // Equal to 0xFBBF. + t_uint16 nVersion; // The version of the load map format. + t_uint32 nRevision; // A counter incremented at each load map list modification. + struct LoadMapItem* pFirstItem;// Pointer on the first item, NULL if no shared library loaded. +}; + +#endif /* __INC_CM_LOADMAP_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp.h new file mode 100644 index 00000000000..1662def6c1a --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief MMDSP elf. + */ +#ifndef __INC_CM_ELF_MMDSP_H +#define __INC_CM_ELF_MMDSP_H + +#include <cm/engine/elf/inc/common.h> + +#define CODE_MEMORY_INDEX 0 +#define ECODE_MEMORY_INDEX 7 + +#define XROM_MEMORY_INDEX 1 +#define YROM_MEMORY_INDEX 2 +#define PRIVATE_DATA_MEMORY_INDEX 8 +#define SHARE_DATA_MEMORY_INDEX 1 + +/* + * Relocation + */ +#define R_MMDSP_IMM16 5 +#define R_MMDSP_IMM20_16 6 +#define R_MMDSP_IMM20_4 7 +#define R_MMDSP_24 13 + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/mpcal.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/mpcal.h new file mode 100644 index 00000000000..718b7f61ceb --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/mpcal.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief MPC Abraction Layer. + * + * \defgroup MPCAL MPC Abraction Layer. + */ +#ifndef __INC_CM_DSP_MPCAL_H +#define __INC_CM_DSP_MPCAL_H + +#include <cm/inc/cm_type.h> +#include <share/inc/nmf.h> + +#include <cm/engine/elf/inc/common.h> + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/reloc.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/reloc.h new file mode 100644 index 00000000000..b38be48d689 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/reloc.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Elf relocation. + */ +#ifndef __INC_CM_ELF_RELOC_H +#define __INC_CM_ELF_RELOC_H + + +void MMDSP_performRelocation( + t_uint32 type, + const char* symbol_name, + t_uint32 symbol_addr, + char* reloc_addr, + const char* inPlaceAddr, + t_uint32 reloc_offset); + +/* + * + * Return: + * 0x0 returned if symbol not found + * 0xFFFFFFFE returned if out of memory + * 0xFFFFFFFF returned if symbol found in static required binding + */ +typedef t_uint32 (*CBresolvSymbol)( + void* context, + t_uint32 type, + const char* symbolName, + char* reloc_addr); + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/elf64.c b/drivers/staging/nmf-cm/cm/engine/elf/src/elf64.c new file mode 100644 index 00000000000..2e0f5928ffd --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/src/elf64.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/elf/inc/common.h> +#include <cm/engine/elf/inc/elfabi.h> + +#include <cm/engine/utils/inc/swap.h> +#include <cm/engine/trace/inc/trace.h> + +typedef Elf64_Half ElfXX_Half; +typedef Elf64_Word ElfXX_Word; +typedef Elf64_Addr ElfXX_Addr; +typedef Elf64_Off ElfXX_Off; + +typedef Elf64_Xword ElfXX_Xword; + +typedef Elf64_Ehdr ElfXX_Ehdr; +typedef Elf64_Shdr ElfXX_Shdr; +typedef Elf64_Sym ElfXX_Sym; +typedef Elf64_Rela ElfXX_Rela; + +#undef ELFXX_R_SYM +#define ELFXX_R_SYM ELF64_R_SYM +#undef ELFXX_R_TYPE +#define ELFXX_R_TYPE ELF64_R_TYPE +#undef ELFXX_R_INFO +#define ELFXX_R_INFO ELF64_R_INFO + +// TODO Here we assume big endian (MMDSP !) +static Elf64_Half swapHalf(Elf64_Half half) +{ + return (Elf64_Half)swap16(half); +} + +static Elf64_Word swapWord(Elf64_Word word) +{ + return (Elf64_Word)swap32(word); +} + +static Elf64_Xword swapXword(Elf64_Xword xword) +{ + return (Elf64_Xword)swap64(xword); +} + +#include "elfxx.c" diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/elfload.c b/drivers/staging/nmf-cm/cm/engine/elf/src/elfload.c new file mode 100644 index 00000000000..7950cd99007 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/src/elfload.c @@ -0,0 +1,755 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * + */ +#include <cm/engine/elf/inc/elfapi.h> +#include <cm/engine/elf/inc/mpcal.h> +#include <cm/inc/cm_def.h> + +//#include <cm/engine/component/inc/introspection.h> + +#include <cm/engine/utils/inc/mem.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/utils/inc/swap.h> +#include <cm/engine/utils/inc/string.h> + +static void* getElfHeaderReference(t_tmp_elfdescription *elftmp, void* hdrref) +{ + if(hdrref != NULL) + return (void*)((int)swap32((t_uint32)hdrref) + (int)elftmp->elfheader); + else + return NULL; +} + +static t_dup_char copyElfString(t_tmp_elfdescription *elftmp, char* idx) +{ + return cm_StringDuplicate((char*)getElfHeaderReference(elftmp, (void*)idx)); +} + +static t_cm_error getMemoryOffset( + t_elfdescription *elfhandle, + t_tmp_elfdescription *elftmp, + t_memory_purpose purpose, + const t_uint32 *addressInNmf, + t_memory_reference *memory) { + + if(elftmp->isExecutable) { + return cm_ELF_GetMemory(elfhandle, elftmp, + swap32(*addressInNmf), + purpose, + memory); + } else { + return ELF64_getRelocationMemory(elfhandle, elftmp, + (t_uint32)addressInNmf - (t_uint32)elftmp->elfheader, + memory); + } +} + +static t_cm_error getAdressForExecutableOffsetElsewhere( + t_elfdescription *elfhandle, + t_tmp_elfdescription *elftmp, + const t_uint32 *addressInNmf, + t_memory_reference *memory) { + t_uint32 address; + + address = swap32(*addressInNmf); + if(address == 0xFFFFFFFF) + { + memory->offset = 0x0; + memory->memory = NULL; + return CM_OK; + } + + if(elftmp->isExecutable) + { + memory->offset = address; + memory->memory = NULL; + return CM_OK; + } + + // Error log in elfhandle by previous call will be check in loadTemplate + return ELF64_getRelocationMemory(elfhandle, elftmp, + (t_uint32)addressInNmf - (t_uint32)elftmp->elfheader, + memory); +} + +/* + * Interface Management + */ +static t_interface_description* interfaceList = NULL; + +static t_interface_description* getInterfaceDescription(t_tmp_elfdescription *elftmp, t_elf_interface_description* elfitf) { + t_dup_char itfType; + t_interface_description* itf; + int i; + + itfType = copyElfString(elftmp, elfitf->type); + if(itfType == NULL) + return NULL; + + // Search if interfane already loaded + for(itf = interfaceList; itf != NULL; itf = itf->next) { + if(itf->type == itfType) { + // TODO Sanity check + + itf->referenceCounter++; + cm_StringRelease(itfType); + return itf; + } + } + + // Create a new interface if not exists + itf = (t_interface_description*)OSAL_Alloc_Zero(sizeof(t_interface_description) + sizeof(t_dup_char) * (elfitf->methodNumber - 1)); + if(itf == NULL) + goto out_itf_type; + itf->referenceCounter = 1; + itf->type = itfType; + itf->methodNumber = elfitf->methodNumber; + for(i = 0; i < itf->methodNumber; i++) { + itf->methodNames[i] = copyElfString(elftmp, elfitf->methodNames[i]); + if(itf->methodNames[i] == NULL) + goto out_method; + } + + // Put it in Top + itf->next = interfaceList; + interfaceList = itf; + + return itf; + +out_method: + for(i = 0; i < itf->methodNumber; i++) + cm_StringRelease(itf->methodNames[i]); + OSAL_Free(itf); +out_itf_type: + cm_StringRelease(itfType); + return NULL; +} + +static void releaseInterfaceDescription(t_interface_description* itf) { + if(itf == NULL) + return; + + if(--itf->referenceCounter == 0) { + int i; + + // Remove it from list + if(interfaceList == itf) { + interfaceList = interfaceList->next; + } else { + t_interface_description* prev = interfaceList; + while(prev->next != itf) + prev = prev->next; + prev->next = itf->next; + } + + // Destroy interface description + for(i = 0; i < itf->methodNumber; i++) { + cm_StringRelease(itf->methodNames[i]); + } + cm_StringRelease(itf->type); + OSAL_Free(itf); + } +} + + +t_cm_error cm_ELF_CheckFile( + const char *elfdata, + t_bool temporaryDescription, + t_elfdescription **elfhandlePtr) +{ + t_elfdescription *elfhandle; + t_tmp_elfdescription elftmp; + t_cm_error error; + t_uint32 version; + t_uint32 compatibleVersion; + int i, j, k; + + /* + * Sanity check + */ + if (elfdata[EI_MAG0] != ELFMAG0 || + elfdata[EI_MAG1] != ELFMAG1 || + elfdata[EI_MAG2] != ELFMAG2 || + elfdata[EI_MAG3] != ELFMAG3 || + elfdata[EI_CLASS] != ELFCLASS64) + { + ERROR("CM_INVALID_ELF_FILE: component file is not a MMDSP ELF file\n", 0, 0, 0, 0, 0, 0); + return CM_INVALID_ELF_FILE; + } + + /* + * Create elf data + */ + if((error = ELF64_LoadComponent(EM_MMDSP_PLUS, elfdata, elfhandlePtr, &elftmp)) != CM_OK) + return error; + + elfhandle = *elfhandlePtr; + + elfhandle->temporaryDescription = temporaryDescription; + + version = swap32(elftmp.elfheader->nmfVersion); + + compatibleVersion = (VERSION_MAJOR(version) == VERSION_MAJOR(NMF_VERSION)); + if(compatibleVersion) + { + switch(VERSION_MINOR(NMF_VERSION)) + { + case 10: // Compatible with 2.9, 2.10 + compatibleVersion = + (VERSION_MINOR(version) == 9) || + (VERSION_MINOR(version) == 10); + break; + default: // Strict compatibility 2.x == 2.x + compatibleVersion = (VERSION_MINOR(version) == VERSION_MINOR(NMF_VERSION)); + } + } + + if(! compatibleVersion) + { + ERROR("CM_INVALID_ELF_FILE: incompatible version for Component %d.%d.x != CM:%d.%d.x\n", + VERSION_MAJOR(version), VERSION_MINOR(version), + VERSION_MAJOR(NMF_VERSION), VERSION_MINOR(NMF_VERSION), 0, 0); + error = CM_INVALID_ELF_FILE; + goto onerror; + } + + + /* + * Commented since to many noise !!!! + if(VERSION_PATCH(version) != VERSION_PATCH(NMF_VERSION)) + { + WARNING("CM_INVALID_ELF_FILE: incompatible version, Component:%d.%d.%d != CM:%d.%d.%d\n", + VERSION_MAJOR(version), VERSION_MINOR(version), VERSION_PATCH(version), + VERSION_MAJOR(NMF_VERSION), VERSION_MINOR(NMF_VERSION), VERSION_PATCH(NMF_VERSION)); + } + */ + + if((error = ELF64_ComputeSegment(elfhandle, &elftmp)) != CM_OK) + goto onerror; + + // + elfhandle->foundedTemplateName = copyElfString(&elftmp, elftmp.elfheader->templateName); + if(elfhandle->foundedTemplateName == NULL) + goto oom; + elfhandle->minStackSize = swap32(elftmp.elfheader->minStackSize); + + // Get Life-cycle memory + if((error = getAdressForExecutableOffsetElsewhere(elfhandle, &elftmp, &elftmp.elfheader->LCCConstruct, &elfhandle->memoryForConstruct)) != CM_OK) + goto onerror; + if((error = getAdressForExecutableOffsetElsewhere(elfhandle, &elftmp, &elftmp.elfheader->LCCStart, &elfhandle->memoryForStart)) != CM_OK) + goto onerror; + if((error = getAdressForExecutableOffsetElsewhere(elfhandle, &elftmp, &elftmp.elfheader->LCCStop, &elfhandle->memoryForStop)) != CM_OK) + goto onerror; + if((error = getAdressForExecutableOffsetElsewhere(elfhandle, &elftmp, &elftmp.elfheader->LCCDestroy, &elfhandle->memoryForDestroy)) != CM_OK) + goto onerror; + + // Copy attributes information + elfhandle->attributeNumber = swap32(elftmp.elfheader->attributeNumber); + if(elfhandle->attributeNumber > 0) + { + elfhandle->attributes = + (t_attribute*)OSAL_Alloc_Zero(sizeof(t_attribute) * elfhandle->attributeNumber); + if(elfhandle->attributes == NULL) + goto oom; + + if(elfhandle->attributeNumber > 0) + { + t_elf_attribute *attributes = (t_elf_attribute*)getElfHeaderReference(&elftmp, (void*)elftmp.elfheader->attributes); + + for(i = 0; i < elfhandle->attributeNumber; i++) + { + elfhandle->attributes[i].name = copyElfString(&elftmp, attributes[i].name); + if(elfhandle->attributes[i].name == NULL) + goto oom; + + if((error = getMemoryOffset(elfhandle, &elftmp, + MEM_DATA, + &attributes[i].symbols, + &elfhandle->attributes[i].memory)) != CM_OK) + goto onerror; + LOG_INTERNAL(2, " attribute %s mem=%s offset=%x\n", + elfhandle->attributes[i].name, + elfhandle->attributes[i].memory.memory->memoryName, + elfhandle->attributes[i].memory.offset, + 0, 0, 0); + } + } + } + + // Copy properties information + elfhandle->propertyNumber = swap32(elftmp.elfheader->propertyNumber); + if(elfhandle->propertyNumber > 0) + { + elfhandle->properties = + (t_property*)OSAL_Alloc_Zero(sizeof(t_property) * elfhandle->propertyNumber); + if(elfhandle->properties == NULL) + goto oom; + + if(elfhandle->propertyNumber > 0) + { + t_elf_property *properties = (t_elf_property*)getElfHeaderReference(&elftmp, (void*)elftmp.elfheader->properties); + + for(i = 0; i < elfhandle->propertyNumber; i++) + { + elfhandle->properties[i].name = copyElfString(&elftmp, properties[i].name); + if(elfhandle->properties[i].name == NULL) + goto oom; + + elfhandle->properties[i].value = copyElfString(&elftmp, properties[i].value); + if(elfhandle->properties[i].value == NULL) + goto oom; + + LOG_INTERNAL(3, " property %s = %s\n", + elfhandle->properties[i].name, + elfhandle->properties[i].value, + 0, 0, 0, 0); + } + } + } + + // Copy requires information + elfhandle->requireNumber = swap32(elftmp.elfheader->requireNumber); + if(elfhandle->requireNumber > 0) + { + char *ref = getElfHeaderReference(&elftmp, (void*)elftmp.elfheader->requires); + + elfhandle->requires = (t_interface_require*)OSAL_Alloc_Zero(sizeof(t_interface_require) * elfhandle->requireNumber); + if(elfhandle->requires == NULL) + goto oom; + + for(i = 0; i < elfhandle->requireNumber; i++) + { + t_elf_required_interface *require = (t_elf_required_interface*)ref; + t_elf_interface_description *interface = (t_elf_interface_description*)getElfHeaderReference(&elftmp, (void*)require->interface); + + elfhandle->requires[i].name = copyElfString(&elftmp, require->name); + if(elfhandle->requires[i].name == NULL) + goto oom; + + elfhandle->requires[i].requireTypes = require->requireTypes; + elfhandle->requires[i].collectionSize = require->collectionSize; + elfhandle->requires[i].interface = getInterfaceDescription(&elftmp, interface); + if(elfhandle->requires[i].interface == NULL) + goto oom; + + LOG_INTERNAL(2, " require %s <%s> %x\n", + elfhandle->requires[i].name, + elfhandle->requires[i].interface->type, + elfhandle->requires[i].requireTypes, 0, 0, 0); + CM_ASSERT(elfhandle->requires[i].collectionSize != 0); + + ref = (char*)&require->indexes[0]; + + if((elfhandle->requires[i].requireTypes & VIRTUAL_REQUIRE) == 0 && + (elfhandle->requires[i].requireTypes & STATIC_REQUIRE) == 0) + { + elfhandle->requires[i].indexes = + (t_interface_require_index*)OSAL_Alloc_Zero(sizeof(t_interface_require_index) * elfhandle->requires[i].collectionSize); + if(elfhandle->requires[i].indexes == NULL) + goto oom; + + for(j = 0; j < elfhandle->requires[i].collectionSize; j++) + { + t_elf_interface_require_index* index = (t_elf_interface_require_index*)ref; + + elfhandle->requires[i].indexes[j].numberOfClient = swap32(index->numberOfClient); + if(elfhandle->requires[i].indexes[j].numberOfClient != 0) + { + elfhandle->requires[i].indexes[j].memories = + (t_memory_reference*)OSAL_Alloc(sizeof(t_memory_reference) * elfhandle->requires[i].indexes[j].numberOfClient); + if(elfhandle->requires[i].indexes[j].memories == NULL) + goto oom; + + for(k = 0; k < elfhandle->requires[i].indexes[j].numberOfClient; k++) { + if((error = getMemoryOffset(elfhandle,&elftmp, + MEM_DATA, + &index->symbols[k], + &elfhandle->requires[i].indexes[j].memories[k])) != CM_OK) + goto onerror; + LOG_INTERNAL(2, " [%d, %d] mem=%s offset=%x\n", + j, k, + elfhandle->requires[i].indexes[j].memories[k].memory->memoryName, + elfhandle->requires[i].indexes[j].memories[k].offset, + 0, 0); + } + } + + ref += sizeof(index->numberOfClient) + elfhandle->requires[i].indexes[j].numberOfClient * sizeof(index->symbols[0]); + } + } + } + } + + // Copy provides informations + elfhandle->provideNumber = swap32(elftmp.elfheader->provideNumber); + if(elfhandle->provideNumber != 0) + { + elfhandle->provides = + (t_interface_provide*)OSAL_Alloc_Zero(sizeof(t_interface_provide) * elfhandle->provideNumber); + if(elfhandle->provides == NULL) + goto oom; + + if(elfhandle->provideNumber > 0) + { + char *ref = getElfHeaderReference(&elftmp, (void*)elftmp.elfheader->provides); + + for(i = 0; i < elfhandle->provideNumber; i++) + { + t_elf_provided_interface *provide = (t_elf_provided_interface*)ref; + t_elf_interface_description *interface = (t_elf_interface_description*)getElfHeaderReference(&elftmp, (void*)provide->interface); + + elfhandle->provides[i].name = copyElfString(&elftmp, provide->name); + if(elfhandle->provides[i].name == NULL) + goto oom; + + elfhandle->provides[i].provideTypes = provide->provideTypes; + elfhandle->provides[i].interruptLine = provide->interruptLine; + elfhandle->provides[i].collectionSize = provide->collectionSize; + elfhandle->provides[i].interface = getInterfaceDescription(&elftmp, interface); + if(elfhandle->provides[i].interface == NULL) + goto oom; + + LOG_INTERNAL(2, " provide %s <%s>\n", + elfhandle->provides[i].name, + elfhandle->provides[i].interface->type, + 0,0, 0, 0); + CM_ASSERT(elfhandle->provides[i].collectionSize != 0); + + ref = (char*)&provide->methodSymbols[0]; + + { + t_uint32 *methodSymbols = (t_uint32*)ref; + + elfhandle->provides[i].indexes = (t_interface_provide_index**)OSAL_Alloc_Zero( + sizeof(t_interface_provide_index*) * elfhandle->provides[i].collectionSize); + if(elfhandle->provides[i].indexes == NULL) + goto oom; + + if(elfhandle->provides[i].interface->methodNumber != 0) + { + for(j = 0; j < elfhandle->provides[i].collectionSize; j++) + { + elfhandle->provides[i].indexes[j] = (t_interface_provide_index*)OSAL_Alloc( + sizeof(t_interface_provide_index) * elfhandle->provides[i].interface->methodNumber); + if(elfhandle->provides[i].indexes[j] == NULL) + goto oom; + + for(k = 0; k < elfhandle->provides[i].interface->methodNumber; k++) + { + if((error = getAdressForExecutableOffsetElsewhere(elfhandle, &elftmp, + methodSymbols++, + &elfhandle->provides[i].indexes[j][k].memory)) != CM_OK) + goto onerror; + + if(elfhandle->provides[i].indexes[j][k].memory.memory != NULL) + LOG_INTERNAL(2, " [%d, %d] method '%s' mem=%s offset=%x\n", + j, k, + elfhandle->provides[i].interface->methodNames[k], + elfhandle->provides[i].indexes[j][k].memory.memory->memoryName, + elfhandle->provides[i].indexes[j][k].memory.offset, + 0); + else + LOG_INTERNAL(2, " [%d, %d] method '%s' address=%x\n", + j, k, + elfhandle->provides[i].interface->methodNames[k], + elfhandle->provides[i].indexes[j][k].memory.offset, + 0, 0); + } + } + } + + ref += elfhandle->provides[i].collectionSize * elfhandle->provides[i].interface->methodNumber * sizeof(methodSymbols[0]); + } + } + } + } + + return CM_OK; + +oom: + error = CM_NO_MORE_MEMORY; +onerror: + cm_ELF_CloseFile(temporaryDescription, elfhandle); + *elfhandlePtr = NULL; + return error; +} + +void cm_ELF_ReleaseDescription( + t_uint32 requireNumber, t_interface_require *requires, + t_uint32 attributeNumber, t_attribute *attributes, + t_uint32 propertyNumber, t_property *properties, + t_uint32 provideNumber, t_interface_provide *provides) +{ + int i, j; + + // Free provides (Number set when array allocated) + if(provides != NULL) + { + for(i = 0; i < provideNumber; i++) + { + if(provides[i].indexes != NULL) + { + for(j = 0; j < provides[i].collectionSize; j++) + { + OSAL_Free(provides[i].indexes[j]); + } + OSAL_Free(provides[i].indexes); + } + releaseInterfaceDescription(provides[i].interface); + cm_StringRelease(provides[i].name); + } + OSAL_Free(provides); + } + + // Free requires (Number set when array allocated) + if(requires != NULL) + { + for(i = 0; i < requireNumber; i++) + { + if(requires[i].indexes != 0) + { + for(j = 0; j < requires[i].collectionSize; j++) + { + OSAL_Free(requires[i].indexes[j].memories); + } + OSAL_Free(requires[i].indexes); + } + releaseInterfaceDescription(requires[i].interface); + cm_StringRelease(requires[i].name); + } + OSAL_Free(requires); + } + + // Free properties (Number set when array allocated) + if(properties != NULL) + { + for(i = 0; i < propertyNumber; i++) + { + cm_StringRelease(properties[i].value); + cm_StringRelease(properties[i].name); + } + OSAL_Free(properties); + } + + // Free Attributes (Number set when array allocated) + if(attributes != NULL) + { + for(i = 0; i < attributeNumber; i++) + { + cm_StringRelease(attributes[i].name); + } + OSAL_Free(attributes); + } +} + +void cm_ELF_CloseFile( + t_bool temporaryDescription, + t_elfdescription *elfhandle) +{ + if(elfhandle == NULL) + return; + + if(temporaryDescription && ! elfhandle->temporaryDescription) + return; + + // Release description if not moved to template + cm_ELF_ReleaseDescription( + elfhandle->requireNumber, elfhandle->requires, + elfhandle->attributeNumber, elfhandle->attributes, + elfhandle->propertyNumber, elfhandle->properties, + elfhandle->provideNumber, elfhandle->provides); + + cm_StringRelease(elfhandle->foundedTemplateName); + + ELF64_UnloadComponent(elfhandle); +} + + +static t_cm_error allocSegment( + t_cm_domain_id domainId, + t_elfdescription *elfhandle, + t_memory_handle memories[NUMBER_OF_MMDSP_MEMORY], + t_memory_property property, + t_bool isSingleton) { + t_memory_id memId; + const t_elfmemory *thisMemory; //!< Memory used to determine this + const t_elfmemory *codeMemory; //!< Memory used to determine code + + MMDSP_serializeMemories(elfhandle->instanceProperty, &codeMemory, &thisMemory); + + for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++) + { + const t_elfmemory* mapping; + + if(elfhandle->segments[memId].sumSize == 0x0) + continue; + + mapping = MMDSP_getMappingById(memId); + + if( + (mapping->property == property && elfhandle->instanceProperty != MEM_FOR_SINGLETON) || + (property == MEM_SHARABLE && elfhandle->instanceProperty == MEM_FOR_SINGLETON) ) + { + // Allocate segment + memories[memId] = cm_DM_Alloc(domainId, mapping->dspMemType, + elfhandle->segments[memId].sumSize / mapping->fileEntSize, + mapping->memAlignement, TRUE); + + if(memories[memId] == INVALID_MEMORY_HANDLE) + { + ERROR("CM_NO_MORE_MEMORY(%s): %x too big\n", mapping->memoryName, elfhandle->segments[memId].sumSize / mapping->fileEntSize, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + // Get reference in memory + elfhandle->segments[memId].hostAddr = cm_DSP_GetHostLogicalAddress(memories[memId]); + + cm_DSP_GetDspAddress(memories[memId], &elfhandle->segments[memId].mpcAddr); + + if (isSingleton) + cm_DM_SetDefaultDomain(memories[memId], cm_DM_GetDomainCoreId(domainId)); + + // Log it + LOG_INTERNAL(1, "\t%s%s: 0x%x..+0x%x (0x%x)\n", + mapping->memoryName, + (thisMemory == mapping) ? "(THIS)" : "", + elfhandle->segments[memId].mpcAddr, + elfhandle->segments[memId].sumSize / mapping->fileEntSize, + elfhandle->segments[memId].hostAddr, 0); + } + else if(property == MEM_PRIVATE) // Since we allocate private segment, if not allocate, it's a share one + { + // In order to allow further relocation based on cached address like mpcAddr & hostAddr, + // initialize them also ! + + // Get reference in memory + elfhandle->segments[memId].hostAddr = cm_DSP_GetHostLogicalAddress(memories[memId]); + + cm_DSP_GetDspAddress(memories[memId], &elfhandle->segments[memId].mpcAddr); + } + } + + return CM_OK; +} + +/* + * Note: in case of error, part of memory could have been allocated and must be free by calling cm_DSPABI_FreeTemplate + */ +t_cm_error cm_ELF_LoadTemplate( + t_cm_domain_id domainId, + t_elfdescription *elfhandle, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY], + t_bool isSingleton) +{ + t_cm_error error; + + if((error = allocSegment(domainId, elfhandle, sharedMemories, MEM_SHARABLE, isSingleton)) != CM_OK) + return error; + + // Load each readonly segment + if((error = ELF64_loadSegment(elfhandle, sharedMemories, MEM_SHARABLE)) != CM_OK) + return error; + + return CM_OK; +} + +t_cm_error cm_ELF_LoadInstance( + t_cm_domain_id domainId, + t_elfdescription *elfhandle, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY], + t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY], + t_bool isSingleton) +{ + t_memory_id memId; + t_cm_error error; + + // Erase whole memories to make free in case of error + for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++) + { + privateMemories[memId] = sharedMemories[memId]; + } + + if((error = allocSegment(domainId, elfhandle, privateMemories, MEM_PRIVATE, isSingleton)) != CM_OK) + return error; + + // Load each writable memory + if((error = ELF64_loadSegment(elfhandle, privateMemories, MEM_PRIVATE)) != CM_OK) + return error; + + return CM_OK; +} + +void cm_ELF_FlushTemplate( + t_nmf_core_id coreId, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY]) +{ + t_memory_id memId; + + for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++) + { + if(sharedMemories[memId] != INVALID_MEMORY_HANDLE) + MMDSP_loadedSection( + coreId, memId, + sharedMemories[memId]); + } +} + +void cm_ELF_FlushInstance( + t_nmf_core_id coreId, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY], + t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY]) +{ + t_memory_id memId; + + for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++) + { + if(privateMemories[memId] != INVALID_MEMORY_HANDLE && privateMemories[memId] != sharedMemories[memId]) + MMDSP_loadedSection( + coreId, memId, + privateMemories[memId]); + } +} + +void cm_ELF_FreeInstance( + t_nmf_core_id coreId, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY], + t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY]) +{ + t_memory_id memId; + + if(privateMemories == NULL) + return; + + for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++) + { + if(privateMemories[memId] != INVALID_MEMORY_HANDLE && privateMemories[memId] != sharedMemories[memId]) + { + MMDSP_unloadedSection(coreId, memId, privateMemories[memId]); + cm_DM_Free(privateMemories[memId], TRUE); + } + } +} + +void cm_ELF_FreeTemplate( + t_nmf_core_id coreId, + t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY]) +{ + t_memory_id memId; + + if(sharedMemories == NULL) + return; + + for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++) + { + if(sharedMemories[memId] != INVALID_MEMORY_HANDLE) + { + MMDSP_unloadedSection(coreId, memId, sharedMemories[memId]); + cm_DM_Free(sharedMemories[memId], TRUE); + } + } +} diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/elfmmdsp.c b/drivers/staging/nmf-cm/cm/engine/elf/src/elfmmdsp.c new file mode 100644 index 00000000000..5f6641b188d --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/src/elfmmdsp.c @@ -0,0 +1,575 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/elf/inc/mmdsp.h> +#include <cm/engine/elf/inc/bfd.h> +#include <cm/engine/elf/inc/mpcal.h> + +#include <cm/engine/component/inc/initializer.h> + +#include <cm/engine/utils/inc/string.h> +#include <cm/engine/utils/inc/swap.h> +#include <cm/engine/trace/inc/trace.h> + +#include <cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h> + +static const t_elfmemory mmdspMemories[NUMBER_OF_MMDSP_MEMORY] = { + {0, SDRAM_CODE, SDRAMTEXT_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_CODE, 8, 8, "SDRAM_CODE"}, /* 0: Program memory */ + {1, INTERNAL_XRAM24, 0, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 4, "XROM"}, /* 1: Internal X memory */ + {2, INTERNAL_YRAM24, 0, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 4, "YROM"}, /* 2: Y memory */ + {3, SDRAM_EXT24, SDRAMMEM24_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 4, "SDR0M24"}, /* 5: SDRAM24 */ + {4, SDRAM_EXT16, SDRAMMEM16_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 2, "SDROM16"}, /* 6: SDRAM16 */ + {5, ESRAM_EXT24, ESRAMMEM24_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 4, "ESROM24"}, /* 8: ESRAM24 */ + {6, ESRAM_EXT16, ESRAMMEM16_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 2, "ESROM16"}, /* 9: ESRAM16 */ + {7, ESRAM_CODE, ESRAMTEXT_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_CODE, 8, 8, "ESRAM_CODE"}, /*10: ESRAM code */ + {8, INTERNAL_XRAM24, 0, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 4, "XRAM"}, /* 1: Internal X memory */ + {9, INTERNAL_YRAM24, 0, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 4, "YRAM"}, /* 2: Y memory */ + {10, SDRAM_EXT24, SDRAMMEM24_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 4, "SDRAM24"}, /* 5: SDRAM24 */ + {11, SDRAM_EXT16, SDRAMMEM16_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 2, "SDRAM16"}, /* 6: SDRAM16 */ + {12, ESRAM_EXT24, ESRAMMEM24_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 4, "ESRAM24"}, /* 8: ESRAM24 */ + {13, ESRAM_EXT16, ESRAMMEM16_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 2, "ESRAM16"}, /* 9: ESRAM16 */ + {14, LOCKED_CODE, SDRAMTEXT_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_CODE, 8, 8, "LOCKED_CODE"}, /* : .locked */ +}; + +#define MAX_ELFSECTIONNAME 10 +struct memoryMapping { + char *elfSectionName; + t_uint32 memoryIndex[MEM_FOR_LAST]; // memoryIndex[t_instance_property] +}; + +static const struct memoryMapping mappingmem0[] = { + {"mem0.0", {0, 0}}, + {"mem0.1", {0, 0}}, + {"mem0.2", {0, 0}} +}; +static const struct memoryMapping mappingmem10 = + {"mem10", {7, 7}}; +static const struct memoryMapping mappinglocked = + {".locked", {14, 14}}; +static const struct memoryMapping mappingmem1[] = { + {"", {0xff, 0xff}}, + {"mem1.1", {1, 1}}, + {"mem1.2", {8, 1}}, + {"mem1.3", {1, 1}}, + {"mem1.4", {8, 1}}, + {"mem1.stack", {8, 1}} +}; +static const struct memoryMapping mappingmem2[] = { + {"", {0xff, 0xff}}, + {"mem2.1", {2, 2}}, + {"mem2.2", {9, 2}}, + {"mem2.3", {2, 2}}, + {"mem2.4", {9, 2}} +}; +static const struct memoryMapping mappingmem5[] = { + {"", {0xff, 0xff}}, + {"mem5.1", {3, 3}}, + {"mem5.2", {10, 3}}, + {"mem5.3", {3, 3}}, + {"mem5.4", {10, 3}} +}; +static const struct memoryMapping mappingmem6[] = { + {"", {0xff, 0xff}}, + {"mem6.1", {4, 4}}, + {"mem6.2", {11, 4}}, + {"mem6.3", {4, 4}}, + {"mem6.4", {11, 4}} +}; +static const struct memoryMapping mappingmem8[] = { + {"", {0xff, 0xff}}, + {"mem8.1", {5, 5}}, + {"mem8.2", {12, 5}}, + {"mem8.3", {5, 5}}, + {"mem8.4", {12, 5}} +}; +static const struct memoryMapping mappingmem9[] = { + {"", {0xff, 0xff}}, + {"mem9.1", {6, 6}}, + {"mem9.2", {13, 6}}, + {"mem9.3", {6, 6}}, + {"mem9.4", {13, 6}} +}; + +static const struct { + const struct memoryMapping* mapping; + unsigned int number; +} hashMappings[10] = { + {mappingmem0, sizeof(mappingmem0) / sizeof(mappingmem0[0])}, + {mappingmem1, sizeof(mappingmem1) / sizeof(mappingmem1[0])}, + {mappingmem2, sizeof(mappingmem2) / sizeof(mappingmem2[0])}, + {0x0, 0}, + {0x0, 0}, + {mappingmem5, sizeof(mappingmem5) / sizeof(mappingmem5[0])}, + {mappingmem6, sizeof(mappingmem6) / sizeof(mappingmem6[0])}, + {0x0, 0}, + {mappingmem8, sizeof(mappingmem8) / sizeof(mappingmem8[0])}, + {mappingmem9, sizeof(mappingmem9) / sizeof(mappingmem9[0])}, +}; + +const t_elfmemory* MMDSP_getMappingById(t_memory_id memId) +{ + return &mmdspMemories[memId]; +} + +const t_elfmemory* MMDSP_getMappingByName(const char* sectionName, t_instance_property property) +{ + if(sectionName[0] == 'm' && sectionName[1] == 'e' && sectionName[2] == 'm') + { + if(sectionName[4] == '.') + { + if(sectionName[5] >= '0' && sectionName[5] <= '9') + { + if(sectionName[3] >= '0' && sectionName[3] <= '9') + { + unsigned int m, sm; + + m = sectionName[3] - '0'; + sm = sectionName[5] - '0'; + if(sm < hashMappings[m].number) + return &mmdspMemories[hashMappings[m].mapping[sm].memoryIndex[property]]; + } + } else if(sectionName[3] == '1' && sectionName[5] == 's') + return &mmdspMemories[mappingmem1[5].memoryIndex[property]]; + } + else if(sectionName[3] == '1' && sectionName[4] == '0') + return &mmdspMemories[mappingmem10.memoryIndex[property]]; + } + else if(sectionName[0] == '.' && sectionName[1] == 'l' && sectionName[2] == 'o' && sectionName[3] == 'c' && + sectionName[4] == 'k' && sectionName[5] == 'e' && sectionName[6] == 'd') + { + return &mmdspMemories[mappinglocked.memoryIndex[property]]; + } + + return NULL; +} + +void MMDSP_serializeMemories(t_instance_property property, + const t_elfmemory** codeMemory, const t_elfmemory** thisMemory) { + // Return meory reference + *codeMemory = &mmdspMemories[0]; + if(property == MEM_FOR_SINGLETON) + { + *thisMemory = &mmdspMemories[1]; + } + else + { + *thisMemory = &mmdspMemories[8]; + } +} + +void MMDSP_copyCode(t_uint64 * remoteAddr64, const char* origAddr, int nb) +{ + int m; + + // Linux allow unaligned access +#ifdef LINUX + t_uint64 *origAddr64 = (t_uint64*)origAddr; +#else + __packed t_uint64 *origAddr64 = (__packed t_uint64*)origAddr; +#endif + + for (m = 0; m < nb; m += 8) + { + *remoteAddr64++ = swap64(*origAddr64++); + } +} + +void MMDSP_copyData24(t_uint32 * remoteAddr32, const char* origAddr, int nb) +{ + int m; + + for (m = 0; m < nb; m+=4) + { + t_uint32 value1; + + value1 = (*origAddr++ << 16); + value1 |= (*origAddr++ << 8); + value1 |= (*origAddr++ << 0); + *remoteAddr32++ = value1; + } +} + +void MMDSP_copyData16(t_uint16 * remoteAddr16, const char* origAddr, int nb) +{ + int m; + + for (m = 0; m < nb; m+=2) + { + t_uint16 value1; + + origAddr++; // Skip this byte (which is put in elf file for historical reason) + value1 = (*origAddr++ << 8); + value1 |= (*origAddr++ << 0); + *remoteAddr16++ = value1; + } +} + +#if 0 +__asm void MMDSP_copyCode(void* dst, const void* src, int nb) +{ + PUSH {r4-r8, lr} + SUBS r2,r2,#0x20 + BCC l4 + +l5 + SETEND BE + LDR r4, [r1], #0x4 + LDR r3, [r1], #0x4 + LDR r6, [r1], #0x4 + LDR r5, [r1], #0x4 + LDR r8, [r1], #0x4 + LDR r7, [r1], #0x4 + LDR lr, [r1], #0x4 + LDR r12, [r1], #0x4 + + SETEND LE + STM r0!,{r3-r8,r12, lr} + SUBS r2,r2,#0x20 + BCS l5 + +l4 + LSLS r12,r2,#28 + + SETEND BE + LDRCS r4, [r1], #0x4 + LDRCS r3, [r1], #0x4 + LDRCS r6, [r1], #0x4 + LDRCS r5, [r1], #0x4 + SETEND LE + STMCS r0!,{r3-r6} + + SETEND BE + LDRMI r4, [r1], #0x4 + LDRMI r3, [r1], #0x4 + SETEND LE + STMMI r0!,{r3-r4} + + POP {r4-r8, pc} +} +#endif + +#ifdef LINUX +static void PLD5(int r) +{ + asm volatile ( + "PLD [r0, #0x20] \n\t" + "PLD [r0, #0x40] \n\t" + "PLD [r0, #0x60] \n\t" + "PLD [r0, #0x80] \n\t" + "PLD [r0, #0xA0]" ); +} + +static void PLD1(int r) +{ + asm volatile ( + "PLD [r0, #0xC0]" ); +} +#else /* Symbian, Think -> We assume ARMCC */ +static __asm void PLD5(int r) +{ + PLD [r0, #0x20] + PLD [r0, #0x40] + PLD [r0, #0x60] + PLD [r0, #0x80] + PLD [r0, #0xA0] + + bx lr +} + +static __asm void PLD1(int r) +{ + PLD [r0, #0xC0] + + bx lr +} +#endif + +#if 0 +__asm void COPY(void* dst, const void* src, int nb) +{ + PUSH {r4-r8, lr} + SUBS r2,r2,#0x20 + BCC l4a + PLD [r1, #0x20] + PLD [r1, #0x40] + PLD [r1, #0x60] + PLD [r1, #0x80] + PLD [r1, #0xA0] + +l5a + PLD [r1, #0xC0] + LDM r1!,{r3-r8,r12,lr} + STM r0!,{r3-r8,r12,lr} + SUBS r2,r2,#0x20 + BCS l5a + +l4a + LSLS r12,r2,#28 + LDMCS r1!,{r3,r4,r12,lr} + STMCS r0!,{r3,r4,r12,lr} + LDMMI r1!,{r3,r4} + STMMI r0!,{r3,r4} + POP {r4-r8,lr} + LSLS r12,r2,#30 + LDRCS r3,[r1],#4 + STRCS r3,[r0],#4 + BXEQ lr +l6b + LSLS r2,r2,#31 + LDRHCS r3,[r1],#2 + LDRBMI r2,[r1],#1 + STRHCS r3,[r0],#2 + STRBMI r2,[r0],#1 + BX lr +} +#endif + + +void MMDSP_copySection(t_uint32 origAddr, t_uint32 remoteAddr, t_uint32 sizeInByte) { + t_uint32 endAddr = remoteAddr + sizeInByte; + + PLD5(origAddr); + + // Align on 32bits + if((remoteAddr & 0x3) != 0) + { + *(t_uint16*)remoteAddr = *(t_uint16*)origAddr; + remoteAddr += sizeof(t_uint16); + origAddr += sizeof(t_uint16); + } + + // Align on 64bits + if((remoteAddr & 0x7) != 0 && (remoteAddr <= endAddr - sizeof(t_uint32))) + { + *(t_uint32*)remoteAddr = *(t_uint32*)origAddr; + remoteAddr += sizeof(t_uint32); + origAddr += sizeof(t_uint32); + } + + // 64bits burst access + for(; remoteAddr <= endAddr - sizeof(t_uint64); remoteAddr += sizeof(t_uint64), origAddr += sizeof(t_uint64)) + { + PLD1(origAddr); + *(volatile t_uint64*)remoteAddr = *(t_uint64*)origAddr; + } + + // Remain 32bits access + if(remoteAddr <= endAddr - sizeof(t_uint32)) + { + *(t_uint32*)remoteAddr = *(t_uint32*)origAddr; + remoteAddr += sizeof(t_uint32); + origAddr += sizeof(t_uint32); + } + + // Remain 16bits access + if(remoteAddr <= endAddr - sizeof(t_uint16)) + *(t_uint16*)remoteAddr = *(t_uint16*)origAddr; +} + + +void MMDSP_bzeroSection(t_uint32 remoteAddr, t_uint32 sizeInByte) { + t_uint32 endAddr = remoteAddr + sizeInByte; + + // Align on 32bits + if((remoteAddr & 0x3) != 0) + { + *(t_uint16*)remoteAddr = 0; + remoteAddr += sizeof(t_uint16); + } + + // Align on 64bits + if((remoteAddr & 0x7) != 0 && (remoteAddr <= endAddr - sizeof(t_uint32))) + { + *(t_uint32*)remoteAddr = 0; + remoteAddr += sizeof(t_uint32); + } + + // 64bits burst access + for(; remoteAddr <= endAddr - sizeof(t_uint64); remoteAddr += sizeof(t_uint64)) + *(volatile t_uint64*)remoteAddr = 0ULL; + + // Remain 32bits access + if(remoteAddr <= endAddr - sizeof(t_uint32)) + { + *(t_uint32*)remoteAddr = 0; + remoteAddr += sizeof(t_uint32); + } + + // Remain 16bits access + if(remoteAddr <= endAddr - sizeof(t_uint16)) + *(t_uint16*)remoteAddr = 0; +} + +void MMDSP_loadedSection(t_nmf_core_id coreId, t_memory_id memId, t_memory_handle handle) +{ + if(mmdspMemories[memId].purpose == MEM_CODE) + { + OSAL_CleanDCache(cm_DSP_GetHostLogicalAddress(handle), cm_MM_GetSize(handle)); + } + + if(memId == LOCKED_CODE) + { + t_uint32 DspAddress, DspSize; + + cm_DSP_GetDspMemoryHandleSize(handle, &DspSize); + cm_DSP_GetDspAddress(handle, &DspAddress); + + cm_COMP_InstructionCacheLock(coreId, DspAddress, DspSize); + } +} + +void MMDSP_unloadedSection(t_nmf_core_id coreId, t_memory_id memId, t_memory_handle handle) +{ + if(memId == LOCKED_CODE) + { + t_uint32 DspAddress, DspSize; + + cm_DSP_GetDspMemoryHandleSize(handle, &DspSize); + cm_DSP_GetDspAddress(handle, &DspAddress); + + cm_COMP_InstructionCacheUnlock(coreId, DspAddress, DspSize); + } + +} + +static struct reloc_howto_struct elf64_mmdsp_howto_table[] = +{ + HOWTO (R_MMDSP_IMM20_16, /* type */ + 0, /* rightshift */ + 4, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 8, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + 0x0, /* special_function */ + "R_MMDSP_IMM20_16", /* name */ + FALSE, /* partial_inplace */ + 0x0, /* src_mask */ + 0x0000000000ffff00, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* A 4-bit absolute relocation for splitted 20 bits immediate, shifted by 56 */ + + HOWTO (R_MMDSP_IMM20_4, /* type */ + 16, /* rightshift */ + 4, /* size (0 = byte, 1 = short, 2 = long) */ + 4, /* bitsize */ + FALSE, /* pc_relative */ + 56, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + 0x0, /* special_function */ + "R_MMDSP_IMM20_4", /* name */ + FALSE, /* partial_inplace */ + 0x0, /* src_mask */ + 0x0f00000000000000LL, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_MMDSP_24, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 24, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + 0x0, /* special_function */ + "R_MMDSP_24", /* name */ + FALSE, /* partial_inplace */ + 0x0, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_MMDSP_IMM16, /* type */ + 0, /* rightshift */ + 4, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 8, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + 0x0, /* special_function */ + "R_MMDSP_IMM16", /* name */ + FALSE, /* partial_inplace */ + 0x0, /* src_mask */ + 0x0000000000ffff00, /* dst_mask */ + FALSE), /* pcrel_offset */ +}; + +static const char* lastInPlaceAddr = 0; +static long long lastInPlaceValue; + +void MMDSP_performRelocation( + t_uint32 type, + const char* symbol_name, + t_uint32 symbol_addr, + char* reloc_addr, + const char* inPlaceAddr, + t_uint32 reloc_offset) { + int i; + + for(i = 0; i < sizeof(elf64_mmdsp_howto_table) / sizeof(elf64_mmdsp_howto_table[0]); i++) + { + struct reloc_howto_struct* howto = &elf64_mmdsp_howto_table[i]; + if(howto->type == type) + { + t_uint64 relocation; + + LOG_INTERNAL(2, "reloc '%s:0x%x' type %s at 0x%x (0x%x)\n", + symbol_name ? symbol_name : "??", symbol_addr, + howto->name, + reloc_offset, reloc_addr, 0); + + relocation = symbol_addr; + + if (howto->pc_relative) { + // Not handle yet + } + + if (howto->complain_on_overflow != complain_overflow_dont) { + // Not handle yet + } + + relocation >>= howto->rightshift; + + relocation <<= howto->bitpos; + +#define DOIT(x) \ + x = ( (x & ~howto->dst_mask) | (((x & howto->src_mask) + relocation) & howto->dst_mask)) + + switch (howto->size) { + case 2: { + long x = *(long*)inPlaceAddr; + + // CM_ASSERT(*(long*)inPlaceAddr == *(long*)reloc_addr); + + DOIT (x); + *(long*)reloc_addr = x; + } + break; + case 4: { + long long x; + if(lastInPlaceAddr == inPlaceAddr) + { + x = lastInPlaceValue; + } + else + { + // CM_ASSERT(*(__packed long long*)inPlaceAddr == *(long long*)reloc_addr); + x = *(long long*)inPlaceAddr; + lastInPlaceAddr = inPlaceAddr; + } + + DOIT (x); + *(long long*)reloc_addr = lastInPlaceValue = x; + } + break; + default: + CM_ASSERT(0); + } + + return; + } + } + + ERROR("Relocation type %d not supported for '%s'\n", type, symbol_name, 0, 0, 0, 0); +} diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/elfrelocate.c b/drivers/staging/nmf-cm/cm/engine/elf/src/elfrelocate.c new file mode 100644 index 00000000000..b08ac6a361e --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/src/elfrelocate.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * + */ +#include <cm/engine/elf/inc/bfd.h> +#include <cm/engine/elf/inc/mpcal.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/utils/inc/string.h> + +t_cm_error cm_ELF_relocateSharedSegments( + t_memory_handle *memories, + t_elfdescription *elfhandle, + void *cbContext) +{ + return ELF64_relocateSegments( + memories, + elfhandle, + MEM_SHARABLE, + cbContext); +} + +t_cm_error cm_ELF_relocatePrivateSegments( + t_memory_handle *memories, + t_elfdescription *elfhandle, + void *cbContext) +{ + return ELF64_relocateSegments( + memories, + elfhandle, + MEM_PRIVATE, + cbContext); +} + +void cm_ELF_performRelocation( + t_uint32 type, + const char* symbol_name, + t_uint32 symbol_addr, + char* reloc_addr) +{ + MMDSP_performRelocation( + type, + symbol_name, + symbol_addr, + reloc_addr, + reloc_addr, + 0xBEEF); + + OSAL_CleanDCache((t_uint32)reloc_addr, 8); +} + +t_cm_error cm_ELF_GetMemory( + t_elfdescription *elf, + t_tmp_elfdescription *elftmp, + t_uint32 address, + t_memory_purpose purpose, + t_memory_reference *memory) { + t_memory_id memId; + + for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++) + { + const t_elfmemory* mem = MMDSP_getMappingById(memId); + + if(mem->purpose == purpose && // Memory correspond + elf->segments[mem->id].sumSize != 0 && // Segment allocated + (elf->segments[mem->id].mpcAddr <= address) && + (address < elf->segments[mem->id].mpcAddr + elf->segments[mem->id].sumSize / mem->fileEntSize)) { + memory->memory = mem; + memory->offset = address - elf->segments[mem->id].mpcAddr; + return CM_OK; + } + } + + ERROR("Memory %x,%d not found\n", address, purpose, 0, 0, 0, 0); + return CM_INVALID_ELF_FILE; +} diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/elfxx.c b/drivers/staging/nmf-cm/cm/engine/elf/src/elfxx.c new file mode 100644 index 00000000000..4a2976a6bc1 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/src/elfxx.c @@ -0,0 +1,591 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/elf/inc/mpcal.h> + +#include <cm/engine/utils/inc/string.h> +#include <cm/engine/utils/inc/mem.h> + + +static t_uint32 max(t_uint32 a, t_uint32 b) +{ + return (a >= b) ? a : b; +} +/* +static t_uint32 min(t_uint32 a, t_uint32 b) +{ + return (a <= b) ? a : b; +} +*/ + +struct XXrelocation +{ + t_uint32 st_value; + ElfXX_Half st_shndx; + Elf64_Sxword r_addend; + t_uint32 OffsetInElf; + t_uint32 type; + + t_dup_char symbol_name; // Valid only if st_shndx == SHN_UNDEF +}; + +struct XXSection { + ElfXX_Word sh_type; /* Section type */ + t_uint32 sh_size; /* Section size in bytes */ + ElfXX_Word sh_info; /* Additional section information */ + ElfXX_Word sh_link; /* Link to another section */ + t_uint32 sh_addralign; /* Some sections have address alignment constraints */ + t_uint32 sh_addr; /* Section addr */ + ElfXX_Xword sh_flags; /* Section flags */ + + const char *data; + t_uint32 trueDataSize; /* Valid if different from sh_size */ + const char *sectionName; + + t_uint32 offsetInSegment; + const t_elfmemory *meminfo; + + t_uint32 relocationNumber; + struct XXrelocation *relocations; +}; + +struct XXElf { + t_uint32 e_shnum; + struct XXSection sectionss[1]; +}; + +t_cm_error ELF64_LoadComponent( + t_uint16 e_machine, + const char *elfdata, + t_elfdescription **elfhandlePtr, + t_tmp_elfdescription *elftmp) +{ + t_elfdescription *elfhandle; + const ElfXX_Ehdr *header = (ElfXX_Ehdr*)elfdata; + const ElfXX_Shdr *sections; + const char *strings; + struct XXElf* ELF; + int i, nb; + + elftmp->elfdata = elfdata; + + /* Sanity check */ + if (swapHalf(header->e_machine) != e_machine) + { + ERROR("This is not a executable for such MPC\n", 0, 0, 0, 0, 0, 0); + return CM_INVALID_ELF_FILE; + } + + // Cache elf file informations + nb = swapHalf(header->e_shnum); + elftmp->isExecutable = (swapHalf(header->e_type) == ET_EXEC); + + elfhandle = (t_elfdescription*)OSAL_Alloc_Zero( + sizeof(t_elfdescription) + sizeof(struct XXElf) + sizeof(struct XXSection) * (nb - 1)); + if(elfhandle == NULL) + return CM_NO_MORE_MEMORY; + + ELF = elfhandle->ELF = (struct XXElf*)(elfhandle + 1); + + ELF->e_shnum = nb; + + sections = (ElfXX_Shdr*)&elfdata[swapXword(header->e_shoff)]; + // Compute and swap section infromation + for(i = 0; i < ELF->e_shnum; i++) + { + ELF->sectionss[i].sh_type = swapWord(sections[i].sh_type); + ELF->sectionss[i].sh_info = swapWord(sections[i].sh_info); + ELF->sectionss[i].sh_link = swapWord(sections[i].sh_link); + ELF->sectionss[i].sh_size = (t_uint32)swapXword(sections[i].sh_size); + ELF->sectionss[i].sh_addralign = (t_uint32)swapXword(sections[i].sh_addralign); + ELF->sectionss[i].sh_addr = (t_uint32)swapXword(sections[i].sh_addr); + ELF->sectionss[i].sh_flags = swapXword(sections[i].sh_flags); + + elftmp->sectionData[i] = &elfdata[(t_uint32)swapXword(sections[i].sh_offset)]; + } + + /* + * search nmf_segment + */ + strings = elftmp->sectionData[swapHalf(header->e_shstrndx)]; + for(i = 0; i < ELF->e_shnum; i++) + { + ELF->sectionss[i].sectionName = &strings[swapWord(sections[i].sh_name)]; + + // Found nmf_segment to see if it's + if(cm_StringCompare("nmf_segment", ELF->sectionss[i].sectionName, 11) == 0) { + elftmp->nmfSectionIndex = i; + elftmp->elfheader = (const t_elf_component_header*)elftmp->sectionData[i]; + } + } + + if(elftmp->nmfSectionIndex == 0) + { + ERROR("This is not a NMF component\n", 0, 0, 0, 0, 0, 0); + goto invalid; + } + + /* + * Determine component type + */ + elfhandle->magicNumber = swap32(elftmp->elfheader->magic); + switch(elfhandle->magicNumber) { + case MAGIC_COMPONENT: + elfhandle->instanceProperty = MEM_FOR_MULTIINSTANCE; + break; + case MAGIC_SINGLETON: + case MAGIC_FIRMWARE: + elfhandle->instanceProperty = MEM_FOR_SINGLETON; + break; + } + + // Copy content + for(i = 0; i < ELF->e_shnum; i++) + { + ELF->sectionss[i].meminfo = MMDSP_getMappingByName( + ELF->sectionss[i].sectionName, + elfhandle->instanceProperty); + + if(ELF->sectionss[i].meminfo != NULL) + ELF->sectionss[i].trueDataSize = (ELF->sectionss[i].sh_size / ELF->sectionss[i].meminfo->fileEntSize) * ELF->sectionss[i].meminfo->memEntSize; + + if(ELF->sectionss[i].sh_size != 0 && + ELF->sectionss[i].sh_type == SHT_PROGBITS && + (ELF->sectionss[i].sh_flags & SHF_ALLOC) != 0) + { + const char* elfAddr = elftmp->sectionData[i]; + + ELF->sectionss[i].data = OSAL_Alloc(ELF->sectionss[i].trueDataSize); + if(ELF->sectionss[i].data == NULL) + goto oom; + + if(ELF->sectionss[i].meminfo->purpose == MEM_CODE) + { + MMDSP_copyCode( + (t_uint64*)ELF->sectionss[i].data, + elfAddr, + ELF->sectionss[i].trueDataSize); + } + else if(ELF->sectionss[i].meminfo->purpose == MEM_DATA && + // Always 3 for data ELF->sectionss[i].meminfo->fileEntSize == 3 && + ELF->sectionss[i].meminfo->memEntSize == 4) + { + MMDSP_copyData24( + (t_uint32*)ELF->sectionss[i].data, + elfAddr, + ELF->sectionss[i].trueDataSize); + } + else if(ELF->sectionss[i].meminfo->purpose == MEM_DATA && + // Always 3 for data ELF->sectionss[i].meminfo->fileEntSize == 3 && + ELF->sectionss[i].meminfo->memEntSize == 2) + { + MMDSP_copyData16( + (t_uint16*)ELF->sectionss[i].data, + elfAddr, + ELF->sectionss[i].trueDataSize); + } + else + CM_ASSERT(0); + } + } + + // Copy relocation + // Loop on all relocation section + for(i=0; i < ELF->e_shnum; i++) + { + int sh_info; + + // Does this section is a relocation table (only RELA supported) + if((ELF->sectionss[i].sh_type != SHT_RELA) || + ELF->sectionss[i].sh_size == 0) continue; + + // Copy only relocation for loaded section + sh_info = ELF->sectionss[i].sh_info; + if(ELF->sectionss[sh_info].meminfo != NULL) + { + const ElfXX_Sym* symtab; + const char* strtab; + ElfXX_Rela* rel_start; + int n; + + ELF->sectionss[sh_info].relocationNumber = ELF->sectionss[i].sh_size / sizeof(ElfXX_Rela); + ELF->sectionss[sh_info].relocations = (struct XXrelocation*)OSAL_Alloc_Zero(sizeof(struct XXrelocation) * ELF->sectionss[sh_info].relocationNumber); + if(ELF->sectionss[sh_info].relocations == NULL) + goto oom; + + symtab = (ElfXX_Sym *)elftmp->sectionData[ELF->sectionss[i].sh_link]; + strtab = elftmp->sectionData[ELF->sectionss[ELF->sectionss[i].sh_link].sh_link]; + rel_start = (ElfXX_Rela*)elftmp->sectionData[i]; + for(n = 0; n < ELF->sectionss[sh_info].relocationNumber; n++, rel_start++) + { + struct XXrelocation* relocation = &ELF->sectionss[sh_info].relocations[n]; + ElfXX_Xword r_info = swapXword(rel_start->r_info); + int strtab_index = ELFXX_R_SYM(r_info); + const char* symbol_name = &strtab[swapWord(symtab[strtab_index].st_name)]; + + relocation->st_shndx = swapHalf(symtab[strtab_index].st_shndx); + relocation->st_value = (t_uint32)swapXword(symtab[strtab_index].st_value); + relocation->r_addend = swapXword(rel_start->r_addend); + relocation->OffsetInElf = (t_uint32)swapXword(rel_start->r_offset) / ELF->sectionss[sh_info].meminfo->fileEntSize; + relocation->type = ELFXX_R_TYPE(r_info); + + switch(relocation->st_shndx) { + case SHN_UNDEF: + relocation->symbol_name = cm_StringDuplicate(symbol_name + 1); /* Remove '_' prefix */ + if(relocation->symbol_name == NULL) + goto oom; + break; + case SHN_COMMON: + ERROR("SHN_COMMON not handle for %s\n", symbol_name, 0, 0, 0, 0, 0); + goto invalid; + } + } + } + } + + *elfhandlePtr = elfhandle; + return CM_OK; +invalid: + ELF64_UnloadComponent(elfhandle); + return CM_INVALID_ELF_FILE; +oom: + ELF64_UnloadComponent(elfhandle); + return CM_NO_MORE_MEMORY; +} + +t_cm_error ELF64_ComputeSegment( + t_elfdescription *elfhandle, + t_tmp_elfdescription *elftmp) +{ + struct XXElf* ELF = elfhandle->ELF; + int i; + + for(i = 0; i < ELF->e_shnum; i++) + { + ELF->sectionss[i].offsetInSegment = 0xFFFFFFFF; + + if(ELF->sectionss[i].sh_type == SHT_PROGBITS || ELF->sectionss[i].sh_type == SHT_NOBITS) { + // This is a loadable memory (memory size could be zero since we can have symbol on it)... + const t_elfmemory* meminfo = ELF->sectionss[i].meminfo; + + if(meminfo != NULL) { + // Which correspond to MPC memory + + if(elftmp->isExecutable) + { + if(! elfhandle->segments[meminfo->id].sumSizeSetted) + { + CM_ASSERT(ELF->sectionss[i].sh_addr >= meminfo->startAddr * meminfo->fileEntSize); + + elfhandle->segments[meminfo->id].sumSizeSetted = TRUE; + elfhandle->segments[meminfo->id].sumSize = ELF->sectionss[i].sh_addr - meminfo->startAddr * meminfo->fileEntSize; + } + else + CM_ASSERT(elfhandle->segments[meminfo->id].sumSize == ELF->sectionss[i].sh_addr - meminfo->startAddr * meminfo->fileEntSize); + } + else + { + while(elfhandle->segments[meminfo->id].sumSize % ELF->sectionss[i].sh_addralign != 0) + elfhandle->segments[meminfo->id].sumSize++; + } + + elfhandle->segments[meminfo->id].maxAlign = max(elfhandle->segments[meminfo->id].maxAlign, ELF->sectionss[i].sh_addralign); + ELF->sectionss[i].offsetInSegment = elfhandle->segments[meminfo->id].sumSize / meminfo->fileEntSize; + elfhandle->segments[meminfo->id].sumSize += ELF->sectionss[i].sh_size; + } + } else if(ELF->sectionss[i].sh_type == SHT_RELA && ELF->sectionss[i].sh_info == elftmp->nmfSectionIndex) { + int secsym = ELF->sectionss[i].sh_link; + elftmp->relaNmfSegment = (ElfXX_Rela*)elftmp->sectionData[i]; + elftmp->relaNmfSegmentEnd = (ElfXX_Rela*)((t_uint32)elftmp->relaNmfSegment + ELF->sectionss[i].sh_size); + elftmp->relaNmfSegmentSymbols = (ElfXX_Sym*)elftmp->sectionData[secsym]; + elftmp->relaNmfSegmentStrings = elftmp->sectionData[ELF->sectionss[secsym].sh_link]; + } + } + + return CM_OK; +} + +void ELF64_UnloadComponent( + t_elfdescription *elfhandle) +{ + struct XXElf* ELF = elfhandle->ELF; + int i, n; + + for(i = 0; i < ELF->e_shnum; i++) + { + if(ELF->sectionss[i].relocations != NULL) + { + for(n = 0; n < ELF->sectionss[i].relocationNumber; n++) + cm_StringRelease(ELF->sectionss[i].relocations[n].symbol_name); + OSAL_Free(ELF->sectionss[i].relocations); + } + + OSAL_Free((void*)ELF->sectionss[i].data); + } + OSAL_Free(elfhandle); +} + +t_cm_error ELF64_loadSegment( + t_elfdescription *elfhandle, + t_memory_handle *memory, + t_memory_property property) +{ + struct XXElf* ELF = elfhandle->ELF; + int i; + + /* + * Copy ELF data in this segment + */ + for(i = 0; i < ELF->e_shnum; i++) + { + const t_elfmemory* mapping = ELF->sectionss[i].meminfo; + + if(mapping == NULL) + continue; + if((! (ELF->sectionss[i].sh_flags & SHF_ALLOC)) || (ELF->sectionss[i].sh_size == 0)) + continue; + + // This is a loadable memory ... + if( + (mapping->property == property && elfhandle->instanceProperty != MEM_FOR_SINGLETON) || + (property == MEM_SHARABLE && elfhandle->instanceProperty == MEM_FOR_SINGLETON) ) + { + // Where memory exist and waited share/private correspond + t_uint32 remoteData = elfhandle->segments[mapping->id].hostAddr + + ELF->sectionss[i].offsetInSegment * mapping->memEntSize; + + if(ELF->sectionss[i].sh_type != SHT_NOBITS) + { + LOG_INTERNAL(2, "loadSection(%s, 0x%x, 0x%x, 0x%08x)\n", + ELF->sectionss[i].sectionName, remoteData, ELF->sectionss[i].trueDataSize, + (t_uint32)ELF->sectionss[i].data, 0, 0); + + MMDSP_copySection((t_uint32)ELF->sectionss[i].data, remoteData, ELF->sectionss[i].trueDataSize); + } + else + { + LOG_INTERNAL(2, "bzeroSection(%s, 0x%x, 0x%x)\n", + ELF->sectionss[i].sectionName, remoteData, ELF->sectionss[i].trueDataSize, 0, 0, 0); + + MMDSP_bzeroSection(remoteData, ELF->sectionss[i].trueDataSize); + } + } + } + + return CM_OK; +} + + + +static const t_elfmemory* getSectionAddress( + t_memory_handle *memories, + t_elfdescription *elfhandle, + t_uint32 sectionIdx, + t_uint32 *sectionOffset, + t_cm_logical_address *sectionAddr) { + struct XXElf* ELF = elfhandle->ELF; + const t_elfmemory* mapping = ELF->sectionss[sectionIdx].meminfo; + + if(mapping != NULL) { + *sectionOffset = (elfhandle->segments[mapping->id].mpcAddr + + ELF->sectionss[sectionIdx].offsetInSegment); + + *sectionAddr = (t_cm_logical_address)(elfhandle->segments[mapping->id].hostAddr + + ELF->sectionss[sectionIdx].offsetInSegment * mapping->memEntSize); + } + + return mapping; +} + +static t_uint32 getSymbolAddress( + t_memory_handle *memories, + t_elfdescription *elfhandle, + t_uint32 symbolSectionIdx, + t_uint32 symbolOffet) { + struct XXElf* ELF = elfhandle->ELF; + const t_elfmemory* mapping = ELF->sectionss[symbolSectionIdx].meminfo; + + if(mapping == NULL) + return 0xFFFFFFFF; + // CM_ASSERT(elfhandle->segments[mapping->id].sumSize != 0); + // CM_ASSERT(elfhandle->sections[symbolSectionIdx].offsetInSegment != 0xFFFFFFFF); + + return elfhandle->segments[mapping->id].mpcAddr + + ELF->sectionss[symbolSectionIdx].offsetInSegment + + symbolOffet; +} + +#if 0 +t_bool ELFXX_getSymbolLocation( + const t_mpcal_memory *mpcalmemory, + t_elfdescription *elf, + char *symbolName, + const t_elfmemory **memory, + t_uint32 *offset) { + const ElfXX_Ehdr *header = (ElfXX_Ehdr*)elf->elfdata; + const ElfXX_Shdr *sections = (ElfXX_Shdr*)&elf->elfdata[swapXword(header->e_shoff)]; + const char *strings = &elf->elfdata[swapXword(sections[swapHalf(header->e_shstrndx)].sh_offset)]; + int len = cm_StringLength(symbolName, 256); // TO BE FIXED + int i; + + for(i = 0; i < ELF->e_shnum; i++) + { + ElfXX_Sym* symtab; + const char* strtab; + unsigned int size, j; + + if(ELF->sectionss[i].sh_type != SHT_SYMTAB && ELF->sectionss[i].sh_type != SHT_DYNSYM) continue; + + // Section is a symbol table + symtab = (ElfXX_Sym*)&elf->elfdata[swapXword(sections[i].sh_offset)]; + strtab = &elf->elfdata[swapXword(sections[swapWord(sections[i].sh_link)].sh_offset)]; + size = ELF->sectionss[i].sh_size / (unsigned int)swapXword(sections[i].sh_entsize); + + for(j = 0; j < size; j++) { + const char* foundName = &strtab[swapWord(symtab[j].st_name)]; + + if(cm_StringCompare(symbolName, foundName, len) == 0) { + if(swapHalf(symtab[j].st_shndx) != SHN_UNDEF) { + int sectionIdx = (int)swapHalf(symtab[j].st_shndx); + ElfXX_Xword sh_flags = swapXword(sections[sectionIdx].sh_flags); + + *memory = mpcalmemory->getMappingByName(&strings[swapWord(sections[sectionIdx].sh_name)], + sh_flags & SHF_WRITE ? MEM_RW : (sh_flags & SHF_EXECINSTR ? MEM_X : MEM_RO)); + *offset = (t_uint32)swapXword(symtab[j].st_value); + + return 1; + } + } + } + } + return 0; +} +#endif + +t_cm_error ELF64_relocateSegments( + t_memory_handle *memories, + t_elfdescription *elfhandle, + t_memory_property property, + void *cbContext) { + struct XXElf* ELF = elfhandle->ELF; + int sec, n; + + // Loop on all relocation section + for(sec=0; sec < ELF->e_shnum; sec++) + { + t_cm_logical_address sectionAddr = 0; + t_uint32 sectionOffset = 0; + const t_elfmemory* mapping; + + if(ELF->sectionss[sec].relocations == NULL) + continue; + + // Relocate only section in memory + mapping = getSectionAddress(memories, + elfhandle, + sec, + §ionOffset, + §ionAddr); + if(mapping == NULL) + continue; + + if( + (mapping->property == property && elfhandle->instanceProperty != MEM_FOR_SINGLETON) || + (property == MEM_SHARABLE && elfhandle->instanceProperty == MEM_FOR_SINGLETON) ) + { + LOG_INTERNAL(2, "relocSection(%s)\n", ELF->sectionss[sec].sectionName, 0, 0, 0, 0, 0); + + for(n = 0; n < ELF->sectionss[sec].relocationNumber; n++) + { + struct XXrelocation* relocation = &ELF->sectionss[sec].relocations[n]; + t_uint32 symbol_addr; + char* relocAddr = (char*)(sectionAddr + relocation->OffsetInElf * mapping->memEntSize); + + switch(relocation->st_shndx) { + case SHN_ABS: // Absolute external reference + symbol_addr = relocation->st_value; + break; + case SHN_UNDEF: // External reference + // LOG_INTERNAL(0, "cm_resolvSymbol(%d, %s)\n", relocation->type, relocation->symbol_name, 0,0, 0, 0); + symbol_addr = cm_resolvSymbol(cbContext, + relocation->type, + relocation->symbol_name, + relocAddr); + if(symbol_addr == 0x0) { // Not defined symbol + ERROR("Symbol %s not found\n", relocation->symbol_name, 0, 0, 0, 0, 0); + return CM_INVALID_ELF_FILE; + } else if(symbol_addr == 0xFFFFFFFE) { // OOM + return CM_NO_MORE_MEMORY; + } else if(symbol_addr == 0xFFFFFFFF) { // Defined inside static binding + continue; + } + break; + default: // Internal reference in loaded section + symbol_addr = getSymbolAddress( + memories, + elfhandle, + (t_uint32)relocation->st_shndx, + relocation->st_value); + if(symbol_addr == 0xFFFFFFFF) { + ERROR("Symbol in section %s+%d not loaded\n", + ELF->sectionss[relocation->st_shndx].sectionName, + relocation->st_value, 0, 0, 0, 0); + return CM_INVALID_ELF_FILE; + } + break; + } + + symbol_addr += relocation->r_addend; + + MMDSP_performRelocation( + relocation->type, + relocation->symbol_name, + symbol_addr, + relocAddr, + ELF->sectionss[sec].data + relocation->OffsetInElf * mapping->memEntSize, + sectionOffset + relocation->OffsetInElf); + } + } + } + + return CM_OK; +} + +t_cm_error ELF64_getRelocationMemory( + t_elfdescription *elfhandle, + t_tmp_elfdescription *elftmp, + t_uint32 offsetInNmf, + t_memory_reference *memory) { + struct XXElf* ELF = elfhandle->ELF; + const ElfXX_Rela* rel_start; + const ElfXX_Sym* relaNmfSegmentSymbols = (ElfXX_Sym*)elftmp->relaNmfSegmentSymbols; + + for(rel_start = (ElfXX_Rela*)elftmp->relaNmfSegment; rel_start < (ElfXX_Rela*)elftmp->relaNmfSegmentEnd; rel_start++) + { + if((t_uint32)swapXword(rel_start->r_offset) == offsetInNmf) + { + int strtab_index = ELFXX_R_SYM(swapXword(rel_start->r_info)); + int sectionIdx = (int)swapHalf(relaNmfSegmentSymbols[strtab_index].st_shndx); + + memory->memory = ELF->sectionss[sectionIdx].meminfo; + + if(memory->memory != NULL) { + memory->offset = ( + ELF->sectionss[sectionIdx].offsetInSegment + // Offset in Segment + (t_uint32)swapXword(relaNmfSegmentSymbols[strtab_index].st_value) + // Offset in Elf Section + (t_uint32)swapXword(rel_start->r_addend)); // Addend + + return CM_OK; + } else { + const char* symbol_name = &elftmp->relaNmfSegmentStrings[swapWord(relaNmfSegmentSymbols[strtab_index].st_name)]; + ERROR("Symbol %s not found\n", symbol_name, 0, 0, 0, 0, 0); + return CM_INVALID_ELF_FILE; + } + } + } + + ERROR("Unknown relocation error\n", 0, 0, 0, 0, 0, 0); + return CM_INVALID_ELF_FILE; +} diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/mmdsp-debug.c b/drivers/staging/nmf-cm/cm/engine/elf/src/mmdsp-debug.c new file mode 100644 index 00000000000..1342e9e0155 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/src/mmdsp-debug.c @@ -0,0 +1,439 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * + */ +#include <cm/engine/elf/inc/mmdsp-loadmap.h> +#include <cm/engine/elf/inc/mmdsp.h> +#include <cm/engine/dsp/inc/semaphores_dsp.h> +#include <cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> + +#include <cm/engine/power_mgt/inc/power.h> + +#include <cm/engine/utils/inc/string.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/memory/inc/domain.h> +#include <cm/engine/component/inc/instance.h> +#include <cm/engine/component/inc/component_type.h> +#include <inc/nmf-limits.h> + +#define LOADMAP_SEMAPHORE_USE_NB 7 + +static t_memory_handle headerHandle[NB_CORE_IDS] = {INVALID_MEMORY_HANDLE, }; +static struct LoadMapHdr *headerAddresses[NB_CORE_IDS] = {0, }; +static t_uint32 headerOffsets[NB_CORE_IDS] = {0, }; +static t_uint32 entryNumber[NB_CORE_IDS] = {0, }; + +#undef myoffsetof +#define myoffsetof(TYPE, MEMBER) ((unsigned int) &((TYPE *)0)->MEMBER) + +t_cm_error cm_DSPABI_AddLoadMap( + t_cm_domain_id domainId, + const char* templateName, + const char* localname, + t_memory_handle *memories, + void *componentHandle) +{ + t_nmf_core_id coreId = cm_DM_GetDomainCoreId(domainId); + int count=0; + struct LoadMapItem* curItem = NULL; + + if (headerHandle[coreId] == 0) /* Create loadmap header */ + { + headerHandle[coreId] = cm_DM_Alloc(domainId, SDRAM_EXT16, + sizeof(struct LoadMapHdr)/2, CM_MM_ALIGN_2WORDS, TRUE); + if (headerHandle[coreId] == INVALID_MEMORY_HANDLE) { + ERROR("CM_NO_MORE_MEMORY: Unable to allocate loadmap in cm_DSPABI_AddLoadMap()\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + headerAddresses[coreId] = (struct LoadMapHdr*)cm_DSP_GetHostLogicalAddress(headerHandle[coreId]); + + headerAddresses[coreId]->nMagicNumber = LOADMAP_MAGIC_NUMBER; + headerAddresses[coreId]->nVersion = (LOADMAP_VERSION_MSB<<8)|(LOADMAP_VERSION_LSB); + headerAddresses[coreId]->nRevision = 0; + headerAddresses[coreId]->pFirstItem = 0; + + //Register Header into XRAM:2 + cm_DSP_GetDspAddress(headerHandle[coreId], &headerOffsets[coreId]); + cm_DSP_WriteXRamWord(coreId, 2, headerOffsets[coreId]); + } + + // update Header nRevision field + headerAddresses[coreId]->nRevision++; + + /* + * Build loadmap entry + */ + { + t_memory_handle handle; + struct LoadMapItem* pItem; + t_uint32 dspentry; + unsigned char* pos; + t_uint32 fnlen, lnlen; + t_uint32 fnlenaligned, lnlenaligned; + t_uint32 address; + t_uint32 postStringLength; + int i; + + postStringLength = cm_StringLength(".elf", 16); + fnlenaligned = fnlen = cm_StringLength(templateName, MAX_COMPONENT_FILE_PATH_LENGTH) + postStringLength + 2; + if((fnlenaligned % 2) != 0) fnlenaligned++; + lnlenaligned = lnlen = cm_StringLength(localname, MAX_TEMPLATE_NAME_LENGTH); + if((lnlenaligned % 2) != 0) lnlenaligned++; + + // Allocate new loap map + handle = cm_DM_Alloc(domainId, SDRAM_EXT16, + sizeof(struct LoadMapItem)/2 + (1 + fnlenaligned/2) + (1 + lnlenaligned/2), + CM_MM_ALIGN_2WORDS, TRUE); + if (handle == INVALID_MEMORY_HANDLE) { + ERROR("CM_NO_MORE_MEMORY: Unable to allocate loadmap entry in cm_DSPABI_AddLoadMap\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + pItem = (struct LoadMapItem*)cm_DSP_GetHostLogicalAddress(handle); + cm_DSP_GetDspAddress(handle, &dspentry); + count++; + entryNumber[coreId]++; + + // Link this new loadmap with the previous one + if(headerAddresses[coreId]->pFirstItem == NULL) + headerAddresses[coreId]->pFirstItem = (struct LoadMapItem *)dspentry; + else + { + const t_dsp_desc* pDspDesc = cm_DSP_GetState(coreId); + t_uint32 endSegmentAddr = SDRAMMEM16_BASE_ADDR + pDspDesc->segments[SDRAM_DATA_USER].size / 2; + struct LoadMapItem* curItem, *prevItem = NULL; + t_uint32 curItemDspAdress; + + if( + ((t_uint32)headerAddresses[coreId]->pFirstItem < SDRAMMEM16_BASE_ADDR) || + ((t_uint32)headerAddresses[coreId]->pFirstItem > endSegmentAddr)) + { + ERROR("Memory corruption in MMDSP: at data DSP address=%x or ARM address=%x\n", + headerOffsets[coreId], &headerAddresses[coreId]->pFirstItem, 0, 0, 0, 0); + + return CM_INVALID_DATA; + } + curItemDspAdress = (t_uint32)headerAddresses[coreId]->pFirstItem; + curItem = (struct LoadMapItem*)((curItemDspAdress - headerOffsets[coreId]) * 2 + (t_uint32)headerAddresses[coreId]); // To ARM address + count++; + while(curItem->pNextItem != NULL) + { + if(((t_uint32)curItem->pNextItem < SDRAMMEM16_BASE_ADDR) || ((t_uint32)curItem->pNextItem > endSegmentAddr)) + { + if (prevItem == NULL) + ERROR("AddLoadMap: Memory corruption in MMDSP: at data DSP address=%x or ARM address=%x\n" + "Previou (first) component name %s<%s>\n", + curItemDspAdress + myoffsetof(struct LoadMapItem, pNextItem), &curItem->pNextItem, + curItem->pARMThis ? (char*)(((t_component_instance *)&curItem->pARMThis)->pathname) : "<null>", + curItem->pARMThis ? (char*)(((t_component_instance *)&curItem->pARMThis)->Template->name) : "<null>", 0, 0); + else + ERROR("AddLoadMap: Memory corruption in MMDSP: at data DSP address=%x or ARM address=%x\n" + "Previous valid component name %s<%s>", + curItemDspAdress + myoffsetof(struct LoadMapItem, pNextItem), &curItem->pNextItem, + prevItem->pARMThis ? (char*)(((t_component_instance *)&prevItem->pARMThis)->pathname) : "<null>", + prevItem->pARMThis ? (char*)(((t_component_instance *)&prevItem->pARMThis)->Template->name) : "<null>", 0, 0); + return CM_INVALID_DATA; + } + curItemDspAdress = (t_uint32)curItem->pNextItem; + prevItem = curItem; + curItem = (struct LoadMapItem*)((curItemDspAdress - headerOffsets[coreId]) * 2 + (t_uint32)headerAddresses[coreId]); // To ARM address + count++; + } + curItem->pNextItem = (struct LoadMapItem *)dspentry; + } + + // DSP Address of the string at the end of the load map + pos = (unsigned char*)pItem + sizeof(struct LoadMapItem); + + /* + * Set SolibFilename address information + * -> string = "./origfilename" + */ + pItem->pSolibFilename = (char*)(dspentry + sizeof(struct LoadMapItem) / 2); + *(t_uint16*)pos = fnlen; + pos += 2; + *pos++ = '.'; + *pos++ = '\\'; + for(i = 0; i < fnlen - 2 - postStringLength; i++) + { + *pos++ = (templateName[i] == '.') ? '\\' : templateName[i]; + } + *pos++ = '.'; + *pos++ = 'e'; + *pos++ = 'l'; + *pos++ = 'f'; + // add padding if needed + if ((t_uint32)pos & 1) + *pos++ = '\0'; + + /* + * Set Component Name address information + */ + if (lnlen != 0) + { + pItem->pComponentName = (char*)(dspentry + sizeof(struct LoadMapItem) / 2 + 1 + fnlenaligned / 2); + + *(t_uint16*)pos = lnlen; + pos += 2; + for(i = 0; i < lnlenaligned; i++) + { + // If not aligned null ending copied + *pos++ = localname[i]; + } + } + else + { + pItem->pComponentName = 0; + } + + /* + * Set PROG information + */ + if(memories[CODE_MEMORY_INDEX] == INVALID_MEMORY_HANDLE) + address = 0; + else + cm_DSP_GetDspAddress(memories[CODE_MEMORY_INDEX], &address); + pItem->pAddrProg = (void*)address; + + /* + * Set ERAMCODE information + */ + if(memories[ECODE_MEMORY_INDEX] == INVALID_MEMORY_HANDLE) + address = 0; + else + cm_DSP_GetDspAddress(memories[ECODE_MEMORY_INDEX], &address); + pItem->pAddrEmbProg = (void*)address; + + /* + * Set THIS information + */ + if(memories[PRIVATE_DATA_MEMORY_INDEX] != INVALID_MEMORY_HANDLE) { + // Standard component + cm_DSP_GetDspAddress(memories[PRIVATE_DATA_MEMORY_INDEX], &address); + } else if(memories[SHARE_DATA_MEMORY_INDEX] != INVALID_MEMORY_HANDLE) { + // Singleton component where data are shared (simulate THIS with shared memory) + cm_DSP_GetDspAddress(memories[SHARE_DATA_MEMORY_INDEX], &address); + } else { + // Component without data (take unique identifier -> arbitrary take host component handle) + address = (t_uint32)componentHandle; + } + pItem->pThis = (void*)address; + + /* + * Set ARM THIS information + */ + pItem->pARMThis = componentHandle; + + /* + * Set Link to null (end of list) + */ + pItem->pNextItem = 0; + + /* + * Set XROM information + */ + if(memories[XROM_MEMORY_INDEX] == INVALID_MEMORY_HANDLE) + address = 0; + else + cm_DSP_GetDspAddress(memories[XROM_MEMORY_INDEX], &address); + pItem->pXROM = (void*)address; + + /* + * Set YROM information + */ + if(memories[YROM_MEMORY_INDEX] == INVALID_MEMORY_HANDLE) + address = 0; + else + cm_DSP_GetDspAddress(memories[YROM_MEMORY_INDEX], &address); + pItem->pYROM = (void*)address; + + /* + * Set memory handle (not used externally) + */ + ((t_component_instance *)componentHandle)->loadMapHandle = handle; + } + + OSAL_mb(); + + if (count != entryNumber[coreId]) { + ERROR("AddLoadMap: corrumption, number of component differs: count=%d, expected %d (last item @ %p)\n", + count, entryNumber[coreId], curItem, 0, 0, 0); + return CM_INVALID_DATA; + } + return CM_OK; +} + +t_cm_error cm_DSPABI_RemoveLoadMap( + t_cm_domain_id domainId, + const char* templateName, + t_memory_handle *memories, + const char* localname, + void *componentHandle) +{ + struct LoadMapItem **prevItemReference; + t_uint32 prevItemReferenceDspAddress, curItemDspAdress; + t_nmf_core_id coreId = cm_DM_GetDomainCoreId(domainId); + const t_dsp_desc* pDspDesc = cm_DSP_GetState(coreId); + t_uint32 endSegmentAddr = SDRAMMEM16_BASE_ADDR + pDspDesc->segments[SDRAM_DATA_USER].size / 2; + struct LoadMapItem* curItem = NULL; + + CM_ASSERT (headerHandle[coreId] != INVALID_MEMORY_HANDLE); + + /* parse list until we find this */ + prevItemReferenceDspAddress = 0x2; // DSP address of load map head pointer + prevItemReference = &headerAddresses[coreId]->pFirstItem; + curItemDspAdress = (t_uint32)*prevItemReference; + while(curItemDspAdress != 0x0) + { + if((curItemDspAdress < SDRAMMEM16_BASE_ADDR) || (curItemDspAdress > endSegmentAddr)) + { + ERROR("Memory corruption in MMDSP: at data DSP address=%x or ARM address=%x\n", + prevItemReferenceDspAddress, prevItemReference, 0, 0, 0, 0); + + /* free the entry anyway to avoid leakage */ + cm_DM_Free(((t_component_instance *)componentHandle)->loadMapHandle, TRUE); + + return CM_OK; + } + + curItem = (struct LoadMapItem*)((curItemDspAdress - headerOffsets[coreId]) * 2 + (t_uint32)headerAddresses[coreId]); // To ARM address + + if(curItem->pARMThis == componentHandle) + { + // Remove component from loadmap + + /* take local semaphore */ + cm_DSP_SEM_Take(coreId,LOADMAP_SEMAPHORE_USE_NB); + + /* remove element from list */ + *prevItemReference = curItem->pNextItem; + + /* update nRevision field in header */ + headerAddresses[coreId]->nRevision++; + + /* If this is the last item, deallocate !!! */ + if(headerAddresses[coreId]->pFirstItem == NULL) + { + // Deallocate memory + cm_DM_Free(headerHandle[coreId], TRUE); + headerHandle[coreId] = INVALID_MEMORY_HANDLE; + + //Register Header into XRAM:2 + cm_DSP_WriteXRamWord(coreId, 2, 0); + } + + /* deallocate memory */ + cm_DM_Free(((t_component_instance *)componentHandle)->loadMapHandle, TRUE); + + /* be sure memory is updated before releasing local semaphore */ + OSAL_mb(); + + /* release local semaphore */ + cm_DSP_SEM_Give(coreId,LOADMAP_SEMAPHORE_USE_NB); + + entryNumber[coreId]--; + + return CM_OK; + } + + prevItemReferenceDspAddress = curItemDspAdress + myoffsetof(struct LoadMapItem, pNextItem); + prevItemReference = &curItem->pNextItem; + curItemDspAdress = (t_uint32)*prevItemReference; + }; + + ERROR("Memory corruption in MMDSP: component not in LoadMap %s\n", localname, 0, 0, 0, 0, 0); + + /* free the entry anyway to avoid leakage */ + cm_DM_Free(((t_component_instance *)componentHandle)->loadMapHandle, TRUE); + + return CM_OK; +} + +#if 0 +t_cm_error cm_DSPABI_CheckLoadMap_nolock(t_nmf_core_id coreId) +{ + int count=0; + static int dump = 5; + struct LoadMapItem* curItem = NULL; + + if (!dump) + return CM_OK; + if (headerHandle[coreId] == 0) /* No load map yet */ + return CM_OK; + + { + // No entry in loadmap + if(headerAddresses[coreId]->pFirstItem == NULL) + return CM_OK; + + { + const t_dsp_desc* pDspDesc = cm_DSP_GetState(coreId); + t_uint32 endSegmentAddr = SDRAMMEM16_BASE_ADDR + pDspDesc->segments[SDRAM_DATA_USER].size / 2; + struct LoadMapItem *prevItem=NULL; + t_uint32 curItemDspAdress; + + if (((t_uint32)headerAddresses[coreId]->pFirstItem < SDRAMMEM16_BASE_ADDR) || + ((t_uint32)headerAddresses[coreId]->pFirstItem > endSegmentAddr)) + { + ERROR("CheckLoadMap: Memory corruption in MMDSP at first item: at data DSP address=%x or ARM address=%x\n", + headerOffsets[coreId], &headerAddresses[coreId]->pFirstItem, 0, 0, 0, 0); + dump--; + return CM_INVALID_COMPONENT_HANDLE; + } + curItemDspAdress = (t_uint32)headerAddresses[coreId]->pFirstItem; + curItem = (struct LoadMapItem*)((curItemDspAdress - headerOffsets[coreId]) * 2 + (t_uint32)headerAddresses[coreId]); + count++; + while(curItem->pNextItem != NULL) + { + if(((t_uint32)curItem->pNextItem < SDRAMMEM16_BASE_ADDR) || ((t_uint32)curItem->pNextItem > endSegmentAddr)) + { + if (!prevItem) + ERROR("CheckLoadMap: Memory corruption in MMDSP (count=%d): at data DSP address=%x or ARM address=%x\n" + "Previous (first) component name %s<%s>\n", + count, + curItemDspAdress + myoffsetof(struct LoadMapItem, pNextItem), &curItem->pNextItem, + (char*)(((t_component_instance *)&curItem->pARMThis)->pathname), + (char*)(((t_component_instance *)&curItem->pARMThis)->Template->name), 0); + else + ERROR("CheckLoadMap: Memory corruption in MMDSP (count=%d): at data DSP address=%x or ARM address=%x\n" + "Previous valid component name %s<%s>", + count, + curItemDspAdress + myoffsetof(struct LoadMapItem, pNextItem), &curItem->pNextItem, + (char*)(((t_component_instance *)&prevItem->pARMThis)->pathname), + (char*)(((t_component_instance *)&prevItem->pARMThis)->Template->name), 0); + dump--; + return CM_INVALID_COMPONENT_HANDLE; + } + curItemDspAdress = (t_uint32)curItem->pNextItem; + prevItem = curItem; + curItem = (struct LoadMapItem*)((curItemDspAdress - headerOffsets[coreId]) * 2 + (t_uint32)headerAddresses[coreId]); // To ARM address + count++; + } + } + + } + + if (count != entryNumber[coreId]) { + ERROR("CheckLoadMap: number of component differs: count=%d, expected %d (last item @ %p)\n", count, entryNumber[coreId], + curItem, 0, 0, 0); + dump--; + return CM_INVALID_COMPONENT_HANDLE; + } + return CM_OK; +} + +t_cm_error cm_DSPABI_CheckLoadMap(t_nmf_core_id coreId) +{ + t_cm_error error; + OSAL_LOCK_API(); + error = cm_DSPABI_CheckLoadMap_nolock(coreId); + OSAL_UNLOCK_API(); + return error; +} +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/mpcal.c b/drivers/staging/nmf-cm/cm/engine/elf/src/mpcal.c new file mode 100644 index 00000000000..93d910a5ed6 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/elf/src/mpcal.c @@ -0,0 +1,6 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/elf/inc/mpcal.h> diff --git a/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h b/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h new file mode 100644 index 00000000000..aacffd15a17 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef __INC_EE_MGT_H +#define __INC_EE_MGT_H + +#include <cm/engine/component/inc/instance.h> +#include <cm/engine/dsp/inc/dsp.h> + +typedef struct { + t_component_instance *instance; + t_nmf_executive_engine_id executiveEngineId; + t_uint32 currentStackSize[NMF_SCHED_URGENT + 1]; + t_uint32 voidAddr; + t_uint32 traceState; + t_uint32 printLevel; + t_uint32 nbOfForceWakeup; + struct { + t_memory_handle handle; + t_cm_logical_address addr; + } panicArea; +} t_ee_state; + +//TODO, juraj, this should be done more properly, like accessor method, instead making this global variable.. +extern t_ee_state eeState[NB_CORE_IDS]; + +/******************************************************************************/ +/************************ FUNCTIONS PROTOTYPES ********************************/ +/******************************************************************************/ + +PUBLIC t_cm_error cm_EEM_Init(t_nmf_core_id coreId, const char *eeName, t_nmf_executive_engine_id executiveEngineId); +PUBLIC void cm_EEM_Close(t_nmf_core_id coreId); +PUBLIC t_uint32 cm_EEM_isStackUpdateNeed(t_nmf_core_id coreId, t_nmf_ee_priority priority, t_uint32 isInstantiate, t_uint32 needMinStackSize); +PUBLIC t_cm_error cm_EEM_UpdateStack(t_nmf_core_id coreId, t_nmf_ee_priority priority, t_uint32 needMinStackSize, t_uint32 *pNewStackValue); +PUBLIC t_ee_state* cm_EEM_getExecutiveEngine(t_nmf_core_id coreId); +PUBLIC void cm_EEM_setTraceMode(t_nmf_core_id coreId, t_uint32 state); +PUBLIC void cm_EEM_setPrintLevel(t_nmf_core_id coreId, t_uint32 level); +t_cm_error cm_EEM_ForceWakeup(t_nmf_core_id coreId); +void cm_EEM_AllowSleep(t_nmf_core_id coreId); + +#endif /* __INC_EE_MGT_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/src/executive_engine_mgt.c b/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/src/executive_engine_mgt.c new file mode 100644 index 00000000000..f2c833ff7dc --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/src/executive_engine_mgt.c @@ -0,0 +1,392 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*----------------------------------------------------------------------------* + * This module provides functions that allow to manage DSPs' Firmwares. * + ******************************************************************************/ + + +/******************************************************************* Includes + ****************************************************************************/ + +#include "../inc/executive_engine_mgt.h" +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/component/inc/introspection.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/memory/inc/domain.h> +#include <cm/engine/utils/inc/convert.h> +#include <cm/engine/component/inc/initializer.h> +#include <cm/engine/power_mgt/inc/power.h> +#include <cm/engine/perfmeter/inc/mpcload.h> + +#include <share/communication/inc/nmf_service.h> + +t_ee_state eeState[NB_CORE_IDS]; + +/****************************************************************** Functions + ****************************************************************************/ +static t_cm_error cm_EEM_allocPanicArea(t_nmf_core_id coreId, t_cm_domain_id domainId); +static void cm_EEM_freePanicArea(t_nmf_core_id coreId); + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetExecutiveEngineHandle( + t_cm_domain_id domainId, + t_cm_instance_handle *executiveEngineHandle) +{ + t_nmf_core_id coreId; + + if (cm_DM_CheckDomain(domainId, DOMAIN_NORMAL) != CM_OK) { + return CM_INVALID_DOMAIN_HANDLE; + } + + coreId = cm_DM_GetDomainCoreId(domainId); + //in case someone ask for ee on component manager !!!! + if (coreId == ARM_CORE_ID) {*executiveEngineHandle = 0;} + else {*executiveEngineHandle = eeState[coreId].instance->instance;} + + return CM_OK; +} + +PUBLIC t_cm_error cm_EEM_Init( + t_nmf_core_id coreId, + const char *eeName, + t_nmf_executive_engine_id executiveEngineId) +{ + t_rep_component *pRepComponent; + t_cm_error error; + t_uint32 i; + + eeState[coreId].instance = (t_component_instance *)0; + eeState[coreId].executiveEngineId = executiveEngineId; + for(i = NMF_SCHED_BACKGROUND; i < NMF_SCHED_URGENT + 1;i++) + { + eeState[coreId].currentStackSize[i] = MIN_STACK_SIZE; + } + + // Try to load component file + if((error = cm_REP_lookupComponent(eeName, &pRepComponent)) != CM_OK) + { + if (error == CM_COMPONENT_NOT_FOUND) + ERROR("CM_COMPONENT_NOT_FOUND: Execution Engine %s\n", eeName, 0, 0, 0, 0, 0); + return error; + } + + // Set to 1 during bootstrap since MMDSP forceWakeup is to one also in order to not go in idle state + // while configuration not finish !!! + eeState[coreId].nbOfForceWakeup = 1; + + if ((error = cm_DSP_Boot(coreId)) != CM_OK) + return error; + + if((error = cm_instantiateComponent( + eeName, + cm_DSP_GetState(coreId)->domainEE, + NMF_SCHED_URGENT, + eeName, + pRepComponent->elfhandle, + &eeState[coreId].instance)) != CM_OK) + { + cm_DSP_Shutdown(coreId); + return error; + } + + /* Get Void Function */ + eeState[coreId].voidAddr = cm_getFunction(eeState[coreId].instance, "helper", "Void"); + + /* allocate xram space for stack */ + if (executiveEngineId == SYNCHRONOUS_EXECUTIVE_ENGINE) + { + error = cm_DSP_setStackSize(coreId, MIN_STACK_SIZE); + } + else + { + error = cm_DSP_setStackSize(coreId, (NMF_SCHED_URGENT + 1) * MIN_STACK_SIZE); + } + if (error != CM_OK) + { + cm_delayedDestroyComponent(eeState[coreId].instance); + eeState[coreId].instance = (t_component_instance *)0; + cm_DSP_Shutdown(coreId); + return error; + } + + /* allocate sdram memory for panic area */ + error = cm_EEM_allocPanicArea(coreId, cm_DSP_GetState(coreId)->domainEE); + if (error != CM_OK) { + cm_delayedDestroyComponent(eeState[coreId].instance); + eeState[coreId].instance = (t_component_instance *)0; + cm_DSP_Shutdown(coreId); + return error; + } + + /* allocate sdram memory to share perfmeters data */ + error = cm_PFM_allocatePerfmeterDataMemory(coreId, cm_DSP_GetState(coreId)->domainEE); + if (error != CM_OK) { + cm_EEM_freePanicArea(coreId); + cm_delayedDestroyComponent(eeState[coreId].instance); + eeState[coreId].instance = (t_component_instance *)0; + cm_DSP_Shutdown(coreId); + return error; + } + + /* set initial stack value */ + cm_writeAttribute(eeState[coreId].instance, "rtos/scheduler/topOfStack", cm_DSP_getStackAddr(coreId)); + + /* set myCoreId for trace */ + cm_writeAttribute(eeState[coreId].instance, "xti/myCoreId", coreId - 1); + +#if defined(__STN_8500) && (__STN_8500 > 10) + /* set myCoreId for prcmu if exist */ + cm_writeAttribute(eeState[coreId].instance, "sleep/prcmu/myCoreId", coreId + 1); +#endif + + /* go go go ... */ + cm_DSP_Start(coreId); + + /* Waiting for End Of Boot */ + //TODO : remove infinite while loop + //TODO : to be paranoiac, add a read to serviceReasonOffset before starting core and check value is MPC_SERVICE_BOOT as it should be + { + while(cm_readAttributeNoError(eeState[coreId].instance, "rtos/commonpart/serviceReason") == MPC_SERVICE_BOOT) + { + volatile t_uint32 i; + for (i=0; i < 1000; i++); + } + } + + /* set some attributes after boot to avoid being erase by mmdsp boot */ + cm_writeAttribute(eeState[coreId].instance, "xti/traceActive", eeState[coreId].traceState); + cm_writeAttribute(eeState[coreId].instance, "rtos/commonpart/printLevel", eeState[coreId].printLevel); + + cm_DSP_ConfigureAfterBoot(coreId); + + return CM_OK; +} + +/****************************************************************************/ +/* NAME: cm_EEM_Close */ +/*--------------------------------------------------------------------------*/ +/* DESCRIPTION: Inform us that ee for coreId has been destroyed */ +/* */ +/* PARAMETERS: id: dsp identifier */ +/* */ +/* RETURN: none */ +/* */ +/****************************************************************************/ +PUBLIC void cm_EEM_Close(t_nmf_core_id coreId) +{ + cm_DSP_setStackSize(coreId, 0); + cm_delayedDestroyComponent(eeState[coreId].instance); + eeState[coreId].instance = (t_component_instance *)0; + cm_PFM_deallocatePerfmeterDataMemory(coreId); + cm_EEM_freePanicArea(coreId); + cm_DSP_Shutdown(coreId); +} + +/****************************************************************************/ +/* NAME: cm_EEM_isStackUpdateNeed( */ +/* t_nmf_core_id id, */ +/* t_nmf_ee_priority priority, */ +/* t_uint32 isInstantiate, */ +/* t_uint32 needMinStackSize */ +/* ) */ +/*--------------------------------------------------------------------------*/ +/* DESCRIPTION: Return a boolean to inform if a ee stack size update is need*/ +/* when instantiate or destroying a component */ +/****************************************************************************/ +PUBLIC t_uint32 cm_EEM_isStackUpdateNeed( + t_nmf_core_id coreId, + t_nmf_ee_priority priority, + t_uint32 isInstantiate, + t_uint32 needMinStackSize) +{ + /* in case of SYNCHRONOUS_EXECUTIVE_ENGINE we only use currentStackSize[NMF_SCHED_BACKGROUND] */ + if (eeState[coreId].executiveEngineId == SYNCHRONOUS_EXECUTIVE_ENGINE) {priority = NMF_SCHED_BACKGROUND;} + if (isInstantiate) + { + if (needMinStackSize > eeState[coreId].currentStackSize[priority]) {return TRUE;} + } + else + { + if (needMinStackSize == eeState[coreId].currentStackSize[priority]) {return TRUE;} + } + + return FALSE; +} + +/****************************************************************************/ +/* NAME: cm_EEM_UpdateStack( */ +/* t_nmf_core_id id, */ +/* t_nmf_ee_priority priority, */ +/* t_uint32 needMinStackSize, */ +/* t_uint32 *pNewStackValue */ +/* ) */ +/*--------------------------------------------------------------------------*/ +/* DESCRIPTION: If cm_EEM_isStackUpdateNeed() has return true then caller */ +/* must inform EEM about new stack value for priority. */ +/* cm_EEM_UpdateStack() will return new global stack size to */ +/* provide to ee. */ +/****************************************************************************/ +PUBLIC t_cm_error cm_EEM_UpdateStack( + t_nmf_core_id coreId, + t_nmf_ee_priority priority, + t_uint32 needMinStackSize, + t_uint32 *pNewStackValue) +{ + t_cm_error error; + t_uint32 recoveryStackSize = eeState[coreId].currentStackSize[priority]; + t_uint32 i; + + /* in case of SYNCHRONOUS_EXECUTIVE_ENGINE we only use currentStackSize[NMF_SCHED_BACKGROUND] */ + if (eeState[coreId].executiveEngineId == SYNCHRONOUS_EXECUTIVE_ENGINE) {priority = NMF_SCHED_BACKGROUND;} + eeState[coreId].currentStackSize[priority] = needMinStackSize; + if (eeState[coreId].executiveEngineId == SYNCHRONOUS_EXECUTIVE_ENGINE) {*pNewStackValue = needMinStackSize;} + else + { + *pNewStackValue = 0; + for(i = NMF_SCHED_BACKGROUND; i < NMF_SCHED_URGENT + 1;i++) + { + *pNewStackValue += eeState[coreId].currentStackSize[i]; + } + } + + /* try to increase size of stack by modifying xram allocator size */ + error = cm_DSP_setStackSize(coreId, *pNewStackValue); + if (error != CM_OK) { + eeState[coreId].currentStackSize[priority] = recoveryStackSize; + } else { + LOG_INTERNAL(1, "\n##### Stack update: size=%d, prio=%d on %s #####\n", *pNewStackValue, priority, cm_getDspName(coreId), 0, 0, 0); + } + + return error; +} + +/****************************************************************************/ +/* NAME: t_nmf_executive_engine_id( */ +/* t_nmf_core_id id */ +/* ) */ +/*--------------------------------------------------------------------------*/ +/* DESCRIPTION: return executive engine load on id core. */ +/****************************************************************************/ +PUBLIC t_ee_state * cm_EEM_getExecutiveEngine(t_nmf_core_id coreId) +{ + return &eeState[coreId]; +} + +/****************************************************************************/ +/* NAME: cm_EEM_setTraceMode( */ +/* t_nmf_core_id id, */ +/* t_uint32 state */ +/* ) */ +/*--------------------------------------------------------------------------*/ +/* DESCRIPTION: activate/deactivate trace for ee running on id. In case ee */ +/* is not yet load then information is store. */ +/****************************************************************************/ +PUBLIC void cm_EEM_setTraceMode(t_nmf_core_id coreId, t_uint32 state) +{ + eeState[coreId].traceState = state; + if (eeState[coreId].instance) + { + if(cm_EEM_ForceWakeup(coreId) == CM_OK) + { + cm_writeAttribute(eeState[coreId].instance, "xti/traceActive", eeState[coreId].traceState); + + cm_EEM_AllowSleep(coreId); + } + } +} + +/****************************************************************************/ +/* NAME: cm_EEM_setPrintLevel( */ +/* t_nmf_core_id id, */ +/* t_uint32 level */ +/* ) */ +/*--------------------------------------------------------------------------*/ +/* DESCRIPTION: set print level for ee running on id. In case ee */ +/* is not yet load then information is store. */ +/****************************************************************************/ +PUBLIC void cm_EEM_setPrintLevel(t_nmf_core_id coreId, t_uint32 level) +{ + eeState[coreId].printLevel = level; + if (eeState[coreId].instance) + { + if(cm_EEM_ForceWakeup(coreId) == CM_OK) + { + cm_writeAttribute(eeState[coreId].instance, "rtos/commonpart/printLevel", eeState[coreId].printLevel); + + cm_EEM_AllowSleep(coreId); + } + } +} + +t_cm_error cm_EEM_ForceWakeup(t_nmf_core_id coreId) +{ + if(eeState[coreId].nbOfForceWakeup++ == 0) + { + t_cm_error error; + + LOG_INTERNAL(2, "ARM: Try to wake up\n", 0, 0, 0, 0, 0, 0); + + if (cm_DSP_GetState(coreId)->state != MPC_STATE_BOOTED) + { + return CM_MPC_NOT_RESPONDING; + } + else if ((error = cm_COMP_ULPForceWakeup(coreId)) != CM_OK) + { + if (error == CM_MPC_NOT_RESPONDING) { + if(cm_DSP_GetState(coreId)->state == MPC_STATE_PANIC) + /* Don't print error which has been done by Panic handling */; + else + { + ERROR("CM_MPC_NOT_RESPONDING: DSP %s can't be wakeup'ed\n", cm_getDspName(coreId), 0, 0, 0, 0, 0); + cm_DSP_SetStatePanic(coreId); + } + } + return error; + } + } + return CM_OK; +} + +void cm_EEM_AllowSleep(t_nmf_core_id coreId) +{ + if(--eeState[coreId].nbOfForceWakeup == 0) + { + LOG_INTERNAL(2, "ARM: Allow sleep\n", 0, 0, 0, 0, 0, 0); + + if (cm_DSP_GetState(coreId)->state != MPC_STATE_BOOTED) + { + } + else if (cm_COMP_ULPAllowSleep(coreId) != CM_OK) + { + ERROR("CM_MPC_NOT_RESPONDING: DSP %s can't be allow sleep'ed\n", cm_getDspName(coreId), 0, 0, 0, 0, 0); + } + } +} + +/* internal api */ +t_cm_error cm_EEM_allocPanicArea(t_nmf_core_id coreId, t_cm_domain_id domainId) +{ + t_cm_error error = CM_OK; + + eeState[coreId].panicArea.handle = cm_DM_Alloc(cm_DSP_GetState(coreId)->domainEE, SDRAM_EXT24, 45 /* 42 registers, pc, 2 magic words */,CM_MM_ALIGN_WORD, TRUE); + if (eeState[coreId].panicArea.handle == INVALID_MEMORY_HANDLE) + error = CM_NO_MORE_MEMORY; + else { + t_uint32 mmdspAddr; + + eeState[coreId].panicArea.addr = cm_DSP_GetHostLogicalAddress(eeState[coreId].panicArea.handle); + cm_DSP_GetDspAddress(eeState[coreId].panicArea.handle, &mmdspAddr); + + cm_writeAttribute(eeState[coreId].instance, "rtos/commonpart/panicDataAddr", mmdspAddr); + } + + return error; +} + +void cm_EEM_freePanicArea(t_nmf_core_id coreId) +{ + eeState[coreId].panicArea.addr = 0; + cm_DM_Free(eeState[coreId].panicArea.handle, TRUE); +} diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/chunk_mgr.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/chunk_mgr.h new file mode 100644 index 00000000000..340301a9259 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/chunk_mgr.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef CHUNK_MGR_H_ +#define CHUNK_MGR_H_ + +#include <cm/engine/memory/inc/remote_allocator.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> + +t_cm_error allocChunkPool(void); +t_cm_error fillChunkPool(void); +void freeChunkPool(void); + +/***************************************************************************/ +/* + * allocChunk + * param current : Pointer on chunck to free + * + * Add a chunk in the chunck list + * + */ +/***************************************************************************/ +t_cm_chunk* allocChunk(void); + +/***************************************************************************/ +/* + * freeChunk + * param current : Pointer on chunck to free + * + * Remove a chunk in the chunck list + * + */ +/***************************************************************************/ +void freeChunk(t_cm_chunk *chunk); + +#endif /*CHUNK_MGR_H_*/ diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/domain.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/domain.h new file mode 100644 index 00000000000..c9b39956c63 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/domain.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/***************************************************************************/ +/* file : domain.h + * author : NMF team + * version : 1.0 + * + * brief : NMF domain definitions + */ +/***************************************************************************/ + +#ifndef DOMAIN_H_ +#define DOMAIN_H_ + +#include <cm/inc/cm_type.h> +#include <cm/engine/memory/inc/domain_type.h> +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/dsp/inc/dsp.h> + +/* These default domains are used for singleton only ! */ +#define DEFAULT_SVA_DOMAIN (t_cm_domain_id)1 +#define DEFAULT_SIA_DOMAIN (t_cm_domain_id)2 + +/*! + * \brief Domain type. + * \internal + * \ingroup CM_DOMAIN_API + */ +typedef enum { + DOMAIN_ANY = 0, + DOMAIN_NORMAL, + DOMAIN_SCRATCH_PARENT, + DOMAIN_SCRATCH_CHILD +} t_cm_domain_type; + +/*! + * \brief Domain descriptor. Holds offsets for all memory types present in the system. + * \internal + * \ingroup CM_DOMAIN_API + */ +typedef struct { + t_cm_domain_memory domain; // the actual memory ranges + t_cm_domain_type type; // domain type + t_uint32 refcount; // reference counter for scratch domain dependencies + t_nmf_client_id client; // client id for cleaning + + union { + struct { + t_memory_handle handle; // memory handle of the allocated chunk the covers the esram-data scratch region + } parent; + struct { + t_cm_allocator_desc *alloc; //allocator descriptor for the scratch domain + t_cm_domain_id parent_ref; //parent domain reference + } child; + } scratch; + void *dbgCooky; //pointer to OS internal data +} t_cm_domain_desc; + +#ifdef DEBUG +#define DOMAIN_DEBUG(handle) \ + handle = handle & ~0xc0; +#else +#define DOMAIN_DEBUG(handle) +#endif + +/*! + * \brief Domain descriptor array. + */ +extern t_cm_domain_desc domainDesc[]; + +typedef struct { + t_cm_domain_id parentId; + t_cm_domain_id domainId; + t_cm_allocator_desc *allocDesc; +} t_cm_domain_scratch_desc; + +extern t_cm_domain_scratch_desc domainScratchDesc[]; + +typedef struct { + t_cm_system_address sdramCode; + t_cm_system_address sdramData; + t_cm_system_address esramCode; + t_cm_system_address esramData; +} t_cm_domain_info; + +/*! + * \brief Init of the domain subsystem. + */ +PUBLIC t_cm_error cm_DM_Init(void); + +/*! + * \brief Clean-up of the domain subsystem. + */ +PUBLIC void cm_DM_Destroy(void); + +/*! + * \brief Domain creation. + * + * Allocates in slot in the domain descriptors array and copies segment infos from the domain + * parameter to the descriptor. The resulting handle is returned via @param handle. + * + * Returns: CM_DOMAIN_INVALID in case of error, otherwise CM_OK. + */ +PUBLIC t_cm_error cm_DM_CreateDomain(const t_nmf_client_id client, const t_cm_domain_memory *domain, t_cm_domain_id *handle); + +/*! + * \brief Scratch (or overlap) domain creation. + * + * Create a scratch domain, ie domain where allocation may overlap. + */ +PUBLIC t_cm_error cm_DM_CreateDomainScratch(const t_nmf_client_id client, const t_cm_domain_id parentId, const t_cm_domain_memory *domain, t_cm_domain_id *handle); + +/* ! + * \brief Retrieve the coreId from a given domain. Utility. + */ +PUBLIC t_nmf_core_id cm_DM_GetDomainCoreId(const t_cm_domain_id domainId); + +/*! + * \brief Destroy all domains belonging to a given client. + */ +PUBLIC t_cm_error cm_DM_DestroyDomains(const t_nmf_client_id client); + +/*! + * \brief Destroy a given domain. + */ +PUBLIC t_cm_error cm_DM_DestroyDomain(t_cm_domain_id handle); + +/*! + * \brief Check if the handle is valid. + */ +PUBLIC t_cm_error cm_DM_CheckDomain(t_cm_domain_id handle, t_cm_domain_type type); +PUBLIC t_cm_error cm_DM_CheckDomainWithClient(t_cm_domain_id handle, t_cm_domain_type type, t_nmf_client_id client); + +/*! + * \brief Memory allocation in a given domain, for a given memory type (see CM_AllocMpcMemory). + */ +PUBLIC t_memory_handle cm_DM_Alloc(t_cm_domain_id domainId, t_dsp_memory_type_id memType, t_uint32 size, t_cm_mpc_memory_alignment memAlignment, t_bool powerOn); + +/*! + * \brief Memory free using a given domain handle + */ +PUBLIC void cm_DM_FreeWithInfo(t_memory_handle memHandle, t_nmf_core_id *coreId, t_dsp_memory_type_id *memType, t_bool powerOff); + +/*! + * \brief Memory free using a given domain handle + */ +PUBLIC void cm_DM_Free(t_memory_handle memHandle, t_bool powerOff); + +/*! + * \brief Wrapper function for CM_GetMpcMemoryStatus. + */ +PUBLIC t_cm_error cm_DM_GetAllocatorStatus(t_cm_domain_id domainId, t_dsp_memory_type_id memType, t_cm_allocator_status *pStatus); + +PUBLIC t_cm_error cm_DM_GetDomainAbsAdresses(t_cm_domain_id domainId, t_cm_domain_info *info); + +/*! + * \brief Change the domain for the given allocated chunk + */ +PUBLIC void cm_DM_SetDefaultDomain(t_memory_handle memHandle, t_nmf_core_id coreId); +#endif /* DOMAIN_H_ */ diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/domain_type.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/domain_type.h new file mode 100644 index 00000000000..7463f82eb89 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/domain_type.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/***************************************************************************/ +/* file : domain.h + * author : NMF team + * version : 1.0 + * + * brief : NMF domain definitions + */ +/***************************************************************************/ + +#ifndef DOMAIN_TYPE_H_ +#define DOMAIN_TYPE_H_ + +#include <cm/inc/cm_type.h> +#include <cm/engine/memory/inc/memory_type.h> + +/*! + * \brief Domain identifier + * \ingroup CM_DOMAIN_API + */ +typedef t_uint8 t_cm_domain_id; + +/*! + * \brief Client identifier + * 0 (zero) is considered as an invalid or 'NO' client identifier + * \ingroup CM_DOMAIN_API + */ +typedef t_uint32 t_nmf_client_id; +#define NMF_CORE_CLIENT (t_nmf_client_id)-1 +#define NMF_CURRENT_CLIENT (t_nmf_client_id)0 + +typedef struct { + t_uint32 offset; //!< offset relativ to segment start in memory (in bytes) + t_uint32 size; //!< size in bytes of the domain segment +} t_cm_domain_segment; + +/*! + * \brief Domain memory description structure + * \ingroup CM_DOMAIN_API + */ +typedef struct { + t_nmf_core_id coreId; //!< MMDSP Core Id for this domain (used for TCM-X and TCM-Y at instantiate) + t_cm_domain_segment esramCode; //!< ESRAM code segment + t_cm_domain_segment esramData; //!< ESRAM data segment + t_cm_domain_segment sdramCode; //!< SDRAM code segment + t_cm_domain_segment sdramData; //!< SDRAM data segment +} t_cm_domain_memory; + +#define INIT_DOMAIN_SEGMENT {0, 0} +#define INIT_DOMAIN {MASK_ALL8, INIT_DOMAIN_SEGMENT, INIT_DOMAIN_SEGMENT, INIT_DOMAIN_SEGMENT, INIT_DOMAIN_SEGMENT} + + +#endif /* DOMAIN_TYPE_H_ */ diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/memory.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/memory.h new file mode 100644 index 00000000000..c6d1fb2dfff --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/memory.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Internal Memory Management API. + * + * \defgroup MEMORY_INTERNAL Private Memory API. + * + */ +#ifndef __INC_MEMORY_H +#define __INC_MEMORY_H + +#include <cm/engine/api/control/configuration_engine.h> +#include <cm/engine/memory/inc/remote_allocator.h> + +#endif /* __INC_MEMORY_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/memory_type.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/memory_type.h new file mode 100644 index 00000000000..91246f1ec0e --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/memory_type.h @@ -0,0 +1,152 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Public Component Manager Memory API type. + * + * This file contains the Component Manager API type for manipulating memory. + */ +#ifndef __INC_MEMORY_TYPE_H +#define __INC_MEMORY_TYPE_H + +#include <cm/inc/cm_type.h> + +/*! + * @defgroup t_cm_mpc_memory_type t_cm_mpc_memory_type + * \brief Definition of symbols used to reference the various type of Media Processor Core adressable memory + * @{ + * \ingroup MEMORY + */ +typedef t_uint8 t_cm_mpc_memory_type; //!< Fake enumeration type +#define CM_MM_MPC_TCM16_X ((t_cm_mpc_memory_type)0) +#define CM_MM_MPC_TCM24_X ((t_cm_mpc_memory_type)1) +#define CM_MM_MPC_ESRAM16 ((t_cm_mpc_memory_type)2) +#define CM_MM_MPC_ESRAM24 ((t_cm_mpc_memory_type)3) +#define CM_MM_MPC_SDRAM16 ((t_cm_mpc_memory_type)4) +#define CM_MM_MPC_SDRAM24 ((t_cm_mpc_memory_type)5) +#define CM_MM_MPC_TCM16_Y ((t_cm_mpc_memory_type)6) +#define CM_MM_MPC_TCM24_Y ((t_cm_mpc_memory_type)7) +#define CM_MM_MPC_TCM16 CM_MM_MPC_TCM16_X +#define CM_MM_MPC_TCM24 CM_MM_MPC_TCM24_X + +/* @} */ + +/*! + * @defgroup t_cm_memory_alignment t_cm_memory_alignment + * \brief Definition of symbols used to constraint the alignment of the allocated memory + * @{ + * \ingroup MEMORY + */ +typedef t_uint16 t_cm_memory_alignment; //!< Fake enumeration type +#define CM_MM_ALIGN_NONE ((t_cm_memory_alignment)0x00000000) +#define CM_MM_ALIGN_BYTE ((t_cm_memory_alignment)CM_MM_ALIGN_NONE) +#define CM_MM_ALIGN_HALFWORD ((t_cm_memory_alignment)0x00000001) +#define CM_MM_ALIGN_WORD ((t_cm_memory_alignment)0x00000003) +#define CM_MM_ALIGN_2WORDS ((t_cm_memory_alignment)0x00000007) +#define CM_MM_ALIGN_16BYTES ((t_cm_memory_alignment)0x0000000F) +#define CM_MM_ALIGN_4WORDS ((t_cm_memory_alignment)0x0000000F) +#define CM_MM_ALIGN_AHB_BURST ((t_cm_memory_alignment)0x0000000F) +#define CM_MM_ALIGN_32BYTES ((t_cm_memory_alignment)0x0000001F) +#define CM_MM_ALIGN_8WORDS ((t_cm_memory_alignment)0x0000001F) +#define CM_MM_ALIGN_64BYTES ((t_cm_memory_alignment)0x0000003F) +#define CM_MM_ALIGN_16WORDS ((t_cm_memory_alignment)0x0000003F) +#define CM_MM_ALIGN_128BYTES ((t_cm_memory_alignment)0x0000007F) +#define CM_MM_ALIGN_32WORDS ((t_cm_memory_alignment)0x0000007F) +#define CM_MM_ALIGN_256BYTES ((t_cm_memory_alignment)0x000000FF) +#define CM_MM_ALIGN_64WORDS ((t_cm_memory_alignment)0x000000FF) +#define CM_MM_ALIGN_512BYTES ((t_cm_memory_alignment)0x000001FF) +#define CM_MM_ALIGN_128WORDS ((t_cm_memory_alignment)0x000001FF) +#define CM_MM_ALIGN_1024BYTES ((t_cm_memory_alignment)0x000003FF) +#define CM_MM_ALIGN_256WORDS ((t_cm_memory_alignment)0x000003FF) +#define CM_MM_ALIGN_2048BYTES ((t_cm_memory_alignment)0x000007FF) +#define CM_MM_ALIGN_512WORDS ((t_cm_memory_alignment)0x000007FF) +#define CM_MM_ALIGN_4096BYTES ((t_cm_memory_alignment)0x00000FFF) +#define CM_MM_ALIGN_1024WORDS ((t_cm_memory_alignment)0x00000FFF) +#define CM_MM_ALIGN_65536BYTES ((t_cm_memory_alignment)0x0000FFFF) +#define CM_MM_ALIGN_16384WORDS ((t_cm_memory_alignment)0x0000FFFF) +/* @} */ + +/*! + * @defgroup t_cm_mpc_memory_alignment t_cm_mpc_memory_alignment + * \brief Definition of symbols used to constraint the alignment of the allocated mpc memory + * @{ + * \ingroup MEMORY + */ +typedef t_uint16 t_cm_mpc_memory_alignment; //!< Fake enumeration type +#define CM_MM_MPC_ALIGN_NONE ((t_cm_mpc_memory_alignment)0x00000000) +#define CM_MM_MPC_ALIGN_HALFWORD ((t_cm_mpc_memory_alignment)0x00000001) +#define CM_MM_MPC_ALIGN_WORD ((t_cm_mpc_memory_alignment)0x00000003) +#define CM_MM_MPC_ALIGN_2WORDS ((t_cm_mpc_memory_alignment)0x00000007) +#define CM_MM_MPC_ALIGN_4WORDS ((t_cm_mpc_memory_alignment)0x0000000F) +#define CM_MM_MPC_ALIGN_8WORDS ((t_cm_mpc_memory_alignment)0x0000001F) +#define CM_MM_MPC_ALIGN_16WORDS ((t_cm_mpc_memory_alignment)0x0000003F) +#define CM_MM_MPC_ALIGN_32WORDS ((t_cm_mpc_memory_alignment)0x0000007F) +#define CM_MM_MPC_ALIGN_64WORDS ((t_cm_mpc_memory_alignment)0x000000FF) +#define CM_MM_MPC_ALIGN_128WORDS ((t_cm_mpc_memory_alignment)0x000001FF) +#define CM_MM_MPC_ALIGN_256WORDS ((t_cm_mpc_memory_alignment)0x000003FF) +#define CM_MM_MPC_ALIGN_512WORDS ((t_cm_mpc_memory_alignment)0x000007FF) +#define CM_MM_MPC_ALIGN_1024WORDS ((t_cm_mpc_memory_alignment)0x00000FFF) +#define CM_MM_MPC_ALIGN_65536BYTES ((t_cm_mpc_memory_alignment)0x0000FFFF) +#define CM_MM_MPC_ALIGN_16384WORDS ((t_cm_mpc_memory_alignment)0x0000FFFF) +/* @} */ + +/*! + * \brief Identifier of a memory handle + * \ingroup MEMORY + */ +typedef t_uint32 t_cm_memory_handle; + +/*! + * \brief Description of a memory segment + * + * <=> allocable addressable space + * \ingroup MEMORY + */ +typedef struct { + t_cm_system_address systemAddr; //!< Logical AND physical segment start address + t_uint32 size; //!< segment size (in bytes) +} t_nmf_memory_segment; +#define INIT_MEMORY_SEGMENT {{0, 0}, 0} + +/*! + * \brief Definition of structure used for an allocator status + * \ingroup MEMORY + */ +typedef struct +{ + struct { + t_uint32 size; //!< size of the allocator + /* Block counters */ + t_uint16 used_block_number; //!< used block number + t_uint16 free_block_number; //!< free block number + + /* Free memory min/max */ + t_uint32 maximum_free_size; //!< maximum free size + t_uint32 minimum_free_size; //!< minimum free size + + /* Accumulation of free and used memory */ + t_uint32 accumulate_free_memory; //!< accumulate free memory + t_uint32 accumulate_used_memory; //!< accumulate used memory + } global; + + struct { + t_uint32 size; //!< size of the domain + t_uint32 maximum_free_size; //!< maximum free size in the given domain + t_uint32 minimum_free_size; //!< minimum free size in the given domain + t_uint32 accumulate_free_memory; //all free memory of the given domain + t_uint32 accumulate_used_memory; //all used memory of the given domain + } domain; + + struct { + t_uint32 sizes[3]; + } stack[NB_CORE_IDS]; + +} t_cm_allocator_status; + +#endif /* __INC_MEMORY_TYPE_H */ + diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/migration.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/migration.h new file mode 100644 index 00000000000..824d25374b3 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/migration.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Migration API. + * + * \defgroup + * + */ +#ifndef __INC_MIGRATION_H +#define __INC_MIGRATION_H + +#include <cm/engine/memory/inc/domain_type.h> +#include <cm/engine/dsp/inc/dsp.h> + +typedef enum { + STATE_MIGRATED = 1, + STATE_NORMAL = 0, +} t_cm_migration_state; + +PUBLIC t_cm_error cm_migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst); + +PUBLIC t_cm_error cm_unmigrate(void); + +PUBLIC t_uint32 cm_migration_translate(t_dsp_segment_type segmentType, t_uint32 addr); + +PUBLIC void cm_migration_check_state(t_nmf_core_id coreId, t_cm_migration_state expected); + +#endif /* __INC_MIGRATION_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator.h new file mode 100644 index 00000000000..36994f37daa --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator.h @@ -0,0 +1,275 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + * + * \note: In this module, we assume that parameters were checked !! + */ +#ifndef __REMOTE_ALLOCATOR_H_ +#define __REMOTE_ALLOCATOR_H_ + +/* + * Include + */ +#include <cm/inc/cm_type.h> +#include <cm/engine/memory/inc/memory_type.h> + + +/* + * Description of the memory block status + */ +typedef enum { + MEM_USED = 0, /* Memory block is used */ + MEM_FREE = 1 /* Memory block is free */ +} t_mem_status; + +/* + * Chunk structure. + */ +struct cm_allocator_desc; +typedef struct chunk_struct +{ + /* Double linked list of chunks */ + struct chunk_struct *prev; + struct chunk_struct *next; + + /* Double linked list of free memory */ + struct chunk_struct *prev_free_mem; + struct chunk_struct *next_free_mem; + + /* Offset of the block memory */ + t_uint32 offset; + + /* Size of the block memory */ + t_cm_size size; + + /* Status of the block memory */ + t_mem_status status; + + /* User data */ + t_uint16 userData; + + /* Alloc debug info*/ + t_uint32 domainId; + + /* Alloc desc backlink */ + struct cm_allocator_desc *alloc; +} t_cm_chunk; + +/*! + * \brief Identifier of an internal memory handle + * \ingroup MEMORY_INTERNAL + */ +typedef t_cm_chunk* t_memory_handle; + +#define INVALID_MEMORY_HANDLE ((t_cm_chunk*)NULL) + + +/* + * Context structure + */ +#define BINS 63 + +//TODO, juraj, add memType to alloc struct ? +typedef struct cm_allocator_desc { + const char *pAllocName; /* Name of the allocator */ + t_uint32 maxSize; /* Max size of the allocator -> Potentially increase/decrease by stack management */ + t_uint32 sbrkSize; /* Current size of allocator */ + t_uint32 offset; /* Offset of the allocator */ + t_cm_chunk *chunks; /* Array of chunk */ + t_cm_chunk *lastChunk; /* Null terminated last chunk of previous array declaration */ + t_cm_chunk *free_mem_chunks[BINS]; /* List of free memory */ + struct cm_allocator_desc* next; /* List of allocator */ +} t_cm_allocator_desc; + +int bin_index(unsigned int sz); + +/* + * Functions + */ +/*! + * \brief Create a new allocator for a piece of memory (hw mapped (xram, yram)) + * Any further allocation into this piece of memory will return an offset inside it. + * (a constant offset value can be added to this offset) + * + * \retval t_cm_allocator_desc* new memory allocator identifier + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC t_cm_allocator_desc* cm_MM_CreateAllocator( + t_cm_size size, //!< [in] Size of the addressable space in bytes + t_uint32 offset, //!< [in] Constant offset to add to each allocated block base address + const char* name //!< [in] Name of the allocator + ); + +/*! + * \brief Free a memory allocator descriptor + * + * \retval t_cm_error + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC t_cm_error cm_MM_DeleteAllocator( + t_cm_allocator_desc* alloc //!< [in] Identifier of the memory allocator to be freed + ); + + +/*! + * \brief Resize an allocator to the size value. + * + * \retval t_cm_error + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC t_cm_error cm_MM_ResizeAllocator( + t_cm_allocator_desc* alloc, //!< [in] Identifier of the memory allocator used to allocate the piece of memory + t_cm_size size //!< [in] Size of the addressable space in allocDesc granularity + ); + +/*! + * \brief Check validity of a user handle + */ +t_cm_error cm_MM_getValidMemoryHandle(t_cm_memory_handle handle, t_memory_handle* validHandle); + +/*! + * \brief Wrapper routine to allocate some memory into a given allocator + * + * \retval t_memory_handle handle on the new allocated piece of memory + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC t_memory_handle cm_MM_Alloc( + t_cm_allocator_desc* alloc, //!< [in] Identifier of the memory allocator + t_cm_size size, //!< [in] Size of the addressable space + t_cm_memory_alignment memAlignment, //!< [in] Alignment constraint + t_uint32 seg_offset, //!< [in] Offset of range where allocating + t_uint32 seg_size, //!< [in] Size of range where allocating + t_uint32 domainId + ); + + +/*! + * \brief Routine to reallocate memory for a given handle + * + * Routine to reallocate memory for a given handle. The chunk can be extended or shrinked in both + * directions - top and bottom, depending on the offset and size arguments. + * + * \retval t_memory_handle handle on the reallocated piece of memory + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC t_cm_error cm_MM_Realloc( + t_cm_allocator_desc* alloc, + const t_cm_size size, + const t_uint32 offset, + t_memory_handle *handle); +/*! + * \brief Frees the allocated chunk + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC void cm_MM_Free( + t_cm_allocator_desc* alloc, //!< [in] Identifier of the memory allocator + t_memory_handle memHandle //!< [in] Memory handle to free + ); + + +/*! + * \brief Get the allocator status + * + * \param[in] alloc Identifier of the memory allocator + * \param[out] pStatus Status of the allocator + * + * \retval t_cm_error + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC t_cm_error cm_MM_GetAllocatorStatus(t_cm_allocator_desc* alloc, t_uint32 offset, t_uint32 size, t_cm_allocator_status *pStatus); + +/*! + * \brief Returns the offset into a given memory allocator of an allocated piece of memory + * + * \param[in] memHandle handle on the given memory + * + * \retval t_uint32 offset into the given memory allocator + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC t_uint32 cm_MM_GetOffset(t_memory_handle memHandle); + + +/*! + * \brief Returns the size in word size for a given memory allocator of an allocated piece of memory + * + * \param[in] memHandle handle on the given memory + * + * \retval t_uint32 size in wordsize for the given memory allocator + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC t_uint32 cm_MM_GetSize(t_memory_handle memHandle); + +/*! + * \brief Returns the size in bytes for a given memory allocator + * + * \param[in] allocDesc Identifier of the memory allocator + * \retval size + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC t_uint32 cm_MM_GetAllocatorSize(t_cm_allocator_desc* allocDesc); + + +/*! + * \brief Set the user data of an allocated piece of memory + * + * \param[in] memHandle handle on the given memory + * \param[in] userData UsedData of the given memory piece + * + * \retval t_cm_error + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC void cm_MM_SetMemoryHandleUserData (t_memory_handle memHandle, t_uint16 userData); + + +/*! + * \brief Return the user data of an allocated piece of memory + * + * \param[in] memHandle handle on the given memory + * \param[out] pUserData returned UsedData of the given memory piece + * + * \retval t_cm_error + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC void cm_MM_GetMemoryHandleUserData(t_memory_handle memHandle, t_uint16 *pUserData, t_cm_allocator_desc **alloc); + +/*! + * \brief Dump chunkd in the range of [start:end] + * + * \param[in] alloc Allocator descriptor + * \param[in] start Range start + * \param[in] end Range end + * + * \retval void + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC void cm_MM_DumpMemory(t_cm_allocator_desc* alloc, t_uint32 start, t_uint32 end); + +/*! + * \brief Change the domain for the given chunk of memory + * + * \param[in] memHandle The given chunk of memory + * \param[in] domainId The new domain id to set + * + * \retval void + * + * \ingroup MEMORY_INTERNAL + */ +PUBLIC void cm_MM_SetDefaultDomain(t_memory_handle memHandle, t_uint32 domainId); +#endif /* _REMOTE_ALLOCATOR_H_*/ diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator_utils.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator_utils.h new file mode 100644 index 00000000000..0a7c901187b --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator_utils.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef REMOTE_ALLOCATOR_UTILS_H_ +#define REMOTE_ALLOCATOR_UTILS_H_ + +#include <cm/engine/memory/inc/remote_allocator.h> +#include <cm/engine/memory/inc/chunk_mgr.h> + +typedef enum { + FREE_CHUNK_BEFORE, + FREE_CHUNK_AFTER, +} t_mem_split_position; + + +PUBLIC void updateFreeList(t_cm_allocator_desc* alloc, t_cm_chunk* chunk); + +PUBLIC void linkChunk(t_cm_allocator_desc* alloc, t_cm_chunk* prev,t_cm_chunk* add); +PUBLIC void unlinkChunk(t_cm_allocator_desc* alloc,t_cm_chunk* current); + +PUBLIC void unlinkFreeMem(t_cm_allocator_desc* alloc,t_cm_chunk* current); +PUBLIC void linkFreeMemBefore(t_cm_chunk* add, t_cm_chunk* next); +PUBLIC void linkFreeMemAfter(t_cm_chunk* prev,t_cm_chunk* add); + +PUBLIC t_cm_chunk* splitChunk(t_cm_allocator_desc* alloc, t_cm_chunk *chunk, t_uint32 offset, t_mem_split_position position); + +#endif /*REMOTE_ALLOCATOR_UTILS_H_*/ diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/chunk_mgr.c b/drivers/staging/nmf-cm/cm/engine/memory/src/chunk_mgr.c new file mode 100644 index 00000000000..78a549a74ce --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/src/chunk_mgr.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * Include + */ +#include <cm/inc/cm_type.h> +#include "../inc/chunk_mgr.h" +#include <cm/engine/trace/inc/trace.h> + +#define CHUNKS_PER_PAGE 500 +#define CHUNK_THRESOLD 5 + +struct t_page_chuncks { + struct t_page_chuncks *nextPage; + // unsigned int freeChunkInPage; + t_cm_chunk chunks[CHUNKS_PER_PAGE]; +}; + +static struct t_page_chuncks *firstPage = 0; + +static unsigned int freeChunks = 0; +static t_cm_chunk *firstFreeChunk = 0; + +t_cm_chunk* allocChunk() +{ + t_cm_chunk* chunk = firstFreeChunk; + + firstFreeChunk = chunk->next; + + chunk->next_free_mem = 0; + chunk->prev_free_mem = 0; + chunk->prev = 0; + chunk->next = 0; + chunk->status = MEM_FREE; + // chunk->offset = 0; + // chunk->size = 0; + // chunk->alloc = 0; + // chunk->userData = 0; + + freeChunks--; + + return chunk; +} + +void freeChunk(t_cm_chunk* chunk) +{ + // Link chunk in free list + chunk->next = firstFreeChunk; + firstFreeChunk = chunk; + + // Increase counter + freeChunks++; +} + +t_cm_error allocChunkPool(void) +{ + struct t_page_chuncks* newPage; + int i; + + newPage = (struct t_page_chuncks*)OSAL_Alloc(sizeof(struct t_page_chuncks)); + if(newPage == NULL) + return CM_NO_MORE_MEMORY; + + // Link page + newPage->nextPage = firstPage; + firstPage = newPage; + + // Put chunk in free list + for(i = 0; i < CHUNKS_PER_PAGE; i++) + freeChunk(&newPage->chunks[i]); + + return CM_OK; +} + +t_cm_error fillChunkPool(void) +{ + if(freeChunks < CHUNK_THRESOLD) + return allocChunkPool(); + + return CM_OK; +} + +void freeChunkPool(void) +{ + while(firstPage != NULL) + { + struct t_page_chuncks* tofree = firstPage; + firstPage = firstPage->nextPage; + OSAL_Free(tofree); + } + + firstFreeChunk = 0; + freeChunks = 0; +} diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/domain.c b/drivers/staging/nmf-cm/cm/engine/memory/src/domain.c new file mode 100644 index 00000000000..a605cc475a9 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/src/domain.c @@ -0,0 +1,608 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/inc/cm_type.h> +#include <inc/nmf-limits.h> + +#include <cm/engine/memory/inc/domain.h> +#include <cm/engine/memory/inc/migration.h> +#include <cm/engine/memory/inc/chunk_mgr.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/component/inc/instance.h> +#include <cm/engine/power_mgt/inc/power.h> +#include <cm/engine/trace/inc/trace.h> + +/* + * domain_memory structure is all we need + */ +#define MAX_USER_DOMAIN_NB 64 +#define MAX_SCRATCH_DOMAIN_NB 16 + +t_cm_domain_desc domainDesc[MAX_USER_DOMAIN_NB]; +t_cm_domain_scratch_desc domainScratchDesc[MAX_SCRATCH_DOMAIN_NB]; + +static t_cm_allocator_desc *cm_DM_getAllocator(t_cm_domain_id domainId, t_dsp_memory_type_id memType); +static void cm_DM_DomainError(const t_cm_domain_id parentId, const t_nmf_client_id client); + +#define INIT_DOMAIN_STRUCT(domainDesc) do { \ + domainDesc.client = 0; \ + domainDesc.type = DOMAIN_NORMAL; \ + domainDesc.refcount = 0; \ + domainDesc.domain.coreId = MASK_ALL8; \ + domainDesc.domain.esramCode.offset = 0; \ + domainDesc.domain.esramCode.size = 0; \ + domainDesc.domain.esramData.offset = 0; \ + domainDesc.domain.esramData.size = 0; \ + domainDesc.domain.sdramCode.offset = 0; \ + domainDesc.domain.sdramCode.size = 0; \ + domainDesc.domain.sdramData.offset = 0; \ + domainDesc.domain.sdramData.size = 0; \ + domainDesc.scratch.parent.handle = 0; \ + domainDesc.scratch.child.alloc = 0; \ + domainDesc.scratch.child.parent_ref = 0; \ + domainDesc.dbgCooky = NULL; \ + } while (0) + +#define FIND_DOMAIN_ID(domainId) \ + { \ + domainId = 0; \ + while (domainDesc[domainId].client != 0 && domainId < MAX_USER_DOMAIN_NB) { \ + domainId++; \ + } \ + if (domainId >= MAX_USER_DOMAIN_NB) { \ + return CM_INTERNAL_DOMAIN_OVERFLOW; \ + } \ + } + +#define FIND_SCRATCH_DOMAIN_ID(domainId) \ + { \ + domainId = 0; \ + while (domainScratchDesc[domainId].allocDesc != 0 && domainId < MAX_SCRATCH_DOMAIN_NB) { \ + domainId++; \ + } \ + if (domainId >= MAX_SCRATCH_DOMAIN_NB) { \ + return CM_INTERNAL_DOMAIN_OVERFLOW; \ + } \ + } + +PUBLIC t_cm_error cm_DM_CheckDomain(t_cm_domain_id handle, t_cm_domain_type type) +{ + if ((handle <= 3) + || (handle >= MAX_USER_DOMAIN_NB)) { //remember, domain[0-3] are reserved + return CM_INVALID_DOMAIN_HANDLE; + } + + if (domainDesc[handle].client == 0) { + return CM_INVALID_DOMAIN_HANDLE; + } + + if (type != DOMAIN_ANY) { + if (domainDesc[handle].type != type) { + return CM_INVALID_DOMAIN_HANDLE; + } + } + + return CM_OK; +} + +PUBLIC t_cm_error cm_DM_CheckDomainWithClient(t_cm_domain_id handle, t_cm_domain_type type, t_nmf_client_id client) +{ + t_cm_error error; + + if((error = cm_DM_CheckDomain(handle, type)) != CM_OK) + return error; + +#ifdef CHECK_TO_BE_REACTIVATED_IN_2_11 + if(domainDesc[handle].client != client) + { + ERROR("CM_DOMAIN_VIOLATION: domain %d created by client %d not usable by client %d.", handle, domainDesc[handle].client, client, 0, 0, 0); + return CM_DOMAIN_VIOLATION; + } +#endif + + return CM_OK; +} + +PUBLIC t_cm_error cm_DM_Init(void) +{ + t_cm_error error; + + int i = 0; + for(i = 0; i < MAX_USER_DOMAIN_NB; i++) { + INIT_DOMAIN_STRUCT(domainDesc[i]); + } + + //domains[0-3] are reserved - allows to catch some cases of incorrect usage, + //especially when user uses coreId instead of domainId, ie id = 1, 2, 3 + domainDesc[0].client = NMF_CORE_CLIENT; + domainDesc[1].client = NMF_CORE_CLIENT; + domainDesc[2].client = NMF_CORE_CLIENT; + domainDesc[3].client = NMF_CORE_CLIENT; + + /* We use domain 1 and 2 for the singleton, only used for components structure */ + domainDesc[DEFAULT_SVA_DOMAIN].type = DOMAIN_NORMAL; + domainDesc[DEFAULT_SVA_DOMAIN].domain.coreId= SVA_CORE_ID; + domainDesc[DEFAULT_SVA_DOMAIN].domain.esramCode.size = (t_uint32)-1; + domainDesc[DEFAULT_SVA_DOMAIN].domain.esramData.size = (t_uint32)-1; + domainDesc[DEFAULT_SVA_DOMAIN].domain.sdramCode.size = (t_uint32)-1; + domainDesc[DEFAULT_SVA_DOMAIN].domain.sdramData.size = (t_uint32)-1; + domainDesc[DEFAULT_SIA_DOMAIN].type = DOMAIN_NORMAL; + domainDesc[DEFAULT_SIA_DOMAIN].domain.coreId= SIA_CORE_ID; + domainDesc[DEFAULT_SIA_DOMAIN].domain.esramCode.size = (t_uint32)-1; + domainDesc[DEFAULT_SIA_DOMAIN].domain.esramData.size = (t_uint32)-1; + domainDesc[DEFAULT_SIA_DOMAIN].domain.sdramCode.size = (t_uint32)-1; + domainDesc[DEFAULT_SIA_DOMAIN].domain.sdramData.size = (t_uint32)-1; + + for(i = 0; i < MAX_SCRATCH_DOMAIN_NB; i++) { + domainScratchDesc[i].domainId = 0; + domainScratchDesc[i].parentId = 0; + domainScratchDesc[i].allocDesc = 0; + } + + // Alloc twice for having comfortable chunk + if((error = allocChunkPool()) != CM_OK) + return error; + if((error = allocChunkPool()) != CM_OK) + { + freeChunkPool(); + return error; + } + + return CM_OK; +} + +PUBLIC void cm_DM_Destroy(void) +{ + //cm_DM_Init(); + freeChunkPool(); +} + +PUBLIC t_nmf_core_id cm_DM_GetDomainCoreId(const t_cm_domain_id domainId) +{ + + return domainDesc[domainId].domain.coreId; +} + +#if 0 +static t_uint32 cm_DM_isSegmentOverlaping(const t_cm_domain_segment *d0, const t_cm_domain_segment *d1) +{ + t_uint32 min0 = d0->offset; + t_uint32 max0 = d0->offset + d0->size; + t_uint32 min1 = d1->offset; + t_uint32 max1 = d1->offset + d1->size; + + if ( (min0 < min1) && (min1 < max0) ){ /* min0 < min1 < max0 OR min1 in [min0:max0] */ + return 1; + } + if ( (min1 < min0) && (min0 <= max1) ){ /* min1 < min0 < max0 OR min0 in [min1:max1] */ + return 1; + } + + return 0; +} +{ + ... + + t_uint32 i; + //check non-overlapp with other domains + for (i = 0; i < MAX_USER_DOMAIN_NB; i++) { + if (domainDesc[i].client != 0) { + if (cm_DM_isSegmentOverlaping(&domainDesc[i].domain.esramData, &domain->esramData)) { + return CM_DOMAIN_OVERLAP; + } + /* + if (cm_DM_isSegmentOverlaping(&domainDesc[i].domain.esramData, &domain->esramData)) { + return CM_DOMAIN_OVERLAP; + } + */ + } + } + + ... +} +#endif + +PUBLIC t_cm_error cm_DM_CreateDomain(const t_nmf_client_id client, const t_cm_domain_memory *domain, t_cm_domain_id *handle) +{ + t_cm_domain_id domainId; + FIND_DOMAIN_ID(domainId); + + if (client == 0) + return CM_INVALID_PARAMETER; + + if (domain->coreId > LAST_CORE_ID) + return CM_INVALID_DOMAIN_DEFINITION; + + //FIXME, juraj, check invalid domain definition + domainDesc[domainId].client = client; + domainDesc[domainId].domain = *domain; + + if (osal_debug_ops.domain_create) + osal_debug_ops.domain_create(domainId); + + *handle = domainId; + + return CM_OK; +} + +//TODO, juraj, add assert to cm_MM_GetOffset(), if domain is scratch parent +PUBLIC t_cm_error cm_DM_CreateDomainScratch(const t_nmf_client_id client, const t_cm_domain_id parentId, const t_cm_domain_memory *domain, t_cm_domain_id *handle) +{ + t_cm_error error; + t_memory_handle memhandle; + t_cm_allocator_desc *alloc; + t_uint32 parentMin, parentMax; + t_uint32 scratchMin, scratchMax; + + /* check if the parent domain exists */ + /* parent could be DOMAIN_NORMAL (1st call) or DOMAIN_SCRATCH_PARENT (other calls) */ + if ((error = cm_DM_CheckDomain(parentId, DOMAIN_ANY)) != CM_OK) { + return error; + } + + parentMin = domainDesc[parentId].domain.esramData.offset; + parentMax = domainDesc[parentId].domain.esramData.offset + domainDesc[parentId].domain.esramData.size; + scratchMin = domain->esramData.offset; + scratchMax = domain->esramData.offset + domain->esramData.size; + /* check if the scratch domain respects the parent domain (esram data only )*/ + if ( (parentMin > scratchMin) || (parentMax < scratchMax) ) { + return CM_INVALID_DOMAIN_DEFINITION; + } + + /* create the scratch domain */ + if ((error = cm_DM_CreateDomain(client, domain, handle)) != CM_OK) { + return error; + } + + /* check if this is the first scratch domain */ + if (domainDesc[parentId].scratch.parent.handle == 0) { + /* 1st scratch domain */ + t_cm_domain_segment tmp; + + /* reserve the zone for the scratch domain */ + tmp = domainDesc[parentId].domain.esramData; + domainDesc[parentId].domain.esramData = domain->esramData; + memhandle = cm_DM_Alloc(parentId, ESRAM_EXT16, domain->esramData.size / 2, CM_MM_ALIGN_NONE, FALSE); //note byte to 16bit-word conversion + domainDesc[parentId].domain.esramData = tmp; + if (memhandle == 0) { + cm_DM_DestroyDomain(*handle); + cm_DM_DomainError(parentId, client); + return CM_NO_MORE_MEMORY; + } + + domainDesc[parentId].type = DOMAIN_SCRATCH_PARENT; + domainDesc[parentId].refcount = 0; //reinit the refcount + domainDesc[parentId].scratch.parent.handle = memhandle; + + } else { + /* nth scratch domain */ + t_uint32 i; + t_uint32 oldMin = domainDesc[parentId].domain.esramData.offset + domainDesc[parentId].domain.esramData.offset; + t_uint32 oldMax = 0; + + /* compute the new scratch zone size */ + for(i = 0; i < MAX_USER_DOMAIN_NB; i++) { + if ((domainDesc[i].type == DOMAIN_SCRATCH_CHILD) && (domainDesc[i].scratch.child.parent_ref == parentId)) { + /* ok, here we have a scratch domain created from the same child domain */ + t_uint32 min = domainDesc[i].domain.esramData.offset; + t_uint32 max = domainDesc[i].domain.esramData.offset + domainDesc[i].domain.esramData.size; + + oldMin = (min < oldMin)?min:oldMin; + oldMax = (max > oldMax)?max:oldMax; + } + } + + /* resize the scratch zone */ + if ((oldMin > scratchMin) || (oldMax < scratchMax)) { + t_uint32 newMin = (oldMin > scratchMin)?scratchMin:oldMin; + t_uint32 newMax = (oldMax < scratchMax)?scratchMax:oldMax; + + if(cm_MM_Realloc(cm_DM_getAllocator(parentId, ESRAM_EXT16), newMax - newMin, newMin, + &domainDesc[parentId].scratch.parent.handle) != CM_OK) + { + /* failed to extend the zone */ + cm_DM_DestroyDomain(*handle); + cm_DM_DomainError(parentId, client); + return CM_NO_MORE_MEMORY; + } + } + } + + /* create esram-data allocator in the scratch domain */ + alloc = cm_MM_CreateAllocator(domainDesc[*handle].domain.esramData.size, + domainDesc[*handle].domain.esramData.offset, + "scratch"); + + domainDesc[*handle].type = DOMAIN_SCRATCH_CHILD; + domainDesc[*handle].scratch.child.parent_ref = parentId; + domainDesc[*handle].scratch.child.alloc = alloc; + domainDesc[parentId].refcount++; + + return error; +} + +PUBLIC t_cm_error cm_DM_DestroyDomains(const t_nmf_client_id client) +{ + t_cm_domain_id handle; + t_cm_error error, status=CM_OK; + + for (handle=0; handle<MAX_USER_DOMAIN_NB; handle++) { + if ((domainDesc[handle].client == client) + && ((error=cm_DM_DestroyDomain(handle)) != CM_OK)) { + LOG_INTERNAL(0, "Error (%d) destroying remaining domainId %d for client %u\n", error, handle, client, 0, 0, 0); + status = error; + } + } + return status; +} + +PUBLIC t_cm_error cm_DM_DestroyDomain(t_cm_domain_id handle) +{ + t_cm_error error = CM_OK; + t_uint32 i; + + if ((error = cm_DM_CheckDomain(handle, DOMAIN_ANY)) != CM_OK) { + return error; + } + + //forbid destruction of cm domains + //if (handle == cm_DSP_GetState(domainDesc[handle].domain.coreId)->domainEE) + // return CM_INVALID_DOMAIN_HANDLE; + + /* loop all components and check if there are still components instantiated with this handle */ + //actually this check is redundant with the usage counters as component instantiations allocate memory + for (i=0; i<ComponentTable.idxMax; i++) + { + if (NULL != componentEntry(i) && componentEntry(i)->domainId == handle) { + return CM_ILLEGAL_DOMAIN_OPERATION; + } + } + + //perform check based on usage counters + if (domainDesc[handle].refcount != 0) { + return CM_ILLEGAL_DOMAIN_OPERATION; + } + + if (domainDesc[handle].type == DOMAIN_SCRATCH_PARENT) { + return CM_ILLEGAL_DOMAIN_OPERATION; //parent destroyed implicitly with the last scratch + } else if (domainDesc[handle].type == DOMAIN_SCRATCH_CHILD) { + t_cm_allocator_status status; + t_cm_domain_id parentId = domainDesc[handle].scratch.child.parent_ref; + + cm_MM_GetAllocatorStatus(domainDesc[handle].scratch.child.alloc, 0, 0xffff, &status); + if (status.global.accumulate_used_memory != 0) { + //something is still allocated + return CM_ILLEGAL_DOMAIN_OPERATION; + } + + domainDesc[parentId].refcount--; + cm_MM_DeleteAllocator(domainDesc[handle].scratch.child.alloc); //returns no error + + if (domainDesc[parentId].refcount == 0) { + /* last scratch domain */ + cm_DM_Free(domainDesc[parentId].scratch.parent.handle, FALSE); + domainDesc[parentId].scratch.parent.handle = 0; + domainDesc[parentId].type = DOMAIN_NORMAL; + } else { + /* other child scratch domains exist, check if the reserved zone needs resize, ie reduce */ + + t_uint32 i; + /* init oldMin and oldMax to values we are sure will get overwritten below */ + t_uint32 oldMin = 0xffffffff; + t_uint32 oldMax = 0x0; + t_uint32 scratchMin = domainDesc[handle].domain.esramData.offset; + t_uint32 scratchMax = domainDesc[handle].domain.esramData.offset + domainDesc[handle].domain.esramData.size; + + /* compute the remaining reserved zone size */ + for(i = 0; i < MAX_USER_DOMAIN_NB; i++) { + if (i == handle) + continue; //do not consider the current domain to be destroyed later in this function + if ((domainDesc[i].type == DOMAIN_SCRATCH_CHILD) && (domainDesc[i].scratch.child.parent_ref == parentId)) { + /* ok, here we have a scratch domain created from the same child domain */ + t_uint32 min = domainDesc[i].domain.esramData.offset; + t_uint32 max = domainDesc[i].domain.esramData.offset + domainDesc[i].domain.esramData.size; + + oldMin = (min < oldMin)?min:oldMin; + oldMax = (max > oldMax)?max:oldMax; + } + } + + /* resize the scratch zone */ + if ((oldMin > scratchMin) || (oldMax < scratchMax)) { + CM_ASSERT(cm_MM_Realloc(cm_DM_getAllocator(parentId, ESRAM_EXT16), oldMax - oldMin, oldMin, + &domainDesc[parentId].scratch.parent.handle) == CM_OK); //the realloc shouldn't fail.. + } + } + } + + if (osal_debug_ops.domain_destroy) + osal_debug_ops.domain_destroy(handle); + + //reset the domain desc + INIT_DOMAIN_STRUCT(domainDesc[handle]); + + return CM_OK; +} + +/* + * - if the domainId is scratch parent, all allocations are done as in normal domains + * - if the domainId is scratch child + * if allocation type is esram, retrieve the allocator from the domainDesc + * else allocation is done as for normal domain + * - if the domainId is normal, allocator is retrieved from mpcDesc via cm_DSP_GetAllocator() + */ +static t_cm_allocator_desc *cm_DM_getAllocator(t_cm_domain_id domainId, t_dsp_memory_type_id memType) +{ + t_cm_allocator_desc *alloc = 0; + + if ((domainDesc[domainId].type == DOMAIN_SCRATCH_CHILD) + && ((memType == ESRAM_EXT16) || (memType == ESRAM_EXT24))) { + alloc = domainDesc[domainId].scratch.child.alloc; + } else { + alloc = cm_DSP_GetAllocator(domainDesc[domainId].domain.coreId, memType); + } + + return alloc; +} + +void START(void); +void END(const char*); + +//TODO, juraj, alloc would need to return finer errors then 0 +PUBLIC t_memory_handle cm_DM_Alloc(t_cm_domain_id domainId, t_dsp_memory_type_id memType, t_uint32 wordSize, t_cm_mpc_memory_alignment memAlignment, t_bool powerOn) +{ + t_nmf_core_id coreId = domainDesc[domainId].domain.coreId; + t_memory_handle handle; + t_cm_allocator_desc *alloc; + t_uint32 offset; + t_uint32 size; + + cm_DSP_GetInternalMemoriesInfo(domainId, memType, &offset, &size); + + if ((alloc = cm_DM_getAllocator(domainId, memType)) == 0) { + return 0; + } + + handle = cm_MM_Alloc(alloc, + cm_DSP_ConvertSize(memType, wordSize), + (t_cm_memory_alignment) memAlignment, + offset, size, domainId); + + if(handle != INVALID_MEMORY_HANDLE) + { + cm_MM_SetMemoryHandleUserData(handle, (coreId << SHIFT_BYTE1) | (memType << SHIFT_BYTE0)); + + if (powerOn) { + // [Pwr] The associated power domain can be enabled only after the Alloc request. + // Associated MPC memory chunk is not accessed (Remote allocator feature) + cm_PWR_EnableMemory( + coreId, + memType, + /* + * Compute physical address based on cm_DSP_GetHostSystemAddress but in optimized way + * -> See it for information + * -> Note TCM memory is not correctly compute, but it's not used + */ + cm_DSP_GetState(coreId)->allocator[memType]->baseAddress.physical + cm_MM_GetOffset(handle), + cm_MM_GetSize(handle)); + } + } else { + LOG_INTERNAL(0, "CM_NO_MORE_MEMORY domainId: %d, memType %d, wordSize %d, alignement %d\n", + domainId, memType, wordSize, memAlignment, 0, 0); + cm_MM_DumpMemory(alloc, offset, offset + size); + } + + return handle; +} + +PUBLIC void cm_DM_FreeWithInfo(t_memory_handle memHandle, t_nmf_core_id *coreId, t_dsp_memory_type_id *memType, t_bool powerOff) +{ + t_dsp_chunk_info chunk_info; + + cm_DSP_GetDspChunkInfo(memHandle, &chunk_info); + + if (powerOff) { + cm_PWR_DisableMemory( + chunk_info.coreId, + chunk_info.memType, + cm_DSP_GetPhysicalAdress(memHandle), + cm_MM_GetSize(memHandle)); + } + + cm_MM_Free(chunk_info.alloc, memHandle); + + *coreId = chunk_info.coreId; + *memType = chunk_info.memType; +} + +PUBLIC void cm_DM_Free(t_memory_handle memHandle, t_bool powerOff) +{ + t_nmf_core_id coreId; + t_dsp_memory_type_id memType; + + cm_DM_FreeWithInfo(memHandle, &coreId, &memType, powerOff); +} + +PUBLIC t_cm_error cm_DM_GetAllocatorStatus(t_cm_domain_id domainId, t_dsp_memory_type_id memType, t_cm_allocator_status *pStatus) +{ + t_cm_error error; + t_uint32 dOffset; + t_uint32 dSize; + + //TODO, scratch + error = cm_DM_CheckDomain(domainId, DOMAIN_ANY); + if (error != CM_OK) { + return error; + } + + cm_DSP_GetInternalMemoriesInfo(domainId, memType, &dOffset, &dSize); + + return cm_DSP_GetAllocatorStatus(domainDesc[domainId].domain.coreId, memType, + dOffset, dSize, pStatus); +} + +//WARNING: this function is only correct *before* migration! because +//the computation of absolute adresses of a domain is based on the allocator for the given +//segment (this is hidden in cm_DSP_GetDspBaseAddress and this info is not valid +//after migration (non-contiguous address-space from the ARM-side) +PUBLIC t_cm_error cm_DM_GetDomainAbsAdresses(t_cm_domain_id domainId, t_cm_domain_info *info) +{ + t_cm_error error; + t_nmf_core_id coreId = domainDesc[domainId].domain.coreId; + + cm_migration_check_state(coreId, STATE_NORMAL); + + error = cm_DM_CheckDomain(domainId, DOMAIN_NORMAL); + if (error != CM_OK) { + return error; + } + + cm_DSP_GetDspBaseAddress(coreId, SDRAM_CODE, &info->sdramCode); + cm_DSP_GetDspBaseAddress(coreId, ESRAM_CODE, &info->esramCode); + cm_DSP_GetDspBaseAddress(coreId, SDRAM_EXT24, &info->sdramData); + cm_DSP_GetDspBaseAddress(coreId, ESRAM_EXT24, &info->esramData); + + info->sdramCode.physical += domainDesc[domainId].domain.sdramCode.offset; + info->sdramCode.logical += domainDesc[domainId].domain.sdramCode.offset; + info->esramCode.physical += domainDesc[domainId].domain.esramCode.offset; + info->esramCode.logical += domainDesc[domainId].domain.esramCode.offset; + info->sdramData.physical += domainDesc[domainId].domain.sdramData.offset; + info->sdramData.logical += domainDesc[domainId].domain.sdramData.offset; + info->esramData.physical += domainDesc[domainId].domain.esramData.offset; + info->esramData.logical += domainDesc[domainId].domain.esramData.offset; + + return CM_OK; +} + +static void cm_DM_DomainError(const t_cm_domain_id parentId, const t_nmf_client_id client) +{ + int i; + LOG_INTERNAL(0, "NMF_DEBUG_SCRATCH failed to allocate domain (client %u): 0x%08x -> 0x%08x\n", + client, + domainDesc[parentId].domain.esramData.offset, + domainDesc[parentId].domain.esramData.offset + domainDesc[parentId].domain.esramData.size, + 0, 0, 0); + for(i = 0; i < MAX_USER_DOMAIN_NB; i++) { + if (domainDesc[i].type == DOMAIN_SCRATCH_CHILD) { + LOG_INTERNAL(0, "NMF_DEBUG_SCRATCH scratch domain %d allocated (client %u): 0x%08x -> 0x%08x\n", + i, domainDesc[i].client, + domainDesc[i].domain.esramData.offset, + domainDesc[i].domain.esramData.offset + domainDesc[i].domain.esramData.size, + 0, 0); + } + } + cm_MM_DumpMemory(cm_DM_getAllocator(parentId, ESRAM_EXT16), + domainDesc[parentId].domain.esramData.offset, + domainDesc[parentId].domain.esramData.offset + domainDesc[parentId].domain.esramData.size); +} + +PUBLIC void cm_DM_SetDefaultDomain(t_memory_handle memHandle, t_nmf_core_id coreId) +{ + if (coreId == SVA_CORE_ID) + cm_MM_SetDefaultDomain(memHandle, DEFAULT_SVA_DOMAIN); + else if (coreId == SIA_CORE_ID) + cm_MM_SetDefaultDomain(memHandle, DEFAULT_SIA_DOMAIN); +} diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/domain_wrapper.c b/drivers/staging/nmf-cm/cm/engine/memory/src/domain_wrapper.c new file mode 100644 index 00000000000..ec305812f15 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/src/domain_wrapper.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/api/domain_engine.h> +#include <cm/engine/api/migration_engine.h> +#include <cm/engine/memory/inc/domain.h> +#include <cm/engine/memory/inc/migration.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_CreateMemoryDomain( + const t_nmf_client_id client, + const t_cm_domain_memory *domain, + t_cm_domain_id *handle + ) +{ + t_cm_error error; + + OSAL_LOCK_API(); + error = cm_DM_CreateDomain(client, domain, handle); + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_CreateMemoryDomainScratch( + const t_nmf_client_id client, + const t_cm_domain_id parentId, + const t_cm_domain_memory *domain, + t_cm_domain_id *handle + ) +{ + t_cm_error error; + + OSAL_LOCK_API(); + error = cm_DM_CreateDomainScratch(client, parentId, domain, handle); + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_DestroyMemoryDomain( + t_cm_domain_id handle) +{ + t_cm_error error; + + OSAL_LOCK_API(); + error = cm_DM_DestroyDomain(handle); + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_FlushMemoryDomains( + t_nmf_client_id client) +{ + t_cm_error error; + + OSAL_LOCK_API(); + error = cm_DM_DestroyDomains(client); + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetDomainCoreId(const t_cm_domain_id domainId, t_nmf_core_id *coreId) +{ + t_cm_error error; + OSAL_LOCK_API(); + //TODO, scratch + error = cm_DM_CheckDomain(domainId, DOMAIN_NORMAL); + if (error != CM_OK) { + OSAL_UNLOCK_API(); + return error; + } + + *coreId = cm_DM_GetDomainCoreId(domainId); + OSAL_UNLOCK_API(); + return CM_OK; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_Migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst) +{ + t_cm_error error; + OSAL_LOCK_API(); + error = cm_migrate(srcShared, src, dst); + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_Unmigrate(void) +{ + t_cm_error error; + OSAL_LOCK_API(); + error = cm_unmigrate(); + OSAL_UNLOCK_API(); + return error; +} diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/memory_wrapper.c b/drivers/staging/nmf-cm/cm/engine/memory/src/memory_wrapper.c new file mode 100644 index 00000000000..37bea690e48 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/src/memory_wrapper.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/component/inc/instance.h> +#include <cm/engine/configuration/inc/configuration.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> +#include <cm/engine/trace/inc/trace.h> + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_AllocMpcMemory( + t_cm_domain_id domainId, + t_nmf_client_id clientId, + t_cm_mpc_memory_type memType, + t_cm_size size, + t_cm_mpc_memory_alignment memAlignment, + t_cm_memory_handle *pHandle + ) +{ + t_dsp_memory_type_id dspMemType; + + switch(memType) + { + case CM_MM_MPC_TCM16_X: + dspMemType = INTERNAL_XRAM16; + break; + case CM_MM_MPC_TCM24_X: + dspMemType = INTERNAL_XRAM24; + break; + case CM_MM_MPC_TCM16_Y: + dspMemType = INTERNAL_YRAM16; + break; + case CM_MM_MPC_TCM24_Y: + dspMemType = INTERNAL_YRAM24; + break; +#ifndef __STN_8810 + case CM_MM_MPC_ESRAM16: + dspMemType = ESRAM_EXT16; + break; + case CM_MM_MPC_ESRAM24: + dspMemType = ESRAM_EXT24; + break; +#endif /* ndef __STN_8810 */ + case CM_MM_MPC_SDRAM16: + dspMemType = SDRAM_EXT16; + break; + case CM_MM_MPC_SDRAM24: + dspMemType = SDRAM_EXT24; + break; + default: + return CM_INVALID_PARAMETER; + } + + OSAL_LOCK_API(); + { + t_cm_error error; + error = cm_DM_CheckDomainWithClient(domainId, DOMAIN_ANY, clientId); + if (error != CM_OK) { + OSAL_UNLOCK_API(); + return error; + } + } + + switch(memAlignment) { + case CM_MM_MPC_ALIGN_NONE : + case CM_MM_MPC_ALIGN_HALFWORD : + case CM_MM_MPC_ALIGN_WORD : + case CM_MM_MPC_ALIGN_2WORDS : + case CM_MM_MPC_ALIGN_4WORDS : + case CM_MM_MPC_ALIGN_8WORDS : + case CM_MM_MPC_ALIGN_16WORDS : + case CM_MM_MPC_ALIGN_32WORDS : + case CM_MM_MPC_ALIGN_64WORDS : + case CM_MM_MPC_ALIGN_128WORDS : + case CM_MM_MPC_ALIGN_256WORDS : + case CM_MM_MPC_ALIGN_512WORDS : + case CM_MM_MPC_ALIGN_1024WORDS : + case CM_MM_MPC_ALIGN_65536BYTES : + //case CM_MM_MPC_ALIGN_16384WORDS : maps to the same value as above + break; + default: + OSAL_UNLOCK_API(); + return CM_INVALID_PARAMETER; + } + + /* in case we allocate in tcm x be sure ee is load before */ + if ( memType == CM_MM_MPC_TCM16_X || memType == CM_MM_MPC_TCM24_X || + memType == CM_MM_MPC_TCM16_Y || memType == CM_MM_MPC_TCM24_Y ) + { + t_cm_error error; + if ((error = cm_CFG_CheckMpcStatus(cm_DM_GetDomainCoreId(domainId))) != CM_OK) + { + OSAL_UNLOCK_API(); + return error; + } + } + + /* alloc memory */ + *pHandle = (t_cm_memory_handle)cm_DM_Alloc(domainId, dspMemType, size, memAlignment, TRUE); + if(*pHandle == (t_cm_memory_handle)INVALID_MEMORY_HANDLE) + { + OSAL_UNLOCK_API(); + ERROR("CM_NO_MORE_MEMORY: CM_AllocMpcMemory() failed\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + OSAL_UNLOCK_API(); + return CM_OK; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_FreeMpcMemory(t_cm_memory_handle handle) +{ + t_cm_error error; + t_memory_handle validHandle; + t_nmf_core_id coreId; + t_dsp_memory_type_id memType; + + OSAL_LOCK_API(); + + if((error = cm_MM_getValidMemoryHandle(handle, &validHandle)) != CM_OK) + { + OSAL_UNLOCK_API(); + return error; + } + + cm_DM_FreeWithInfo(validHandle, &coreId, &memType, TRUE); + + /* in case we allocate in tcm x be sure ee is load before */ + if ( memType == INTERNAL_XRAM16 || memType == INTERNAL_XRAM24 || + memType == INTERNAL_YRAM16 || memType == INTERNAL_YRAM24 ) + { + cm_CFG_ReleaseMpc(coreId); + } + + OSAL_UNLOCK_API(); + return CM_OK; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemorySystemAddress(t_cm_memory_handle handle, t_cm_system_address *pSystemAddress) +{ + t_cm_error error; + t_memory_handle validHandle; + + OSAL_LOCK_API(); + + if((error = cm_MM_getValidMemoryHandle(handle, &validHandle)) != CM_OK) + { + OSAL_UNLOCK_API(); + return error; + } + + cm_DSP_GetHostSystemAddress(validHandle, pSystemAddress); + + OSAL_UNLOCK_API(); + return CM_OK; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemoryMpcAddress(t_cm_memory_handle handle, t_uint32 *pDspAddress) +{ + t_cm_error error; + t_memory_handle validHandle; + + OSAL_LOCK_API(); + + if((error = cm_MM_getValidMemoryHandle(handle, &validHandle)) != CM_OK) + { + OSAL_UNLOCK_API(); + return error; + } + + cm_DSP_GetDspAddress(validHandle, pDspAddress); + + OSAL_UNLOCK_API(); + return CM_OK; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemoryStatus(t_nmf_core_id coreId, t_cm_mpc_memory_type memType, t_cm_allocator_status *pStatus) +{ + t_dsp_memory_type_id dspMemType; + t_cm_error error; + + switch(memType) + { + case CM_MM_MPC_TCM16_X: + dspMemType = INTERNAL_XRAM16; + break; + case CM_MM_MPC_TCM24_X: + dspMemType = INTERNAL_XRAM24; + break; + case CM_MM_MPC_TCM16_Y: + dspMemType = INTERNAL_YRAM16; + break; + case CM_MM_MPC_TCM24_Y: + dspMemType = INTERNAL_YRAM24; + break; +#ifndef __STN_8810 + case CM_MM_MPC_ESRAM16: + dspMemType = ESRAM_EXT16; + break; + case CM_MM_MPC_ESRAM24: + dspMemType = ESRAM_EXT24; + break; +#endif /* ndef __STN_8810 */ + case CM_MM_MPC_SDRAM16: + dspMemType = SDRAM_EXT16; + break; + case CM_MM_MPC_SDRAM24: + dspMemType = SDRAM_EXT24; + break; + default: + return CM_INVALID_PARAMETER; + } + + OSAL_LOCK_API(); + error = cm_DSP_GetAllocatorStatus(coreId, dspMemType, 0, 0, pStatus); + OSAL_UNLOCK_API(); + + return error; +} + diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/migration.c b/drivers/staging/nmf-cm/cm/engine/memory/src/migration.c new file mode 100644 index 00000000000..d68898d830e --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/src/migration.c @@ -0,0 +1,392 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/inc/cm_type.h> +#include <inc/type.h> +#include <inc/nmf-limits.h> + +#include <cm/engine/communication/fifo/inc/nmf_fifo_arm.h> +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/memory/inc/domain.h> +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/memory/inc/migration.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/utils/inc/mem.h> + +#if defined(__STN_8500) && (__STN_8500 > 10) + +typedef enum { + CM_MIGRATION_OK = 0, + CM_MIGRATION_INVALID_ARGUMENT = 1, + CM_MIGRATION_ERROR = 2, +} t_cm_migration_error; + +extern t_nmf_fifo_arm_desc* mpc2mpcComsFifoId[NB_CORE_IDS][NB_CORE_IDS]; + +/*! + * \brief Data structure representing a segment to migrate + * + * segment: + * - used to determine which mmdsp-hw base is to be updated, index in mpcDesc->segments[] structure + * * this is hard-coded in cm_migrate(), could be computed (would be nice) LIMITATION + * srcAdr.physical: + * - new base setting + * * computed from the src domain in cm_DM_GetAbsAdresses() which uses the start of the allocator for the memory + * this is a LIMITATION, as this information is valid only before migration + * srcAdr.logical: + * - cm_MemCopy() + * * computed as srcAdr.logical + * dstAdr.physical: see srcAdr.physical + * dstAdr.logical: see srcAdr.logical + * size: + * - cm_MemCopy() + * - setting the top when new base is set + */ +typedef struct { + t_dsp_segment_type segment; //!< the link to the segment type + t_cm_system_address srcAdr; //!< source address + t_cm_system_address dstAdr; //!< destination address + t_uint32 size; //!< size of the segment +} t_cm_migration_segment; + +/*! + * \brief Internal data structure 1/ during migration, and 2/ between migration and unmigration calls + * + * all needed information are computed before calling _cm_migration_move() + */ +typedef struct { + t_cm_migration_state state; //!< migration state + t_nmf_core_id coreId; //!< migration only on one mpc + t_cm_migration_segment segments[NB_MIGRATION_SEGMENT]; //!< segments to migrate (selected on migration_move) + t_memory_handle handles[NB_MIGRATION_SEGMENT]; //!< memory handles for destination chunks allocated prior migration +} t_cm_migration_internal_state; + +static t_cm_migration_internal_state migrationState = {STATE_NORMAL, }; + +static t_cm_error _cm_migration_initSegment( + t_dsp_segment_type dspSegment, + t_cm_system_address *srcAdr, + t_uint32 size, + t_cm_domain_id dst, + t_cm_migration_internal_state *info + ) +{ + t_cm_system_address dstAdr; + t_cm_migration_segment *segment = &info->segments[dspSegment]; + t_memory_handle handle; + + handle = cm_DM_Alloc(dst, ESRAM_EXT16, size >> 1, CM_MM_ALIGN_AHB_BURST, TRUE); //note: byte to half-word conversion + if (handle == 0) { + ERROR("CM_NO_MORE_MEMORY: Unable to init segment for migration\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + info->handles[dspSegment] = handle; + + cm_DSP_GetHostSystemAddress(handle, &dstAdr); + + segment->segment = dspSegment; //this is redundant and could be avoided by recoding move(), but nice to have for debug + segment->size = size; + segment->srcAdr = *srcAdr; + segment->dstAdr = dstAdr; + + return CM_OK; +} + +static void _cm_migration_releaseSegment(t_cm_migration_internal_state *info, t_dsp_segment_type segId) +{ + cm_DM_Free(info->handles[segId], TRUE); +} + +static t_cm_migration_error _cm_migration_release(t_cm_migration_internal_state *info) +{ + t_uint32 i = 0; + for (i = 0; i < NB_MIGRATION_SEGMENT; i++) { + cm_DM_Free(info->handles[i], TRUE); + } + + return CM_MIGRATION_OK; +} + +#define SEGMENT_START(seg) \ + seg.offset + +#define SEGMENT_END(seg) \ + seg.offset + seg.size + +static t_cm_error _cm_migration_check( + const t_cm_domain_id srcShared, + const t_cm_domain_id src, + const t_cm_domain_id dst, + t_cm_migration_internal_state *info + ) +{ + t_cm_error error = CM_OK; + t_cm_domain_info domainInfoSrc; + t_cm_domain_info domainInfoShared; + t_cm_domain_desc *domainEE; + t_cm_domain_desc *domainShared; + t_nmf_core_id coreId = cm_DM_GetDomainCoreId(src); + + //coreIds in src, srcShared and dst match + if (!((domainDesc[src].domain.coreId == domainDesc[srcShared].domain.coreId) + && (domainDesc[src].domain.coreId == domainDesc[dst].domain.coreId))) { + return CM_INVALID_PARAMETER; + } + + //check srcShared starts at 0 + //FIXME, juraj, today EE code is in SDRAM, but this is flexible, so must find out where EE is instantiated + if (domainDesc[srcShared].domain.sdramCode.offset != 0x0) { + return CM_INVALID_PARAMETER; + } + + //check srcShared contains EE domain + domainEE = &domainDesc[cm_DSP_GetState(coreId)->domainEE]; + domainShared = &domainDesc[srcShared]; + if ((SEGMENT_START(domainEE->domain.esramCode) < SEGMENT_START(domainShared->domain.esramCode)) + ||(SEGMENT_END(domainEE->domain.esramCode) > SEGMENT_END(domainShared->domain.esramCode)) + ||(SEGMENT_START(domainEE->domain.esramData) < SEGMENT_START(domainShared->domain.esramData)) + ||(SEGMENT_END(domainEE->domain.esramData) > SEGMENT_END(domainShared->domain.esramData)) + ||(SEGMENT_START(domainEE->domain.sdramCode) < SEGMENT_START(domainShared->domain.sdramCode)) + ||(SEGMENT_END(domainEE->domain.sdramCode) > SEGMENT_END(domainShared->domain.sdramCode)) + ||(SEGMENT_START(domainEE->domain.sdramData) < SEGMENT_START(domainShared->domain.sdramData)) + ||(SEGMENT_END(domainEE->domain.sdramData) > SEGMENT_END(domainShared->domain.sdramData)) + ) { + return CM_INVALID_PARAMETER; + } + + info->coreId = coreId; + cm_DM_GetDomainAbsAdresses(srcShared, &domainInfoShared); + cm_DM_GetDomainAbsAdresses(src, &domainInfoSrc); + + if ((error = _cm_migration_initSegment(SDRAM_CODE_EE, &domainInfoShared.sdramCode, + domainDesc[srcShared].domain.sdramCode.size, dst, info)) != CM_OK) + goto _migration_error1; + if ((error = _cm_migration_initSegment(SDRAM_CODE_USER, &domainInfoSrc.sdramCode, + domainDesc[src].domain.sdramCode.size, dst, info)) != CM_OK) + goto _migration_error2; + if ((error = _cm_migration_initSegment(SDRAM_DATA_EE, &domainInfoShared.sdramData, + domainDesc[srcShared].domain.sdramData.size, dst, info)) != CM_OK) + goto _migration_error3; + if ((error = _cm_migration_initSegment(SDRAM_DATA_USER, &domainInfoSrc.sdramData, + domainDesc[src].domain.sdramData.size, dst, info)) != CM_OK) + goto _migration_error4; + return error; + +_migration_error4: _cm_migration_releaseSegment(info, SDRAM_DATA_EE); +_migration_error3: _cm_migration_releaseSegment(info, SDRAM_CODE_USER); +_migration_error2: _cm_migration_releaseSegment(info, SDRAM_CODE_EE); +_migration_error1: + OSAL_Log("Couldn't allocate memory for migration\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; +} + +typedef t_cm_error (*updateBase_t)(t_nmf_core_id, t_dsp_segment_type, t_cm_system_address, t_cm_system_address); + +static t_cm_migration_error _cm_migration_move( + t_nmf_core_id coreId, + t_cm_migration_segment *seg, + updateBase_t updateBase, + char* name + ) +{ + LOG_INTERNAL(1, "##### Migration %s: 0x%x -> 0x%x\n", name, seg->srcAdr.logical, seg->dstAdr.logical, 0, 0, 0); + cm_MemCopy((void*)seg->dstAdr.logical, (void*)seg->srcAdr.logical, seg->size); + updateBase(coreId, seg->segment, seg->srcAdr, seg->dstAdr); + cm_MemSet((void*)seg->srcAdr.logical, 0xdead, seg->size); //for debug, to be sure that we have actually moved the code and bases + + return CM_MIGRATION_OK; +} + +static t_cm_migration_error _cm_migration_update_internal( + t_cm_migration_internal_state *info, + t_cm_migration_state state + ) +{ + t_nmf_fifo_arm_desc *pArmFifo; + + migrationState.state = state; + + switch(state) { + case STATE_MIGRATED: + //move fifos + pArmFifo = mpc2mpcComsFifoId[ARM_CORE_ID][info->coreId]; + pArmFifo->fifoDesc = (t_nmf_fifo_desc*)cm_migration_translate(pArmFifo->dspAddressInfo.segmentType, (t_shared_addr)pArmFifo->fifoDescShadow); + pArmFifo = mpc2mpcComsFifoId[info->coreId][ARM_CORE_ID]; + pArmFifo->fifoDesc = (t_nmf_fifo_desc*)cm_migration_translate(pArmFifo->dspAddressInfo.segmentType, (t_shared_addr)pArmFifo->fifoDescShadow); + break; + + case STATE_NORMAL: + //move fifos + pArmFifo = mpc2mpcComsFifoId[ARM_CORE_ID][info->coreId]; + pArmFifo->fifoDesc = pArmFifo->fifoDescShadow; + pArmFifo = mpc2mpcComsFifoId[info->coreId][ARM_CORE_ID]; + pArmFifo->fifoDesc = pArmFifo->fifoDescShadow; + break; + + default: + OSAL_Log("unknown state", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + + return CM_MIGRATION_OK; +} + +PUBLIC t_cm_error cm_migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst) +{ + t_cm_migration_error mError; + t_cm_error error; + + if ((error = _cm_migration_check(srcShared, src, dst, &migrationState)) != CM_OK) { + return error; + } + + /* stop DSP execution */ + cm_DSP_Stop(migrationState.coreId); + + /* migrate EE and FX */ + mError = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_CODE_EE], cm_DSP_updateCodeBase, "code"); + if (mError) { + OSAL_Log("EE code migration failed", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + mError = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_DATA_EE], cm_DSP_updateDataBase, "data"); + if (mError) { + OSAL_Log("EE data migration failed", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + /* migrate user domain */ + mError = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_CODE_USER], cm_DSP_updateCodeBase, "code"); + if (mError) { + OSAL_Log("User code migration failed", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + mError = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_DATA_USER], cm_DSP_updateDataBase, "data"); + if (mError) { + OSAL_Log("User data migration failed", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + /* update CM internal structures */ + mError = _cm_migration_update_internal(&migrationState, STATE_MIGRATED); + if (mError) { + OSAL_Log("Update internal data failed", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + + /* Be sure everything has been write before restarting mmdsp */ + OSAL_mb(); + + /* resume DSP execution */ + cm_DSP_Start(migrationState.coreId); + + return CM_OK; +} + +static void _cm_migration_swapSegments( + t_cm_migration_segment *segment + ) +{ + t_cm_system_address tmp; + tmp = segment->dstAdr; + segment->dstAdr = segment->srcAdr; + segment->srcAdr = tmp; +} + +PUBLIC t_cm_error cm_unmigrate(void) +{ + t_cm_migration_error merror; + + if (migrationState.state != STATE_MIGRATED) + return CM_INVALID_PARAMETER; //TODO, juraj, define a proper error for this migration case + + cm_DSP_Stop(migrationState.coreId); + + _cm_migration_swapSegments(&migrationState.segments[SDRAM_CODE_EE]); + _cm_migration_swapSegments(&migrationState.segments[SDRAM_DATA_EE]); + _cm_migration_swapSegments(&migrationState.segments[SDRAM_CODE_USER]); + _cm_migration_swapSegments(&migrationState.segments[SDRAM_DATA_USER]); + + merror = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_CODE_EE], cm_DSP_updateCodeBase, "code"); + if (merror) { + OSAL_Log("EE code unmigration failed", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + merror = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_DATA_EE], cm_DSP_updateDataBase, "data"); + if (merror) { + OSAL_Log("EE data unmigration failed", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + merror = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_CODE_USER], cm_DSP_updateCodeBase, "code"); + if (merror) { + OSAL_Log("User code unmigration failed", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + merror = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_DATA_USER], cm_DSP_updateDataBase, "data"); + if (merror) { + OSAL_Log("User data unmigration failed", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + + /* update CM internal structures */ + merror = _cm_migration_update_internal(&migrationState, STATE_NORMAL); + if (merror) { + OSAL_Log("Update internal data failed", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + + /* Be sure everything has been write before restarting mmdsp */ + OSAL_mb(); + + cm_DSP_Start(migrationState.coreId); + + /* update CM internal structures */ + merror = _cm_migration_release(&migrationState); + if (merror) { + OSAL_Log("Update internal data failed", 0, 0, 0, 0, 0, 0); + CM_ASSERT(0); + } + + return CM_OK; +} + +// here we make the assumption that the offset doesn't depend from the dsp!! +PUBLIC t_uint32 cm_migration_translate(t_dsp_segment_type segmentType, t_uint32 addr) +{ + //TODO, juraj, save delta instead of recalculating it + t_sint32 offset; + if (migrationState.state == STATE_MIGRATED) { + offset = migrationState.segments[segmentType].dstAdr.logical - migrationState.segments[segmentType].srcAdr.logical; + } else { + offset = 0; + } + return addr + offset; +} + +PUBLIC void cm_migration_check_state(t_nmf_core_id coreId, t_cm_migration_state expected) +{ + CM_ASSERT(migrationState.state == expected); +} + +#else +PUBLIC t_cm_error cm_migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst) +{ + return CM_OK; +} + +PUBLIC t_cm_error cm_unmigrate(void) +{ + return CM_OK; +} + +PUBLIC t_uint32 cm_migration_translate(t_dsp_segment_type segmentType, t_uint32 addr) +{ + return addr; +} + +PUBLIC void cm_migration_check_state(t_nmf_core_id coreId, t_cm_migration_state expected) +{ + return; +} +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator.c b/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator.c new file mode 100644 index 00000000000..0d000d37371 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator.c @@ -0,0 +1,656 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* +Â * Copyright (C) ST-Ericsson SA 2010 +Â *Â Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com>Â for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. +Â */ +/* + * Include + */ +#include "../inc/remote_allocator.h" +#include "../inc/remote_allocator_utils.h" +#include "../inc/chunk_mgr.h" + +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/trace/inc/xtitrace.h> + +static void cm_MM_RA_checkAllocator(t_cm_allocator_desc* alloc); +//static void cm_MM_RA_checkAlloc(t_cm_allocator_desc* alloc, t_uint32 size, t_uint32 align, t_uint32 min, t_uint32 max); + +int bin_index(unsigned int sz) { + /* + * 32 bins of size 2 + * 16 bins of size 16 + * 8 bins of size 128 + * 4 bins of size 1024 + * 2 bins of size 8192 + * 1 bin of size what's left + * + */ + return (((sz >> 6) == 0) ? (sz >> 1): // 0 -> 0 .. 31 + ((sz >> 6) <= 4) ? 28 + (sz >> 4): // 64 -> 32 .. 47 + ((sz >> 6) <= 20) ? 46 + (sz >> 7): // 320 -> 48 .. 55 + ((sz >> 6) <= 84) ? 55 + (sz >> 10): // 1344 -> 56 .. 59 + ((sz >> 6) <= 340) ? 59 + (sz >> 13): // 5440 -> 60 .. 61 + 62); // 21824.. +} + +static t_cm_allocator_desc* ListOfAllocators = NULL; + +PUBLIC t_cm_allocator_desc* cm_MM_CreateAllocator(t_cm_size size, t_uint32 offset, const char* name) +{ + t_cm_allocator_desc *alloc; + + CM_ASSERT(fillChunkPool() == CM_OK); + + /* Alloc structure */ + alloc = (t_cm_allocator_desc*)OSAL_Alloc_Zero(sizeof(t_cm_allocator_desc)); + CM_ASSERT(alloc != NULL); + + // Add allocator in list + alloc->next = ListOfAllocators; + ListOfAllocators = alloc; + + /* assign name */ + alloc->pAllocName = name; + + alloc->maxSize = size; + alloc->sbrkSize = 0; + alloc->offset = offset; + + //TODO, juraj, alloc impacts trace format + cm_TRC_traceMemAlloc(TRACE_ALLOCATOR_COMMAND_CREATE, 0, size, name); + + return alloc; +} + +PUBLIC t_cm_error cm_MM_DeleteAllocator(t_cm_allocator_desc *alloc) +{ + t_cm_chunk *chunk, *next_cm_chunk; + + cm_TRC_traceMemAlloc(TRACE_ALLOCATOR_COMMAND_DESTROY, 0, 0, alloc->pAllocName); + + /* Parse all chunks and free them */ + chunk = alloc->chunks; + while(chunk != 0) + { + next_cm_chunk = chunk->next; + unlinkChunk(alloc, chunk); + freeChunk(chunk); + + chunk = next_cm_chunk; + } + + // Remove allocator from the list + if(ListOfAllocators == alloc) + ListOfAllocators = alloc->next; + else { + t_cm_allocator_desc *prev = ListOfAllocators; + while(prev->next != alloc) + prev = prev->next; + prev->next = alloc->next; + } + + + /* Free allocator descriptor */ + OSAL_Free(alloc); + + return CM_OK; +} + +PUBLIC t_cm_error cm_MM_ResizeAllocator(t_cm_allocator_desc *alloc, t_cm_size size) +{ + /* sanity check */ + if (size == 0) + return CM_INVALID_PARAMETER; + + if(alloc->sbrkSize > size) + return CM_NO_MORE_MEMORY; + + alloc->maxSize = size; + + if (cmIntensiveCheckState) + cm_MM_RA_checkAllocator(alloc); + + return CM_OK; +} + +t_cm_error cm_MM_getValidMemoryHandle(t_cm_memory_handle handle, t_memory_handle* validHandle) +{ +#ifdef LINUX + /* On linux, there is already a check within the linux part + * => we don't need to check twice */ + *validHandle = (t_memory_handle)handle; + return CM_OK; +#else + t_cm_allocator_desc *alloc = ListOfAllocators; + + for(; alloc != NULL; alloc = alloc->next) + { + t_cm_chunk* chunk = alloc->chunks; + + /* Parse all chunks */ + for(; chunk != NULL; chunk = chunk->next) + { + if(chunk == (t_memory_handle)handle) + { + if(chunk->status == MEM_FREE) + return CM_MEMORY_HANDLE_FREED; + + *validHandle = (t_memory_handle)handle; + + return CM_OK; + } + } + } + + return CM_UNKNOWN_MEMORY_HANDLE; +#endif +} + +//TODO, juraj, add appartenance to allocHandle (of chunk) and degage setUserData +PUBLIC t_memory_handle cm_MM_Alloc( + t_cm_allocator_desc* alloc, + t_cm_size size, + t_cm_memory_alignment memAlignment, + t_uint32 seg_offset, + t_uint32 seg_size, + t_uint32 domainId) +{ + t_cm_chunk* chunk; + t_uint32 aligned_offset; + t_uint32 aligned_end; + t_uint32 seg_end = seg_offset + seg_size; + int i; + + /* Sanity check */ + if ( (size == 0) || (size > seg_size) ) + return INVALID_MEMORY_HANDLE; + + if(fillChunkPool() != CM_OK) + return INVALID_MEMORY_HANDLE; + + /* Get first chunk available for the specific size */ + // Search a list with a free chunk + for(i = bin_index(size); i < BINS; i++) + { + chunk = alloc->free_mem_chunks[i]; + while (chunk != 0) + { + /* Alignment of the lower boundary */ + aligned_offset = ALIGN_VALUE(MAX(chunk->offset, seg_offset), (memAlignment + 1)); + + aligned_end = aligned_offset + size; + + if ((aligned_end <= seg_end) + && aligned_end <= (chunk->offset + chunk->size) + && aligned_offset >= seg_offset + && aligned_offset >= chunk->offset) + goto found; + + chunk = chunk->next_free_mem; + } + } + + // Try to increase sbrkSize through maxSize + aligned_offset = ALIGN_VALUE(MAX((alloc->offset + alloc->sbrkSize), seg_offset), (memAlignment + 1)); + + aligned_end = aligned_offset + size; + + if ((aligned_end <= seg_end) + && aligned_end <= (alloc->offset + alloc->maxSize) + && aligned_offset >= seg_offset + && aligned_offset >= (alloc->offset + alloc->sbrkSize)) + { + /* If that fit requirement, create a new free chunk at the end of current allocator */ + chunk = allocChunk(); + + /* Update chunk size */ + chunk->offset = alloc->offset + alloc->sbrkSize; // offset start at end of current allocator + chunk->size = aligned_end - chunk->offset; + chunk->alloc = alloc; + + /* Chain it with latest chunk */ + linkChunk(alloc, alloc->lastChunk, chunk); + + /* Increase sbrkSize to end of this new chunk */ + alloc->sbrkSize += chunk->size; + + goto foundNew; + } + + return INVALID_MEMORY_HANDLE; + +found: + /* Remove chunk from free list */ + unlinkFreeMem(alloc, chunk); + +foundNew: + //create an empty chunk before the allocated one + if (chunk->offset < aligned_offset) { + chunk = splitChunk(alloc, chunk, aligned_offset, FREE_CHUNK_BEFORE); + } + //create an empty chunk after the allocated one + if (chunk->offset + chunk->size > aligned_end) { + splitChunk(alloc, chunk, aligned_end, FREE_CHUNK_AFTER); + } + + chunk->status = MEM_USED; + chunk->prev_free_mem = 0; + chunk->next_free_mem = 0; + chunk->domainId = domainId; + + //TODO, juraj, alloc impacts trace format + cm_TRC_traceMem(TRACE_ALLOC_COMMAND_ALLOC, 0, chunk->offset, chunk->size); + + if (cmIntensiveCheckState) { + cm_MM_RA_checkAllocator(alloc); + } + + return (t_memory_handle) chunk; +} + +//caution - if successfull, the chunk offset will be aligned with seg_offset +//caution++ the offset of the allocated chunk changes implicitly +PUBLIC t_cm_error cm_MM_Realloc( + t_cm_allocator_desc* alloc, + const t_cm_size size, + const t_uint32 offset, + t_memory_handle *handle) +{ + t_cm_chunk *chunk = (t_cm_chunk*)*handle; + t_uint32 oldOffset = chunk->offset; + t_uint32 oldSize = chunk->size; + t_uint32 oldDomainId = chunk->domainId; + t_uint16 userData = chunk->userData; + + cm_MM_Free(alloc, *handle); + + *handle = cm_MM_Alloc(alloc, size, CM_MM_ALIGN_NONE, offset, size, oldDomainId); + + if(*handle == INVALID_MEMORY_HANDLE) + { + *handle = cm_MM_Alloc(alloc, oldSize, CM_MM_ALIGN_NONE, oldOffset, oldSize, oldDomainId); + + CM_ASSERT(*handle != INVALID_MEMORY_HANDLE); + + chunk = (t_cm_chunk*)*handle; + chunk->userData = userData; + + return CM_NO_MORE_MEMORY; + } + + chunk = (t_cm_chunk*)*handle; + chunk->userData = userData; + + return CM_OK; + +#if 0 + /* check reallocation is related to this chunk! */ + CM_ASSERT(chunk->offset <= (offset + size)); + CM_ASSERT(offset <= (chunk->offset + chunk->size)); + CM_ASSERT(size); + + /* check if extend low */ + if (offset < chunk->offset) { + /* note: it is enough to check only the previous chunk, + * because adjacent chunks of same status are merged + */ + if ((chunk->prev == 0) + ||(chunk->prev->status != MEM_FREE) + ||(chunk->prev->offset > offset)) { + return INVALID_MEMORY_HANDLE; + } + } + + /* check if extend high, extend sbrk if necessary */ + if ( (offset + size) > (chunk->offset + chunk->size)) { + if(chunk->next == 0) + { + // check if allocator can be extended to maxSize + if((offset + size) > (alloc->offset + alloc->maxSize)) + return INVALID_MEMORY_HANDLE; + } + else + { + if ((chunk->next->status != MEM_FREE) + ||( (chunk->next->offset + chunk->next->size) < (offset + size))) { + return INVALID_MEMORY_HANDLE; + } + } + } + + if(fillChunkPool() != CM_OK) + return INVALID_MEMORY_HANDLE; + + + /* extend low + * all conditions should have been checked + * this must not fail + */ + if (offset < chunk->offset) { + t_uint32 delta = chunk->prev->offset + chunk->prev->size - offset; + t_cm_chunk *prev = chunk->prev; + + chunk->offset -= delta; + chunk->size += delta; + + CM_ASSERT(prev->status == MEM_FREE); //TODO, juraj, already checked + unlinkFreeMem(alloc, prev); + prev->size -= delta; + if(prev->size == 0) + { + unlinkChunk(alloc, prev); + freeChunk(prev); + } else { + updateFreeList(alloc, prev); + } + } + + /* extend high */ + if ( (offset + size) > (chunk->offset + chunk->size)) { + t_uint32 delta = size - chunk->size; + t_cm_chunk *next = chunk->next; + + chunk->size += delta; + + if(next == 0) + { + alloc->sbrkSize += delta; + } else { + CM_ASSERT(next->status == MEM_FREE); + unlinkFreeMem(alloc, next); + next->offset += delta; + next->size -= delta; + if(next->size == 0) + { + unlinkChunk(alloc, next); + freeChunk(next); + } else { + updateFreeList(alloc, next); + } + } + } + + /* reduce top */ + if ((offset + size) < (chunk->offset + chunk->size)) { + t_uint32 delta = chunk->size - size; + + if(chunk->next == 0) { + alloc->sbrkSize -= delta; + chunk->size -= delta; + + } else if (chunk->next->status == MEM_FREE) { + unlinkFreeMem(alloc, chunk->next); + chunk->size -= delta; + chunk->next->offset -= delta; + chunk->next->size += delta; + updateFreeList(alloc, chunk->next); + } else { + t_cm_chunk *tmp = splitChunk(alloc, chunk, offset + size, FREE_CHUNK_AFTER); //tmp = chunk, chunk = result + tmp->status = MEM_USED; + tmp->next->status = MEM_FREE; + } + } + + /* reduce bottom */ + if (offset > chunk->offset) { + if (chunk->prev->status == MEM_FREE) { + t_uint32 delta = offset - chunk->offset; + unlinkFreeMem(alloc, chunk->prev); + chunk->prev->size += delta; + chunk->offset = offset; + chunk->size -= delta; + updateFreeList(alloc, chunk->prev); + } else { + t_cm_chunk *tmp = splitChunk(alloc, chunk, offset, FREE_CHUNK_BEFORE); //tmp->next = chunk, tmp = result + tmp->status = MEM_USED; + tmp->prev->status = MEM_FREE; + } + } + + cm_MM_RA_checkAllocator(alloc); + + return (t_memory_handle)chunk; +#endif +} + +PUBLIC void cm_MM_Free(t_cm_allocator_desc* alloc, t_memory_handle memHandle) +{ + t_cm_chunk* chunk = (t_cm_chunk*)memHandle; + + //TODO, juraj, alloc impacts trace format + cm_TRC_traceMem(TRACE_ALLOC_COMMAND_FREE, 0, + chunk->offset, chunk->size); + + /* Update chunk status */ + chunk->status = MEM_FREE; + chunk->domainId = 0x0; + + // Invariant: Current chunk is free but not in free list + + /* Check if the previous chunk is free */ + if((chunk->prev != 0) && (chunk->prev->status == MEM_FREE)) + { + t_cm_chunk* prev = chunk->prev; + + // Remove chunk to be freed from memory list + unlinkChunk(alloc, chunk); + + // Remove previous from free list + unlinkFreeMem(alloc, prev); + + // Update previous size + prev->size += chunk->size; + + freeChunk(chunk); + + chunk = prev; + } + + /* Check if the next chunk is free */ + if((chunk->next != 0) && (chunk->next->status == MEM_FREE)) + { + t_cm_chunk* next = chunk->next; + + // Remove next from memory list + unlinkChunk(alloc, next); + + // Remove next from free list + unlinkFreeMem(alloc, next); + + // Update previous size + chunk->size += next->size; + + freeChunk(next); + } + + if(chunk->next == 0) + { + // If we are the last one, decrease sbrkSize + alloc->sbrkSize -= chunk->size; + + unlinkChunk(alloc, chunk); + freeChunk(chunk); + + } + else + { + // Add it in free list + updateFreeList(alloc, chunk); + } + + if (cmIntensiveCheckState) { + cm_MM_RA_checkAllocator(alloc); + } +} + +PUBLIC t_cm_error cm_MM_GetAllocatorStatus(t_cm_allocator_desc* alloc, t_uint32 offset, t_uint32 size, t_cm_allocator_status *pStatus) +{ + t_cm_chunk* chunk = alloc->chunks; + t_uint32 sbrkFree = alloc->maxSize - alloc->sbrkSize; + t_uint8 min_free_size_updated = FALSE; + + /* Init status */ + pStatus->global.used_block_number = 0; + pStatus->global.free_block_number = 0; + pStatus->global.maximum_free_size = 0; + pStatus->global.minimum_free_size = 0xFFFFFFFF; + pStatus->global.accumulate_free_memory = 0; + pStatus->global.accumulate_used_memory = 0; + pStatus->global.size = alloc->maxSize; + pStatus->domain.maximum_free_size = 0; + pStatus->domain.minimum_free_size = 0xFFFFFFFF; + pStatus->domain.accumulate_free_memory = 0; + pStatus->domain.accumulate_used_memory = 0; + pStatus->domain.size= size; + + /* Parse all chunks */ + while(chunk != 0) + { + + /* Chunk is free */ + if (chunk->status == MEM_FREE) { + pStatus->global.free_block_number++; + pStatus->global.accumulate_free_memory += chunk->size; + + /* Check max size */ + if (chunk->size > pStatus->global.maximum_free_size) + { + pStatus->global.maximum_free_size = chunk->size; + } + + /* Check min size */ + if (chunk->size < pStatus->global.minimum_free_size) + { + pStatus->global.minimum_free_size = chunk->size; + min_free_size_updated = TRUE; + } + } else {/* Chunk used */ + pStatus->global.used_block_number++; + pStatus->global.accumulate_used_memory += chunk->size; + } + + chunk = chunk->next; + } + + /* Accumulate free space between sbrkSize and maxSize */ + pStatus->global.accumulate_free_memory += sbrkFree; + if (sbrkFree > 0) + pStatus->global.free_block_number++; + if (pStatus->global.maximum_free_size < sbrkFree) + pStatus->global.maximum_free_size = sbrkFree; + if (pStatus->global.minimum_free_size > sbrkFree) { + pStatus->global.minimum_free_size = sbrkFree; + min_free_size_updated = TRUE; + } + + /* Put max free size to min free size */ + if (min_free_size_updated == FALSE) { + pStatus->global.minimum_free_size = pStatus->global.maximum_free_size; + } + + return CM_OK; +} + +PUBLIC t_uint32 cm_MM_GetOffset(t_memory_handle memHandle) +{ + /* Provide offset */ + return ((t_cm_chunk*)memHandle)->offset; +} + +PUBLIC t_uint32 cm_MM_GetSize(t_memory_handle memHandle) +{ + return ((t_cm_chunk*)memHandle)->size; +} + +PUBLIC t_uint32 cm_MM_GetAllocatorSize(t_cm_allocator_desc* alloc) +{ + return alloc->maxSize; +} + +PUBLIC void cm_MM_SetMemoryHandleUserData(t_memory_handle memHandle, t_uint16 userData) +{ + ((t_cm_chunk*)memHandle)->userData = userData; +} + +PUBLIC void cm_MM_GetMemoryHandleUserData(t_memory_handle memHandle, t_uint16 *pUserData, t_cm_allocator_desc **alloc) +{ + *pUserData = ((t_cm_chunk*)memHandle)->userData; + if (alloc) + *alloc = ((t_cm_chunk*)memHandle)->alloc; +} + +/* + * check free list is ordered + * check all chunks are correctly linked + * check adjacent chunks are not FREE + */ +static void cm_MM_RA_checkAllocator(t_cm_allocator_desc* alloc) +{ + t_cm_chunk *chunk = alloc->chunks; + t_uint32 size = 0; + int i; + + CM_ASSERT(alloc->sbrkSize <= alloc->maxSize); + + while(chunk != 0) { + if(chunk == alloc->chunks) + CM_ASSERT(chunk->prev == 0); + if(chunk == alloc->lastChunk) + CM_ASSERT(chunk->next == 0); + + CM_ASSERT(chunk->alloc == alloc); + + if (chunk->next != 0) { + CM_ASSERT(!((chunk->status == MEM_FREE) && (chunk->next->status == MEM_FREE))); //two free adjacent blocks + CM_ASSERT(chunk->offset < chunk->next->offset); //offsets reverted + CM_ASSERT(chunk->offset + chunk->size == chunk->next->offset); // Not hole in allocator + } + size += chunk->size; + chunk = chunk->next; + } + + CM_ASSERT(size == alloc->sbrkSize); + + for(i = 0; i < BINS; i++) + { + chunk = alloc->free_mem_chunks[i]; + while(chunk != 0) { + if (chunk->next_free_mem != 0) { + CM_ASSERT(chunk->size <= chunk->next_free_mem->size); //free list not ordered + } + chunk = chunk->next_free_mem; + } + } +} + +PUBLIC void cm_MM_DumpMemory(t_cm_allocator_desc* alloc, t_uint32 start, t_uint32 end) +{ + t_cm_chunk *chunk = alloc->chunks; + + LOG_INTERNAL(0, "ALLOCATOR Dumping allocator \"%s\" [0x%08x:0x%08x]\n", alloc->pAllocName, start, end, 0, 0, 0); + while(chunk != 0) { + if (((chunk->offset < start) && (chunk->offset + chunk->size > start)) + || ((chunk->offset < end) && (chunk->offset + chunk->size > end)) + || ((chunk->offset > start) && (chunk->offset + chunk->size < end)) + || ((chunk->offset < start) && (chunk->offset + chunk->size > end))) + { + LOG_INTERNAL(0, "ALLOCATOR chunk [0x%08x -> 0x%08x[: status:%s, domainId: 0x%x\n", + chunk->offset, + chunk->offset + chunk->size, + chunk->status?"FREE":"USED", + chunk->domainId, 0, 0); + } + chunk = chunk->next; + } +} + +PUBLIC void cm_MM_SetDefaultDomain(t_memory_handle memHandle, t_uint32 domainId) +{ + ((t_cm_chunk *) memHandle)->domainId = domainId; +} diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator_utils.c b/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator_utils.c new file mode 100644 index 00000000000..4e800376dbb --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator_utils.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/memory/inc/remote_allocator_utils.h> +#include <cm/engine/trace/inc/trace.h> + +/***************************************************************************/ +/* + * linkChunk + * param prev : Pointer on previous chunk where the chunk will be added + * param add : Pointer on chunk to add + * + * Add a chunk in the memory list + * + */ +/***************************************************************************/ +PUBLIC void linkChunk(t_cm_allocator_desc* alloc, t_cm_chunk* prev, t_cm_chunk* add) +{ + // Link previous + if(prev == 0) + { + add->next = alloc->chunks; + alloc->chunks = add; + } + else + { + add->prev = prev; + add->next = prev->next; + prev->next = add; + } + + // Link next + if(add->next == 0) + { + // Link at the end + alloc->lastChunk = add; + } + else + add->next->prev = add; +} + +/***************************************************************************/ +/* + * unlinkChunk + * param allocHandle : Allocator handle + * param current : Pointer on chunk to remove + * + * Remove a chunk in the memory list and update first pointer + * + */ +/***************************************************************************/ +PUBLIC void unlinkChunk(t_cm_allocator_desc* alloc, t_cm_chunk* current) +{ + /* Link previous with next */ + if (current->prev != 0) + current->prev->next = current->next; + else + { + CM_ASSERT(alloc->chunks == current); + + // We remove the first, update chunks + alloc->chunks = current->next; + } + + /* Link next with previous */ + if(current->next != 0) + current->next->prev= current->prev; + else + { + CM_ASSERT(alloc->lastChunk == current); + + // We remove the last, update lastChunk + alloc->lastChunk = current->prev; + } +} + + +/***************************************************************************/ +/* + * unlinkFreeMem() unlinks chunk from free memory double-linked list + * makes the previous and next chunk in the list point to each other.. + * param allocHandle : Allocator handle + * param current : Pointer on chunk to remove + * + * Remove a chunk in the free memory list and update pointer + * + */ +/***************************************************************************/ +PUBLIC void unlinkFreeMem(t_cm_allocator_desc* alloc ,t_cm_chunk* current) +{ + int bin = bin_index(current->size); + + /* unlink previous */ + if (current->prev_free_mem != 0) + { + current->prev_free_mem->next_free_mem = current->next_free_mem; + } + + /* Unlink next */ + if (current->next_free_mem !=0 ) + { + current->next_free_mem->prev_free_mem = current->prev_free_mem; + } + + /* update first free pointer */ + if (alloc->free_mem_chunks[bin] == current) + { + alloc->free_mem_chunks[bin] = current->next_free_mem; + } + + current->prev_free_mem = 0; + current->next_free_mem = 0; +} + +/***************************************************************************/ +/* + * linkFreeMemBefore + * param add : Pointer on chunk to add + * param next : Pointer on next chunk where the chunk will be added before + * + * Add a chunk in the free memory list + * + */ +/***************************************************************************/ +PUBLIC void linkFreeMemBefore(t_cm_chunk* add, t_cm_chunk* next) +{ + /* Link next */ + add->prev_free_mem = next->prev_free_mem; + add->next_free_mem = next; + + /* Link previous */ + if (next->prev_free_mem != 0) + { + next->prev_free_mem->next_free_mem = add; + } + next->prev_free_mem = add; +} + +/***************************************************************************/ +/* + * linkFreeMemAfter + * param add : Pointer on chunk to add + * param prev : Pointer on previous chunk where the chunk will be added after + * + * Add a chunk in the free memory list + * + */ +/***************************************************************************/ +PUBLIC void linkFreeMemAfter(t_cm_chunk* prev,t_cm_chunk* add) +{ + /* Link previous */ + add->prev_free_mem = prev; + add->next_free_mem = prev->next_free_mem; + + /* Link next */ + if (prev->next_free_mem != 0) + { + prev->next_free_mem->prev_free_mem = add; + } + prev->next_free_mem = add; +} + + +/***************************************************************************/ +/* + * updateFreeList + * param allocHandle : Allocator handle + * param offset : Pointer on chunk + * + * Update free memory list, ordered by size + * + */ +/***************************************************************************/ +PUBLIC void updateFreeList(t_cm_allocator_desc* alloc , t_cm_chunk* chunk) +{ + t_cm_chunk* free_chunk; + int bin = bin_index(chunk->size); + + /* check case with no more free block */ + if (alloc->free_mem_chunks[bin] == 0) + { + alloc->free_mem_chunks[bin] = chunk; + return ; + } + + /* order list */ + free_chunk = alloc->free_mem_chunks[bin]; + while ((free_chunk->next_free_mem != 0) && (chunk->size > free_chunk->size)) + { + free_chunk = free_chunk->next_free_mem; + } + + /* Add after free chunk if smaller -> we are the last */ + if(free_chunk->size <= chunk->size) + { + linkFreeMemAfter(free_chunk,chunk); + } + else // This mean that we are smaller + { + linkFreeMemBefore(chunk,free_chunk); + + /* Update first free chunk */ + if (alloc->free_mem_chunks[bin] == free_chunk) + { + alloc->free_mem_chunks[bin] = chunk; + } + } +} + + +/***************************************************************************/ +/* + * splitChunk + * param allocHandle : Allocator handle + * param chunk : Current chunk (modified in place) + * param offset : Offset address of the start memory + * return : New chunk handle or 0 if an error occurs + * + * Create new chunk before/after the current chunk with the size + */ +/***************************************************************************/ +PUBLIC t_cm_chunk* splitChunk(t_cm_allocator_desc* alloc ,t_cm_chunk *chunk, + t_uint32 offset, t_mem_split_position position) +{ + t_cm_chunk *free; + t_cm_chunk *returned; + + t_cm_chunk* new_chunk = allocChunk(); + + if (position == FREE_CHUNK_AFTER) { + returned = chunk; + free = new_chunk; + } else { //FREE_CHUNK_BEFORE + returned = new_chunk; + free = chunk; + } + + new_chunk->offset = offset; + new_chunk->size = chunk->offset + chunk->size - offset; + new_chunk->alloc = alloc; + chunk->size = offset - chunk->offset; + + linkChunk(alloc, chunk, new_chunk); + unlinkFreeMem(alloc, free); + updateFreeList(alloc, free); + + return returned; +} diff --git a/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h b/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h new file mode 100644 index 00000000000..f57c92ea41b --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h @@ -0,0 +1,494 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \brief OS Adaptation Layer API + * + * \defgroup CM_ENGINE_OSAL_API CM Engine OSAL (Operating System Abstraction Layer) API + * \ingroup CM_ENGINE_MODULE + */ +#ifndef __INC_CM_OSAL_H +#define __INC_CM_OSAL_H + +#include <cm/inc/cm_type.h> +#include <cm/engine/communication/inc/communication_type.h> +#include <cm/engine/component/inc/instance.h> + +/*! + * \brief Identifier of a trace channel (id in [0..255]) + * \ingroup CM_ENGINE_OSAL_API + */ +typedef t_uint8 t_nmf_trace_channel; + +/*! + * \brief Identifier of lock create by OSAL + * \ingroup CM_ENGINE_OSAL_API + */ +typedef t_uint32 t_nmf_osal_sync_handle; + +/*! + * \brief Identifier of semaphore create by OSAL + * \ingroup CM_ENGINE_OSAL_API + */ +typedef t_uint32 t_nmf_osal_sem_handle; + +/*! + * \brief Identifier of semaphore wait error return by semaphore OSAL API + * \ingroup CM_ENGINE_OSAL_API + */ +typedef t_uint8 t_nmf_osal_sync_error; +#define SYNC_ERROR_TIMEOUT ((t_nmf_osal_sync_error)-1) +#define SYNC_OK ((t_nmf_osal_sync_error)0) +#define SEM_TIMEOUT_NORMAL 3000 +#define SEM_TIMEOUT_DEBUG 300000 + +struct osal_debug_operations { + void (*component_create)(t_component_instance *component); + void (*component_destroy)(t_component_instance *component); + void (*domain_create)(t_cm_domain_id id); + void (*domain_destroy)(t_cm_domain_id id); +}; + +extern struct osal_debug_operations osal_debug_ops; + +/*! + * \brief Description of the Scheduling part of the OS Adaptation Layer + * + * <B>Goal:</B> Support of uplink communication path (from Media Processors to Host (ARM)) + * + * Post a function call outside of Host CPU Interrupt mode in order to minimize ISR execution time + * \param[in] upLayerTHIS : this one provided by user when calling CM_ENGINE_BindComponentToCMCore() (first field of the interface context) \n + * \param[in] methodIndex : index method to be called \n + * \param[in] anyPtr : internal NMF marshaled parameters block (to be passed as second parameter when calling the previous pSkeleton method) \n + * \param[in] ptrSize : size of anyPtr in bytes \n + * + * Called by: + * - CM_ProcessMpcEvent() call (shall be bound by OS integrator to HSEM IRQ) + * + * \ingroup CM_ENGINE_OSAL_API + */ + +PUBLIC void OSAL_PostDfc( + t_nmf_mpc2host_handle upLayerTHIS, + t_uint32 methodIndex, + t_event_params_handle anyPtr, + t_uint32 ptrSize); + + +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * <B>Goal:</B> Use by CM to protect global variable against multiple call. Interrupt and scheduler function are use when + * we take hardware/local semaphore. Scheduler lock functions can have empty implementation but this may + * impact performance (dsp waiting semaphore because host thread was preempted whereas it has already take semaphore + * but not yet release it). + * + * \return handle of the Mutex created + * + * Called by: + * - any CM API call + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC t_nmf_osal_sync_handle OSAL_CreateLock(void); + +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * <B>Goal:</B> Use by CM to protect global variable against multiple call. Interrupt and scheduler function are use when + * we take hardware/local semaphore. Scheduler lock functions can have empty implementation but this may + * impact performance (dsp waiting semaphore because host thread was preempted whereas it has already take semaphore + * but not yet release it). + * + * \param[in] handle handle of the Mutex to be locked + * + * Called by: + * - any CM API call + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_Lock( + t_nmf_osal_sync_handle handle); + +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * <B>Goal:</B> Use by CM to protect global variable against multiple call. Interrupt and scheduler function are use when + * we take hardware/local semaphore. Scheduler lock functions can have empty implementation but this may + * impact performance (dsp waiting semaphore because host thread was preempted whereas it has already take semaphore + * but not yet release it). + * + * \param[in] handle handle of the Mutex to be unlocked + * + * Called by: + * - any CM API call + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_Unlock( + t_nmf_osal_sync_handle handle); + +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * <B>Goal:</B> Use by CM to protect global variable against multiple call. Interrupt and scheduler function are use when + * we take hardware/local semaphore. Scheduler lock functions can have empty implementation but this may + * impact performance (dsp waiting semaphore because host thread was preempted whereas it has already take semaphore + * but not yet release it). + * + * \param[in] handle handle of the Mutex to be destroyed + * + * Called by: + * - any CM API call + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_DestroyLock( + t_nmf_osal_sync_handle handle); + +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * <B>Goal:</B> Use by CM to allow to synchronize with code running on mpc side. + * + * \param[in] value : Initial value of semaphore. + * + * \return handle of the Semaphore created + * + * Called by: + * - any CM API call + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC t_nmf_osal_sem_handle OSAL_CreateSemaphore( + t_uint32 value); + +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * <B>Goal:</B> Use by CM to allow to synchronize with code running on mpc side. This function can be call under + * Irq context by CM. + * + * \param[in] handle handle of the Semaphore for which we increase value and so potentially wake up thread. + * + * \param[in] aCtx is a hint to indicate to os that we are in a none normal context (e.g under interruption). + * + * Called by: + * - any CM API call + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_SemaphorePost( + t_nmf_osal_sem_handle handle, + t_uint8 aCtx); + +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * <B>Goal:</B> Use by CM to allow to synchronize with code running on mpc side. + * + * \param[in] handle of the Semaphore for which we decrease value and so potentially block current thread. + * + * \param[in] timeOutInMs maximun time in ms after which the block thread is wake up. In this case function return SYNC_ERROR_TIMEOUT value. + * + * \return error number: SYNC_ERROR_TIMEOUT in case semaphore is not release withing timeOutInMs. + * + * Called by: + * - any CM API call + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC t_nmf_osal_sync_error OSAL_SemaphoreWaitTimed( + t_nmf_osal_sem_handle handle, + t_uint32 timeOutInMs); + +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * <B>Goal:</B> Use by CM to allow to synchronize with code running on mpc side. + * + * \param[in] handle handle of the Semaphore to be destroyed + * + * Called by: + * - any CM API call + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_DestroySemaphore( + t_nmf_osal_sem_handle handle); + +/*! + * \brief Description of the System Memory Allocator part of the OS Adaptation Layer + * + * <B>Goal:</B> Allocate CM some cacheable and bufferable memory (SDRAM) for internal usage \n + * This memory will be accessed only by Host CPU (ARM) + * + * This function provide a simple, general-purpose memory allocation. The + * OSAL_Alloc macro returns a pointer to a block of at least size bytes + * suitably aligned for any use. If there is no available memory, this + * function returns a null pointer. + * + * \param[in] size size in bytes, of memory to be allocated + * \return pointer on the beginning of the allocated memory + * + * Called by: + * - any CM API call + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void* OSAL_Alloc( + t_cm_size size); + +/*! + * \brief Description of the System Memory Allocator part of the OS Adaptation Layer with memory set to zero + * + * Compare to \see OSAL_Alloc, same allocation is done but memory is set with zero before returning. + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void* OSAL_Alloc_Zero( + t_cm_size size); + +/*! + * \brief Description of the System Memory Allocator part of the OS Adaptation Layer + * + * <B>Goal:</B> Free CM some cacheable and bufferable memory (SDRAM) for internal usage \n + * This memory will be accessed only by Host CPU (ARM) + * + * \param[in] pHandle pointer on the begining of the memory previously allocated + * + * Called by: + * - any CM API call + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_Free( + void *pHandle); + +/*! + * \brief Clean data cache in DDR in order to be accessible from peripheral. + * + * This method must be synchronized with MMDSP Code cache attribute. + * Strongly Ordered -> nothing + * Shared device -> dsb + L2 Write buffer drain + * Non cacheable, Bufferable -> dsb + L2 Write buffer drain + * WT or WB -> Flush cache range + dsb + L2 Write buffer drain + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_CleanDCache( + t_uint32 startAddr, //!< [in] Start data address of range to clean + t_uint32 Size //!< [in] Size of range to clean + ); + +/*! + * \brief Flush write buffer. + * + * This method must be synchronized with MMDSP Data cache attribute. + * Strongly Ordered -> nothing + * Shared device -> dsb + L2 Write buffer drain + * Non cacheable, Bufferable -> dsb + L2 Write buffer drain + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_mb(void); + +/*! + * \brief Description of the System Memory part of the OS Adaptation Layer + * + * <B>Goal:</B> Copy some cacheable and bufferable memory (SDRAM) provided by a client to\n + * internal memory. + * + * \param[in] dst : pointer on the begining of the internal memory previously allocated + * \param[in] src : pointer on the begining of the client's memory + * \param[in] size : The size of the data to copy + * + * Called by: + * - CM_ENGINE_PushComponent() + * + * \note This API is mainly provided for the OS were the client application does execute in the same + * address space as the CM. + * For example in Linux or Symbian, the client's address space is userland but the CM execute in + * kernel space. Thus, 'dst' is supposed to be a kernel address but src is supposed to be a user + * space address + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC t_cm_error OSAL_Copy( + void *dst, + const void *src, + t_cm_size size); + +/*! + * \brief Description of the internal log traces configuration of the Component Manager + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_Log( + const char *format, + int param1, + int param2, + int param3, + int param4, + int param5, + int param6); + +/*! + * \brief Generate an OS-Panic. Called in from CM_ASSERT(). + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_Panic(void); + +/*! + * \brief Description of the configuration of the trace features + * + * (trace output itself is provided by user through his custom implementation of the generic APIs) + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_Write64( + t_nmf_trace_channel channel, + t_uint8 isTimestamped, + t_uint64 value); + +/*! + * \brief Power enabling/disabling commands description. + * + * \ingroup CM_ENGINE_OSAL_API + */ +typedef enum +{ + CM_OSAL_POWER_SxA_CLOCK, //!< SxA Power & Clock, firstParam contains Core ID + CM_OSAL_POWER_SxA_AUTOIDLE, //!< SxA AutoIdle, firstParam contains Core ID + CM_OSAL_POWER_SxA_HARDWARE, //!< SxA Hardware Power, firstParam contains Core ID + CM_OSAL_POWER_HSEM, //!< HSEM Power + CM_OSAL_POWER_SDRAM, //!< SDRAM memory, firstParam contains physical resource address, secondParam contains size + CM_OSAL_POWER_ESRAM //!< ESRAM memory, firstParam contains physical resource address, secondParam contains size +} t_nmf_power_resource; + +/*! + * \brief Description of the Power Management part of the OS Adaptation Layer + * + * Use by CM engine to disable a logical power domain (see \ref t_nmf_power_resource) + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_DisablePwrRessource( + t_nmf_power_resource resource, //!< [in] Describe the domain which must be disabled + t_uint32 firstParam, //!< [in] Eventual first parameter to power to disable + t_uint32 secondParam //!< [in] Eventual second parameter to power to disable + ); + +/*! + * \brief Description of the Power Management part of the OS Adaptation Layer + * + * Use by CM engine to enable a logical power domain (see \ref t_nmf_power_resource) + * + * \return + * - \ref CM_OK + * - \ref CM_PWR_NOT_AVAILABLE A specified power domain is not managed (see returned value in aPowerMask) + * + * Called by: + * - any CM API call + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC t_cm_error OSAL_EnablePwrRessource( + t_nmf_power_resource resource, //!< [in] Describing the domains which must be enabled + t_uint32 firstParam, //!< [in] Eventual first parameter to power to disable + t_uint32 secondParam //!< [in] Eventual second parameter to power to disable + ); + + +/*! + * \brief return prcmu timer value. + * + * This is need for perfmeter api (see \ref t_nmf_power_resource) + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC t_uint64 OSAL_GetPrcmuTimer(void); + +/*! + * \brief Disable the service message handling (panic, etc) + * + * It must disable the handling of all service messages + * If a service message is currently handled, it must wait till the end + * of its managment before returning. + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_DisableServiceMessages(void); + +/*! + * \brief Enable the service message handling (panic, etc) + * + * It enables the handling of all service messages + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_EnableServiceMessages(void); + +/*! + * \brief Generate 'software' panic due to dsp crash + * + * We request that the os part generate a panic to notify cm users +* that a problem occur but not dsp panic has been sent (for example +* a dsp crash) + * + * \param[in] t_nmf_core_id : core_id is the id of dsp for which we need to generate a panic. + * \param[in] reason : additional information. Today only 0 is valid. + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_GeneratePanic(t_nmf_core_id coreId, t_uint32 reason); + +extern /*const*/ t_nmf_osal_sync_handle lockHandleApi; +extern /*const*/ t_nmf_osal_sync_handle lockHandleCom; +extern /*const*/ t_nmf_osal_sem_handle semHandle; + +/*! + * \brief Take a lock before entering critical section. Can suspend current thread if lock already taken. \n + * Use this macro in api function. For com function use OSAL_LOCK_COM. + * + * \ingroup CM_ENGINE_OSAL_API + */ +#define OSAL_LOCK_API() OSAL_Lock(lockHandleApi) + +/*! + * \brief Release lock before leaving critical section. + * + * \ingroup CM_ENGINE_OSAL_API + */ +#define OSAL_UNLOCK_API() OSAL_Unlock((lockHandleApi)) + +/*! + * \brief Take a lock before entering critical section. Can suspend current thread if lock already taken. \n + * Use this macro in com function. For com function use OSAL_LOCK_API. + * + * \ingroup CM_ENGINE_OSAL_API + */ +#define OSAL_LOCK_COM() OSAL_Lock(lockHandleCom) + +/*! + * \brief Release lock before leaving critical section. + * + * \ingroup CM_ENGINE_OSAL_API + */ +#define OSAL_UNLOCK_COM() OSAL_Unlock((lockHandleCom)) + +/*! + * \brief Go to sleep untill post done on semaphore or timeout expire. In that case SYNC_ERROR_TIMEOUT is return. + * + * \ingroup CM_ENGINE_OSAL_API + */ +#define OSAL_SEMAPHORE_WAIT_TIMEOUT(semHandle) OSAL_SemaphoreWaitTimed(semHandle, (cm_PWR_GetMode() == NORMAL_PWR_MODE)?SEM_TIMEOUT_NORMAL:SEM_TIMEOUT_DEBUG) + +/****************/ +/* Generic part */ +/****************/ +t_cm_error cm_OSAL_Init(void); +void cm_OSAL_Destroy(void); + +#endif /* __INC_CM_OSAL_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/src/os_adaptation_layer.c b/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/src/os_adaptation_layer.c new file mode 100644 index 00000000000..380692e3cd8 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/src/os_adaptation_layer.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> +#include <cm/engine/utils/inc/mem.h> + +t_nmf_osal_sync_handle lockHandleApi; +t_nmf_osal_sync_handle lockHandleCom; +t_nmf_osal_sem_handle semHandle; +struct osal_debug_operations osal_debug_ops; + +/****************/ +/* Generic part */ +/****************/ +PUBLIC t_cm_error cm_OSAL_Init(void) +{ + + /* create locks */ + lockHandleApi = OSAL_CreateLock(); + if (lockHandleApi == 0) {return CM_INVALID_PARAMETER;} + lockHandleCom = OSAL_CreateLock(); + if (lockHandleCom == 0) {return CM_INVALID_PARAMETER;} + + /* create semaphore */ + semHandle = OSAL_CreateSemaphore(0); + if (semHandle == 0) {return CM_INVALID_PARAMETER;} + + /* init to zero */ + cm_MemSet(&osal_debug_ops, 0, sizeof(osal_debug_ops)); + + return CM_OK; +} + +PUBLIC void cm_OSAL_Destroy(void) +{ + /* destroy locks */ + OSAL_DestroyLock(lockHandleApi); + OSAL_DestroyLock(lockHandleCom); + + /* destroy semaphore */ + OSAL_DestroySemaphore(semHandle); +} diff --git a/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/mpcload.h b/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/mpcload.h new file mode 100644 index 00000000000..0831f1940ca --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/mpcload.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + */ +#ifndef MPCLOAD_H_ +#define MPCLOAD_H_ + +#include <cm/engine/component/inc/instance.h> + +/******************************************************************************/ +/************************ FUNCTIONS PROTOTYPES ********************************/ +/******************************************************************************/ + +PUBLIC t_cm_error cm_PFM_allocatePerfmeterDataMemory(t_nmf_core_id coreId, t_cm_domain_id domainId); +PUBLIC void cm_PFM_deallocatePerfmeterDataMemory(t_nmf_core_id coreId); + +#endif /* MPCLOAD_H_ */ diff --git a/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/perfmeter_type.h b/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/perfmeter_type.h new file mode 100644 index 00000000000..78de395acc2 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/perfmeter_type.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Public Component Manager Performance Meter API type. + * + * This file contains the Component Manager API type for performance meter. + * + * \defgroup PERFMETER CM Monitoring API + * \ingroup CM_USER_API + */ +#ifndef CM_COMMON_PERFMETER_TYPE_H_ +#define CM_COMMON_PERFMETER_TYPE_H_ + +#include <cm/inc/cm_type.h> +/*! + * \brief Description of mpc load structure. + * + * This contain mpc load value. + * + * \ingroup PERFMETER + */ +typedef struct { + t_uint64 totalCounter; + t_uint64 loadCounter; +} t_cm_mpc_load_counter; + + +#endif /* CM_COMMON_PERFMETER_TYPE_H_ */ diff --git a/drivers/staging/nmf-cm/cm/engine/perfmeter/src/mpcload.c b/drivers/staging/nmf-cm/cm/engine/perfmeter/src/mpcload.c new file mode 100644 index 00000000000..193d155b97b --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/perfmeter/src/mpcload.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/engine/perfmeter/inc/mpcload.h> +#include <cm/engine/component/inc/introspection.h> +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> + +#include <cm/engine/api/perfmeter_engine.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> + +#include <cm/engine/trace/inc/trace.h> + +#define PERFMETER_MAX_RETRIES 32 +#define PERFMETER_DATA_WORD_NB 7 + +/* private type */ +typedef struct { + t_memory_handle perfmeterDataHandle; + t_cm_logical_address perfmeterDataAddr; +} t_mpcLoad; + +/* private globals */ +t_mpcLoad mpcLoad_i[NB_CORE_IDS]; + +/* engine api */ +PUBLIC EXPORT_SHARED t_cm_error CM_GetMpcLoadCounter( + t_nmf_core_id coreId, + t_cm_mpc_load_counter *pMpcLoadCounter +) +{ + t_uint24 data[PERFMETER_DATA_WORD_NB]; + t_uint32 i; + t_uint64 prcmuBeforeAttributes; + t_uint32 retryCounter = 0; + volatile t_uint32 *pData; + + pMpcLoadCounter->totalCounter = 0; + pMpcLoadCounter->loadCounter = 0; + /* check core id is an mpc */ + if (coreId < FIRST_MPC_ID || coreId > LAST_CORE_ID) {return CM_INVALID_PARAMETER;} + + /* check core has been booted */ + pData = (t_uint32 *) mpcLoad_i[coreId].perfmeterDataAddr; + if (pData == NULL) {return CM_OK;} + + do { + prcmuBeforeAttributes = OSAL_GetPrcmuTimer(); + /* get attributes */ + do + { + for(i = 0;i < PERFMETER_DATA_WORD_NB;i++) + data[i] = pData[i]; + } + while(((data[0] & 0xff0000) != (data[1] & 0xff0000) || (data[0] & 0xff0000) != (data[2] & 0xff0000) || + (data[0] & 0xff0000) != (data[3] & 0xff0000) || (data[0] & 0xff0000) != (data[4] & 0xff0000) || + (data[0] & 0xff0000) != (data[5] & 0xff0000) || (data[0] & 0xff0000) != (data[6] & 0xff0000) || + (data[0] & 0xff0000) != (data[6] & 0xff0000)) + && retryCounter-- < PERFMETER_MAX_RETRIES); // check data coherence + if (retryCounter >= PERFMETER_MAX_RETRIES) + return CM_MPC_NOT_RESPONDING; + + /* read forever counter for totalCounter */ + pMpcLoadCounter->totalCounter = OSAL_GetPrcmuTimer(); + } while(pMpcLoadCounter->totalCounter - prcmuBeforeAttributes >= 32); //we loop until it seems we have not be preempt too long (< 1ms) + + /* we got coherent data, use them */ + pMpcLoadCounter->loadCounter = ((data[0] & (t_uint64)0xffff) << 32) + ((data[1] & (t_uint64)0xffff) << 16) + ((data[2] & (t_uint64)0xffff) << 0); + //fix load counter if needed + if ((data[6] & 0xffff) == 1) { + t_uint64 lastEvent; + + lastEvent = ((data[3] & (t_uint64)0xffff) << 32) + ((data[4] & (t_uint64)0xffff) << 16) + ((data[5] & (t_uint64)0xffff) << 0); + pMpcLoadCounter->loadCounter += pMpcLoadCounter->totalCounter - lastEvent; + } + + return CM_OK; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_getMpcLoadCounter( + t_nmf_core_id coreId, + t_cm_mpc_load_counter *pMpcLoadCounter +) +{ + t_cm_error error; + + OSAL_LOCK_API(); + error = CM_GetMpcLoadCounter(coreId, pMpcLoadCounter); + OSAL_UNLOCK_API(); + return error; +} + +/* internal api */ +PUBLIC t_cm_error cm_PFM_allocatePerfmeterDataMemory(t_nmf_core_id coreId, t_cm_domain_id domainId) +{ + t_cm_error error = CM_OK; + t_mpcLoad *pMpcLoad = (t_mpcLoad *) &mpcLoad_i[coreId]; + + pMpcLoad->perfmeterDataHandle = cm_DM_Alloc(domainId, SDRAM_EXT24, PERFMETER_DATA_WORD_NB, CM_MM_ALIGN_WORD, TRUE); + if (pMpcLoad->perfmeterDataHandle == INVALID_MEMORY_HANDLE) { + error = CM_NO_MORE_MEMORY; + ERROR("CM_NO_MORE_MEMORY: Unable to allocate perfmeter\n", 0, 0, 0, 0, 0, 0); + } else { + t_uint32 mmdspAddr; + + pMpcLoad->perfmeterDataAddr = cm_DSP_GetHostLogicalAddress(pMpcLoad->perfmeterDataHandle); + cm_DSP_GetDspAddress(pMpcLoad->perfmeterDataHandle, &mmdspAddr); + cm_writeAttribute(cm_EEM_getExecutiveEngine(coreId)->instance, "rtos/perfmeter/perfmeterDataAddr", mmdspAddr); + } + + return error; +} + +PUBLIC void cm_PFM_deallocatePerfmeterDataMemory(t_nmf_core_id coreId) +{ + mpcLoad_i[coreId].perfmeterDataAddr = 0; + cm_DM_Free(mpcLoad_i[coreId].perfmeterDataHandle, TRUE); +} diff --git a/drivers/staging/nmf-cm/cm/engine/power_mgt/inc/power.h b/drivers/staging/nmf-cm/cm/engine/power_mgt/inc/power.h new file mode 100644 index 00000000000..942805df2f3 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/power_mgt/inc/power.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Enable a CM power domain by CoreID. + * + * \ingroup COMPONENT_INTERNAL + */ +#ifndef __INC_NMF_POWER +#define __INC_NMF_POWER + +#include <cm/inc/cm_type.h> +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/dsp/inc/dsp.h> + +typedef enum +{ + DISABLE_PWR_MODE = 0x0, //!< Disable mode - CM Power management is disabled. CM Power domain are always enabled and the EEs are loaded by default + NORMAL_PWR_MODE = 0x1 //!< Normal mode +} t_nmf_power_mode; + +extern t_nmf_power_mode powerMode; + +PUBLIC t_cm_error cm_PWR_Init(void); +void cm_PWR_SetMode(t_nmf_power_mode aMode); +t_nmf_power_mode cm_PWR_GetMode(void); +t_uint32 cm_PWR_GetMPCMemoryCount(t_nmf_core_id coreId); + +typedef enum +{ + MPC_PWR_CLOCK, + MPC_PWR_AUTOIDLE, + MPC_PWR_HWIP +} t_mpc_power_request; + +PUBLIC t_cm_error cm_PWR_EnableMPC( + t_mpc_power_request request, + t_nmf_core_id coreId); +PUBLIC void cm_PWR_DisableMPC( + t_mpc_power_request request, + t_nmf_core_id coreId); + +PUBLIC t_cm_error cm_PWR_EnableHSEM(void); +PUBLIC void cm_PWR_DisableHSEM(void); + +PUBLIC t_cm_error cm_PWR_EnableMemory( + t_nmf_core_id coreId, + t_dsp_memory_type_id dspMemType, + t_cm_physical_address address, + t_cm_size size); +PUBLIC void cm_PWR_DisableMemory( + t_nmf_core_id coreId, + t_dsp_memory_type_id dspMemType, + t_cm_physical_address address, + t_cm_size size); + + +#endif /* __INC_NMF_POWER */ diff --git a/drivers/staging/nmf-cm/cm/engine/power_mgt/src/cmpower.c b/drivers/staging/nmf-cm/cm/engine/power_mgt/src/cmpower.c new file mode 100644 index 00000000000..a104486db6c --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/power_mgt/src/cmpower.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include "../inc/power.h" + +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/utils/inc/convert.h> +#include <cm/engine/dsp/inc/dsp.h> +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> + +// ------------------------------------------------------------------------------- +// Compilation flags +// ------------------------------------------------------------------------------- +#define __PWR_DEBUG_TRACE_LEVEL 2 // Debug trave level for CM power module + +// ------------------------------------------------------------------------------- +// Internal counter to store the TCM allocated chunk (by MPC) +// ------------------------------------------------------------------------------- +static t_uint32 _pwrMPCHWIPCountT[NB_CORE_IDS]; + +// ------------------------------------------------------------------------------- +// Internal counter to store the TCM allocated chunk (by MPC) +// ------------------------------------------------------------------------------- +static t_uint32 _pwrMPCMemoryCountT[NB_CORE_IDS]; + + +// ------------------------------------------------------------------------------- +// Internal data to store the global Power Manager mode (see cm_PWR_Init fct) +// ------------------------------------------------------------------------------- +t_nmf_power_mode powerMode = NORMAL_PWR_MODE; + +// ------------------------------------------------------------------------------- +// cm_PWR_Init +// ------------------------------------------------------------------------------- +PUBLIC t_cm_error cm_PWR_Init(void) +{ + int i; + + for (i=0; i<NB_CORE_IDS;i++) + { + _pwrMPCHWIPCountT[i] = 0; + _pwrMPCMemoryCountT[i] = 0; + } + + return CM_OK; +} + +// ------------------------------------------------------------------------------- +// cm_PWR_SetMode +// ------------------------------------------------------------------------------- +void cm_PWR_SetMode(t_nmf_power_mode aMode) +{ + powerMode = aMode; +} + +t_nmf_power_mode cm_PWR_GetMode() +{ + return powerMode; +} + +t_uint32 cm_PWR_GetMPCMemoryCount(t_nmf_core_id coreId) +{ + return _pwrMPCMemoryCountT[coreId]; +} + + +PUBLIC t_cm_error cm_PWR_EnableMPC( + t_mpc_power_request request, + t_nmf_core_id coreId) +{ + t_cm_error error; + + switch(request) + { + case MPC_PWR_CLOCK: + LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] MPC %s enable clock\n", cm_getDspName(coreId), 0, 0, 0, 0, 0); + if((error = OSAL_EnablePwrRessource(CM_OSAL_POWER_SxA_CLOCK, coreId, 0)) != CM_OK) + { + ERROR("[Pwr] MPC %s clock can't be enabled\n", cm_getDspName(coreId), 0, 0, 0, 0, 0); + return error; + } + break; + case MPC_PWR_AUTOIDLE: + if((error = OSAL_EnablePwrRessource(CM_OSAL_POWER_SxA_AUTOIDLE, coreId, 0)) != CM_OK) + { + ERROR("[Pwr] MPC %s clock can't be auto-idle\n", cm_getDspName(coreId), 0, 0, 0, 0, 0); + return error; + } + break; + case MPC_PWR_HWIP: + if(_pwrMPCHWIPCountT[coreId]++ == 0) + { + LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] MPC %s HW IP enable clock\n",cm_getDspName(coreId), 0, 0, 0, 0, 0); + + // The PRCMU seem not supporting the transition of asking HW IP on while DSP in retention + // -> Thus force wake up of the MMDSP before asking the transition + if ((error = cm_EEM_ForceWakeup(coreId)) != CM_OK) + return error; + + if((error = OSAL_EnablePwrRessource(CM_OSAL_POWER_SxA_HARDWARE, coreId, 0)) != CM_OK) + { + ERROR("[Pwr] MPC %s HW IP clock can't be enabled\n", cm_getDspName(coreId), 0, 0, 0, 0, 0); + cm_EEM_AllowSleep(coreId); + return error; + } + + cm_EEM_AllowSleep(coreId); + } + break; + } + + return CM_OK; +} + +PUBLIC void cm_PWR_DisableMPC( + t_mpc_power_request request, + t_nmf_core_id coreId) +{ + switch(request) + { + case MPC_PWR_CLOCK: + LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] MPC %s disable clock\n",cm_getDspName(coreId), 0, 0, 0, 0, 0); + OSAL_DisablePwrRessource(CM_OSAL_POWER_SxA_CLOCK, coreId, 0); + break; + case MPC_PWR_AUTOIDLE: + OSAL_DisablePwrRessource(CM_OSAL_POWER_SxA_AUTOIDLE, coreId, 0); + break; + case MPC_PWR_HWIP: + if(--_pwrMPCHWIPCountT[coreId] == 0) + { + LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] MPC %s HW IP disable clock\n",cm_getDspName(coreId), 0, 0, 0, 0, 0); + + // The PRCMU seem not supporting the transition of asking HW IP on while DSP in retention + // -> Thus force wake up of the MMDSP before asking the transition + if (cm_EEM_ForceWakeup(coreId) != CM_OK) + return; + + OSAL_DisablePwrRessource(CM_OSAL_POWER_SxA_HARDWARE, coreId, 0); + + cm_EEM_AllowSleep(coreId); + } + break; + } +} + +PUBLIC t_cm_error cm_PWR_EnableHSEM(void) +{ + t_cm_error error; + + LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] HSEM enable clock\n",0 , 0, 0, 0, 0, 0); + if((error = OSAL_EnablePwrRessource(CM_OSAL_POWER_HSEM, 0, 0)) != CM_OK) + { + ERROR("[Pwr] HSEM clock can't be enabled\n", 0, 0, 0, 0, 0, 0); + return error; + } + + return CM_OK; +} + +PUBLIC void cm_PWR_DisableHSEM(void) +{ + LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] HSEM disable clock\n",0 , 0, 0, 0, 0, 0); + OSAL_DisablePwrRessource(CM_OSAL_POWER_HSEM, 0, 0); +} + +PUBLIC t_cm_error cm_PWR_EnableMemory( + t_nmf_core_id coreId, + t_dsp_memory_type_id dspMemType, + t_cm_physical_address address, + t_cm_size size) +{ + switch(dspMemType) + { + case INTERNAL_XRAM24: + case INTERNAL_XRAM16: + case INTERNAL_YRAM24: + case INTERNAL_YRAM16: + _pwrMPCMemoryCountT[coreId]++; + break; + case SDRAM_EXT24: + case SDRAM_EXT16: + case SDRAM_CODE: + case LOCKED_CODE: + return OSAL_EnablePwrRessource( + CM_OSAL_POWER_SDRAM, + address, + size); + case ESRAM_EXT24: + case ESRAM_EXT16: + case ESRAM_CODE: + return OSAL_EnablePwrRessource( + CM_OSAL_POWER_ESRAM, + address, + size); + default: + CM_ASSERT(0); + } + + return CM_OK; +} + +PUBLIC void cm_PWR_DisableMemory( + t_nmf_core_id coreId, + t_dsp_memory_type_id dspMemType, + t_cm_physical_address address, + t_cm_size size) +{ + switch(dspMemType) + { + case INTERNAL_XRAM24: + case INTERNAL_XRAM16: + case INTERNAL_YRAM24: + case INTERNAL_YRAM16: + _pwrMPCMemoryCountT[coreId]--; + break; + case SDRAM_EXT24: + case SDRAM_EXT16: + case SDRAM_CODE: + case LOCKED_CODE: + OSAL_DisablePwrRessource( + CM_OSAL_POWER_SDRAM, + address, + size); + break; + case ESRAM_EXT24: + case ESRAM_EXT16: + case ESRAM_CODE: + OSAL_DisablePwrRessource( + CM_OSAL_POWER_ESRAM, + address, + size); + break; + default: + CM_ASSERT(0); + } +} + + + + + diff --git a/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_mgt.h b/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_mgt.h new file mode 100644 index 00000000000..d2c7185b24f --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_mgt.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Component repository internal methods. + * + * \defgroup REPOSITORY_INTERNAL Component repository. + */ +#ifndef __INC_CM_REP_MGT_H +#define __INC_CM_REP_MGT_H + +#include <cm/inc/cm_type.h> +#include <inc/nmf-limits.h> + +/*! + * \brief Identification of a component entry. + * \ingroup REPOSITORY_INTERNAL + */ +typedef struct t_rep_component { + t_dup_char name; + struct t_rep_component *prev; + struct t_rep_component *next; + t_elfdescription *elfhandle; //!< Must be last as data will be stored here +} t_rep_component; + +/*! + * \brief Search a component entry by name. + * + * \param[in] name The name of the component to look for. + * \param[out] component The corresponding component entry in the repository + * + * \retval t_cm_error + * + * \ingroup REPOSITORY_INTERNAL + */ +PUBLIC t_cm_error cm_REP_lookupComponent(const char *name, t_rep_component **component); + +/*! + * \brief Helper method that return the dataFile found in parameter or in the cache + */ +t_elfdescription* cm_REP_getComponentFile(t_dup_char templateName, t_elfdescription* elfhandle); + +/*! + * \brief Destroy the full repository (remove and free all components) + * + * \retval none + * + * \ingroup REPOSITORY_INTERNAL + */ +PUBLIC void cm_REP_Destroy(void); + +#endif /* __INC_CM_REP_MGT_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_type.h b/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_type.h new file mode 100644 index 00000000000..59abc269236 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_type.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Components Component Manager API type. + * + * \defgroup COMPONENT CM Components API + * \ingroup CM_USER_API + */ + +#ifndef REPOSITORY_TYPE_H_ +#define REPOSITORY_TYPE_H_ + +typedef enum +{ + BIND_ASYNC, + BIND_TRACE, + BIND_FROMUSER, + BIND_TOUSER +} t_action_to_do; + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/repository_mgt/src/repository_mgt.c b/drivers/staging/nmf-cm/cm/engine/repository_mgt/src/repository_mgt.c new file mode 100644 index 00000000000..f6ccb4a9992 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/repository_mgt/src/repository_mgt.c @@ -0,0 +1,322 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * + */ +#include <cm/engine/utils/inc/string.h> + +#include <cm/engine/component/inc/component_type.h> +#include <cm/engine/component/inc/bind.h> +#include <cm/engine/configuration/inc/configuration.h> +#include <cm/engine/component/inc/introspection.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> +#include <cm/engine/repository_mgt/inc/repository_mgt.h> +#include <cm/engine/api/repository_mgt_engine.h> +#include <cm/engine/trace/inc/trace.h> + + +#undef NHASH +#define NHASH 157 //Use a prime number! +#define MULT 17 + +static t_rep_component *componentCaches[NHASH]; + +static unsigned int repcomponentHash(const char *str) +{ + unsigned int h = 0; + for(; *str; str++) + h = MULT * h + *str; + return h % NHASH; +} + +static void repcomponentAdd(t_rep_component *component) +{ + unsigned int h = repcomponentHash(component->name); + + if(componentCaches[h] != NULL) + componentCaches[h]->prev = component; + component->next = componentCaches[h]; + component->prev = NULL; + componentCaches[h] = component; +} + +static void repcomponentRemove(t_rep_component *component) +{ + unsigned int h = repcomponentHash(component->name); + + if(component->prev != NULL) + component->prev->next = component->next; + if(component->next != NULL) + component->next->prev = component->prev; + if(component == componentCaches[h]) + componentCaches[h] = component->next; +} + + +PUBLIC t_cm_error cm_REP_lookupComponent(const char *name, t_rep_component **component) +{ + t_rep_component *tmp; + + for(tmp = componentCaches[repcomponentHash(name)]; tmp != NULL; tmp = tmp->next) + { + if(cm_StringCompare(name, tmp->name, MAX_TEMPLATE_NAME_LENGTH) == 0) + { + if(component != NULL) + *component = tmp; + return CM_OK; + } + } + + return CM_COMPONENT_NOT_FOUND; +} + +t_elfdescription* cm_REP_getComponentFile(t_dup_char templateName, t_elfdescription* elfhandle) +{ + if(elfhandle == NULL) + { + t_rep_component *pRepComponent; + + for(pRepComponent = componentCaches[repcomponentHash(templateName)]; pRepComponent != NULL; pRepComponent = pRepComponent->next) + { + if(pRepComponent->name == templateName) + return pRepComponent->elfhandle; + } + + return NULL; + } + + return elfhandle; +} + + +PUBLIC void cm_REP_Destroy(void) +{ + t_rep_component *component, *next; + int i; + + for(i = 0; i < NHASH; i++) + { + for (component = componentCaches[i]; component != NULL; component = next) + { + next = component->next; + cm_ELF_CloseFile(FALSE, component->elfhandle); + cm_StringRelease(component->name); + OSAL_Free(component); + } + componentCaches[i] = NULL; + } +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetRequiredComponentFiles( + // IN + t_action_to_do action, + const t_cm_instance_handle client, + const char *requiredItfClientName, + const t_cm_instance_handle server, + const char *providedItfServerName, + // OUT component to be pushed + char fileList[][MAX_INTERFACE_TYPE_NAME_LENGTH], + t_uint32 listSize, + // OUT interface information + char type[MAX_INTERFACE_TYPE_NAME_LENGTH], + t_uint32 *methodNumber) +{ + t_cm_error error; + t_component_instance* compClient, *compServer; + int n; + + OSAL_LOCK_API(); + + // No component required + for(n = 0; n < listSize; n++) + fileList[n][0] = 0; + + compClient = cm_lookupComponent(client); + compServer = cm_lookupComponent(server); + switch(action) + { + case BIND_FROMUSER:{ + t_interface_provide_description itfProvide; + + // Check server validity + if((error = cm_checkValidServer(compServer, providedItfServerName, + &itfProvide)) == CM_OK) + { + cm_StringCopy(type, itfProvide.server->Template->provides[itfProvide.provideIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH); + + cm_StringCopy(fileList[0], "_sk.", MAX_INTERFACE_TYPE_NAME_LENGTH); + cm_StringConcatenate(fileList[0], itfProvide.server->Template->provides[itfProvide.provideIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH); + } + } break; + + case BIND_TOUSER: { + /* Get Components names for a BindComponentToCMCore */ + t_interface_require_description itfRequire; + t_bool bindable; + + // Check client validity + if((error = cm_checkValidClient(compClient, requiredItfClientName, + &itfRequire, &bindable)) == CM_OK) + { + cm_StringCopy(type, itfRequire.client->Template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH); + *methodNumber = itfRequire.client->Template->requires[itfRequire.requireIndex].interface->methodNumber; + + cm_StringCopy(fileList[0], "_st.", MAX_INTERFACE_TYPE_NAME_LENGTH); + cm_StringConcatenate(fileList[0], itfRequire.client->Template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH); + } + }; break; + + case BIND_ASYNC: { + /* Get Components names for an asynchronous binding */ + t_interface_require_description itfRequire; + t_interface_provide_description itfProvide; + t_bool bindable; + + // Check invalid binding + if((error = cm_checkValidBinding(compClient, requiredItfClientName, + compServer, providedItfServerName, + &itfRequire, &itfProvide, &bindable)) == CM_OK) + { + if(compClient->Template->dspId != compServer->Template->dspId) + { + cm_StringCopy(fileList[0], "_sk.", MAX_INTERFACE_TYPE_NAME_LENGTH); + cm_StringConcatenate(fileList[0], itfRequire.client->Template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH); + + cm_StringCopy(fileList[1], "_st.", MAX_INTERFACE_TYPE_NAME_LENGTH); + cm_StringConcatenate(fileList[1], itfRequire.client->Template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH); + } + else + { + cm_StringCopy(fileList[0], "_ev.", MAX_INTERFACE_TYPE_NAME_LENGTH); + cm_StringConcatenate(fileList[0], itfRequire.client->Template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH); + } + } + }; break; + + case BIND_TRACE: { + /* Get Components names for an asynchronous binding */ + t_interface_require_description itfRequire; + t_interface_provide_description itfProvide; + t_bool bindable; + + // Check invalid binding + if((error = cm_checkValidBinding(compClient, requiredItfClientName, + compServer, providedItfServerName, + &itfRequire, &itfProvide, &bindable)) == CM_OK) + { + cm_StringCopy(fileList[0], "_tr.", MAX_INTERFACE_TYPE_NAME_LENGTH); + cm_StringConcatenate(fileList[0], itfRequire.client->Template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH); + } + }; break; + + default: + error = CM_OK; + break; + } + + if(error == CM_OK) + { + for(n = 0; n < listSize; n++) + { + t_rep_component *comp; + + // If already loaded, don't ask to load it and put the name to NULL + if (fileList[n][0] != 0 && + cm_REP_lookupComponent(fileList[n], &comp) == CM_OK) + fileList[n][0] = 0; + } + } + + + OSAL_UNLOCK_API(); + return error; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_PushComponent(const char *name, const void *data, t_cm_size size) +{ + t_rep_component *comp; + t_cm_error error; + + OSAL_LOCK_API(); + + if (cm_REP_lookupComponent(name, &comp) == CM_OK) { + /* Component is already there: silently ignore it */ + OSAL_UNLOCK_API(); + return CM_OK; + } + + comp = OSAL_Alloc(sizeof(*comp)); + if (comp == NULL) { + OSAL_UNLOCK_API(); + return CM_NO_MORE_MEMORY; + } + + comp->name = cm_StringDuplicate(name); + if(comp->name == NULL) + { + OSAL_Free(comp); + OSAL_UNLOCK_API(); + return CM_NO_MORE_MEMORY; + } + + if((error = cm_ELF_CheckFile( + data, + FALSE, + &comp->elfhandle)) != CM_OK) { + cm_StringRelease(comp->name); + OSAL_Free(comp); + OSAL_UNLOCK_API(); + return error; + } +/* + if (OSAL_Copy(comp->data, data, size)) { + OSAL_Free(comp); + OSAL_UNLOCK_API(); + return CM_UNKNOWN_MEMORY_HANDLE; + }*/ + + repcomponentAdd(comp); + + OSAL_UNLOCK_API(); + return CM_OK; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_ReleaseComponent (const char *name) +{ + t_rep_component *component; + t_cm_error err; + + OSAL_LOCK_API(); + err = cm_REP_lookupComponent(name , &component); + + if (CM_OK == err) + { + repcomponentRemove(component); + + cm_ELF_CloseFile(FALSE, component->elfhandle); + cm_StringRelease(component->name); + OSAL_Free(component); + } + + OSAL_UNLOCK_API(); + + return err; +} + +PUBLIC EXPORT_SHARED t_bool CM_ENGINE_IsComponentCacheEmpty(void) +{ + int i; + + OSAL_LOCK_API(); + for(i = 0; i < NHASH; i++) { + if (componentCaches[i] != NULL) { + OSAL_UNLOCK_API(); + return FALSE; + } + } + OSAL_UNLOCK_API(); + return TRUE; +} diff --git a/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h b/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h new file mode 100644 index 00000000000..bd914195b6d --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef __INC_HW_SEMA_H_ +#define __INC_HW_SEMA_H_ + +#include <cm/inc/cm_type.h> +#include <cm/engine/semaphores/inc/semaphores.h> +#include <share/semaphores/inc/hwsem_hwp.h> + + +/******************************************************************************/ +/************************ FUNCTIONS PROTOTYPES ********************************/ +/******************************************************************************/ + +PUBLIC t_cm_error cm_HSEM_Init(const t_cm_system_address *pSystemAddr); +PUBLIC t_cm_error cm_HSEM_EnableSemIrq(t_semaphore_id semId, t_nmf_core_id toCoreId); +PUBLIC void cm_HSEM_Take(t_nmf_core_id coreId, t_semaphore_id semId); +PUBLIC void cm_HSEM_Give(t_nmf_core_id coreId, t_semaphore_id semId); +PUBLIC void cm_HSEM_GiveWithInterruptGeneration(t_nmf_core_id coreId, t_semaphore_id semId); +PUBLIC void cm_HSEM_GenerateIrq(t_nmf_core_id coreId, t_semaphore_id semId); +PUBLIC t_nmf_core_id cm_HSEM_GetCoreIdFromIrqSrc(void); + +PUBLIC t_cm_error cm_HSEM_PowerOn(t_nmf_core_id coreId); +PUBLIC void cm_HSEM_PowerOff(t_nmf_core_id coreId); + +#endif /* __INC_HW_SEMA_H_ */ diff --git a/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/src/hw_semaphores.c b/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/src/hw_semaphores.c new file mode 100644 index 00000000000..932058cd4f2 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/src/hw_semaphores.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/******************************************************************* Includes + ****************************************************************************/ + +#include "../inc/hw_semaphores.h" +#include <share/semaphores/inc/hwsem_hwp.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> +#include <cm/engine/power_mgt/inc/power.h> +static t_hw_semaphore_regs *pHwSemRegs = (t_hw_semaphore_regs *)0; + +static t_uint32 semaphoreUseCounter = 0; +static t_uint32 imsc[HSEM_MAX_INTR]; +PRIVATE void restoreMask(void); + +/****************************************************************************/ +/* NAME: t_cm_error cm_HSEM_Init(const t_cm_system_address *pSystemAddr) */ +/*--------------------------------------------------------------------------*/ +/* DESCRIPTION: Initialize the HW Semaphores module */ +/* */ +/* PARAMETERS: */ +/* (in) pSystemAddr: system base address of the HW semaphores IP */ +/* */ +/* RETURN: CM_OK always */ +/* */ +/****************************************************************************/ +PUBLIC t_cm_error cm_HSEM_Init(const t_cm_system_address *pSystemAddr) +{ + t_uint8 i; + + pHwSemRegs = (t_hw_semaphore_regs *)pSystemAddr->logical; + + for (i=HSEM_FIRST_INTR; i < HSEM_MAX_INTR; i++) + { + imsc[i] = 0; // Mask all interrupt + } + + return CM_OK; +} + +static void cm_HSEM_ReInit(void) +{ + t_uint8 i; + + pHwSemRegs->icrall = MASK_ALL16; + + for (i=HSEM_FIRST_INTR; i < HSEM_MAX_INTR; i++) + { + pHwSemRegs->it[i].imsc = imsc[i]; + pHwSemRegs->it[i].icr = MASK_ALL16; + } + + for (i=0; i < NUM_HW_SEMAPHORES; i++) + { + pHwSemRegs->sem[i] = 0; + } +} + +/****************************************************************************/ +/* NAME: t_cm_error cm_HSEM_EnableSemIrq( */ +/* t_semaphore_id semId, */ +/* t_nmf_core_id toCoreId */ +/* ) */ +/*--------------------------------------------------------------------------*/ +/* DESCRIPTION: Enable Irq for a given coreId (communication receiver) */ +/* */ +/* PARAMETERS: */ +/* (in) semId: identifier of the semaphore */ +/* (in) toCoreId: identifier of coreId destination of the coms */ +/* */ +/* RETURN: CM_OK always */ +/* */ +/****************************************************************************/ +PUBLIC t_cm_error cm_HSEM_EnableSemIrq(t_semaphore_id semId, t_nmf_core_id toCoreId) +{ + static t_uint32 CoreIdToIntr[NB_CORE_IDS] = {0, 2, 3}; + int i = CoreIdToIntr[toCoreId]; + + imsc[i] |= (1UL << semId); + + // Allow cm_HSEM_EnableSemIrq to be called before real start in order to save power + if(semaphoreUseCounter > 0) + { + pHwSemRegs->it[i].imsc = imsc[i]; + } + + return CM_OK; +} + +/****************************************************************************/ +/* NAME: void cm_HSEM_GenerateIrq(t_semaphore_id semId) */ +/*--------------------------------------------------------------------------*/ +/* DESCRIPTION: Generate an irq toward correct core according to semId */ +/* */ +/* PARAMETERS: */ +/* (in) semId: identifier of the semaphore to handle */ +/* */ +/* RETURN: none */ +/* */ +/****************************************************************************/ +PUBLIC void cm_HSEM_GenerateIrq(t_nmf_core_id coreId, t_semaphore_id semId) +{ + // TODO Move restore in OS BSP or in PRCMU in order to to it only when wake-up, for now do it always !!!!!!!!!!!! + restoreMask(); + + pHwSemRegs->sem[semId] = CORE_ID_2_HW_CORE_ID(ARM_CORE_ID); + pHwSemRegs->sem[semId] = (HSEM_INTRA_MASK|HSEM_INTRB_MASK|HSEM_INTRC_MASK|HSEM_INTRD_MASK); +} + +/****************************************************************************/ +/* NAME: t_nmf_core_id cm_HSEM_GetCoreIdFromIrqSrc(void) */ +/*--------------------------------------------------------------------------*/ +/* DESCRIPTION: Check Masked Interrupt Status to know which semaphore(s) */ +/* have pending interrupt and return the identifier of the given dsp */ +/* */ +/* PARAMETERS: none */ +/* */ +/* RETURN: none */ +/* */ +/****************************************************************************/ +PUBLIC t_nmf_core_id cm_HSEM_GetCoreIdFromIrqSrc(void) +{ + t_uword misValue = pHwSemRegs->it[ARM_CORE_ID].mis; + t_uint32 mask = 1 << FIRST_NEIGHBOR_SEMID(ARM_CORE_ID) /* == 0 here */; + t_nmf_core_id coreId = FIRST_MPC_ID; + + while ((misValue & mask) == 0) + { + mask <<= 1; + + coreId++; + if(coreId > LAST_MPC_ID) + return coreId; + } + + /* Acknowledge Hsem interrupt */ + pHwSemRegs->it[ARM_CORE_ID].icr = mask; + + return coreId; +} + +PUBLIC t_cm_error cm_HSEM_PowerOn(t_nmf_core_id coreId) +{ + if(semaphoreUseCounter++ == 0) + { + cm_PWR_EnableHSEM(); + + cm_HSEM_ReInit(); // HSEM is called one time only when the HSEM is switched ON + } + + return CM_OK; +} + +PUBLIC void cm_HSEM_PowerOff(t_nmf_core_id coreId) +{ + if(--semaphoreUseCounter == 0) + { + cm_PWR_DisableHSEM(); + } +} + +PRIVATE void restoreMask() +{ + t_uint8 i; + + for (i=HSEM_FIRST_INTR; i < HSEM_MAX_INTR; i++) + pHwSemRegs->it[i].imsc = imsc[i]; +} diff --git a/drivers/staging/nmf-cm/cm/engine/semaphores/inc/semaphores.h b/drivers/staging/nmf-cm/cm/engine/semaphores/inc/semaphores.h new file mode 100644 index 00000000000..7636d8e7c9d --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/semaphores/inc/semaphores.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/** + * \internal + */ +#ifndef __INC_NMF_SEMAPHORE_H +#define __INC_NMF_SEMAPHORE_H + +#include <cm/engine/api/control/configuration_engine.h> +#include <share/semaphores/inc/semaphores.h> +#include <cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h> + +PUBLIC t_cm_error cm_SEM_Init(const t_cm_system_address *pSystemAddr); +PUBLIC t_cm_error cm_SEM_InitMpc(t_nmf_core_id coreId, t_nmf_semaphore_type_id semTypeId); +PUBLIC t_semaphore_id cm_SEM_Alloc(t_nmf_core_id fromCoreId, t_nmf_core_id toCoreId); + +/* Semaphores management virtualized functions */ +extern void (*cm_SEM_GenerateIrq[NB_CORE_IDS])(t_nmf_core_id coreId, t_semaphore_id semId); +extern t_cm_error (*cm_SEM_PowerOn[NB_CORE_IDS])(t_nmf_core_id coreId); +extern void (*cm_SEM_PowerOff[NB_CORE_IDS])(t_nmf_core_id coreId); + +#endif /* __INC_NMF_SEMAPHORE_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/semaphores/src/semaphores.c b/drivers/staging/nmf-cm/cm/engine/semaphores/src/semaphores.c new file mode 100644 index 00000000000..daf95355a56 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/semaphores/src/semaphores.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/inc/cm_type.h> +#include <cm/engine/semaphores/inc/semaphores.h> +#include <cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h> +#include <cm/engine/dsp/inc/semaphores_dsp.h> +#include <cm/engine/trace/inc/trace.h> +#include <share/inc/nmf.h> + +void (*cm_SEM_GenerateIrq[NB_CORE_IDS])(t_nmf_core_id coreId, t_semaphore_id semId); +t_cm_error (*cm_SEM_PowerOn[NB_CORE_IDS])(t_nmf_core_id coreId); +void (*cm_SEM_PowerOff[NB_CORE_IDS])(t_nmf_core_id coreId); + +#define SEM_TYPE_ID_DEFAULT_VALUE ((t_nmf_semaphore_type_id)MASK_ALL32) +static t_nmf_semaphore_type_id semaphoreTypePerCoreId[NB_CORE_IDS]; + +static t_cm_error cm_LSEM_PowerOn(t_nmf_core_id coreId) +{ + return CM_OK; +} + +static void cm_LSEM_PowerOff(t_nmf_core_id coreId) +{ +} + +PUBLIC t_cm_error cm_SEM_Init(const t_cm_system_address *pSystemAddr) +{ + t_nmf_core_id coreId; + + for (coreId = ARM_CORE_ID; coreId < NB_CORE_IDS; coreId++) + { + semaphoreTypePerCoreId[coreId] = SEM_TYPE_ID_DEFAULT_VALUE; + + /* By default, we suppose that we use a full feature NMF ;) */ + cm_SEM_GenerateIrq[coreId] = NULL; + cm_SEM_PowerOn[coreId] = NULL; + cm_SEM_PowerOff[coreId] = NULL; + } + + cm_HSEM_Init(pSystemAddr); + /* if needed local semaphore init will be done coreId per coreId */ + + return CM_OK; +} + +PUBLIC t_cm_error cm_SEM_InitMpc(t_nmf_core_id coreId, t_nmf_semaphore_type_id semTypeId) +{ + if (semaphoreTypePerCoreId[coreId] != SEM_TYPE_ID_DEFAULT_VALUE) + return CM_MPC_ALREADY_INITIALIZED; + + if(semTypeId == SYSTEM_SEMAPHORES) + { + cm_SEM_GenerateIrq[coreId] = cm_HSEM_GenerateIrq; + cm_SEM_PowerOn[coreId] = cm_HSEM_PowerOn; + cm_SEM_PowerOff[coreId] = cm_HSEM_PowerOff; + } + else if (semTypeId == LOCAL_SEMAPHORES) + { + cm_SEM_GenerateIrq[coreId] = cm_DSP_SEM_GenerateIrq; + cm_SEM_PowerOn[coreId] = cm_LSEM_PowerOn; + cm_SEM_PowerOff[coreId] = cm_LSEM_PowerOff; + } + + semaphoreTypePerCoreId[coreId] = semTypeId; + + return CM_OK; +} + +PUBLIC t_semaphore_id cm_SEM_Alloc(t_nmf_core_id fromCoreId, t_nmf_core_id toCoreId) +{ + t_semaphore_id semId; + t_nmf_core_id corex; + + semId = FIRST_NEIGHBOR_SEMID(toCoreId); + for (corex = FIRST_CORE_ID; corex < fromCoreId; corex++) + { + if (corex == toCoreId) + continue; + semId++; + } + + if ( + (toCoreId == ARM_CORE_ID && semaphoreTypePerCoreId[fromCoreId] == SYSTEM_SEMAPHORES) || + (semaphoreTypePerCoreId[toCoreId] == SYSTEM_SEMAPHORES) + ) + { + cm_HSEM_EnableSemIrq(semId, toCoreId); + } + + return semId; +} diff --git a/drivers/staging/nmf-cm/cm/engine/trace/inc/trace.h b/drivers/staging/nmf-cm/cm/engine/trace/inc/trace.h new file mode 100644 index 00000000000..111eaf1324e --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/trace/inc/trace.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Trace facilities management API + * + * \defgroup Trace Facilities + */ +#ifndef __INC_CM_TRACE_H +#define __INC_CM_TRACE_H + +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> +#include <cm/engine/configuration/inc/configuration_status.h> + +/*********************/ +/* Log related stuff */ +/*********************/ +#define ERROR(format, param1, param2, param3, param4, param5, param6) \ +do { \ + if (cm_debug_level != -1) \ + OSAL_Log("Error: " format, (int)(param1), (int)(param2), (int)(param3), (int)(param4), (int)(param5), (int)(param6)); \ + while(cm_error_break);\ +} while(0) + +#define WARNING(format, param1, param2, param3, param4, param5, param6) \ +do { \ + if (cm_debug_level != -1) \ + OSAL_Log("Warning: " format, (int)(param1), (int)(param2), (int)(param3), (int)(param4), (int)(param5), (int)(param6)); \ +} while(0) + +#define LOG_INTERNAL(level, format, param1, param2, param3, param4, param5, param6) \ +do { \ + if (level <= cm_debug_level) \ + OSAL_Log((const char *)format, (int)(param1), (int)(param2), (int)(param3), (int)(param4), (int)(param5), (int)(param6)); \ +} while(0) + +/*************************/ +/* Panic related stuff */ +/*************************/ +#define CM_ASSERT(cond) \ +do { \ + if(!(cond)) { OSAL_Log("CM_ASSERT at %s:%d\n", (int)__FILE__, (int)__LINE__, 0, 0, 0, 0); OSAL_Panic(); while(1); } \ +} while (0) + +#endif /* __INC_CM_TRACE_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/trace/inc/xtitrace.h b/drivers/staging/nmf-cm/cm/engine/trace/inc/xtitrace.h new file mode 100644 index 00000000000..6ab960b69b2 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/trace/inc/xtitrace.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#ifndef __INC_CM_XTITRACE_H +#define __INC_CM_XTITRACE_H + +#include <cm/engine/component/inc/instance.h> + +#include <inc/nmf-tracedescription.h> + +extern t_bool cm_trace_enabled; + +/*************************/ +/* Trace related stuff */ +/*************************/ +void cm_TRC_Dump(void); + +void cm_TRC_traceReset(void); + +void cm_TRC_traceLoadMap( + t_nmfTraceComponentCommandDescription cmd, + const t_component_instance* component); + +#define ARM_TRACE_COMPONENT ((const t_component_instance*)0xFFFFFFFF) + +void cm_TRC_traceBinding( + t_nmfTraceBindCommandDescription command, + const t_component_instance* clientComponent, const t_component_instance* serverComponent, + const char *requiredItfName, const char *providedItfName); + +void cm_TRC_traceCommunication( + t_nmfTraceCommunicationCommandDescription command, + t_nmf_core_id coreId, + t_nmf_core_id remoteCoreId); + +void cm_TRC_traceMemAlloc(t_nmfTraceAllocatorCommandDescription command, t_uint8 allocId, t_uint32 memorySize, const char *allocname); + +void cm_TRC_traceMem(t_nmfTraceAllocCommandDescription command, t_uint8 allocId, t_uint32 startAddress, t_uint32 memorySize); + +#endif /* __INC_CM_TRACE_H */ diff --git a/drivers/staging/nmf-cm/cm/engine/trace/src/panic.c b/drivers/staging/nmf-cm/cm/engine/trace/src/panic.c new file mode 100644 index 00000000000..9cb1a729a92 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/trace/src/panic.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include <cm/inc/cm_type.h> +#include <cm/engine/component/inc/introspection.h> +#include <cm/engine/component/inc/bind.h> +#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/api/control/irq_engine.h> + +#include <cm/engine/utils/inc/convert.h> +#include <share/communication/inc/nmf_service.h> + + +/* + * Panic + */ +const struct { + char* name; + unsigned int info1:1; + unsigned int PC:1; + unsigned int SP:1; + unsigned int interface:1; +} reason_descrs[] = { + {"NONE_PANIC", 0, 0, 0, 0}, + {"INTERNAL_PANIC", 1, 0, 0, 0}, + {"MPC_NOT_RESPONDING_PANIC", 0, 0, 0, 0}, /* Should not be useful since in that case CM_getServiceDescription() not call */ + {"USER_STACK_OVERFLOW", 0, 1, 1, 0}, + {"SYSTEM_STACK_OVERFLOW", 0, 1, 1, 0}, + {"UNALIGNED_LONG_ACCESS", 0, 1, 0, 0}, + {"EVENT_FIFO_OVERFLOW", 0, 0, 0, 1}, + {"PARAM_FIFO_OVERFLOW", 0, 0, 0, 1}, + {"INTERFACE_NOT_BINDED", 0, 1, 0, 0}, + {"USER_PANIC", 1, 0, 0, 0} +}; + +static t_component_instance* getCorrespondingInstance( + t_panic_reason panicReason, + t_uint32 panicThis, + t_dup_char *itfName, + t_cm_instance_handle *instHandle) { + t_component_instance *instance; + t_uint32 k; + + for (k=0; k<ComponentTable.idxMax; k++) { + if ((instance = componentEntry(k)) == NULL) + continue; + if(panicReason == PARAM_FIFO_OVERFLOW || + panicReason == EVENT_FIFO_OVERFLOW) { + // Panic has been generated by binding component, search the client who has call it + // and return the client handle (not the BC one). + int i; + + if(instance->thisAddress == panicThis && panicThis == 0) { + *itfName = "Internal NMF service"; + *instHandle = ENTRY2HANDLE(instance, k); + return instance; + } + + for(i = 0; i < instance->Template->requireNumber; i++) { + int nb = instance->Template->requires[i].collectionSize, j; + for(j = 0; j < nb; j++) { + if(instance->interfaceReferences[i][j].instance != NULL && + instance->interfaceReferences[i][j].instance != (t_component_instance *)NMF_HOST_COMPONENT && + instance->interfaceReferences[i][j].instance != (t_component_instance *)NMF_VOID_COMPONENT && + instance->interfaceReferences[i][j].instance->thisAddress == panicThis) + { + *itfName = instance->Template->requires[i].name; + *instHandle = ENTRY2HANDLE(instance, k); + return instance; + } + } + } + } else { + // The component which has generated the panic is the good one. + + if(instance->thisAddress == panicThis) { + *itfName = "?"; + *instHandle = ENTRY2HANDLE(instance, k); + return instance; + } + } + } + + *itfName = "?"; + *instHandle = 0; + return 0; +} + +PUBLIC EXPORT_SHARED t_cm_error CM_ReadMPCString( + t_nmf_core_id coreId, + t_uint32 dspAddress, + char * buffer, + t_uint32 bufferSize) { + + while(--bufferSize > 0) + { + char ch = cm_DSP_ReadXRamWord(coreId, dspAddress++); + if(ch == 0) + break; + + *buffer++ = ch; + }; + + *buffer = 0; + + // Reset panicReason + cm_writeAttribute(cm_EEM_getExecutiveEngine(coreId)->instance, + "rtos/commonpart/serviceReason", MPC_SERVICE_NONE); + + return CM_OK; +} + +/****************/ +/* Generic part */ +/****************/ +PUBLIC EXPORT_SHARED t_cm_error CM_getServiceDescription( + t_nmf_core_id coreId, + t_cm_service_type *srcType, + t_cm_service_description *srcDescr) +{ + t_uint32 serviceReason; + t_component_instance *ee; + + // Acknowledge interrupt (do it before resetting panicReason) + cm_DSP_AcknowledgeDspIrq(coreId, DSP2ARM_IRQ_1); + + ee = cm_EEM_getExecutiveEngine(coreId)->instance; + + // Read panicReason + serviceReason = cm_readAttributeNoError(ee, "rtos/commonpart/serviceReason"); + if(serviceReason == MPC_SERVICE_PRINT) + { + *srcType = CM_MPC_SERVICE_PRINT; + + srcDescr->u.print.dspAddress = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo0"); + srcDescr->u.print.value1 = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo1"); + srcDescr->u.print.value2 = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo2"); + } + else if(serviceReason != MPC_SERVICE_NONE) + { + t_uint32 panicThis; + t_dup_char itfName; + t_component_instance *instance; + + *srcType = CM_MPC_SERVICE_PANIC; + srcDescr->u.panic.panicReason = (t_panic_reason)serviceReason; + srcDescr->u.panic.panicSource = MPC_EE; + srcDescr->u.panic.info.mpc.coreid = coreId; + + // Read panicThis + panicThis = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo0"); + + instance = getCorrespondingInstance(srcDescr->u.panic.panicReason, panicThis, &itfName, &srcDescr->u.panic.info.mpc.faultingComponent); + + LOG_INTERNAL(0, "Error: Panic(%s, %s), This=%x", cm_getDspName(coreId), + reason_descrs[srcDescr->u.panic.panicReason].name, (void*)panicThis, 0, 0, 0); + + if(reason_descrs[srcDescr->u.panic.panicReason].interface != 0) + { + LOG_INTERNAL(0, ", interface=%s", itfName, 0, 0, 0, 0, 0); + } + + if(reason_descrs[srcDescr->u.panic.panicReason].info1 != 0) + { + // Info 1 + srcDescr->u.panic.info.mpc.panicInfo1 = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo1"); + + LOG_INTERNAL(0, ", Info=%x", srcDescr->u.panic.info.mpc.panicInfo1, 0, 0, 0, 0, 0); + } + + if(reason_descrs[srcDescr->u.panic.panicReason].PC != 0) + { + t_uint32 DspAddress = 0xFFFFFFFF; + t_uint32 DspSize = 0x0; + + // PC need to be read in rtos/commonpart/serviceInfo1 + srcDescr->u.panic.info.mpc.panicInfo1 = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo1"); + + if(instance != 0) + { + cm_DSP_GetDspAddress(instance->memories[instance->Template->codeMemory->id], &DspAddress); + cm_DSP_GetDspMemoryHandleSize(instance->memories[instance->Template->codeMemory->id], &DspSize); + } + + if(DspAddress <= srcDescr->u.panic.info.mpc.panicInfo1 && + srcDescr->u.panic.info.mpc.panicInfo1 < (DspAddress + DspSize)) + LOG_INTERNAL(0, ", PC:off=%x <abs=%x>", + srcDescr->u.panic.info.mpc.panicInfo1 - DspAddress, + srcDescr->u.panic.info.mpc.panicInfo1, 0, 0, 0, 0); + else + LOG_INTERNAL(0, ", PC:<abs=%x>", srcDescr->u.panic.info.mpc.panicInfo1, 0, 0, 0, 0, 0); + } + + if(reason_descrs[srcDescr->u.panic.panicReason].SP != 0) + { + srcDescr->u.panic.info.mpc.panicInfo2 = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo2"); + + LOG_INTERNAL(0, ", SP=%x", srcDescr->u.panic.info.mpc.panicInfo2, 0, 0, 0, 0, 0); + } + + LOG_INTERNAL(0, "\n", 0, 0, 0, 0, 0, 0); + + if(instance != 0) + { + LOG_INTERNAL(0, "Error: Component=%s<%s>\n", + instance->pathname, instance->Template->name, 0, 0, 0, 0); + } + + // We don't set rtos/commonpart/serviceReason = MPC_SERVICE_NONE, since we don't want the + // MMDSP to continue execution, and we put in in Panic state + cm_DSP_SetStatePanic(coreId); + } + else + { + *srcType = CM_MPC_SERVICE_NONE; + } + + return CM_OK; +} diff --git a/drivers/staging/nmf-cm/cm/engine/trace/src/trace.c b/drivers/staging/nmf-cm/cm/engine/trace/src/trace.c new file mode 100644 index 00000000000..e27d3284ed2 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/trace/src/trace.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +#include "../inc/trace.h" +#include "../inc/xtitrace.h" +#include <inc/nmf-tracedescription.h> +#include <inc/nmf-limits.h> +#include <cm/engine/utils/inc/string.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> + +t_bool cm_trace_enabled = FALSE; + +/* + * STM message dump + */ +#define HEADER(t, s) ((t) | (s << 16)) + +static void writeN(struct t_nmfTraceChannelHeader* header) +{ + t_uint64* data = (t_uint64*)header; + t_uint64 *end = (t_uint64*)(((unsigned int)data) + header->traceSize - sizeof(t_uint64)); + + while(data < end) + { + OSAL_Write64(CM_CHANNEL, 0, *data++); + } + + OSAL_Write64(CM_CHANNEL, 1, *data); +} + +void cm_TRC_Dump(void) +{ + t_uint32 i; + + cm_TRC_traceReset(); + + for (i=0; i<ComponentTable.idxMax; i++) + { + if (componentEntry(i) != NULL) + cm_TRC_traceLoadMap(TRACE_COMPONENT_COMMAND_ADD, componentEntry(i)); + } +} + +void cm_TRC_traceReset(void) +{ + if(cm_trace_enabled) + { + struct t_nmfTraceReset trace; + + trace.header.v = HEADER(TRACE_TYPE_RESET, sizeof(trace)); + + trace.minorVersion = TRACE_MINOR_VERSION; + trace.majorVersion = TRACE_MAJOR_VERSION; + + writeN((struct t_nmfTraceChannelHeader*)&trace); + } +} + +void cm_TRC_traceLoadMap( + t_nmfTraceComponentCommandDescription command, + const t_component_instance* component) +{ + if(cm_trace_enabled) + { + struct t_nmfTraceComponent trace; + + /* + * Generate instantiate trace + */ + trace.header.v = HEADER(TRACE_TYPE_COMPONENT, sizeof(trace)); + + trace.command = (t_uint16)command; + trace.domainId = (t_uint16)component->Template->dspId + 1; + trace.componentContext = (t_uint32)component->thisAddress; + trace.componentUserContext = (t_uint32)component; + cm_StringCopy((char*)trace.componentLocalName, component->pathname, MAX_COMPONENT_NAME_LENGTH); + cm_StringCopy((char*)trace.componentTemplateName, component->Template->name, MAX_TEMPLATE_NAME_LENGTH); + + writeN((struct t_nmfTraceChannelHeader*)&trace); + + if(command == TRACE_COMPONENT_COMMAND_ADD) + { + struct t_nmfTraceMethod tracemethod; + int i, j, k; + + /* + * Generate method trace + */ + tracemethod.header.v = HEADER(TRACE_TYPE_METHOD, sizeof(tracemethod)); + + tracemethod.domainId = (t_uint16)component->Template->dspId + 1; + tracemethod.componentContext = (t_uint32)component->thisAddress; + + for(i = 0; i < component->Template->provideNumber; i++) + { + t_interface_provide* provide = &component->Template->provides[i]; + t_interface_provide_loaded* provideLoaded = &component->Template->providesLoaded[i]; + + for(j = 0; j < provide->collectionSize; j++) + { + for(k = 0; k < provide->interface->methodNumber; k++) + { + tracemethod.methodId = provideLoaded->indexesLoaded[j][k].methodAddresses; + + cm_StringCopy((char*)tracemethod.methodName, provide->interface->methodNames[k], MAX_INTERFACE_METHOD_NAME_LENGTH); + + writeN((struct t_nmfTraceChannelHeader*)&tracemethod); + } + } + } + } + } +} + +void cm_TRC_traceBinding( + t_nmfTraceBindCommandDescription command, + const t_component_instance* clientComponent, const t_component_instance* serverComponent, + const char *requiredItfName, const char *providedItfName) +{ + if(cm_trace_enabled) + { + struct t_nmfTraceBind trace; + + trace.header.v = HEADER(TRACE_TYPE_BIND, sizeof(trace)); + + trace.command = (t_uint16)command; + + if(clientComponent == ARM_TRACE_COMPONENT) // ARM + { + trace.clientDomainId = 0x1; + trace.clientComponentContext = 0x0; + } + else + { + trace.clientDomainId = (t_uint16)clientComponent->Template->dspId + 1; + trace.clientComponentContext = (t_uint32)clientComponent->thisAddress; + } + if(requiredItfName != NULL) + cm_StringCopy((char*)trace.requiredItfName, requiredItfName, MAX_INTERFACE_NAME_LENGTH); + else + trace.requiredItfName[0] = 0; + + if(serverComponent == NULL) + { // Unbind or VOID + trace.serverDomainId = 0; + trace.serverComponentContext = 0x0; + } + else if(serverComponent == ARM_TRACE_COMPONENT) + { // ARM + trace.serverDomainId = 0x1; + trace.serverComponentContext = 0x0; + } + else + { + trace.serverDomainId = (t_uint16)serverComponent->Template->dspId + 1; + trace.serverComponentContext = (t_uint32)serverComponent->thisAddress; + } + if(providedItfName != NULL) + cm_StringCopy((char*)trace.providedItfName, providedItfName, MAX_INTERFACE_NAME_LENGTH); + else + trace.providedItfName[0] = 0; + + writeN((struct t_nmfTraceChannelHeader*)&trace); + } +} + +void cm_TRC_traceCommunication( + t_nmfTraceCommunicationCommandDescription command, + t_nmf_core_id coreId, + t_nmf_core_id remoteCoreId) +{ + if(cm_trace_enabled) + { + struct t_nmfTraceCommunication trace; + + trace.header.v = HEADER(TRACE_TYPE_COMMUNICATION, sizeof(trace)); + + trace.command = (t_uint16)command; + trace.domainId = (t_uint16)coreId + 1; + trace.remoteDomainId = (t_uint16)remoteCoreId + 1; + + writeN((struct t_nmfTraceChannelHeader*)&trace); + } +} + +void cm_TRC_traceMemAlloc(t_nmfTraceAllocatorCommandDescription command, t_uint8 allocId, t_uint32 memorySize, const char *allocname) +{ + if(cm_trace_enabled) + { + struct t_nmfTraceAllocator trace; + + trace.header.v = HEADER(TRACE_TYPE_ALLOCATOR, sizeof(trace)); + + trace.command = (t_uint16)command; + trace.allocId = (t_uint16)allocId; + trace.size = memorySize; + cm_StringCopy((char*)trace.name, allocname, sizeof(trace.name)); + + writeN((struct t_nmfTraceChannelHeader*)&trace); + } +} + +void cm_TRC_traceMem(t_nmfTraceAllocCommandDescription command, t_uint8 allocId, t_uint32 startAddress, t_uint32 memorySize) +{ + if(cm_trace_enabled) + { + struct t_nmfTraceAlloc trace; + + trace.header.v = HEADER(TRACE_TYPE_ALLOC, sizeof(trace)); + + trace.command = (t_uint16)command; + trace.allocId = (t_uint16)allocId; + trace.offset = startAddress; + trace.size = memorySize; + + writeN((struct t_nmfTraceChannelHeader*)&trace); + } +} + diff --git a/drivers/staging/nmf-cm/cm/engine/utils/inc/convert.h b/drivers/staging/nmf-cm/cm/engine/utils/inc/convert.h new file mode 100644 index 00000000000..d6912e58687 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/utils/inc/convert.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Conversion utility methods. + */ +#ifndef H_CM_CONVERTS_MEM +#define H_CM_CONVERTS_MEM + +#include <share/inc/nmf.h> + +/* + * Utils convert methods + */ +const char* cm_getDspName(t_nmf_core_id dsp); + + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/utils/inc/mem.h b/drivers/staging/nmf-cm/cm/engine/utils/inc/mem.h new file mode 100644 index 00000000000..c950a94023d --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/utils/inc/mem.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Memory manipulation. + */ +#ifndef H_CM_UTILS_MEM +#define H_CM_UTILS_MEM + +/* + * Utils libc methods + */ +void cm_MemCopy(void* dest, const void *src, int count); +void cm_MemSet(void *str, int c, int count); + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/utils/inc/string.h b/drivers/staging/nmf-cm/cm/engine/utils/inc/string.h new file mode 100644 index 00000000000..d2b7c0b0823 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/utils/inc/string.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief String manipulation. + */ +#ifndef H_CM_UTILS_STRING +#define H_CM_UTILS_STRING + +#include <cm/engine/memory/inc/memory.h> + +#define MAX_INTERNAL_STRING_LENGTH 2048 + +typedef const char *t_dup_char; + +t_dup_char cm_StringGet(const char* str); +t_dup_char cm_StringReference(t_dup_char str); +t_dup_char cm_StringDuplicate(const char* orig); +void cm_StringRelease(t_dup_char orig); + +/* + * Utils libc methods + */ +void cm_StringCopy(char* dest, const char* src, int count); +int cm_StringCompare(const char* str1, const char* str2, int count); +int cm_StringLength(const char * str, int count); +void cm_StringConcatenate(char* dest, const char* src, int count); +char* cm_StringSearch(const char* str, int c); + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/utils/inc/swap.h b/drivers/staging/nmf-cm/cm/engine/utils/inc/swap.h new file mode 100644 index 00000000000..e4f5acb3010 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/utils/inc/swap.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Swap integer manipulation. + */ +#ifndef H_CM_UTILS_SWAP +#define H_CM_UTILS_SWAP + +#include <cm/inc/cm_type.h> + +/* + * Swap methods + */ +t_uint16 swap16(t_uint16 x); +t_uint32 swap32(t_uint32 x); +t_uint64 swap64(t_uint64 x); +t_uint32 noswap32(t_uint32 x); + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/utils/inc/table.h b/drivers/staging/nmf-cm/cm/engine/utils/inc/table.h new file mode 100644 index 00000000000..9d9828a81f6 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/utils/inc/table.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/*! + * \internal + * \brief Dynamic table manipulation. + */ +#ifndef H_CM_UTILS_TABLE +#define H_CM_UTILS_TABLE + +#include <cm/inc/cm_type.h> + +/* + This implement a (generic) dynamic table (the size is dynamic) + to register some pointers of a given kind of elements + + It also allows to compute/convert each kernel pointer registered in the + table to a user handler, that can be checked. + + The "user" handler is composed by the index in this table + (the low INDEX_SHIFT bits) and the low bits of the "local" pointer + shifted by INDEX_SHIFT are stored in the high bits: + + handle bits: 31 ................................ 12 11 ...... 0 + | lower bits of of the local pointer | index | + + This allows a straight translation from a user handle to a local pointer + + a strong check to validate the value of a user handle. + The reverse translation from pointer to a user handle is + slower as it requires an explicit search in the list. + */ + + +/* INDEX_SHIFT determines the index size and thus the max index */ +#define INDEX_SHIFT 12 +#define INDEX_MAX (1UL << INDEX_SHIFT) +#define INDEX_MASK (INDEX_MAX-1) +#define ENTRY2HANDLE(pointer, index) (((unsigned int)pointer << INDEX_SHIFT) | index) +#define TABLE_DEF_SIZE 0x1000 + +typedef struct { + t_uint32 idxNb; /**< number of entries used */ + t_uint32 idxCur; /**< current index: point to next supposed + free entry: used to look for the next + free entry */ + t_uint32 idxMax; /**< index max currently allowed */ + void **entries; /**< table itself */ +} t_nmf_table; + +t_cm_error cm_initTable(t_nmf_table* table); +void cm_destroyTable(t_nmf_table* table); +t_uint32 cm_addEntry(t_nmf_table *table, void *entry); +void cm_delEntry(t_nmf_table *table, t_uint32 idx); +void *cm_lookupEntry(const t_nmf_table *table, const t_uint32 hdl); +t_uint32 cm_lookupHandle(const t_nmf_table *table, const void *entry); + +#endif diff --git a/drivers/staging/nmf-cm/cm/engine/utils/src/convert.c b/drivers/staging/nmf-cm/cm/engine/utils/src/convert.c new file mode 100644 index 00000000000..ad6e097bfe6 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/utils/src/convert.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * + */ +#include <cm/engine/utils/inc/convert.h> + +const char* dspNames[NB_CORE_IDS] = { + "ARM", + "SVA", + "SIA" +}; + + +const char* cm_getDspName(t_nmf_core_id dsp) { + return dspNames[dsp]; +} diff --git a/drivers/staging/nmf-cm/cm/engine/utils/src/mem.c b/drivers/staging/nmf-cm/cm/engine/utils/src/mem.c new file mode 100644 index 00000000000..130a044bbf8 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/utils/src/mem.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * + */ +#include <cm/engine/utils/inc/mem.h> + + +/* + * Methods + */ +void cm_MemCopy(void* dest, const void *src, int count) { + char *tmp = (char *) dest, *s = (char *) src; + + while (count--) + *tmp++ = *s++; +} + +void cm_MemSet(void *str, int c, int count) { + char *tmp = (char *)str; + + while (count--) + *tmp++ = c; +} diff --git a/drivers/staging/nmf-cm/cm/engine/utils/src/string.c b/drivers/staging/nmf-cm/cm/engine/utils/src/string.c new file mode 100644 index 00000000000..89058d5825a --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/utils/src/string.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * + * Shared string manipulation. + * TODO This is a list today, must be a hash later !!!!! + */ +#include <cm/engine/utils/inc/string.h> +#include <cm/engine/trace/inc/trace.h> + +#include <cm/engine/memory/inc/memory.h> +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> + +#undef NHASH +#define NHASH 257 //Use a prime number! +#define MULT 17 + +/* + * Data + */ +struct t_linkedstring +{ + struct t_linkedstring *next; + int referencer; + char string[1]; +}; + +static struct t_linkedstring *list[NHASH]; + +#undef myoffsetof +#define myoffsetof(st, m) \ + ((int) ( (char *)&((st *)(0))->m - (char *)0 )) + +unsigned int hash(const char *str) +{ + unsigned int h = 0; + for(; *str; str++) + h = MULT * h + *str; + return h % NHASH; +} +/* + * Methods + */ +PRIVATE struct t_linkedstring *lookupString( + const char* str, + struct t_linkedstring *first) +{ + while(first != 0) + { + if(cm_StringCompare(str, first->string, MAX_INTERNAL_STRING_LENGTH) == 0) + break; + first = first->next; + } + + return first; +} + +t_dup_char cm_StringGet(const char* str) +{ + struct t_linkedstring *entry; + + entry = lookupString(str, list[hash(str)]); + CM_ASSERT(entry != 0); + + return (t_dup_char)entry->string; +} + +t_dup_char cm_StringReference(t_dup_char str) +{ + struct t_linkedstring* entry = (struct t_linkedstring*)((t_uint32)str - myoffsetof(struct t_linkedstring, string)); + + // One more referencer + entry->referencer++; + + return (t_dup_char)entry->string; +} + +t_dup_char cm_StringDuplicate(const char* str) +{ + struct t_linkedstring *entry; + unsigned int h; + + h = hash(str); + entry = lookupString(str, list[h]); + if(entry != 0) + { + // One more referencer + entry->referencer++; + } + else + { + // Allocate new entry + entry = (struct t_linkedstring *)OSAL_Alloc(sizeof(struct t_linkedstring)-1 + cm_StringLength(str, MAX_INTERNAL_STRING_LENGTH)+1); + if(entry == NULL) + return NULL; + + entry->referencer = 1; + cm_StringCopy(entry->string, str, MAX_INTERNAL_STRING_LENGTH); + + // Link it in list + entry->next = list[h]; + list[h] = entry; + } + + return (t_dup_char)entry->string; +} + +void cm_StringRelease(t_dup_char str) +{ + if(str != NULL) + { + struct t_linkedstring* entry = (struct t_linkedstring*)((t_uint32)str - myoffsetof(struct t_linkedstring, string)); + + // One less referencer + entry->referencer--; + + if(entry->referencer == 0) + { + int h = hash(entry->string); + + if(list[h] == entry) // This first first one + { + list[h] = entry->next; + } + else + { + struct t_linkedstring *tmp = list[h]; + + // Here we assume that entry is in the list + while(/*tmp != NULL && */tmp->next != entry) + tmp = tmp->next; + + tmp->next = entry->next; + } + OSAL_Free(entry); + } + } +} + +#if 0 +void checkString() +{ + struct t_linkedstring *tmp = list; + + while(tmp != 0) + { + printf(" stay %s %d\n", tmp->string, tmp->referencer); + tmp = tmp->next; + } +} +#endif + +/* + * LibC method + */ +void cm_StringCopy(char* dest, const char *src, int count) +{ + while (count-- && (*dest++ = *src++) != '\0') + /* nothing */ + ; +} +#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080) + +int cm_StringCompare(const char* str1, const char* str2, int count) +{ + /* If s1 and s2 are word-aligned, compare them a word at a time. */ + if ((((int)str1 & 3) | ((int)str2 & 3)) == 0) + { + unsigned int *a1 = (unsigned int*)str1; + unsigned int *a2 = (unsigned int*)str2; + + while (count >= sizeof (unsigned int) && *a1 == *a2) + { + count -= sizeof (unsigned int); + + /* If we've run out of bytes or hit a null, return zero since we already know *a1 == *a2. */ + if (count == 0 || DETECTNULL (*a1)) + return 0; + + a1++; + a2++; + } + + /* A difference was detected in last few bytes of s1, so search bytewise */ + str1 = (char*)a1; + str2 = (char*)a2; + } + + while (count-- > 0 && *str1 == *str2) + { + /* If we've run out of bytes or hit a null, return zero + since we already know *s1 == *s2. */ + if (count == 0 || *str1 == '\0') + return 0; + str1++; + str2++; + } + + return (*(unsigned char *) str1) - (*(unsigned char *) str2); +} + +int cm_StringLength(const char * str, int count) +{ + const char *sc; + + for (sc = str; count-- && *sc != '\0'; ++sc) + /* nothing */ + ; + return sc - str; +} + +void cm_StringConcatenate(char* dest, const char* src, int count) +{ + while ((*dest) != '\0') + { + dest++; + count--; + } + cm_StringCopy(dest, src, count); +} + +char* cm_StringSearch(const char* str, int c) +{ + for(; *str != (char) c; ++str) + if (*str == '\0') + return 0; + return (char *) str; +} diff --git a/drivers/staging/nmf-cm/cm/engine/utils/src/swap.c b/drivers/staging/nmf-cm/cm/engine/utils/src/swap.c new file mode 100644 index 00000000000..e3e2d536144 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/utils/src/swap.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * + */ +#include <cm/engine/utils/inc/swap.h> + + +/* + * Methods + */ +t_uint16 swap16(t_uint16 x) +{ + return ((x >> 8) | + ((x << 8) & 0xff00U)); +} + +#ifdef LINUX + +#if defined(__STN_8815) /* __STN_8815 -> ARMv5*/ +t_uint32 swap32(t_uint32 x) +{ + asm volatile ( + "EOR r1, r0, r0, ROR #16 \n\t" + "BIC r1, r1, #0xFF0000 \n\t" + "MOV r0, r0, ROR #8 \n\t" + "EOR r0, r0, r1, LSR #8" + : : : "r3" ); + + return x; +} + +t_uint64 swap64(t_uint64 x) +{ + asm volatile ( + "MOV r2, r1 \n\t" + " \n\t" + "EOR r3, r0, r0, ROR #16 \n\t" + "BIC r3, r3, #0xFF0000 \n\t" + "MOV r0, r0, ROR #8 \n\t" + "EOR r1, r0, r3, LSR #8 \n\t" + " \n\t" + "EOR r3, r2, r2, ROR #16 \n\t" + "BIC r3, r3, #0xFF0000 \n\t" + "MOV r2, r2, ROR #8 \n\t" + "EOR r0, r2, r3, LSR #8" + : : : "r3", "r2" ); + + return x; +} +#else /* -> ARMv6 or later */ + +t_uint32 swap32(t_uint32 x) +{ + asm volatile ( + "REV %0, %0" + : "+r"(x) : ); + + return x; +} + +t_uint64 swap64(t_uint64 x) +{ + asm volatile ( + "REV r2, %Q0 \n\t" + "REV %Q0, %R0 \n\t" + "MOV %R0, r2" + : "+&r" (x) : : "r2" ); + + return x; +} + +#endif + +#else /* Symbian, Think -> We assume ARMCC */ + +#if defined(__thumb__) + +t_uint32 swap32(t_uint32 x) +{ + return ((x >> 24) | + ((x >> 8) & 0xff00U) | + ((x << 8) & 0xff0000U) | + ((x << 24) & 0xff000000U)); +} + +t_uint64 swap64(t_uint64 x) +{ + return ((x >> 56) | + ((x >> 40) & 0xff00UL) | + ((x >> 24) & 0xff0000UL) | + ((x >> 8) & 0xff000000UL) | + ((x << 8) & 0xff00000000ULL) | + ((x << 24) & 0xff0000000000ULL) | + ((x << 40) & 0xff000000000000ULL) | + ((x << 56))); +} + +#elif (__TARGET_ARCH_ARM < 6) + +__asm t_uint32 swap32(t_uint32 x) +{ + EOR r1, r0, r0, ROR #16 + BIC r1, r1, #0xFF0000 + MOV r0, r0, ROR #8 + EOR r0, r0, r1, LSR #8 + + BX lr +} + +__asm t_uint64 swap64(t_uint64 x) +{ + MOV r2, r1 + + EOR r3, r0, r0, ROR #16 // Swap low (r0) and store it in high (r1) + BIC r3, r3, #0xFF0000 + MOV r0, r0, ROR #8 + EOR r1, r0, r3, LSR #8 + + EOR r3, r2, r2, ROR #16 // Swap high (r2 = ex r1) and store it in low (r0) + BIC r3, r3, #0xFF0000 + MOV r2, r2, ROR #8 + EOR r0, r2, r3, LSR #8 + + BX lr +} + +#else /* -> ARMv6 or later */ + +__asm t_uint32 swap32(t_uint32 x) +{ + REV r0, r0 + + BX lr +} + +__asm t_uint64 swap64(t_uint64 x) +{ + REV r2, r0 + REV r0, r1 + MOV r1, r2 + + BX lr +} + +#endif + +#endif + +t_uint32 noswap32(t_uint32 x) { + return x; +} + diff --git a/drivers/staging/nmf-cm/cm/engine/utils/src/table.c b/drivers/staging/nmf-cm/cm/engine/utils/src/table.c new file mode 100644 index 00000000000..708396a01b2 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/engine/utils/src/table.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2. + */ +/* + * + */ +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> +#include <cm/engine/trace/inc/trace.h> +#include <cm/engine/utils/inc/mem.h> +#include <cm/engine/utils/inc/table.h> + +/* + * Methods + */ +t_cm_error cm_initTable(t_nmf_table* table) +{ + table->idxMax = TABLE_DEF_SIZE / sizeof(table->entries); + + table->entries = OSAL_Alloc_Zero(table->idxMax*sizeof(table->entries)); + + if (table->entries == NULL) { + table->idxMax = 0; + return CM_NO_MORE_MEMORY; + } + + return CM_OK; +} + +void cm_destroyTable(t_nmf_table* table) +{ + if (table->idxNb) { + ERROR("Attempt to free non-empty table !!!\n", 0, 0, 0, 0, 0, 0); + return; + } + OSAL_Free(table->entries); + table->idxMax = 0; +} + +static t_cm_error cm_increaseTable(t_nmf_table* table) +{ + t_uint32 new_max; + void *mem; + + if (table->idxMax == INDEX_MASK) { + ERROR("CM_NO_MORE_MEMORY: Maximum table entries reached\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + new_max = table->idxMax + + TABLE_DEF_SIZE / sizeof(table->entries); + + if (new_max > INDEX_MAX) + new_max = INDEX_MAX; + + mem = OSAL_Alloc(new_max * sizeof(table->entries)); + + if (mem == NULL) { + ERROR("CM_NO_MORE_MEMORY: Unable to allocate memory for a table\n", 0, 0, 0, 0, 0, 0); + return CM_NO_MORE_MEMORY; + } + + cm_MemCopy(mem, table->entries, + table->idxMax*sizeof(table->entries)); + cm_MemSet((void *)((t_uint32) mem + table->idxMax*sizeof(*table->entries)), 0, + (new_max-table->idxMax) * sizeof(*table->entries)); + + OSAL_Free(table->entries); + table->entries = mem; + table->idxMax = new_max; + + return CM_OK; +} + +/** cm_addEntry - Add an local pointer to an element to the list + * + * 1. Increase the size of the list if it's full + * 2. Search an empty entry + * 3. Add the element to the list + * 4. Compute and return the "user handle" + */ +t_uint32 cm_addEntry(t_nmf_table *table, void *entry) +{ + unsigned int i; + t_uint32 hdl = 0; + + if (table->idxNb == table->idxMax) + cm_increaseTable(table); + + for (i = table->idxCur; + table->entries[i] != 0 && i != (table->idxCur-1); + i = (i+1)%table->idxMax); + + if (table->entries[i] == 0) { + table->entries[i] = entry; + table->idxCur = (i+1) % table->idxMax; + table->idxNb++; + hdl = ENTRY2HANDLE(entry, i); + } else + ERROR("No free entry found in table\n", 0, 0, 0, 0, 0, 0); + + return hdl; +} + +/** cm_delEntry - remove the given element from the list + * + * 1. Check if the handle is valid + * 2. Search the entry and free it + */ +void cm_delEntry(t_nmf_table *table, t_uint32 idx) +{ + table->entries[idx] = NULL; + table->idxNb--; +} + +/** cm_lookupEntry - search the entry corresponding to + * the user handle. + * + * 1. Check if the handle is valid + * 2. Return a pointer to the element + */ +void *cm_lookupEntry(const t_nmf_table *table, const t_uint32 hdl) +{ + unsigned int idx = hdl & INDEX_MASK; + + if ((idx >= table->idxMax) + || (((unsigned int)table->entries[idx] << INDEX_SHIFT) != (hdl & ~INDEX_MASK))) + return NULL; + else + return table->entries[idx]; +} + +/** cm_lookupHandle - search the handle corresponding + * to the given element + * + * 1. Check if the handler is valid or is a special handler + * 2. Loop in the table to retrieve the entry matching and return its value + */ +t_uint32 cm_lookupHandle(const t_nmf_table *table, const void *entry) +{ + t_uint32 i; + + /* NULL is an invalid value that must be handle separatly + as it'll match all used/free entries value */ + if (entry == NULL) + return 0; + + for (i=0; i < table->idxMax; i++) { + if (table->entries[i] == entry) + return ENTRY2HANDLE(table->entries[i], i); + } + + return 0; +} diff --git a/drivers/staging/nmf-cm/cm/inc/cm.h b/drivers/staging/nmf-cm/cm/inc/cm.h new file mode 100644 index 00000000000..37ccb36a5ee --- /dev/null +++ b/drivers/staging/nmf-cm/cm/inc/cm.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) ST-Ericsson SA 2010. All rights reserved. + * This code is ST-Ericsson proprietary and confidential. + * Any use of the code for whatever purpose is subject to + * specific written permission of ST-Ericsson SA. + */ + +#ifndef __INC_CM_H +#define __INC_CM_H + +#include <cm/inc/cm_def.h> + +/********************************************************************************/ +/* Component Manager API prototypes */ +/********************************************************************************/ + +/* + * User level wrapper + */ +#include <cm/proxy/api/cm_proxy.h> + +#endif /* __INC_CM_H */ diff --git a/drivers/staging/nmf-cm/cm/inc/cm_def.h b/drivers/staging/nmf-cm/cm/inc/cm_def.h new file mode 100644 index 00000000000..dc7a1fdad66 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/inc/cm_def.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) ST-Ericsson SA 2010. All rights reserved. + * This code is ST-Ericsson proprietary and confidential. + * Any use of the code for whatever purpose is subject to + * specific written permission of ST-Ericsson SA. + */ + +/*! + * \brief Component Manager API. + * + * This file contains the Component Manager API for manipulating components. + * + */ + +#ifndef __INC_CM_DEF_H +#define __INC_CM_DEF_H + +#include <cm/inc/cm_type.h> +#include <inc/nmf-def.h> + +/*! + * \brief Get the version of the NMF CM engine at runtime + * + * This method should be used to query the version number of the + * NMF Component Manager engine at runtime. This is useful when using + * to check if version of the engine linked with application correspond + * to engine used for development. + * + * Such code can be used to check compatibility: \code + t_uint32 nmfversion; + + // Print NMF version + CM_GetVersion(&nmfversion); + LOG("NMF Version %d-%d-%d\n", + VERSION_MAJOR(nmfversion), + VERSION_MINOR(nmfversion), + VERSION_PATCH(nmfversion)); + if(NMF_VERSION != nmfversion) { + LOG("Error: Incompatible API version %d != %d\n", NMF_VERSION, nmfversion); + EXIT(); + } + * \endcode + * + * \param[out] version Internal hardcoded version (use \ref VERSION_MAJOR, \ref VERSION_MINOR, \ref VERSION_PATCH macros to decode it). + * + * \ingroup CM + */ +PUBLIC IMPORT_SHARED void CM_GetVersion(t_uint32 *version); + +#endif /* __INC_CM_H */ diff --git a/drivers/staging/nmf-cm/cm/inc/cm_macros.h b/drivers/staging/nmf-cm/cm/inc/cm_macros.h new file mode 100644 index 00000000000..2279c204a20 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/inc/cm_macros.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) ST-Ericsson SA 2010. All rights reserved. + * This code is ST-Ericsson proprietary and confidential. + * Any use of the code for whatever purpose is subject to + * specific written permission of ST-Ericsson SA. + */ + +/*! + * \brief Component Manager Macros. + * + * \defgroup CM_MACROS NMF Macros (ANSI C99) + * The Component Manager Macros are provided to ease FromHost interface call and ToHost callback definition. + * \attention <b>These macros are only ANSI C99 compliant</b> (ARM RVCT 2.x/3.x, GNU gcc 4.x, ...) + * \ingroup CM_USER_API + */ + +#ifndef __INC_CM_MACROS_H +#define __INC_CM_MACROS_H + +/* + * The next macros are supported only with C Ansi 99, so.... + */ + +/* + * The Symbian environment dependency, computation which uses an old gnu cpp, + * does not accept "..." parameters. + * However the actual compiler (armcc) does. + * So remove the macro definitions when computing dependencies. + */ +#if ( defined(__CC_ARM) && !defined(__STRICT_ANSI__) ) || !defined(__SYMBIAN32__) + +/* + * Only for skilled eyes ;) + * The following macros are used to implement NMFCALL[VOID] and NMFMETH[VOID] macros in an elegant way + */ +#define WITH_PARAM(...) __VA_ARGS__) +#define WITH_NOPARAM(...) ) + +/*! + * \brief Macro to ease Host to Dsp interface calling + * + * \attention <b>This macro is only ANSI C99 compliant</b> + * + * The <i>NMFCALL</i> macro can be used to call one method of any previously FromHost bounded interface.\n + * From Host side, today, we have no way to mask the multi-instance handling, so + * this macro is provided to ease FromHost interface calling and to avoid any mistake into the THIS parameter passing. + * + * So, any fromHost interface method call like: \code + * itf.method(itf.THIS, param1, param2, ...); + * \endcode + * can be replaced by: \code + * NMFCALL(itf, method)(param1, param2, ...); + * \endcode + * + * \warning Don't forget to use NMFCALLVOID macro when declaring a FromHost interface method having none application parameter, + * else it will lead to erroneous C code expansion + * \see NMFCALLVOID + * \hideinitializer + * \ingroup CM_MACROS + */ +#define NMFCALL(itfHandle, itfMethodName) \ + (itfHandle).itfMethodName((itfHandle).THIS, WITH_PARAM + +/*! + * \brief Macro to ease Host to Dsp interface calling (method without any user parameter) + * + * \attention <b>This macro is only ANSI C99 compliant</b> + * + * The <i>NMFCALLVOID</i> macro can be used to call one method (those without any user parameter) of any previously FromHost bounded interface.\n + * From Host side, today, we have no way to mask the multi-instance handling, so + * this macro is provided to ease FromHost interface calling and to avoid any mistake into the THIS parameter passing. + * + * So, any FromHost interface method call without any application parameter like:\code + * itf.method(itf.THIS); + * \endcode + * can be replaced by: \code + * NMFCALLVOID(itf, method)(); + * \endcode + * \see NMFCALL + * \hideinitializer + * \ingroup CM_MACROS + */ +#define NMFCALLVOID(itfHandle, itfMethodName) \ + (itfHandle).itfMethodName((itfHandle).THIS WITH_NOPARAM + +/*! + * \brief Macro to ease Dsp to Host interface method declaration + * + * \attention <b>This macro definition is only ANSI C99 compliant</b> + * + * The <i>NMFMETH</i> macro can be used to ease the ToHost interface method declaration.\n + * From Host side, today, we have no way to mask the multi-intance handling, so the user shall handle it by hand + * by passing the "component" context as first parameter of each ToHost interface method through the void *THIS parameter. + * This macro could avoid any mistake into the THIS parameter declaration when never used by the user code. + * + * So, any ToHost interface method declaration like:\code + * void mynotify(void *THIS, mytype1 myparam1, mytype2 myparam2, ...) { + * <body of the interface routine> + * } + * \endcode + * can be replaced by: \code + * void NMFMETH(mynotify)(mytype1 myparam1, mytype2 myparam2, ...) { + * <body of the interface routine> + * } + * \endcode + * + * \warning Don't forget to use NMFMETHVOID macro when declaring a ToHost interface method having none application parameter, + * else it will lead to erroneous C code expansion + * + * \see NMFMETHVOID + * \hideinitializer + * \ingroup CM_MACROS + */ +#define NMFMETH(itfMethodName) \ + itfMethodName(void *THIS, WITH_PARAM + +/*! + * \brief Macro to ease Dsp to Host interface method declaration (method without any user parameter) + * + * \attention <b>This macro is only ANSI C99 compliant</b> + * + * The <i>NMFMETHVOID</i> macro can be used to ease the ToHost interface method (those without any user parameter) declaration.\n + * From Host side, today, we have no way to mask the multi-intance handling, so the user shall handle it by hand + * by passing the "component" context as first parameter of each ToHost interface method through the void *THIS parameter. + * This macro could avoid any mistake into the THIS parameter declaration when never used by the user code. + * + * So, any ToHost interface method declaration having none application parameter like:\code + * void mynotify(void *THIS) { + * <body of the interface routine> + * } + * \endcode + * can be replaced by: \code + * void NMFMETHVOID(mynotify)(void) { + * <body of the interface routine> + * } + * \endcode + * + * \see NMFMETH + * \hideinitializer + * \ingroup CM_MACROS + */ +#define NMFMETHVOID(itfMethodName) \ + itfMethodName(void *THIS WITH_NOPARAM + +#endif /* not Symbian environment or compiling with ARMCC and not in strict ANSI */ + +#endif /* __INC_CM_MACROS_H */ + diff --git a/drivers/staging/nmf-cm/cm/inc/cm_type.h b/drivers/staging/nmf-cm/cm/inc/cm_type.h new file mode 100644 index 00000000000..83a34ecd706 --- /dev/null +++ b/drivers/staging/nmf-cm/cm/inc/cm_type.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Component Manager types. + * + * This file contains the Component Manager types. + * + * \defgroup CM CM Type Definitions + * \ingroup CM_USER_API + */ +#ifndef _CM_TYPE_H_ +#define _CM_TYPE_H_ + +#include <share/inc/nmf.h> +#include <share/inc/macros.h> + +#include <nmf/inc/channel_type.h> + +/*! + * @defgroup t_cm_error t_cm_error + * \brief Description of the various errors returned by CM API routines + * @{ + * \ingroup CM + */ +typedef t_nmf_error t_cm_error; //!< Error type returned by CM API routines + +/*********************************************************************************/ +/* WARNING: UPDATE CM_StringError() func each time an error is added/removed !!! */ +/* CM_StringError() is defined twice in: */ +/* nmf_core/host/cm/proxy/common/wrapper/src/wrapper.c */ +/* tests/src/common/nte/src/nte.c */ +/*********************************************************************************/ +#define CM_LAST_ERROR_ID ((t_cm_error)-128) +#define CM_INTEGRATION_ERROR NMF_INTEGRATION_ERROR0 //!< \ref NMF_INTEGRATION_ERROR0 + + /* Communication */ +#define CM_FLUSH_MESSAGE NMF_FLUSH_MESSAGE //!< Message send after call to CM_FlushChannel() +#define CM_BUFFER_OVERFLOW ((t_cm_error)-105) //!< Buffer overflow (interface binding message bigger than buffer) +#define CM_USER_NOT_REGISTERED ((t_cm_error)-104) //!< User not registered +#define CM_NO_MESSAGE NMF_NO_MESSAGE //!< \ref NMF_NO_MESSAGE +#define CM_PARAM_FIFO_OVERFLOW ((t_cm_error)-102) //!< Param fifo overflow +#define CM_INTERNAL_FIFO_OVERFLOW ((t_cm_error)-101) //!< Internal services fifo overflow (not returned to user) +#define CM_MPC_NOT_RESPONDING ((t_cm_error)-100) //!< MPC not responding (either crash, interrupt handler too long, internal NMF fifo coms overflow, ...). + + /* ELF & File system */ +#define CM_FS_ERROR ((t_cm_error)-96) //!< FileSystem error +#define CM_NO_SUCH_FILE ((t_cm_error)-95) //!< No such file or directory +#define CM_INVALID_ELF_FILE ((t_cm_error)-94) //!< File isn't a valid MMDSP ELF file +#define CM_NO_SUCH_BASE ((t_cm_error)-93) //!< The memory base doesn't exist + + /* Introspection */ +#define CM_NO_SUCH_ATTRIBUTE NMF_NO_SUCH_ATTRIBUTE //!< \ref NMF_NO_SUCH_ATTRIBUTE +#define CM_NO_SUCH_PROPERTY NMF_NO_SUCH_PROPERTY //!< \ref NMF_NO_SUCH_PROPERTY + + /* Component Life Cycle */ +#define CM_COMPONENT_NOT_STOPPED NMF_COMPONENT_NOT_STOPPED //!< \ref NMF_COMPONENT_NOT_STOPPED +#define CM_COMPONENT_NOT_UNBINDED ((t_cm_error)-79) //!< Component must be fully unbinded before perform operation +#define CM_COMPONENT_NOT_STARTED ((t_cm_error)-78) //!< Component must be started to perform operation +#define CM_COMPONENT_WAIT_RUNNABLE ((t_cm_error)-76) //!< Component need acknowlegdment of life cycle start function before perform operation +#define CM_REQUIRE_INTERFACE_UNBINDED ((t_cm_error)-75) //!< Required component interfaces must be binded before perform operation +#define CM_INVALID_COMPONENT_HANDLE ((t_cm_error)-74) //!< Try to access a component already destroyed + + /* Binder */ +#define CM_NO_SUCH_PROVIDED_INTERFACE NMF_NO_SUCH_PROVIDED_INTERFACE //!< \ref NMF_NO_SUCH_PROVIDED_INTERFACE +#define CM_NO_SUCH_REQUIRED_INTERFACE NMF_NO_SUCH_REQUIRED_INTERFACE //!< \ref NMF_NO_SUCH_REQUIRED_INTERFACE +#define CM_ILLEGAL_BINDING ((t_cm_error)-62) //!< Client and server interface type mismatch +#define CM_ILLEGAL_UNBINDING ((t_cm_error)-61) //!< Try to unbind component with bad binding Factories +#define CM_INTERFACE_ALREADY_BINDED NMF_INTERFACE_ALREADY_BINDED//!< \ref NMF_INTERFACE_ALREADY_BINDED +#define CM_INTERFACE_NOT_BINDED NMF_INTERFACE_NOT_BINDED //!< \ref NMF_INTERFACE_NOT_BINDED + + /* Loader */ +#define CM_BINDING_COMPONENT_NOT_FOUND ((t_cm_error)-48) //!< Binding Component template name don't exist on components repository (should be generated thanks nkitf tool) +#define CM_COMPONENT_NOT_FOUND ((t_cm_error)-47) //!< Component template name doesn't exist on components repository +#define CM_NO_SUCH_SYMBOL ((t_cm_error)-46) //!< Symbol name doesn't exported by the underlying component +#define CM_COMPONENT_EXIST ((t_cm_error)-45) //!< Component name already exists in the component cache + + /* Fifo management related ones */ +#define CM_FIFO_FULL ((t_cm_error)-40) //!< Fifo is full +#define CM_FIFO_EMPTY ((t_cm_error)-39) //!< Fifo is empty +#define CM_UNKNOWN_FIFO_ID ((t_cm_error)-38) //!< Fifo handle doesn't exist + + /* Memory management related ones */ +#define CM_DOMAIN_VIOLATION ((t_cm_error)-33) //!< Domain violation +#define CM_CREATE_ALLOC_ERROR ((t_cm_error)-32) //!< Error during allocator creation +#define CM_UNKNOWN_MEMORY_HANDLE ((t_cm_error)-31) //!< Handle doesn't exists +#define CM_NO_MORE_MEMORY NMF_NO_MORE_MEMORY //!< \ref NMF_NO_MORE_MEMORY +#define CM_BAD_MEMORY_ALIGNMENT ((t_cm_error)-29) //!< Memory alignment wanted is not correct +#define CM_MEMORY_HANDLE_FREED ((t_cm_error)-28) //!< Handle was alread freed +#define CM_INVALID_DOMAIN_DEFINITION ((t_cm_error)-27) //!< Domain to be created is not correctly defined +#define CM_INTERNAL_DOMAIN_OVERFLOW ((t_cm_error)-26) //!< Internal domain descriptor overflow (too many domains) //TODO, juraj, remove this error +#define CM_INVALID_DOMAIN_HANDLE ((t_cm_error)-25) //!< Invalid domain handle +#define CM_ILLEGAL_DOMAIN_OPERATION ((t_cm_error)-21) //!< Operation on a domain is illegal (like destroy of a domain with referenced components) + + /* Media Processor related ones */ +#define CM_MPC_INVALID_CONFIGURATION ((t_cm_error)-24) //!< Media Processor Core invalid configuration +#define CM_MPC_NOT_INITIALIZED ((t_cm_error)-23) //!< Media Processor Core not yet initialized +#define CM_MPC_ALREADY_INITIALIZED ((t_cm_error)-22) //!< Media Processor Core already initialized +//ERROR 21 is defined above, with the domains + + /* Power Mgt related ones */ +#define CM_PWR_NOT_AVAILABLE ((t_cm_error)-16) //!< No modification of the state of the power input + + /* Common errors */ +#define CM_INVALID_DATA ((t_cm_error)-4) //!< Invalid internal data encountered +#define CM_OUT_OF_LIMITS ((t_cm_error)-3) //!< User reach an internal nmf limits of limits.h file +#define CM_INVALID_PARAMETER NMF_INVALID_PARAMETER //!< \ref NMF_INVALID_PARAMETER +#define CM_NOT_YET_IMPLEMENTED ((t_cm_error)-1) //!< CM API not yet implemented +#define CM_OK NMF_OK //!< \ref NMF_OK + +/** @} */ + +/*! + * \brief Definition of a physical memory address + * \ingroup MEMORY + */ +typedef t_uint32 t_cm_physical_address; + +/*! + * \brief Definition of a logical memory address + * \ingroup MEMORY + */ +typedef t_uint32 t_cm_logical_address; + +/*! + * \brief Definition of a system address into a system with MMU + * \ingroup MEMORY + */ +typedef struct { + t_cm_physical_address physical; //!< Physical memory address + t_cm_logical_address logical; //!< Logical memory address +} t_cm_system_address; +#define INVALID_SYSTEM_ADDRESS {(t_cm_physical_address)MASK_ALL32, (t_cm_logical_address)MASK_ALL32} + + +/*! + * \brief Define a type used to manipulate size of various buffers + * \ingroup MEMORY + */ +typedef t_uint32 t_cm_size; + +#endif /* _CM_TYPE_H_ */ + diff --git a/drivers/staging/nmf-cm/cm_debug.c b/drivers/staging/nmf-cm/cm_debug.c new file mode 100644 index 00000000000..a934aa33e92 --- /dev/null +++ b/drivers/staging/nmf-cm/cm_debug.c @@ -0,0 +1,836 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#include <linux/proc_fs.h> +#include <linux/sched.h> + +#include "osal-kernel.h" +#include "cm_debug.h" + +#ifdef CONFIG_DEBUG_FS + +static struct dentry *cm_dir; /* nmf-cm/ */ +static struct dentry *proc_dir; /* nmf-cm/proc/ */ +static struct dentry *core_dir; /* nmf-cm/dsp/ */ +static struct dentry *domain_dir; /* nmf-cm/domains/ */ + +/* components data managment */ +struct cm_debug_component_cooky { + struct dentry *comp_file; /* entry in nmf-cm/dsp/sxa/components/ */ + struct dentry *proc_link; /* entry in nmf-cm/proc/ */ +}; + +static ssize_t component_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) { + t_component_instance *component = file->f_dentry->d_inode->i_private; + char buf[640]; + int ret=0; + + OSAL_LOCK_API(); + if ((component != NULL) && (component->dbgCooky != NULL)) { + char nb_i[16] = ""; + int i; + + if (component->Template->classe == SINGLETON) + snprintf(nb_i, sizeof(nb_i), " (%d)", + component->Template->numberOfInstance); + + ret = snprintf(buf, sizeof(buf), + "Name:\t\t%s <%s>\n" + "Class:\t\t%s%s\n" + "State:\t\t%s\n" + "Priority:\t%u\n" + "Domain:\t\t%u\n\n" + "Memory : Physical address Logical address" + " DSP address Size\n" + "---------------------------------------------" + "-----------------------------\n", + component->pathname, + component->Template->name, + component->Template->classe == COMPONENT ? + "Component" : + (component->Template->classe == SINGLETON ? + "Singleton" : + (component->Template->classe == FIRMWARE ? + "Firmware" : + "?")), + nb_i, + component->state == STATE_RUNNABLE ? "Runnable" : + (component->state == STATE_STOPPED ? "Sopped" : + "None"), + (unsigned)component->priority, + component->domainId + ); + + for (i=0; i<NUMBER_OF_MMDSP_MEMORY && ret<sizeof(buf); i++) { + if (component->memories[i]) { + t_cm_system_address addr; + t_uint32 dspAddr, dspSize; + cm_DSP_GetHostSystemAddress( + component->memories[i], &addr); + cm_DSP_GetDspAddress( + component->memories[i], &dspAddr); + cm_DSP_GetDspMemoryHandleSize( + component->memories[i], &dspSize); + ret += snprintf( + &buf[ret], sizeof(buf)-ret, + "%-10s: %p-%p %p-%p %p-%p %8lu\n", + MMDSP_getMappingById(i)->memoryName, + (void *)addr.physical, + (void *)addr.physical + + component->memories[i]->size-1, + (void *)addr.logical, + (void *)addr.logical + + component->memories[i]->size-1, + (void *)dspAddr, + (void *)dspAddr + dspSize - 1, + component->memories[i]->size); + } + } + } + + OSAL_UNLOCK_API(); + return simple_read_from_buffer(userbuf, count, ppos, buf, ret); +} + +static const struct file_operations component_fops = { + .read = component_read, +}; + +static void cm_debug_component_create(t_component_instance *component) +{ + char tmp[12+MAX_COMPONENT_NAME_LENGTH]; + struct cm_debug_component_cooky *cooky; + struct mpcConfig *mpc; + mpc = &osalEnv.mpc[COREIDX(component->Template->dspId)]; + + cooky = OSAL_Alloc_Zero(sizeof(*cooky)); + if (cooky == NULL) + return; + + component->dbgCooky = cooky; + sprintf(tmp, "%s-%08x", component->pathname, + (unsigned int)component->instance); + cooky->comp_file = debugfs_create_file(tmp, S_IRUSR|S_IRGRP, + mpc->comp_dir, + component, &component_fops); + if (IS_ERR(cooky->comp_file)) { + if (PTR_ERR(cooky->comp_file) != -ENODEV) + pr_info("CM: Can't create dsp/%s/components/%s" + "debugfs file: %ld\n", + mpc->name, + tmp, + PTR_ERR(cooky->comp_file)); + cooky->comp_file = NULL; + } else { + char target_lnk[40+MAX_COMPONENT_NAME_LENGTH]; + sprintf(target_lnk, "../../../dsp/%s/components/%s-%08x", + mpc->name, + component->pathname, + (unsigned int)component->instance); + + /* Some firmware, like Executive Engine, do not belong + to any process */ + if (domainDesc[component->domainId].client == current->tgid) { + struct list_head* head; + struct cm_process_priv *entry = NULL; + /* Search the entry for the calling process */ + list_for_each(head, &process_list) { + entry = list_entry(head, + struct cm_process_priv, + entry); + if (entry->pid == current->tgid) + break; + } + + if (entry) { + cooky->proc_link = debugfs_create_symlink( + tmp, + entry->comp_dir, + target_lnk); + if (IS_ERR(cooky->proc_link)) { + long err = PTR_ERR(cooky->proc_link); + if (err != -ENODEV) + pr_info("CM: Can't create " + "proc/%d/%s " + "debugfs link: %ld\n", + entry->pid, tmp, err); + cooky->proc_link = NULL; + } + } + } + } +} + +static void cm_debug_component_destroy(t_component_instance *component) +{ + struct cm_debug_component_cooky *cooky = component->dbgCooky; + + if (cooky) { + component->dbgCooky = NULL; + debugfs_remove(cooky->proc_link); + debugfs_remove(cooky->comp_file); + OSAL_Free(cooky); + } +} + +/* domain data managment */ +struct cm_debug_domain_cooky { + struct dentry *domain_file; /* entry in nmf-cm/components/ */ + struct dentry *proc_link; /* entry in nmf-cm/proc/ */ + struct dentry *dsp_link; /* entry in nmf-cm/dsp/sxa/domains */ +}; + +static ssize_t domain_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + t_cm_domain_id id = + (t_cm_domain_id)(long)file->f_dentry->d_inode->i_private; + t_cm_domain_desc *domain = &domainDesc[id]; + + char buf[640]; + int ret=0; + + OSAL_LOCK_API(); + if ((domain->domain.coreId != MASK_ALL8) + && (domain->dbgCooky != NULL)) { + t_cm_allocator_status status; + t_uint32 dOffset; + t_uint32 dSize; + if (domain->domain.coreId != ARM_CORE_ID) { + t_cm_domain_info info; + + cm_DM_GetDomainAbsAdresses(id, &info); + cm_DSP_GetInternalMemoriesInfo(id, ESRAM_CODE, + &dOffset, &dSize); + cm_MM_GetAllocatorStatus( + cm_DSP_GetAllocator(domain->domain.coreId, + ESRAM_CODE), + dOffset, dSize, &status); + ret = snprintf( + buf, sizeof(buf), + "Core:\t%s\n\n" + "Memory : Physical address Logical address" + " Size Free Used\n" + "---------------------------------------------" + "-----------------------------\n" + "ESRAM Code: %08x-%08lx %08x-%08lx\t%8lu %8lu " + "%8lu\n", + osalEnv.mpc[COREIDX(domain->domain.coreId)].name, + (unsigned int)info.esramCode.physical, + domain->domain.esramCode.size ? + info.esramCode.physical + + domain->domain.esramCode.size - 1 : 0, + (unsigned int)info.esramCode.logical, + domain->domain.esramCode.size ? + info.esramCode.logical + + domain->domain.esramCode.size - 1 : 0, + domain->domain.esramCode.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + + cm_DSP_GetInternalMemoriesInfo(id, ESRAM_EXT24, + &dOffset, &dSize); + cm_MM_GetAllocatorStatus( + cm_DSP_GetAllocator(domain->domain.coreId, + ESRAM_EXT24), + dOffset, dSize, &status); + ret += snprintf( + &buf[ret], sizeof(buf)-ret, + "ESRAM Data: %08x-%08lx " + "%08x-%08lx\t%8lu %8lu %8lu\n", + (unsigned int)info.esramData.physical, + domain->domain.esramData.size ? + info.esramData.physical + + domain->domain.esramData.size - 1 : 0, + (unsigned int)info.esramData.logical, + domain->domain.esramData.size ? + info.esramData.logical + + domain->domain.esramData.size - 1 : 0, + domain->domain.esramData.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + + cm_DSP_GetInternalMemoriesInfo(id, SDRAM_CODE, + &dOffset, &dSize); + cm_MM_GetAllocatorStatus( + cm_DSP_GetAllocator(domain->domain.coreId, + SDRAM_CODE), + dOffset, dSize, &status); + ret += snprintf( + &buf[ret], sizeof(buf)-ret, + "SDRAM Code: %08x-%08lx " + "%08x-%08lx\t%8lu %8lu %8lu\n", + (unsigned int)info.sdramCode.physical, + domain->domain.sdramCode.size ? + info.sdramCode.physical + + domain->domain.sdramCode.size - 1 : 0, + (unsigned int)info.sdramCode.logical, + domain->domain.sdramCode.size ? + info.sdramCode.logical + + domain->domain.sdramCode.size - 1 : 0, + domain->domain.sdramCode.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + + cm_DSP_GetInternalMemoriesInfo(id, SDRAM_EXT24, + &dOffset, &dSize); + cm_MM_GetAllocatorStatus( + cm_DSP_GetAllocator(domain->domain.coreId, + SDRAM_EXT24), + dOffset, dSize, &status); + ret += snprintf( + &buf[ret], sizeof(buf)-ret, + "SDRAM Data: %08x-%08lx " + "%08x-%08lx\t%8lu %8lu %8lu\n", + (unsigned int)info.sdramData.physical, + domain->domain.sdramData.size ? + info.sdramData.physical + + domain->domain.sdramData.size - 1 : 0, + (unsigned int)info.sdramData.logical, + domain->domain.sdramData.size ? + info.sdramData.logical + + domain->domain.sdramData.size - 1 : 0, + domain->domain.sdramData.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + } else { + t_cm_system_address addr; + ret = snprintf( + buf, sizeof(buf), + "Core:\tarm\n\n" + "Memory : Physical address Logical " + "address Size Free Used\n" + "---------------------------------------" + "-----------------------------------\n"); + if (domain->domain.esramCode.size) { + cm_DSP_GetDspBaseAddress(ARM_CORE_ID, + ESRAM_CODE, &addr); + cm_DSP_GetInternalMemoriesInfo(id, ESRAM_CODE, + &dOffset, + &dSize); + cm_MM_GetAllocatorStatus( + cm_DSP_GetAllocator(ARM_CORE_ID, + ESRAM_CODE), + dOffset, dSize, &status); + ret += snprintf( + &buf[ret], sizeof(buf)-ret, + "ESRAM Code: %08x-%08lx " + "%08x-%08lx\t%8lu %8lu %8lu\n", + (unsigned int)addr.physical, + addr.physical + + domain->domain.esramCode.size - 1, + (unsigned int)addr.logical, + addr.logical + + domain->domain.esramCode.size - 1, + domain->domain.esramCode.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + } + if (domain->domain.esramData.size) { + cm_DSP_GetDspBaseAddress(ARM_CORE_ID, + ESRAM_EXT24, &addr); + cm_DSP_GetInternalMemoriesInfo(id, ESRAM_EXT24, + &dOffset, + &dSize); + cm_MM_GetAllocatorStatus( + cm_DSP_GetAllocator(ARM_CORE_ID, + ESRAM_EXT24), + dOffset, dSize, &status); + ret += snprintf( + &buf[ret], sizeof(buf)-ret, + "ESRAM Data: %08x-%08lx " + "%08x-%08lx\t%8lu %8lu %8lu\n", + (unsigned int)addr.physical, + addr.physical + + domain->domain.esramData.size - 1, + (unsigned int)addr.logical, + addr.logical + + domain->domain.esramData.size - 1, + domain->domain.esramData.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + } + if (domain->domain.sdramCode.size) { + cm_DSP_GetDspBaseAddress(ARM_CORE_ID, + SDRAM_CODE, &addr); + cm_DSP_GetInternalMemoriesInfo(id, SDRAM_CODE, + &dOffset, + &dSize); + cm_MM_GetAllocatorStatus( + cm_DSP_GetAllocator(ARM_CORE_ID, + SDRAM_CODE), + dOffset, dSize, &status); + ret += snprintf( + &buf[ret], sizeof(buf)-ret, + "SDRAM Code: %08x-%08lx %08x-%08lx\t" + "%8lu %8lu %8lu\n", + (unsigned int)addr.physical, + addr.physical + + domain->domain.sdramCode.size - 1, + (unsigned int)addr.logical, + addr.logical + + domain->domain.sdramCode.size - 1, + domain->domain.sdramCode.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + } + if (domain->domain.sdramData.size) { + cm_DSP_GetDspBaseAddress(ARM_CORE_ID, + SDRAM_EXT24, &addr); + cm_DSP_GetInternalMemoriesInfo(id, SDRAM_EXT24, + &dOffset, + &dSize); + cm_MM_GetAllocatorStatus( + cm_DSP_GetAllocator(ARM_CORE_ID, + SDRAM_EXT24), + dOffset, dSize, &status); + ret += snprintf( + &buf[ret], sizeof(buf)-ret, + "SDRAM Data: %08x-%08lx %08x-%08lx\t" + "%8lu %8lu %8lu\n", + (unsigned int)addr.physical, + addr.physical + + domain->domain.sdramData.size - 1, + (unsigned int)addr.logical, + addr.logical + + domain->domain.sdramData.size - 1, + domain->domain.sdramData.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + } + } + } + OSAL_UNLOCK_API(); + return simple_read_from_buffer(userbuf, count, ppos, buf, ret);; +} + +static const struct file_operations domain_fops = { + .read = domain_read, +}; + +static void cm_debug_domain_create(t_cm_domain_id id) +{ + char tmp[12]; + struct cm_debug_domain_cooky *cooky; + + cooky = OSAL_Alloc_Zero(sizeof(*cooky)); + if (cooky == NULL) + return; + + domainDesc[id].dbgCooky = cooky; + sprintf(tmp, "%u", id); + cooky->domain_file = debugfs_create_file(tmp, S_IRUSR|S_IRGRP, + domain_dir, + (void *)(long)id, + &domain_fops); + if (IS_ERR(cooky->domain_file)) { + if (PTR_ERR(cooky->domain_file) != -ENODEV) + pr_err("CM: Can't create domains/%s debugfs " + "file: %ld\n", tmp, + PTR_ERR(cooky->domain_file)); + cooky->domain_file = NULL; + } else { + char target_lnk[40]; + sprintf(target_lnk, "../../../domains/%u", id); + + if (domainDesc[id].client != NMF_CORE_CLIENT) { + struct list_head* head; + struct cm_process_priv *entry = NULL; + + /* Search the entry for the target process */ + list_for_each(head, &process_list) { + entry = list_entry(head, + struct cm_process_priv, + entry); + if (entry->pid == domainDesc[id].client) + break; + } + + if (entry) { + cooky->proc_link = debugfs_create_symlink( + tmp, + entry->domain_dir, + target_lnk); + if (IS_ERR(cooky->proc_link)) { + long err = PTR_ERR(cooky->proc_link); + if (err != -ENODEV) + pr_err("CM: Can't create " + "proc/%d/domains/%s " + "debugfs link: %ld\n", + entry->pid, tmp, err); + cooky->proc_link = NULL; + } + } + } + if (domainDesc[id].domain.coreId != ARM_CORE_ID) { + cooky->dsp_link = + debugfs_create_symlink( + tmp, + osalEnv.mpc[COREIDX(domainDesc[id].domain.coreId)].domain_dir, + target_lnk); + if (IS_ERR(cooky->dsp_link)) { + if (PTR_ERR(cooky->dsp_link) != -ENODEV) + pr_err("CM: Can't create dsp/%s/domains/%s " + "debugfs link: %ld\n", + osalEnv.mpc[COREIDX(domainDesc[id].domain.coreId)].name, + tmp, + PTR_ERR(cooky->dsp_link)); + cooky->dsp_link = NULL; + } + } + } +} + +static void cm_debug_domain_destroy(t_cm_domain_id id) +{ + struct cm_debug_domain_cooky *cooky = domainDesc[id].dbgCooky; + if (cooky) { + domainDesc[id].dbgCooky = NULL; + debugfs_remove(cooky->proc_link); + debugfs_remove(cooky->dsp_link); + debugfs_remove(cooky->domain_file); + OSAL_Free(cooky); + } +} + +/* proc directory */ +void cm_debug_proc_init(struct cm_process_priv *entry) +{ + char tmp[PROC_NUMBUF]; + sprintf(tmp, "%d", entry->pid); + entry->dir = debugfs_create_dir(tmp, proc_dir); + if (IS_ERR(entry->dir)) { + if (PTR_ERR(entry->dir) != -ENODEV) + pr_info("CM: Can't create proc/%d debugfs directory: " + "%ld\n", entry->pid, PTR_ERR(entry->dir)); + entry->dir = NULL; + return; + } + entry->comp_dir = debugfs_create_dir("components", entry->dir); + if (IS_ERR(entry->comp_dir)) { + if (PTR_ERR(entry->comp_dir) != -ENODEV) + pr_info("CM: Can't create proc/%d/components debugfs " + "directory: %ld\n", entry->pid, + PTR_ERR(entry->comp_dir)); + entry->comp_dir = NULL; + } + entry->domain_dir = debugfs_create_dir("domains", entry->dir); + if (IS_ERR(entry->domain_dir)) { + if (PTR_ERR(entry->domain_dir) != -ENODEV) + pr_info("CM: Can't create proc/%d/domains debugfs " + "directory: %ld\n", entry->pid, + PTR_ERR(entry->domain_dir)); + entry->domain_dir = NULL; + } +} + +/* DSP meminfo */ +static ssize_t meminfo_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + const t_nmf_core_id id = + *(t_nmf_core_id *)file->f_dentry->d_inode->i_private; + char buf[640]; + int ret=0; + t_cm_allocator_status status; + t_cm_system_address addr; + + OSAL_LOCK_API(); + cm_MM_GetAllocatorStatus(cm_DSP_GetAllocator(id, ESRAM_CODE), + 0, 0, &status); + cm_DSP_GetDspBaseAddress(id, ESRAM_CODE, &addr); + ret = snprintf(buf, sizeof(buf), + "Memory : Physical address Logical address Size " + " Free Used\n" + "-------------------------------------------------------" + "-------------------\n" + "ESRAM Code: %08x-%08lx %08x-%08lx\t%8lu %8lu %8lu\n", + (unsigned int)addr.physical, + addr.physical + status.global.size - 1, + (unsigned int)addr.logical, + addr.logical + status.global.size - 1, + status.global.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + + cm_MM_GetAllocatorStatus(cm_DSP_GetAllocator(id, ESRAM_EXT24), + 0, 0, &status); + cm_DSP_GetDspBaseAddress(id, ESRAM_EXT24, &addr); + ret += snprintf(&buf[ret], sizeof(buf)-ret, + "ESRAM Data: %08x-%08lx %08x-%08lx\t%8lu %8lu %8lu\n", + (unsigned int)addr.physical, + addr.physical + status.global.size - 1, + (unsigned int)addr.logical, + addr.logical + status.global.size - 1, + status.global.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + + cm_MM_GetAllocatorStatus(cm_DSP_GetAllocator(id, SDRAM_CODE), + 0, 0, &status); + cm_DSP_GetDspBaseAddress(id, SDRAM_CODE, &addr); + ret += snprintf(&buf[ret], sizeof(buf)-ret, + "SDRAM Code: %08x-%08lx %08x-%08lx\t%8lu %8lu %8lu\n", + (unsigned int)addr.physical, + addr.physical + status.global.size - 1, + (unsigned int)addr.logical, + addr.logical + status.global.size - 1, + status.global.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + + cm_MM_GetAllocatorStatus(cm_DSP_GetAllocator(id, SDRAM_EXT24), + 0, 0, &status); + cm_DSP_GetDspBaseAddress(id, SDRAM_EXT24, &addr); + ret += snprintf(&buf[ret], sizeof(buf)-ret, + "SDRAM Data: %08x-%08lx %08x-%08lx\t%8lu %8lu %8lu\n", + (unsigned int)addr.physical, + addr.physical + status.global.size - 1, + (unsigned int)addr.logical, + addr.logical + status.global.size - 1, + status.global.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + + cm_MM_GetAllocatorStatus(cm_DSP_GetAllocator(id, INTERNAL_XRAM24), + 0, 0, &status); + cm_DSP_GetDspBaseAddress(id, INTERNAL_XRAM24, &addr); + ret += snprintf(&buf[ret], sizeof(buf)-ret, + "TCM XRAM : %08x-%08lx %08x-%08lx\t%8lu %8lu %8lu\n", + (unsigned int)addr.physical, + addr.physical + status.global.size - 1, + (unsigned int)addr.logical, + addr.logical + status.global.size - 1, + status.global.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + + cm_MM_GetAllocatorStatus(cm_DSP_GetAllocator(id, INTERNAL_YRAM24), + 0, 0, &status); + cm_DSP_GetDspBaseAddress(id, INTERNAL_YRAM24, &addr); + ret += snprintf(&buf[ret], sizeof(buf)-ret, + "TCM YRAM : %08x-%08lx %08x-%08lx\t%8lu %8lu %8lu\n", + (unsigned int)addr.physical, + addr.physical + status.global.size - 1, + (unsigned int)addr.logical, + addr.logical + status.global.size - 1, + status.global.size, + status.global.accumulate_free_memory, + status.global.accumulate_used_memory); + + OSAL_UNLOCK_API(); + return simple_read_from_buffer(userbuf, count, ppos, buf, ret);; +} + +static const struct file_operations mem_fops = { + .read = meminfo_read, +}; + +/* ESRAM file operations */ +static int esram_open(struct inode *inode, struct file *file) +{ + int i, err=0; + for (i=0; i<NB_ESRAM; i++) { + if (regulator_enable(osalEnv.esram_regulator[i]) < 0) { + pr_err("CM (%s): can't enable regulator" + "for esram bank %s\n", __func__, + i ? "34" : "12"); + err = -EIO; + break; + } + } + + if (err) { + for (i--; i>=0; i--) + regulator_disable(osalEnv.esram_regulator[i]); + } + + return err; +} + +static int esram_release(struct inode *inode, struct file *file) +{ + int i; + for (i=0; i<NB_ESRAM; i++) + regulator_disable(osalEnv.esram_regulator[i]); + return 0; +} + +static ssize_t esram_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return simple_read_from_buffer(user_buf, count, ppos, + osalEnv.esram_base, + cfgESRAMSize*ONE_KB); +} + +static const struct file_operations esram_fops = { + .read = esram_read, + .open = esram_open, + .release = esram_release, +}; + + +/* TCM file */ +void cm_debug_create_tcm_file(unsigned mpc_index) +{ + osalEnv.mpc[mpc_index].tcm_file = debugfs_create_blob( + "tcm24", S_IRUSR|S_IRGRP, + osalEnv.mpc[mpc_index].snapshot_dir, + &osalEnv.mpc[mpc_index].base); + if (IS_ERR(osalEnv.mpc[mpc_index].tcm_file)) { + if (PTR_ERR(osalEnv.mpc[mpc_index].tcm_file) != -ENODEV) + pr_info("CM: Can't create dsp/%s/tcm24 debugfs " + "directory: %ld\n", osalEnv.mpc[mpc_index].name, + PTR_ERR(osalEnv.mpc[mpc_index].tcm_file)); + osalEnv.mpc[mpc_index].tcm_file = NULL; + } +} + +void cm_debug_destroy_tcm_file(unsigned mpc_index) +{ + debugfs_remove(osalEnv.mpc[mpc_index].tcm_file); +} + +/* Global init */ +void cm_debug_init(void) +{ + int i; + + cm_dir = debugfs_create_dir(DEBUGFS_ROOT, NULL); + if (IS_ERR(cm_dir)) { + if (PTR_ERR(cm_dir) != -ENODEV) + pr_info("CM: Can't create root debugfs directory: " + "%ld\n", PTR_ERR(cm_dir)); + cm_dir = NULL; + return; + } + + proc_dir = debugfs_create_dir("proc", cm_dir); + if (IS_ERR(proc_dir)) { + if (PTR_ERR(proc_dir) != -ENODEV) + pr_info("CM: Can't create 'proc' debugfs directory: " + "%ld\n", PTR_ERR(proc_dir)); + proc_dir = NULL; + } + + core_dir = debugfs_create_dir("dsp", cm_dir); + if (IS_ERR(core_dir)) { + if (PTR_ERR(core_dir) != -ENODEV) + pr_info("CM: Can't create 'dsp' debugfs directory: %ld\n", + PTR_ERR(core_dir)); + core_dir = NULL; + } + + domain_dir = debugfs_create_dir("domains", cm_dir); + if (IS_ERR(domain_dir)) { + if (PTR_ERR(domain_dir) != -ENODEV) + pr_info("CM: Can't create 'domains' debugfs directory: " + "%ld\n", + PTR_ERR(domain_dir)); + domain_dir = NULL; + } else { + osal_debug_ops.domain_create = cm_debug_domain_create; + osal_debug_ops.domain_destroy = cm_debug_domain_destroy; + } + + for (i=0; i<NB_MPC; i++) { + osalEnv.mpc[i].dir = debugfs_create_dir(osalEnv.mpc[i].name, + core_dir); + if (IS_ERR(osalEnv.mpc[i].dir)) { + if (PTR_ERR(osalEnv.mpc[i].dir) != -ENODEV) + pr_info("CM: Can't create %s debugfs directory: " + "%ld\n", + osalEnv.mpc[i].name, + PTR_ERR(osalEnv.mpc[i].dir)); + osalEnv.mpc[i].dir = NULL; + } else { + osalEnv.mpc[i].mem_file = + debugfs_create_file("meminfo", S_IRUSR|S_IRGRP, + osalEnv.mpc[i].dir, + (void*)&osalEnv.mpc[i].coreId, + &mem_fops); + if (IS_ERR(osalEnv.mpc[i].mem_file)) { + if (PTR_ERR(osalEnv.mpc[i].mem_file) != -ENODEV) + pr_err("CM: Can't create dsp/%s/meminfo " + "debugfs file: %ld\n", + osalEnv.mpc[i].name, + PTR_ERR(osalEnv.mpc[i].mem_file)); + osalEnv.mpc[i].mem_file = NULL; + } + + osalEnv.mpc[i].comp_dir = debugfs_create_dir( + "components", + osalEnv.mpc[i].dir); + if (IS_ERR(osalEnv.mpc[i].comp_dir)) { + if (PTR_ERR(osalEnv.mpc[i].comp_dir) != -ENODEV) + pr_info("CM: Can't create " + "'dsp/%s/components' debugfs " + "directory: %ld\n", + osalEnv.mpc[i].name, + PTR_ERR(osalEnv.mpc[i].comp_dir)); + osalEnv.mpc[i].comp_dir = NULL; + } + + osalEnv.mpc[i].domain_dir = + debugfs_create_dir("domains", + osalEnv.mpc[i].dir); + if (IS_ERR(osalEnv.mpc[i].domain_dir)) { + if (PTR_ERR(osalEnv.mpc[i].domain_dir) != -ENODEV) + pr_info("CM: Can't create " + "'dsp/%s/domains' " + "debugfs directory: %ld\n", + osalEnv.mpc[i].name, + PTR_ERR(osalEnv.mpc[i].domain_dir)); + osalEnv.mpc[i].domain_dir = NULL; + } + + osalEnv.mpc[i].snapshot_dir = debugfs_create_dir( + "snapshot", + osalEnv.mpc[i].dir); + if (IS_ERR(osalEnv.mpc[i].snapshot_dir)) { + if (PTR_ERR(osalEnv.mpc[i].snapshot_dir) != -ENODEV) + pr_info("CM: Can't create " + "'dsp/%s/snapshot' debugfs " + "directory: %ld\n", + osalEnv.mpc[i].name, + PTR_ERR(osalEnv.mpc[i].snapshot_dir)); + osalEnv.mpc[i].snapshot_dir = NULL; + } else { + debugfs_create_file("esram", S_IRUSR|S_IRGRP, + osalEnv.mpc[i].snapshot_dir, + &osalEnv.esram_base, + &esram_fops); + debugfs_create_blob("sdram_data", S_IRUSR|S_IRGRP, + osalEnv.mpc[i].snapshot_dir, + &osalEnv.mpc[i].sdram_data); + debugfs_create_blob("sdram_code", S_IRUSR|S_IRGRP, + osalEnv.mpc[i].snapshot_dir, + &osalEnv.mpc[i].sdram_code); + } + + debugfs_create_bool("running", S_IRUSR|S_IRGRP, + osalEnv.mpc[i].dir, + (u32 *)&osalEnv.mpc[i].monitor_tsk); + debugfs_create_u8("load", S_IRUSR|S_IRGRP, + osalEnv.mpc[i].dir, + &osalEnv.mpc[i].load); + debugfs_create_u8("requested_opp", S_IRUSR|S_IRGRP, + osalEnv.mpc[i].dir, + &osalEnv.mpc[i].opp_request); + } + } + osal_debug_ops.component_create = cm_debug_component_create; + osal_debug_ops.component_destroy = cm_debug_component_destroy; +} + +void cm_debug_exit(void) +{ + debugfs_remove_recursive(cm_dir); +} + +#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/staging/nmf-cm/cm_debug.h b/drivers/staging/nmf-cm/cm_debug.h new file mode 100644 index 00000000000..26c80682d11 --- /dev/null +++ b/drivers/staging/nmf-cm/cm_debug.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#ifndef CM_DEBUG_H +#define CM_DEBUG_H + +#ifdef CONFIG_DEBUG_FS +#include "cmld.h" + +void cm_debug_init(void); +void cm_debug_exit(void); +void cm_debug_proc_init(struct cm_process_priv *entry); +void cm_debug_create_tcm_file(unsigned mpc_index); +void cm_debug_destroy_tcm_file(unsigned mpc_index); + +#else + +#define cm_debug_init() +#define cm_debug_exit() +#define cm_debug_proc_init(entry) +#define cm_debug_create_tcm_file(mpc_index) +#define cm_debug_destroy_tcm_file(mpc_index) + +#endif /* CONFIG_DEBUG_FS */ +#endif /* CM_DEBUG_H */ diff --git a/drivers/staging/nmf-cm/cm_dma.c b/drivers/staging/nmf-cm/cm_dma.c new file mode 100644 index 00000000000..d1c99d6af9a --- /dev/null +++ b/drivers/staging/nmf-cm/cm_dma.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * License terms: GNU General Public License (GPL), version 2. + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <asm/io.h> +#include <mach/db8500-regs.h> + +#include "cm_dma.h" + +#define CMDMA_LIDX (2) +#define CMDMA_REG_LCLA (0x024) + +static void __iomem *virtbase = NULL; + +static int cmdma_write_cyclic_list_mem2per( + unsigned int from_addr, + unsigned int to_addr, + unsigned int segments, + unsigned int segmentsize, + unsigned int LOS); + +static int cmdma_write_cyclic_list_per2mem( + unsigned int from_addr, + unsigned int to_addr, + unsigned int segments, + unsigned int segmentsize, + unsigned int LOS); + +static bool cmdma_setup_relink_area_called = false; + +int cmdma_setup_relink_area( unsigned int mem_addr, + unsigned int per_addr, + unsigned int segments, + unsigned int segmentsize, + unsigned int LOS, + enum cmdma_type type) +{ + if (!cmdma_setup_relink_area_called) + cmdma_setup_relink_area_called = true; + + switch (type) { + + case CMDMA_MEM_2_PER: + return cmdma_write_cyclic_list_mem2per( + mem_addr, + per_addr, + segments, + segmentsize, + LOS); + + case CMDMA_PER_2_MEM: + return cmdma_write_cyclic_list_per2mem( + per_addr, + mem_addr, + segments, + segmentsize, + LOS); + + default : + return -EINVAL; + } + } + + static unsigned int cmdma_getlcla( void) { + + if(!virtbase) + virtbase = ioremap(U8500_DMA_BASE, CMDMA_REG_LCLA + sizeof(int) ); + + return readl(virtbase + CMDMA_REG_LCLA); + } + + static void cmdma_write_relink_params_mem2per ( + int * relink, + unsigned int LOS, + unsigned int nb_element, + unsigned int src_addr, + unsigned int dst_addr, + unsigned int burst_size) { + + relink[0] = (((long)(nb_element & 0xFFFF)) << 16) | + (src_addr & 0xFFFF); + + relink[1] = (((src_addr >> 16) & 0xFFFFUL) << 16) | + (0x1200UL | (LOS << 1) | (burst_size<<10)); + + relink[2] = ((nb_element & 0xFFFF) << 16) | + (dst_addr & 0xFFFF); + + relink[3] = (((dst_addr >> 16) & 0xFFFFUL) << 16 ) | + 0x8201UL | ((LOS+1) << 1) | (burst_size<<10); + + (void) dma_map_single(NULL, relink, 16, DMA_TO_DEVICE); +} + +static void cmdma_write_relink_params_per2mem ( + int * relink, + unsigned int LOS, + unsigned int nb_element, + unsigned int src_addr, + unsigned int dst_addr, + unsigned int burst_size) { + + relink[0] = (((long)(nb_element & 0xFFFF)) << 16) | + (src_addr & 0xFFFF); + + relink[1] = (((src_addr >> 16) & 0xFFFFUL) << 16) | + (0x8201UL | (LOS << 1) | (burst_size<<10)); + + relink[2] = ((nb_element & 0xFFFF) << 16) | + (dst_addr & 0xFFFF); + + relink[3] = (((dst_addr >> 16) & 0xFFFFUL) << 16 ) | + 0x1200UL | ((LOS+1) << 1) | (burst_size<<10); + + (void) dma_map_single(NULL, relink, 16, DMA_TO_DEVICE); +} + +static int cmdma_write_cyclic_list_mem2per( + unsigned int from_addr, + unsigned int to_addr, + unsigned int segments, + unsigned int segmentsize, + unsigned int LOS) { + + unsigned int i,j; + int *relink; + + j = LOS; + + for ( i = 0; i < segments; i++) { + relink = phys_to_virt (cmdma_getlcla() + 1024 * CMDMA_LIDX + 8 * j); + + if (i == (segments-1)) + j = LOS; + else + j += 2; + + cmdma_write_relink_params_mem2per ( + relink, + j, + segmentsize / 4, + from_addr, + to_addr, + 0x2); + + from_addr += segmentsize; + } + + return 0; +} + +static int cmdma_write_cyclic_list_per2mem( + unsigned int from_addr, + unsigned int to_addr, + unsigned int segments, + unsigned int segmentsize, + unsigned int LOS) { + + unsigned int i,j; + int *relink; + j = LOS; + + for ( i = 0; i < segments; i++) { + relink = phys_to_virt (cmdma_getlcla() + 1024 * CMDMA_LIDX + 8 * j); + + if (i == (segments-1)) + j = LOS; + else + j += 2; + + cmdma_write_relink_params_per2mem ( + relink, + j, + segmentsize / 4, + from_addr, + to_addr, + 0x2); + + to_addr += segmentsize; + } + + return 0; +} + +static void __iomem *dmabase = 0; +int cmdma_init(void) +{ + dmabase = ioremap_nocache(U8500_DMA_BASE, PAGE_SIZE); + if (dmabase == NULL) + return -ENOMEM; + else + return 0; +} + +void cmdma_destroy(void) +{ + iounmap(dmabase); +} + +#define SSLNK_CHAN_2 (0x40C + 0x20 * 2) +#define SDLNK_CHAN_2 (0x41C + 0x20 * 2) + +void cmdma_stop_dma(void) +{ + if(cmdma_setup_relink_area_called) { + cmdma_setup_relink_area_called = false; + if (readl(dmabase + SSLNK_CHAN_2) & (0x3 << 28)) { + printk(KERN_ERR "CM: ERROR - RX DMA was running\n"); + } + if (readl(dmabase + SDLNK_CHAN_2) & (0x3 << 28)) { + printk(KERN_ERR "CM: ERROR - TX DMA was running\n"); + } + + writel(~(1 << 28), dmabase + SSLNK_CHAN_2); + while (readl(dmabase + SSLNK_CHAN_2) & (0x3 << 28)); + + writel(~(1 << 28), dmabase + SDLNK_CHAN_2); + while (readl(dmabase + SDLNK_CHAN_2) & (0x3 << 28)); + } +} diff --git a/drivers/staging/nmf-cm/cm_dma.h b/drivers/staging/nmf-cm/cm_dma.h new file mode 100644 index 00000000000..4fccef03830 --- /dev/null +++ b/drivers/staging/nmf-cm/cm_dma.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +#ifndef __CMDMA_H +#define __CMDMA_H +#include "cmioctl.h" + +int cmdma_setup_relink_area( + unsigned int mem_addr, + unsigned int per_addr, + unsigned int segments, + unsigned int segmentsize, + unsigned int LOS, + enum cmdma_type type); + +void cmdma_stop_dma(void); +int cmdma_init(void); +void cmdma_destroy(void); +#endif diff --git a/drivers/staging/nmf-cm/cm_service.c b/drivers/staging/nmf-cm/cm_service.c new file mode 100644 index 00000000000..70cf6f37524 --- /dev/null +++ b/drivers/staging/nmf-cm/cm_service.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +/** \file cm_service.c + * + * Nomadik Multiprocessing Framework Linux Driver + * + */ + +#include <linux/module.h> +#include <linux/plist.h> +#include <linux/slab.h> +#include <linux/spinlock_types.h> +#include <linux/sched.h> + +#include <cm/engine/api/control/irq_engine.h> + +#include "osal-kernel.h" +#include "cmld.h" +#include "cm_service.h" +#include "cm_dma.h" + +/* Panic managment */ +static void service_tasklet_func(unsigned long); +unsigned long service_tasklet_data = 0; +DECLARE_TASKLET(cmld_service_tasklet, service_tasklet_func, 0); + +void dispatch_service_msg(struct osal_msg *msg) +{ + struct list_head *head, *next; +#ifdef CONFIG_DEBUG_FS + bool dump_flag_to_set = true; +#endif + /* + * Note: no lock needed to protect the channel_list against list + * changes, as the current tasklet is disabled each time we modify + * the list + */ + list_for_each_safe(head, next, &channel_list) { + struct cm_channel_priv *channelPriv = list_entry(head, struct cm_channel_priv, entry); + struct osal_msg *new_msg; + size_t msg_size; + + if (channelPriv->state == CHANNEL_CLOSED) + continue; + msg_size = sizeof(new_msg->hdr) + sizeof(new_msg->d.srv); + new_msg = kmalloc(msg_size, GFP_ATOMIC); + if (new_msg == NULL) { + pr_err("[CM] %s: can't allocate memory, service" + " message not dispatched !!\n", __func__); + continue; + } + memcpy(new_msg, msg, msg_size); + plist_node_init(&new_msg->msg_entry, 0); +#ifdef CONFIG_DEBUG_FS + if (cmld_user_has_debugfs && dump_flag_to_set + && (new_msg->d.srv.srvType == NMF_SERVICE_PANIC)) { + /* + * The reciever of this message will do the DSP + * memory dump + */ + new_msg->d.srv.srvData.panic.panicSource + |= DEBUGFS_DUMP_FLAG; + dump_flag_to_set = false; + cmld_dump_ongoing = true; + } +#endif + spin_lock_bh(&channelPriv->bh_lock); + plist_add(&new_msg->msg_entry, &channelPriv->messageQueue); + spin_unlock_bh(&channelPriv->bh_lock); + wake_up_interruptible(&channelPriv->waitq); + } +} + +static void service_tasklet_func(unsigned long unused) +{ + t_cm_service_type type; + t_cm_service_description desc; + int i=0; + + do { + if (test_and_clear_bit(i, &service_tasklet_data)) { + CM_getServiceDescription(osalEnv.mpc[i].coreId, &type, &desc); + + switch (type) { + case CM_MPC_SERVICE_PANIC: { + struct osal_msg msg; + + msg.msg_type = MSG_SERVICE; + msg.d.srv.srvType = NMF_SERVICE_PANIC; + msg.d.srv.srvData.panic = desc.u.panic; + + dispatch_service_msg(&msg); + /* + * Stop DMA directly before shutdown, to avoid + * bad sound. Should be called after DSP has + * stopped executing, to avoid the DSP + * re-starting DMA + */ + if (osalEnv.mpc[i].coreId == SIA_CORE_ID) + cmdma_stop_dma(); + break; + } + case CM_MPC_SERVICE_PRINT: { + char msg[256]; + if (CM_ReadMPCString(osalEnv.mpc[i].coreId, + desc.u.print.dspAddress, msg, + sizeof(msg)) == CM_OK) + printk(msg, desc.u.print.value1, + desc.u.print.value2); + break; + } + default: + pr_err("[CM] %s: MPC Service Type %d not supported\n", __func__, type); + } + enable_irq(osalEnv.mpc[i].interrupt1); + } + i = (i+1) % NB_MPC; + } while (service_tasklet_data != 0); +} diff --git a/drivers/staging/nmf-cm/cm_service.h b/drivers/staging/nmf-cm/cm_service.h new file mode 100644 index 00000000000..39582eae573 --- /dev/null +++ b/drivers/staging/nmf-cm/cm_service.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +/** \file cm_service.c + * + * Nomadik Multiprocessing Framework Linux Driver + * + */ + +#ifndef CM_SERVICE_H +#define CM_SERVICE_H + +#include <linux/interrupt.h> + +extern unsigned long service_tasklet_data; +extern struct tasklet_struct cmld_service_tasklet; +void dispatch_service_msg(struct osal_msg *msg); + +#endif diff --git a/drivers/staging/nmf-cm/cm_syscall.c b/drivers/staging/nmf-cm/cm_syscall.c new file mode 100644 index 00000000000..a687e0206a3 --- /dev/null +++ b/drivers/staging/nmf-cm/cm_syscall.c @@ -0,0 +1,1413 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> +#include <cm/engine/api/cm_engine.h> +#include <linux/sched.h> +#include "cmioctl.h" +#include "osal-kernel.h" +#include "cmld.h" +#include "cm_dma.h" + +/** Dequeue and free per-process messages for specific binding + * + * \note + * This is only safe if the per process mutex is held when called. + */ +static inline void freeMessages(struct cm_channel_priv* cPriv, t_skelwrapper* binding) +{ + struct osal_msg *this, *next; + + spin_lock_bh(&cPriv->bh_lock); + + /* free any pending messages */ + plist_for_each_entry_safe(this, next, &cPriv->messageQueue, msg_entry) { + if (this->msg_type == MSG_INTERFACE + && this->d.itf.skelwrap == binding) { + plist_del(&this->msg_entry, &cPriv->messageQueue); + kfree(this); + } + } + spin_unlock_bh(&cPriv->bh_lock); +} + +static t_cm_error copy_string_from_user(char *dst, const char __user *src, int len) +{ + int ret; + + ret = strncpy_from_user(dst, src, len); + if (ret < 0) /* -EFAULT */ + return CM_INVALID_PARAMETER; + + if (ret >= len) + return CM_OUT_OF_LIMITS; + + return 0; +} + +inline int cmld_InstantiateComponent(struct cm_process_priv* procPriv, + CM_InstantiateComponent_t __user *param) +{ + CM_InstantiateComponent_t data; + char templateName[MAX_TEMPLATE_NAME_LENGTH]; + char localName[MAX_COMPONENT_NAME_LENGTH]; + char *dataFile = NULL; + + /* Copy all user data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if (data.in.dataFile != NULL) { + dataFile = vmalloc(data.in.dataFileSize); + if (dataFile == NULL) { + data.out.error = CM_NO_MORE_MEMORY; + goto out; + } + /* coverity[tainted_data : FALSE] */ + if (copy_from_user(dataFile, data.in.dataFile, data.in.dataFileSize)) { + data.out.error = CM_INVALID_PARAMETER; + goto out; + } + } + + if ((data.out.error = copy_string_from_user(templateName, + data.in.templateName, + sizeof(templateName)))) + goto out; + + if ((data.in.localName != NULL) && + (data.out.error = copy_string_from_user(localName, + data.in.localName, + sizeof(localName)))) + goto out; + + /* Do appropriate CM Engine call */ + data.out.error = CM_ENGINE_InstantiateComponent(templateName, + data.in.domainId, + procPriv->pid, + data.in.priority, + data.in.localName ? localName : NULL, + dataFile, + &data.out.component); + +out: + if (dataFile) + vfree(dataFile); + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_BindComponentFromCMCore(struct cm_process_priv* procPriv, + CM_BindComponentFromCMCore_t __user *param) +{ + CM_BindComponentFromCMCore_t data; + char providedItfServerName[MAX_INTERFACE_NAME_LENGTH]; + char *dataFileSkeleton = NULL; + + /* Copy all user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(providedItfServerName, + data.in.providedItfServerName, + sizeof(providedItfServerName)))) + goto out; + + if (data.in.dataFileSkeleton != NULL) { + dataFileSkeleton = OSAL_Alloc(data.in.dataFileSkeletonSize); + if (dataFileSkeleton == NULL) { + data.out.error = CM_NO_MORE_MEMORY; + goto out; + } + /* coverity[tainted_data : FALSE] */ + if (copy_from_user(dataFileSkeleton, data.in.dataFileSkeleton, + data.in.dataFileSkeletonSize)) { + data.out.error = CM_INVALID_PARAMETER; + goto out; + } + } + + data.out.error = CM_ENGINE_BindComponentFromCMCore(data.in.server, + providedItfServerName, + data.in.fifosize, + data.in.eventMemType, + &data.out.host2mpcId, + procPriv->pid, + dataFileSkeleton); +out: + if (dataFileSkeleton) + OSAL_Free(dataFileSkeleton); + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + + return 0; +} + +inline int cmld_UnbindComponentFromCMCore(CM_UnbindComponentFromCMCore_t __user *param) +{ + CM_UnbindComponentFromCMCore_t data; + + /* Copy all user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_UnbindComponentFromCMCore(data.in.host2mpcId); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_BindComponentToCMCore(struct cm_channel_priv* channelPriv, + CM_BindComponentToCMCore_t __user *param) +{ + CM_BindComponentToCMCore_t data; + t_skelwrapper *skelwrapper; + struct cm_process_priv *procPriv = channelPriv->proc; + char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH]; + char *dataFileStub = NULL; + + /* Copy all user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(requiredItfClientName, + data.in.requiredItfClientName, + sizeof(requiredItfClientName)))) + goto out; + + /* Do appropriate CM Engine call */ + skelwrapper = (t_skelwrapper *)OSAL_Alloc(sizeof(*skelwrapper)); + if (skelwrapper == NULL) { + data.out.error = CM_NO_MORE_MEMORY; + goto out; + } + + if (data.in.dataFileStub != NULL) { + dataFileStub = OSAL_Alloc(data.in.dataFileStubSize); + if (dataFileStub == NULL) { + data.out.error = CM_NO_MORE_MEMORY; + goto out; + } + /* coverity[tainted_data : FALSE] */ + if (copy_from_user(dataFileStub, data.in.dataFileStub, data.in.dataFileStubSize)) { + data.out.error = CM_INVALID_PARAMETER; + goto out; + } + } + + if ((data.out.error = CM_ENGINE_BindComponentToCMCore( + data.in.client, + requiredItfClientName, + data.in.fifosize, + (t_nmf_mpc2host_handle)skelwrapper, + dataFileStub, + &data.out.mpc2hostId, + procPriv->pid)) != CM_OK) { + OSAL_Free(skelwrapper); + goto out; + } + + skelwrapper->upperLayerThis = data.in.upLayerThis; + skelwrapper->mpc2hostId = data.out.mpc2hostId; + skelwrapper->channelPriv = channelPriv; + mutex_lock(&channelPriv->skelListLock); + list_add(&skelwrapper->entry, &channelPriv->skelList); + mutex_unlock(&channelPriv->skelListLock); +out: + if (dataFileStub != NULL) + OSAL_Free(dataFileStub); + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_UnbindComponentToCMCore(struct cm_process_priv* procPriv, + CM_UnbindComponentToCMCore_t __user *param) +{ + CM_UnbindComponentToCMCore_t data; + t_skelwrapper *skelwrapper; + char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH]; + + /* Copy all user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(requiredItfClientName, + data.in.requiredItfClientName, + sizeof(requiredItfClientName)))) + goto out; + + data.out.error = CM_ENGINE_UnbindComponentToCMCore( + data.in.client, requiredItfClientName, + (t_nmf_mpc2host_handle*)&skelwrapper, + procPriv->pid); + + if (data.out.error != CM_OK && data.out.error != CM_MPC_NOT_RESPONDING) + goto out; + + data.out.upLayerThis = skelwrapper->upperLayerThis; + + mutex_lock(&skelwrapper->channelPriv->msgQueueLock); + freeMessages(skelwrapper->channelPriv, skelwrapper); + mutex_lock(&skelwrapper->channelPriv->skelListLock); + list_del(&skelwrapper->entry); + mutex_unlock(&skelwrapper->channelPriv->skelListLock); + mutex_unlock(&skelwrapper->channelPriv->msgQueueLock); + OSAL_Free(skelwrapper); +out: + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_BindComponentAsynchronous(struct cm_process_priv* procPriv, + CM_BindComponentAsynchronous_t __user *param) +{ + CM_BindComponentAsynchronous_t data; + char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH]; + char providedItfServerName[MAX_INTERFACE_NAME_LENGTH]; + char *dataFileSkeletonOrEvent = NULL; + char *dataFileStub = NULL; + + /* Copy all user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(requiredItfClientName, + data.in.requiredItfClientName, + sizeof(requiredItfClientName)))) + goto out; + + if ((data.out.error = copy_string_from_user(providedItfServerName, + data.in.providedItfServerName, + sizeof(providedItfServerName)))) + goto out; + + if (data.in.dataFileSkeletonOrEvent != NULL) { + dataFileSkeletonOrEvent = + OSAL_Alloc(data.in.dataFileSkeletonOrEventSize); + if (dataFileSkeletonOrEvent == NULL) { + data.out.error = CM_NO_MORE_MEMORY; + goto out; + } + /* coverity[tainted_data : FALSE] */ + if (copy_from_user(dataFileSkeletonOrEvent, data.in.dataFileSkeletonOrEvent, data.in.dataFileSkeletonOrEventSize)) { + data.out.error = CM_INVALID_PARAMETER; + goto out; + } + } + + if (data.in.dataFileStub != NULL) { + dataFileStub = OSAL_Alloc(data.in.dataFileStubSize); + if (dataFileStub == NULL) { + data.out.error = CM_NO_MORE_MEMORY; + goto out; + } + /* coverity[tainted_data : FALSE] */ + if (copy_from_user(dataFileStub, data.in.dataFileStub, data.in.dataFileStubSize)) { + data.out.error = CM_INVALID_PARAMETER; + goto out; + } + } + + /* Do appropriate CM Engine call */ + data.out.error = CM_ENGINE_BindComponentAsynchronous(data.in.client, + requiredItfClientName, + data.in.server, + providedItfServerName, + data.in.fifosize, + data.in.eventMemType, + procPriv->pid, + dataFileSkeletonOrEvent, + dataFileStub); + +out: + if (dataFileSkeletonOrEvent != NULL) + OSAL_Free(dataFileSkeletonOrEvent); + if (dataFileStub != NULL) + OSAL_Free(dataFileStub); + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_UnbindComponentAsynchronous(struct cm_process_priv* procPriv, + CM_UnbindComponentAsynchronous_t __user *param) +{ + CM_UnbindComponentAsynchronous_t data; + char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH]; + + /* Copy all user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(requiredItfClientName, + data.in.requiredItfClientName, + sizeof(requiredItfClientName)))) + goto out; + + /* Do appropriate CM Engine call */ + /* coverity[tainted_data : FALSE] */ + data.out.error = CM_ENGINE_UnbindComponentAsynchronous(data.in.client, + requiredItfClientName, + procPriv->pid); +out: + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_BindComponent(struct cm_process_priv* procPriv, + CM_BindComponent_t __user *param) +{ + CM_BindComponent_t data; + char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH]; + char providedItfServerName[MAX_INTERFACE_NAME_LENGTH]; + char *dataFileTrace = NULL; + + /* Copy all user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(requiredItfClientName, + data.in.requiredItfClientName, + sizeof(requiredItfClientName)))) + goto out; + + if ((data.out.error = copy_string_from_user(providedItfServerName, + data.in.providedItfServerName, + sizeof(providedItfServerName)))) + goto out; + + if (data.in.dataFileTrace != NULL) { + dataFileTrace = OSAL_Alloc(data.in.dataFileTraceSize); + if (dataFileTrace == NULL) { + data.out.error = CM_NO_MORE_MEMORY; + goto out; + } + /* coverity[tainted_data : FALSE] */ + if (copy_from_user(dataFileTrace, data.in.dataFileTrace, + data.in.dataFileTraceSize)) { + data.out.error = CM_INVALID_PARAMETER; + goto out; + } + } + + /* Do appropriate CM Engine call */ + data.out.error = CM_ENGINE_BindComponent(data.in.client, + requiredItfClientName, + data.in.server, + providedItfServerName, + data.in.traced, + procPriv->pid, + dataFileTrace); +out: + if (dataFileTrace != NULL) + OSAL_Free(dataFileTrace); + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_UnbindComponent(struct cm_process_priv* procPriv, + CM_UnbindComponent_t __user *param) +{ + CM_UnbindComponent_t data; + char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH]; + + /* Copy all user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(requiredItfClientName, + data.in.requiredItfClientName, + sizeof(requiredItfClientName)))) + goto out; + + /* Do appropriate CM Engine call */ + data.out.error = CM_ENGINE_UnbindComponent(data.in.client, + requiredItfClientName, + procPriv->pid); + +out: + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_BindComponentToVoid(struct cm_process_priv* procPriv, + CM_BindComponentToVoid_t __user *param) +{ + CM_BindComponentToVoid_t data; + char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH]; + + /* Copy all user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(requiredItfClientName, + data.in.requiredItfClientName, + sizeof(requiredItfClientName)))) + goto out; + + data.out.error = CM_ENGINE_BindComponentToVoid(data.in.client, + requiredItfClientName, + procPriv->pid); + +out: + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_DestroyComponent(struct cm_process_priv* procPriv, + CM_DestroyComponent_t __user *param) +{ + CM_DestroyComponent_t data; + + /* Copy all user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_DestroyComponent(data.in.component, + procPriv->pid); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_CreateMemoryDomain(struct cm_process_priv *procPriv, + CM_CreateMemoryDomain_t __user *param) +{ + CM_CreateMemoryDomain_t data; + t_cm_domain_memory domain; + + /* Copy all user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if (copy_from_user(&domain, data.in.domain, sizeof(domain))) + return -EFAULT; + + if (data.in.client == NMF_CURRENT_CLIENT) + data.out.error = CM_ENGINE_CreateMemoryDomain(procPriv->pid, + &domain, + &data.out.handle); + else { + /* Check if client is valid (ie already registered) */ + struct list_head* head; + struct cm_process_priv *entry; + + list_for_each(head, &process_list) { + entry = list_entry(head, struct cm_process_priv, + entry); + if (entry->pid == data.in.client) + break; + } + if (head == &process_list) + data.out.error = CM_INVALID_PARAMETER; + else + data.out.error = + CM_ENGINE_CreateMemoryDomain(data.in.client, + &domain, + &data.out.handle); + } + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_CreateMemoryDomainScratch(struct cm_process_priv *procPriv, + CM_CreateMemoryDomainScratch_t __user *param) +{ + CM_CreateMemoryDomainScratch_t data; + t_cm_domain_memory domain; + + /* Copy all user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if (copy_from_user(&domain, data.in.domain, sizeof(domain))) + return -EFAULT; + + data.out.error = CM_ENGINE_CreateMemoryDomainScratch(procPriv->pid, + data.in.parentId, + &domain, + &data.out.handle); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_DestroyMemoryDomain(CM_DestroyMemoryDomain_t __user *param) +{ + CM_DestroyMemoryDomain_t data; + + /* Copy all user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + /* coverity[tainted_data : FALSE] */ + data.out.error = CM_ENGINE_DestroyMemoryDomain(data.in.domainId); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetDomainCoreId(CM_GetDomainCoreId_t __user *param) +{ + CM_GetDomainCoreId_t data; + + /* Copy all user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + /* coverity[tainted_data : FALSE] */ + data.out.error = CM_ENGINE_GetDomainCoreId(data.in.domainId, + &data.out.coreId); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_AllocMpcMemory(struct cm_process_priv *procPriv, + CM_AllocMpcMemory_t __user *param) +{ + t_cm_error err; + CM_AllocMpcMemory_t data; + t_cm_memory_handle handle = 0; + struct memAreaDesc_t* memAreaDesc; + t_cm_system_address systemAddress; + t_uint32 mpcAddress; + + /* Copy all user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + /* Disregard alignment information and force 4kB memory alignment, + in any case (see devnotes.txt) */ + /* PP: Disable this 'force' for now, because of the low amount of + available MPC Memory */ + //data.in.memAlignment = CM_MM_MPC_ALIGN_1024WORDS; + + /* coverity[tainted_data : FALSE] */ + data.out.error = CM_ENGINE_AllocMpcMemory(data.in.domainId, + procPriv->pid, + data.in.memType, + data.in.size, + data.in.memAlignment, + &handle); + + data.out.pHandle = handle; + + if (data.out.error != CM_OK) + goto out; + + /* Get memory area decriptors in advance + so to fill in list elements right now */ + err = CM_ENGINE_GetMpcMemorySystemAddress(handle, &systemAddress); + if (err != CM_OK) { + pr_err("%s: failed CM_ENGINE_GetMpcMemorySystemAddress (%i)\n", __func__, err); + /* If we can't manage internally this allocated memory latter, it's + better to report the error now. + Free the handle to not let the driver in an inconsistent state */ + CM_ENGINE_FreeMpcMemory(handle); + return -EFAULT; + } + + /* Get MPC address in advance so to fill in list elements right now */ + err = CM_ENGINE_GetMpcMemoryMpcAddress(handle, &mpcAddress); + if (err != CM_OK) { + pr_err("%s: failed CM_ENGINE_GetMpcMemoryMpcAddress (%i)\n", __func__, err); + /* see comments above */ + CM_ENGINE_FreeMpcMemory(handle); + return -EFAULT; + } + + /* Allocate and fill a new memory area descriptor. Add it to the list */ + memAreaDesc = OSAL_Alloc(sizeof(struct memAreaDesc_t)); + if (memAreaDesc == NULL) { + pr_err("%s: failed allocating memAreaDesc\n", __func__); + /* see comments above */ + CM_ENGINE_FreeMpcMemory(handle); + return -ENOMEM; + } + + memAreaDesc->procPriv = procPriv; + memAreaDesc->handle = handle; + memAreaDesc->tid = 0; + memAreaDesc->physAddr = systemAddress.physical; + memAreaDesc->kernelLogicalAddr = systemAddress.logical; + memAreaDesc->userLogicalAddr = 0; + memAreaDesc->mpcPhysAddr = mpcAddress; + memAreaDesc->size = data.in.size * ((data.in.memType % 2) ? 4 : 2); // betzw: set size in bytes for host (ugly version) + atomic_set(&memAreaDesc->count, 0); + + if (lock_process(procPriv)) { + /* may be rather call lock_process_uninterruptible() */ + CM_ENGINE_FreeMpcMemory(handle); + OSAL_Free(memAreaDesc); + return -ERESTARTSYS; + } + list_add(&memAreaDesc->list, &procPriv->memAreaDescList); + unlock_process(procPriv); +out: + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_FreeMpcMemory(struct cm_process_priv *procPriv, + CM_FreeMpcMemory_t __user *param) +{ + CM_FreeMpcMemory_t data; + struct list_head *cursor, *next; + + /* Copy user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + /* check that it is actually owned by the process */ + data.out.error = CM_UNKNOWN_MEMORY_HANDLE; + + if (lock_process(procPriv)) + return -ERESTARTSYS; + list_for_each_safe(cursor, next, &procPriv->memAreaDescList){ + struct memAreaDesc_t* curr; + curr = list_entry(cursor, struct memAreaDesc_t, list); + if (curr->handle == data.in.handle){ + if (atomic_read(&curr->count) != 0) { + pr_err("%s: Memory area (phyAddr: %x, size: %d) " + "still in use (count=%d)!\n", __func__, + curr->physAddr, curr->size, + atomic_read(&curr->count)); + data.out.error = CM_INVALID_PARAMETER; + } else { + data.out.error = + CM_ENGINE_FreeMpcMemory(data.in.handle); + if (data.out.error == CM_OK) { + list_del(cursor); + OSAL_Free(curr); + } + } + break; + } + } + unlock_process(procPriv); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetMpcMemoryStatus(CM_GetMpcMemoryStatus_t __user *param) +{ + CM_GetMpcMemoryStatus_t data; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + /* coverity[tainted_data : FALSE] */ + data.out.error = CM_ENGINE_GetMpcMemoryStatus(data.in.coreId, + data.in.memType, + &data.out.pStatus); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_StartComponent(struct cm_process_priv *procPriv, + CM_StartComponent_t __user *param) +{ + CM_StartComponent_t data; + + /* Copy user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_StartComponent(data.in.client, + procPriv->pid); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_StopComponent(struct cm_process_priv *procPriv, + CM_StopComponent_t __user *param) +{ + CM_StopComponent_t data; + + /* Copy user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_StopComponent(data.in.client, + procPriv->pid); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetMpcLoadCounter(CM_GetMpcLoadCounter_t __user *param) +{ + CM_GetMpcLoadCounter_t data; + + /* Copy user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_getMpcLoadCounter(data.in.coreId, + &data.out.pMpcLoadCounter); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetComponentDescription(struct cm_process_priv *procPriv, + CM_GetComponentDescription_t __user *param) +{ + CM_GetComponentDescription_t data; + char templateName[MAX_TEMPLATE_NAME_LENGTH]; + char localName[MAX_COMPONENT_NAME_LENGTH]; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_GetComponentDescription(data.in.component, + templateName, + data.in.templateNameLength, + &data.out.coreId, + localName, + data.in.localNameLength, + &data.out.priority); + + /* Copy results back to userspace */ + if (data.out.error == CM_OK) { + /* coverity[tainted_data : FALSE] */ + if (copy_to_user(data.in.templateName, templateName, data.in.templateNameLength)) + return -EFAULT; + /* coverity[tainted_data : FALSE] */ + if (copy_to_user(data.in.localName, localName, data.in.localNameLength)) + return -EFAULT; + } + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetComponentListHeader(struct cm_process_priv *procPriv, + CM_GetComponentListHeader_t __user *param) +{ + CM_GetComponentListHeader_t data; + + data.out.error = CM_ENGINE_GetComponentListHeader(procPriv->pid, + &data.out.headerComponent); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetComponentListNext(struct cm_process_priv *procPriv, + CM_GetComponentListNext_t __user *param) +{ + CM_GetComponentListNext_t data; + + /* Copy user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_GetComponentListNext(procPriv->pid, + data.in.prevComponent, + &data.out.nextComponent); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetComponentRequiredInterfaceNumber(struct cm_process_priv *procPriv, + CM_GetComponentRequiredInterfaceNumber_t __user *param) +{ + CM_GetComponentRequiredInterfaceNumber_t data; + + /* Copy user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_GetComponentRequiredInterfaceNumber(data.in.component, + &data.out.numberRequiredInterfaces); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetComponentRequiredInterface(struct cm_process_priv *procPriv, + CM_GetComponentRequiredInterface_t __user *param) +{ + CM_GetComponentRequiredInterface_t data; + char itfName[MAX_INTERFACE_NAME_LENGTH]; + char itfType[MAX_INTERFACE_TYPE_NAME_LENGTH]; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_GetComponentRequiredInterface(data.in.component, + data.in.index, + itfName, + data.in.itfNameLength, + itfType, + data.in.itfTypeLength, + &data.out.requireState, + &data.out.collectionSize); + + /* Copy results back to userspace */ + if (data.out.error == CM_OK) { + /* coverity[tainted_data : FALSE] */ + if (copy_to_user(data.in.itfName, itfName, data.in.itfNameLength)) + return -EFAULT; + /* coverity[tainted_data : FALSE] */ + if (copy_to_user(data.in.itfType, itfType, data.in.itfTypeLength)) + return -EFAULT; + } + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetComponentRequiredInterfaceBinding(struct cm_process_priv *procPriv, + CM_GetComponentRequiredInterfaceBinding_t __user *param) +{ + CM_GetComponentRequiredInterfaceBinding_t data; + char itfName[MAX_INTERFACE_NAME_LENGTH]; + char serverItfName[MAX_INTERFACE_NAME_LENGTH]; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + if ((data.out.error = copy_string_from_user(itfName, + data.in.itfName, + sizeof(itfName)))) + goto out; + + data.out.error = CM_ENGINE_GetComponentRequiredInterfaceBinding(data.in.component, + itfName, + &data.out.server, + serverItfName, + data.in.serverItfNameLength); + + /* Copy results back to userspace */ + if (data.out.error != CM_OK) + goto out; + + /* coverity[tainted_data : FALSE] */ + if (copy_to_user(data.in.serverItfName, serverItfName, data.in.serverItfNameLength)) + return -EFAULT; +out: + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetComponentProvidedInterfaceNumber(struct cm_process_priv *procPriv, + CM_GetComponentProvidedInterfaceNumber_t __user *param) +{ + CM_GetComponentProvidedInterfaceNumber_t data; + + /* Copy user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_GetComponentProvidedInterfaceNumber(data.in.component, + &data.out.numberProvidedInterfaces); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetComponentProvidedInterface(struct cm_process_priv *procPriv, + CM_GetComponentProvidedInterface_t __user *param) +{ + CM_GetComponentProvidedInterface_t data; + char itfName[MAX_INTERFACE_NAME_LENGTH]; + char itfType[MAX_INTERFACE_TYPE_NAME_LENGTH]; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_GetComponentProvidedInterface(data.in.component, + data.in.index, + itfName, + data.in.itfNameLength, + itfType, + data.in.itfTypeLength, + &data.out.collectionSize); + + /* Copy results back to userspace */ + if (data.out.error == CM_OK) { + /* coverity[tainted_data : FALSE] */ + if (copy_to_user(data.in.itfName, itfName, data.in.itfNameLength)) + return -EFAULT; + /* coverity[tainted_data : FALSE] */ + if (copy_to_user(data.in.itfType, itfType, data.in.itfTypeLength)) + return -EFAULT; + } + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetComponentPropertyNumber(struct cm_process_priv *procPriv, + CM_GetComponentPropertyNumber_t __user *param) +{ + CM_GetComponentPropertyNumber_t data; + + /* Copy user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_GetComponentPropertyNumber(data.in.component, + &data.out.numberProperties); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetComponentPropertyName(struct cm_process_priv *procPriv, + CM_GetComponentPropertyName_t __user *param) +{ + CM_GetComponentPropertyName_t data; + char propertyName[MAX_PROPERTY_NAME_LENGTH]; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_GetComponentPropertyName(data.in.component, + data.in.index, + propertyName, + data.in.propertyNameLength); + + /* Copy results back to userspace */ + /* coverity[tainted_data : FALSE] */ + if ((data.out.error == CM_OK) && + copy_to_user(data.in.propertyName, propertyName, data.in.propertyNameLength)) + return -EFAULT; + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetComponentPropertyValue(struct cm_process_priv *procPriv, + CM_GetComponentPropertyValue_t __user *param) +{ + CM_GetComponentPropertyValue_t data; + char propertyName[MAX_PROPERTY_NAME_LENGTH]; + char propertyValue[MAX_PROPERTY_VALUE_LENGTH]; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(propertyName, + data.in.propertyName, + sizeof(propertyName)))) + goto out; + + data.out.error = CM_ENGINE_GetComponentPropertyValue(data.in.component, + propertyName, + propertyValue, + data.in.propertyValueLength); + /* Copy results back to userspace */ + /* coverity[tainted_data : FALSE] */ + if ((data.out.error == CM_OK) && + copy_to_user(data.in.propertyValue, propertyValue, data.in.propertyValueLength)) + return -EFAULT; +out: + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_ReadComponentAttribute(struct cm_process_priv *procPriv, + CM_ReadComponentAttribute_t __user *param) +{ + CM_ReadComponentAttribute_t data; + char attrName[MAX_ATTRIBUTE_NAME_LENGTH]; + + /* Copy user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(attrName, + data.in.attrName, + sizeof(attrName)))) + goto out; + + data.out.error = CM_ENGINE_ReadComponentAttribute(data.in.component, + attrName, + &data.out.value); +out: + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetExecutiveEngineHandle(struct cm_process_priv *procPriv, + CM_GetExecutiveEngineHandle_t __user *param) +{ + CM_GetExecutiveEngineHandle_t data; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + /* coverity[tainted_data : FALSE] */ + data.out.error = CM_ENGINE_GetExecutiveEngineHandle(data.in.domainId, + &data.out.executiveEngineHandle); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_SetMode(CM_SetMode_t __user *param) +{ + CM_SetMode_t data; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + /* coverity[tainted_data : FALSE] */ + data.out.error = CM_ENGINE_SetMode(data.in.aCmdID, data.in.aParam); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_GetRequiredComponentFiles(struct cm_process_priv *procPriv, + CM_GetRequiredComponentFiles_t __user *param) +{ + CM_GetRequiredComponentFiles_t data; + char components[4][MAX_INTERFACE_TYPE_NAME_LENGTH]; + char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH]; + char providedItfServerName[MAX_INTERFACE_NAME_LENGTH]; + char type[MAX_INTERFACE_TYPE_NAME_LENGTH]; + unsigned int i; + int err; + + /* Copy user input data in kernel space */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if (data.in.requiredItfClientName && + (data.out.error = copy_string_from_user(requiredItfClientName, + data.in.requiredItfClientName, + sizeof(requiredItfClientName)))) + goto out; + + if (data.in.providedItfServerName && + (data.out.error = copy_string_from_user(providedItfServerName, + data.in.providedItfServerName, + sizeof(providedItfServerName)))) + goto out; + + data.out.error = CM_ENGINE_GetRequiredComponentFiles(data.in.action, + data.in.client, + requiredItfClientName, + data.in.server, + providedItfServerName, + components, + data.in.listSize, + data.in.type ? type : NULL, + &data.out.methodNumber); + + if (data.out.error) + goto out; + + if (data.in.fileList) { + /* Copy results back to userspace */ + for (i=0; i<data.in.listSize; i++) { + err = copy_to_user(&((char*)data.in.fileList)[i*MAX_INTERFACE_TYPE_NAME_LENGTH], components[i], MAX_INTERFACE_TYPE_NAME_LENGTH); + if (err) + return -EFAULT; + } + } + if (data.in.type + && copy_to_user(data.in.type, type, MAX_INTERFACE_TYPE_NAME_LENGTH)) + return -EFAULT; +out: + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_Migrate(CM_Migrate_t __user *param) +{ + CM_Migrate_t data; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + data.out.error = CM_ENGINE_Migrate(data.in.srcShared, data.in.src, data.in.dst); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_Unmigrate(CM_Unmigrate_t __user *param) +{ + CM_Unmigrate_t data; + + data.out.error = CM_ENGINE_Unmigrate(); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +int cmld_SetupRelinkArea(struct cm_process_priv *procPriv, + CM_SetupRelinkArea_t __user *param) +{ + CM_SetupRelinkArea_t data; + struct list_head *cursor, *next; + struct memAreaDesc_t *entry = NULL; + + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + + /* check that it is actually owned by the process */ + data.out.error = CM_UNKNOWN_MEMORY_HANDLE; + + if (lock_process(procPriv)) + return -ERESTARTSYS; + list_for_each_safe(cursor, next, &procPriv->memAreaDescList){ + entry = list_entry(cursor, struct memAreaDesc_t, list); + if (entry->handle == data.in.mem_handle) + break; + } + unlock_process(procPriv); + + if ((entry == NULL) || (entry->handle != data.in.mem_handle)) + goto out; + + if (entry->size < data.in.segments * data.in.segmentsize) + { + data.out.error = CM_INVALID_PARAMETER; + goto out; + } + + data.out.error = cmdma_setup_relink_area( + entry->physAddr, + data.in.peripheral_addr, + data.in.segments, + data.in.segmentsize, + data.in.LOS, + data.in.type); +out: + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + + return 0; +} + + +inline int cmld_PushComponent(CM_PushComponent_t __user *param) +{ + CM_PushComponent_t data; + char name[MAX_INTERFACE_TYPE_NAME_LENGTH]; + void *dataFile = NULL; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(name, + data.in.name, + sizeof(name)))) + goto out; + + if (data.in.data != NULL) { + dataFile = OSAL_Alloc(data.in.size); + if (dataFile == NULL) { + data.out.error = CM_NO_MORE_MEMORY; + goto out; + } + /* coverity[tainted_data : FALSE] */ + if (copy_from_user(dataFile, data.in.data, data.in.size)) + data.out.error = CM_INVALID_PARAMETER; + else + data.out.error = CM_ENGINE_PushComponent(name, dataFile, + data.in.size); + OSAL_Free(dataFile); + } + +out: + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_ReleaseComponent(CM_ReleaseComponent_t __user *param) +{ + CM_ReleaseComponent_t data; + char name[MAX_INTERFACE_TYPE_NAME_LENGTH]; + + /* Copy user input data in kernel space */ + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if ((data.out.error = copy_string_from_user(name, + data.in.name, + sizeof(name)))) + goto out; + + /* coverity[tainted_data : FALSE] */ + data.out.error = CM_ENGINE_ReleaseComponent(name); + +out: + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +inline int cmld_PrivGetMPCMemoryDesc(struct cm_process_priv *procPriv, CM_PrivGetMPCMemoryDesc_t __user *param) +{ + CM_PrivGetMPCMemoryDesc_t data; + struct list_head* cursor; + + if (copy_from_user(&data.in, ¶m->in, sizeof(data.in))) + return -EFAULT; + + if (lock_process(procPriv)) + return -ERESTARTSYS; + /* Scan the memory descriptors list looking for the requested handle */ + data.out.error = CM_UNKNOWN_MEMORY_HANDLE; + list_for_each(cursor, &procPriv->memAreaDescList) { + struct memAreaDesc_t* curr; + curr = list_entry(cursor, struct memAreaDesc_t, list); + if (curr->handle == data.in.handle) { + data.out.size = curr->size; + data.out.physAddr = curr->physAddr; + data.out.kernelLogicalAddr = curr->kernelLogicalAddr; + data.out.userLogicalAddr = curr->userLogicalAddr; + data.out.mpcPhysAddr = curr->mpcPhysAddr; + data.out.error = CM_OK; + break; + } + } + unlock_process(procPriv); + + /* Copy results back to userspace */ + if (copy_to_user(¶m->out, &data.out, sizeof(data.out))) + return -EFAULT; + return 0; +} + +int cmld_PrivReserveMemory(struct cm_process_priv *procPriv, unsigned int physAddr) +{ + struct list_head* cursor; + struct memAreaDesc_t* curr; + int err = -ENXIO; + + if (lock_process(procPriv)) + return -ERESTARTSYS; + list_for_each(cursor, &procPriv->memAreaDescList) { + curr = list_entry(cursor, struct memAreaDesc_t, list); + if (curr->physAddr == physAddr) { + /* Mark this memory area reserved for a mapping for this thread ID */ + /* It must not be already reserved but this should not happen */ + if (curr->tid) { + /*pr_err("%s: thread %d can't reseveved memory %x already " + "reserved for %d\n", + __func__, current->pid, physAddr, (int)curr->tid);*/ + err = -EBUSY; + } else { + curr->tid = current->pid; + err = 0; + } + break; + } + } + unlock_process(procPriv); + return err; +} diff --git a/drivers/staging/nmf-cm/cmioctl.h b/drivers/staging/nmf-cm/cmioctl.h new file mode 100644 index 00000000000..96481be38b8 --- /dev/null +++ b/drivers/staging/nmf-cm/cmioctl.h @@ -0,0 +1,602 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +#ifndef __CMIOCTL_H +#define __CMIOCTL_H + +#ifndef __KERNEL__ +#define BITS_PER_BYTE 8 +#endif + +#include <cm/engine/component/inc/component_type.h> +#include <cm/engine/communication/inc/communication_type.h> +#include <cm/engine/configuration/inc/configuration_type.h> +#include <cm/engine/memory/inc/domain_type.h> +#include <cm/engine/memory/inc/memory_type.h> +#include <cm/engine/perfmeter/inc/perfmeter_type.h> +#include <cm/engine/repository_mgt/inc/repository_type.h> + +#define DEBUGFS_ROOT "nmf-cm" +#define DEBUGFS_DUMP_FLAG (1 << (sizeof(t_panic_source)*BITS_PER_BYTE - 1)) + +enum cmdma_type { + CMDMA_MEM_2_PER, + CMDMA_PER_2_MEM +}; + +#define CMLD_DEV_NAME \ + { "cm_control", \ + "cm_channel" \ + } + +/* + * The following structures are used to exchange CM_SYSCALL parameters with + * the driver. There is one structure per ioctl command, ie per CM_SYSCAL. + * Each of them contains: + * - One set of fields placed in a struture 'in' which are all input + * parameters of the syscall (parameters that kernel side must retrieve + * from user space) + * - One set of fields placed in a struture 'out' which contains all output + * parameters of the syscall plus the error code. + * + * NOTE: all pointers to (user) buffer are always placed in struct 'in', including + * buffers used as output parameters; because the pointer itself is considered as + * an input parameter, as it is directly accessed from kernel space. + */ +typedef struct{ + struct { + const char * templateName; + t_cm_domain_id domainId; + t_nmf_ee_priority priority; + const char * localName; + const char *dataFile; + t_uint32 dataFileSize; + } in; + struct { + t_cm_instance_handle component; /** < Output parameter */ + t_cm_error error; + } out; +} CM_InstantiateComponent_t; + +typedef struct { + struct { + t_cm_bf_host2mpc_handle host2mpcId; + t_event_params_handle h; + t_uint32 size; + t_uint32 methodIndex; + } in; + struct { + t_cm_error error; + } out; +} CM_PushEventWithSize_t; + +typedef struct { + struct { + t_cm_instance_handle server; + const char * providedItfServerName; + t_uint32 fifosize; + t_cm_mpc_memory_type eventMemType; + const char *dataFileSkeleton; + t_uint32 dataFileSkeletonSize; + } in; + struct { + t_cm_bf_host2mpc_handle host2mpcId; /** < Output parameter */ + t_cm_error error; + } out; +} CM_BindComponentFromCMCore_t; + +typedef struct { + struct { + t_cm_bf_host2mpc_handle host2mpcId; + } in; + struct { + t_cm_error error; + } out; +} CM_UnbindComponentFromCMCore_t; + +typedef struct { + struct { + t_cm_instance_handle client; + const char *requiredItfClientName; + t_uint32 fifosize; + t_nmf_mpc2host_handle upLayerThis; + const char *dataFileStub; + t_uint32 dataFileStubSize; + } in; + struct { + t_cm_bf_mpc2host_handle mpc2hostId; /** < Output parameter */ + t_cm_error error; + } out; +} CM_BindComponentToCMCore_t; + +typedef struct { + struct { + t_cm_instance_handle client; + const char *requiredItfClientName; + } in; + struct { + t_nmf_mpc2host_handle upLayerThis; /** < Output parameter */ + t_cm_error error; + } out; +} CM_UnbindComponentToCMCore_t; + +typedef struct { + struct { + t_cm_instance_handle component; + } in; + struct { + t_cm_error error; /** < Output parameter */ + } out; /** < Output parameter */ +} CM_DestroyComponent_t; + +typedef struct { + struct { + const t_cm_domain_memory *domain; + t_nmf_client_id client; + } in; + struct { + t_cm_domain_id handle; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; /** < Out parameter */ +} CM_CreateMemoryDomain_t; + +typedef struct { + struct { + t_cm_domain_id parentId; + const t_cm_domain_memory *domain; + } in; + struct { + t_cm_domain_id handle; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; /** < Out parameter */ +} CM_CreateMemoryDomainScratch_t; + +typedef struct { + struct { + t_cm_domain_id domainId; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; /** < Out parameter */ +} CM_DestroyMemoryDomain_t; + +typedef struct { + struct { + t_cm_domain_id domainId; /** < In parameter */ + } in; + struct { + t_nmf_core_id coreId; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; /** < Out parameter */ +} CM_GetDomainCoreId_t; + +typedef struct { + struct { + t_cm_domain_id domainId; + t_cm_mpc_memory_type memType; + t_cm_size size; + t_cm_memory_alignment memAlignment; + } in; + struct { + t_cm_memory_handle pHandle; /** < Output parameter */ + t_cm_error error; + } out; /** < Output parameter */ +} CM_AllocMpcMemory_t; + +typedef struct{ + struct { + t_cm_memory_handle handle; + } in; + struct { + t_cm_error error; + } out; /** < Output parameter */ +} CM_FreeMpcMemory_t; + +typedef struct { + struct { + t_cm_memory_handle handle; + } in; + struct { + t_uint32 size; /** < Out parameter */ + t_uint32 physAddr; /** < Out parameter */ + t_uint32 kernelLogicalAddr; /** < Out parameter */ + t_uint32 userLogicalAddr; /** < Out parameter */ + t_uint32 mpcPhysAddr; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; /** < Output parameter */ +} CM_PrivGetMPCMemoryDesc_t; + +typedef struct { + struct { + t_cm_instance_handle client; + const char *requiredItfClientName; + t_cm_instance_handle server; + const char *providedItfServerName; + t_uint32 fifosize; + t_cm_mpc_memory_type eventMemType; + const char *dataFileSkeletonOrEvent; + t_uint32 dataFileSkeletonOrEventSize; + const char *dataFileStub; + t_uint32 dataFileStubSize; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_BindComponentAsynchronous_t; + +typedef struct { + struct { + t_cm_instance_handle client; + const char* requiredItfClientName; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_UnbindComponentAsynchronous_t; + +typedef struct { + struct { + t_cm_instance_handle client; + const char *requiredItfClientName; + t_cm_instance_handle server; + const char *providedItfServerName; + t_bool traced; + const char *dataFileTrace; + t_uint32 dataFileTraceSize; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_BindComponent_t; + +typedef struct { + struct { + t_cm_instance_handle client; + const char* requiredItfClientName; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_UnbindComponent_t; + +typedef struct { + struct { + t_cm_instance_handle client; + const char* requiredItfClientName; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_BindComponentToVoid_t; + +typedef struct { + struct { + t_cm_instance_handle client; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_StartComponent_t; + +typedef struct { + struct { + t_cm_instance_handle client; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_StopComponent_t; + +typedef struct { + struct { + t_nmf_core_id coreId; + } in; + struct { + t_cm_mpc_load_counter pMpcLoadCounter; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetMpcLoadCounter_t; + +typedef struct { + struct { + t_nmf_core_id coreId; + t_cm_mpc_memory_type memType; + } in; + struct { + t_cm_allocator_status pStatus; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; /** < Out parameter */ +} CM_GetMpcMemoryStatus_t; + +typedef struct { + struct { + t_cm_instance_handle component; + t_uint32 templateNameLength; + t_uint32 localNameLength; + char *templateName; /** < Out parameter */ + char *localName; /** < Out parameter */ + } in; + struct { + t_nmf_core_id coreId; /** < Out parameter */ + t_nmf_ee_priority priority; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; /** < Out parameter */ +} CM_GetComponentDescription_t; + +typedef struct { + struct { + t_cm_instance_handle headerComponent; /** < Output parameter */ + t_cm_error error; /** < Out parameter */ + } out; /** < Out parameter */ +} CM_GetComponentListHeader_t; + +typedef struct { + struct { + t_cm_instance_handle prevComponent; + } in; + struct { + t_cm_instance_handle nextComponent; /** < Output parameter */ + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetComponentListNext_t; + +typedef struct { + struct { + t_cm_instance_handle component; + } in; + struct { + t_uint8 numberRequiredInterfaces; /** < Output parameter */ + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetComponentRequiredInterfaceNumber_t; + +typedef struct { + struct { + t_cm_instance_handle component; + t_uint8 index; + t_uint32 itfNameLength; + t_uint32 itfTypeLength; + char *itfName; /** < Out parameter */ + char *itfType; /** < Out parameter */ + } in; + struct { + t_cm_require_state requireState; /** < Out parameter */ + t_sint16 collectionSize; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetComponentRequiredInterface_t; + +typedef struct { + struct { + t_cm_instance_handle component; + const char *itfName; + t_uint32 serverItfNameLength; + char *serverItfName; /** < Out parameter */ + } in; + struct { + t_cm_instance_handle server; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetComponentRequiredInterfaceBinding_t; + +typedef struct { + struct { + t_cm_instance_handle component; + } in; + struct { + t_uint8 numberProvidedInterfaces; /** < Output parameter */ + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetComponentProvidedInterfaceNumber_t; + +typedef struct { + struct { + t_cm_instance_handle component; + t_uint8 index; + t_uint32 itfNameLength; + t_uint32 itfTypeLength; + char *itfName; /** < Out parameter */ + char *itfType; /** < Out parameter */ + } in; + struct { + t_sint16 collectionSize; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetComponentProvidedInterface_t; + +typedef struct { + struct { + t_cm_instance_handle component; + } in; + struct { + t_uint8 numberProperties; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetComponentPropertyNumber_t; + +typedef struct { + struct { + t_cm_instance_handle component; + const char *attrName; + t_uint8 index; + t_uint32 propertyNameLength; + char *propertyName; /** < Out parameter */ + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetComponentPropertyName_t; + +typedef struct { + struct { + t_cm_instance_handle component; + const char *propertyName; + t_uint32 propertyValueLength; + char *propertyValue; /** < Out parameter */ + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetComponentPropertyValue_t; + +typedef struct { + struct { + t_cm_instance_handle component; + const char *attrName; + } in; + struct { + t_uint32 value; /** < Out parameter */ + t_cm_error error; /** < Out parameter */ + } out; +} CM_ReadComponentAttribute_t; + +typedef struct { + struct { + t_cm_domain_id domainId; + } in; + struct { + t_cm_instance_handle executiveEngineHandle; + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetExecutiveEngineHandle_t; + +typedef struct { + struct { + t_cm_cmd_id aCmdID; + t_sint32 aParam; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_SetMode_t; + +typedef struct { + struct { + t_action_to_do action; + t_cm_instance_handle client; + const char *requiredItfClientName; + t_cm_instance_handle server; + const char *providedItfServerName; + char **fileList; + unsigned int listSize; + char *type; + } in; + struct { + t_uint32 methodNumber; /** < Output parameter */ + t_cm_error error; /** < Out parameter */ + } out; +} CM_GetRequiredComponentFiles_t; + +typedef struct { + struct { + const char *name; + const void *data; + t_cm_size size; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_PushComponent_t; + +typedef struct { + struct { + const char *name; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_ReleaseComponent_t; + +typedef struct { + struct { + t_cm_domain_id srcShared; + t_cm_domain_id src; + t_cm_domain_id dst; + } in; + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_Migrate_t; + +typedef struct { + struct { + t_cm_error error; /** < Out parameter */ + } out; +} CM_Unmigrate_t; + +typedef struct{ + struct { + t_cm_memory_handle mem_handle; + unsigned int peripheral_addr; + unsigned int segments; + unsigned int segmentsize; + unsigned int LOS; + enum cmdma_type type; + } in; + struct { + t_cm_error error; + } out; +} CM_SetupRelinkArea_t; + +#define CM_PUSHEVENTWITHSIZE _IOWR('c', 0, CM_PushEventWithSize_t) +#define CM_GETVERSION _IOR('c', 1, t_uint32) +#define CM_INSTANTIATECOMPONENT _IOWR('c', 2, CM_InstantiateComponent_t) +#define CM_BINDCOMPONENTFROMCMCORE _IOWR('c', 3, CM_BindComponentFromCMCore_t) +#define CM_UNBINDCOMPONENTFROMCMCORE _IOWR('c', 4, CM_UnbindComponentFromCMCore_t) +#define CM_BINDCOMPONENTTOCMCORE _IOWR('c', 5, CM_BindComponentToCMCore_t) +#define CM_UNBINDCOMPONENTTOCMCORE _IOWR('c', 6, CM_UnbindComponentToCMCore_t) +#define CM_DESTROYCOMPONENT _IOWR('c', 7, CM_DestroyComponent_t) +#define CM_CREATEMEMORYDOMAIN _IOWR('c', 8, CM_CreateMemoryDomain_t) +#define CM_CREATEMEMORYDOMAINSCRATCH _IOWR('c', 9, CM_CreateMemoryDomainScratch_t) +#define CM_DESTROYMEMORYDOMAIN _IOWR('c', 10, CM_DestroyMemoryDomain_t) +#define CM_GETDOMAINCOREID _IOWR('c', 11, CM_GetDomainCoreId_t) +#define CM_ALLOCMPCMEMORY _IOWR('c', 12, CM_AllocMpcMemory_t) +#define CM_FREEMPCMEMORY _IOWR('c', 13, CM_FreeMpcMemory_t) +#define CM_BINDCOMPONENTASYNCHRONOUS _IOWR('c', 14, CM_BindComponentAsynchronous_t) +#define CM_UNBINDCOMPONENTASYNCHRONOUS _IOWR('c', 15, CM_UnbindComponentAsynchronous_t) +#define CM_BINDCOMPONENT _IOWR('c', 16, CM_BindComponent_t) +#define CM_UNBINDCOMPONENT _IOWR('c', 17, CM_UnbindComponent_t) +#define CM_BINDCOMPONENTTOVOID _IOWR('c', 18, CM_BindComponentToVoid_t) +#define CM_STARTCOMPONENT _IOWR('c', 19, CM_StartComponent_t) +#define CM_STOPCOMPONENT _IOWR('c', 20, CM_StopComponent_t) +#define CM_GETMPCLOADCOUNTER _IOWR('c', 21, CM_GetMpcLoadCounter_t) +#define CM_GETMPCMEMORYSTATUS _IOWR('c', 22, CM_GetMpcMemoryStatus_t) +#define CM_GETCOMPONENTDESCRIPTION _IOWR('c', 23, CM_GetComponentDescription_t) +#define CM_GETCOMPONENTLISTHEADER _IOWR('c', 24, CM_GetComponentListHeader_t) +#define CM_GETCOMPONENTLISTNEXT _IOWR('c', 25, CM_GetComponentListNext_t) +#define CM_GETCOMPONENTREQUIREDINTERFACENUMBER _IOWR('c', 26, CM_GetComponentRequiredInterfaceNumber_t) +#define CM_GETCOMPONENTREQUIREDINTERFACE _IOWR('c', 27, CM_GetComponentRequiredInterface_t) +#define CM_GETCOMPONENTREQUIREDINTERFACEBINDING _IOWR('c', 28, CM_GetComponentRequiredInterfaceBinding_t) +#define CM_GETCOMPONENTPROVIDEDINTERFACENUMBER _IOWR('c', 29, CM_GetComponentProvidedInterfaceNumber_t) +#define CM_GETCOMPONENTPROVIDEDINTERFACE _IOWR('c', 30, CM_GetComponentProvidedInterface_t) +#define CM_GETCOMPONENTPROPERTYNUMBER _IOWR('c', 31, CM_GetComponentPropertyNumber_t) +#define CM_GETCOMPONENTPROPERTYNAME _IOWR('c', 32, CM_GetComponentPropertyName_t) +#define CM_GETCOMPONENTPROPERTYVALUE _IOWR('c', 33, CM_GetComponentPropertyValue_t) +#define CM_READCOMPONENTATTRIBUTE _IOWR('c', 34, CM_ReadComponentAttribute_t) +#define CM_GETEXECUTIVEENGINEHANDLE _IOWR('c', 35, CM_GetExecutiveEngineHandle_t) +#define CM_SETMODE _IOWR('c', 36, CM_SetMode_t) +#define CM_GETREQUIREDCOMPONENTFILES _IOWR('c', 37, CM_GetRequiredComponentFiles_t) +#define CM_PUSHCOMPONENT _IOWR('c', 38, CM_PushComponent_t) +#define CM_FLUSHCHANNEL _IO('c', 39) +#define CM_MIGRATE _IOWR('c', 40, CM_Migrate_t) +#define CM_UNMIGRATE _IOR('c', 41, CM_Unmigrate_t) +#define CM_RELEASECOMPONENT _IOWR('c', 42, CM_ReleaseComponent_t) +#define CM_SETUPRELINKAREA _IOWR('c', 43, CM_SetupRelinkArea_t) + +#define CM_PRIVGETMPCMEMORYDESC _IOWR('c', 100, CM_PrivGetMPCMemoryDesc_t) +#define CM_PRIVRESERVEMEMORY _IOW('c', 101, unsigned int) +#define CM_PRIV_GETBOARDVERSION _IOR('c', 102, unsigned int) +#define CM_PRIV_ISCOMPONENTCACHEEMPTY _IO('c', 103) +#define CM_PRIV_DEBUGFS_READY _IO('c', 104) +#define CM_PRIV_DEBUGFS_WAIT_DUMP _IO('c', 105) +#define CM_PRIV_DEBUGFS_DUMP_DONE _IO('c', 106) + +enum board_version { + U8500_V2 +}; +#endif diff --git a/drivers/staging/nmf-cm/cmld.c b/drivers/staging/nmf-cm/cmld.c new file mode 100644 index 00000000000..d96ceaa3fe5 --- /dev/null +++ b/drivers/staging/nmf-cm/cmld.c @@ -0,0 +1,1261 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +/** \file cmld.c + * + * Nomadik Multiprocessing Framework Linux Driver + * + */ + +#include <linux/cdev.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/sched.h> + +#include <cm/inc/cm_def.h> +#include <cm/engine/api/cm_engine.h> +#include <cm/engine/api/control/irq_engine.h> + +#include "osal-kernel.h" +#include "cmld.h" +#include "cmioctl.h" +#include "cm_debug.h" +#include "cm_service.h" +#include "cm_dma.h" + +#define CMDRIVER_PATCH_VERSION 117 +#define O_FLUSH 0x1000000 + +static int cmld_major; +static struct cdev cmld_cdev; +static struct class cmld_class = { + .name = "cm", + .owner = THIS_MODULE, +}; +const char *cmld_devname[] = CMLD_DEV_NAME; +static struct device *cmld_dev[ARRAY_SIZE(cmld_devname)]; + +/* List of per process structure (struct cm_process_priv list) */ +LIST_HEAD(process_list); +static DEFINE_MUTEX(process_lock); /* lock used to protect previous list */ +/* List of per channel structure (struct cm_channel_priv list). + A channel == One file descriptor */ +LIST_HEAD(channel_list); +static DEFINE_MUTEX(channel_lock); /* lock used to protect previous list */ + +#ifdef CONFIG_DEBUG_FS +/* Debugfs support */ +bool cmld_user_has_debugfs = false; +bool cmld_dump_ongoing = false; +module_param(cmld_dump_ongoing, bool, S_IWUSR|S_IRUGO); +static DECLARE_WAIT_QUEUE_HEAD(dump_waitq); +#endif + +static inline struct cm_process_priv *getProcessPriv(void) +{ + struct list_head* head; + struct cm_process_priv *entry; + + mutex_lock(&process_lock); + + /* Look for an entry for the calling process */ + list_for_each(head, &process_list) { + entry = list_entry(head, struct cm_process_priv, entry); + if (entry->pid == current->tgid) { + kref_get(&entry->ref); + goto out; + } + } + mutex_unlock(&process_lock); + + /* Allocate, init and register a new one otherwise */ + entry = OSAL_Alloc(sizeof(*entry)); + if (entry == NULL) + return ERR_PTR(-ENOMEM); + + /* init host2mpcLock */ + mutex_init(&entry->host2mpcLock); + + INIT_LIST_HEAD(&entry->memAreaDescList); + kref_init(&entry->ref); + mutex_init(&entry->mutex); + + entry->pid = current->tgid; + mutex_lock(&process_lock); + list_add(&entry->entry, &process_list); + cm_debug_proc_init(entry); +out: + mutex_unlock(&process_lock); + return entry; +} + +/* Free all messages */ +static inline void freeMessages(struct cm_channel_priv* channelPriv) +{ + struct osal_msg *this, *next; + int warn = 0; + + spin_lock_bh(&channelPriv->bh_lock); + plist_for_each_entry_safe(this, next, &channelPriv->messageQueue, msg_entry) { + plist_del(&this->msg_entry, &channelPriv->messageQueue); + kfree(this); + warn = 1; + } + spin_unlock_bh(&channelPriv->bh_lock); + if (warn) + pr_err("[CM - PID=%d]: Some remaining" + " message(s) freed\n", current->tgid); +} + +/* Free all pending memory areas and relative descriptors */ +static inline void freeMemHandles(struct cm_process_priv* processPriv) +{ + struct list_head* head, *next; + int warn = 0; + + list_for_each_safe(head, next, &processPriv->memAreaDescList) { + struct memAreaDesc_t* curr; + int err; + curr = list_entry(head, struct memAreaDesc_t, list); + err=CM_ENGINE_FreeMpcMemory(curr->handle); + if (err) + pr_err("[CM - PID=%d]: Error (%d) freeing remaining memory area " + "handle\n", current->tgid, err); + list_del(head); + OSAL_Free(curr); + warn = 1; + } + if (warn) { + pr_err("[CM - PID=%d]: Some remaining memory area " + "handle(s) freed\n", current->tgid); + warn = 0; + } +} + +/* Free any skeleton, called when freeing the process entry */ +static inline void freeSkelList(struct list_head* skelList) +{ + struct list_head* head, *next; + int warn = 0; + + /* No lock held, we know that we are the only and the last user + of the list */ + list_for_each_safe(head, next, skelList) { + t_skelwrapper* curr; + curr = list_entry(head, t_skelwrapper, entry); + list_del(head); + OSAL_Free(curr); + warn = 1; + } + if (warn) + pr_err("[CM - PID=%d]: Some remaining skeleton " + "wrapper(s) freed\n", current->tgid); +} + +/* Free any remaining channels belonging to this process */ +/* Called _only_ when freeing the process entry, once the network constructed by + this process has been destroyed. + See cmld_release() to see why there can be some remaining non-freed channels */ +static inline void freeChannels(struct cm_process_priv* processPriv) +{ + struct list_head* head, *next; + int warn = 0; + + list_for_each_safe(head, next, &channel_list) { + struct cm_channel_priv *channelPriv; + channelPriv = list_entry(head, struct cm_channel_priv, entry); + /* Only channels belonging to this process are concerned */ + if (channelPriv->proc == processPriv) { + tasklet_disable(&cmld_service_tasklet); + mutex_lock(&channel_lock); + list_del(&channelPriv->entry); + mutex_unlock(&channel_lock); + tasklet_enable(&cmld_service_tasklet); + + /* Free all remaining messages if any + (normally none, but double check) */ + freeMessages(channelPriv); + + /* Free any pending skeleton wrapper */ + /* Here it's safe, we know that all bindings have been undone */ + freeSkelList(&channelPriv->skelList); + + /* Free the per-channel descriptor */ + OSAL_Free(channelPriv); + } + warn = 1; + } + if (warn) + pr_err("[CM - PID=%d]: Some remaining channel entries " + "freed\n", current->tgid); +} + +/* Free the process priv structure and all related stuff */ +/* Called only when the last ref to this structure is released */ +static void freeProcessPriv(struct kref *ref) +{ + struct cm_process_priv *entry = container_of(ref, struct cm_process_priv, ref); + t_nmf_error err; + + mutex_lock(&process_lock); + list_del(&entry->entry); + mutex_unlock(&process_lock); + + /* Destroy all remaining components */ + err=CM_ENGINE_FlushComponents(entry->pid); + if (err != NMF_OK) + pr_err("[CM - PID=%d]: Error while flushing some remaining" + " components: error=%d\n", current->tgid, err); + + freeChannels(entry); + + /* Free any pending memory areas and relative descriptors */ + freeMemHandles(entry); + + /* Destroy all remaining domains */ + err=CM_ENGINE_FlushMemoryDomains(entry->pid); + if (err != NMF_OK) + pr_err("[CM - PID=%d]: Error while flushing some remaining" + " domains: error=%d\n", current->tgid, err); + +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(entry->dir); +#endif + + /* Free the per-process descriptor */ + OSAL_Free(entry); +} + +/** Driver's open method + * Allocates per-process resources: private data, wait queue, + * memory area descriptors linked list, message queue. + * + * \return POSIX error code + */ +static int cmld_open(struct inode *inode, struct file *file) +{ + struct cm_process_priv *procPriv = getProcessPriv(); + + if (IS_ERR(procPriv)) + return PTR_ERR(procPriv); + + if (iminor(inode) == 0) + file->private_data = procPriv; + else { + struct cm_channel_priv *channelPriv = (struct cm_channel_priv*)OSAL_Alloc(sizeof(*channelPriv)); + if (channelPriv == NULL) { + kref_put(&procPriv->ref, freeProcessPriv); + return -ENOMEM; + } + + channelPriv->proc = procPriv; + channelPriv->state = CHANNEL_OPEN; + + /* Initialize wait_queue, lists and mutexes */ + init_waitqueue_head(&channelPriv->waitq); + plist_head_init(&channelPriv->messageQueue); + INIT_LIST_HEAD(&channelPriv->skelList); + spin_lock_init(&channelPriv->bh_lock); + mutex_init(&channelPriv->msgQueueLock); + mutex_init(&channelPriv->skelListLock); + + tasklet_disable(&cmld_service_tasklet); + mutex_lock(&channel_lock); + list_add(&channelPriv->entry, &channel_list); + mutex_unlock(&channel_lock); + tasklet_enable(&cmld_service_tasklet); + + file->private_data = channelPriv; // store channel private struct in file descriptor + } + return 0; +} + +/** Driver's release method. + * Frees any per-process pending resource: components, bindings, memory areas. + * + * \return POSIX error code + */ +static int cmld_release(struct inode *inode, struct file *file) +{ + struct cm_process_priv* procPriv; + + /* The driver must guarantee that all related resources are released. + Thus all these checks below are necessary to release all remaining + resources still linked to this 'client', in case of abnormal process + exit. + => These are error cases ! + In the usual case, nothing should be done except the free of + the cmPriv itself + */ + + if (iminor(inode) != 0) { + struct cm_channel_priv* channelPriv; + channelPriv = file->private_data; + procPriv = channelPriv->proc; + + /* We don't need to synchronize here by using the skelListLock: + the list is only accessed during ioctl() and we can't be here + if an ioctl() is on-going */ + if (list_empty(&channelPriv->skelList)) { + /* There is no pending MPC->HOST binding + => we can quietly delete the channel */ + tasklet_disable(&cmld_service_tasklet); + mutex_lock(&channel_lock); + list_del(&channelPriv->entry); + mutex_unlock(&channel_lock); + tasklet_enable(&cmld_service_tasklet); + + /* Free all remaining messages if any */ + freeMessages(channelPriv); + + /* Free the per-channel descriptor */ + OSAL_Free(channelPriv); + } else { + /* Uh: there are still some MPC->HOST binding but we don't have the + required info to unbind them. + => we must keep all skel structures because possibly used in OSAL_PostDfc + (incoming callback msg) */ + /* We flag the channel as closed to discard any new msg that will never be read anyway */ + channelPriv->state = CHANNEL_CLOSED; + + /* Already Free all remaining messages if any, + they will never be read anyway */ + freeMessages(channelPriv); + } + } else + procPriv = file->private_data; + + kref_put(&procPriv->ref, freeProcessPriv); + file->private_data = NULL; + + return 0; +} + +/** Reads Component Manager messages destinated to this process. + * The message is composed by three fields: + * 1) mpc2host handle (distinguishes interfaces) + * 2) methodIndex (distinguishes interface's methods) + * 3) Variable length parameters (method's parameters values) + * + * \note cfr GetEvent() + * \return POSIX error code + */ +static ssize_t cmld_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + int err = 0; + struct cm_channel_priv* channelPriv = (struct cm_channel_priv*)(file->private_data); + int msgSize = 0; + struct plist_head* messageQueue; + struct osal_msg* msg; + t_os_message *os_msg = (t_os_message *)buf; + int block = !(file->f_flags & O_NONBLOCK); + + if (iminor(file->f_dentry->d_inode) == 0) + return -ENOSYS; + + messageQueue = &channelPriv->messageQueue; + + if (mutex_lock_killable(&channelPriv->msgQueueLock)) + return -ERESTARTSYS; + +wait: + while (plist_head_empty(messageQueue)) { + mutex_unlock(&channelPriv->msgQueueLock); + if (block == 0) + return -EAGAIN; + /* Wait until there is a message to ferry up */ + if (wait_event_interruptible(channelPriv->waitq, ((!plist_head_empty(messageQueue)) || (file->f_flags & O_FLUSH)))) + return -ERESTARTSYS; + if (file->f_flags & O_FLUSH) { + file->f_flags &= ~O_FLUSH; + return 0; + } + if (mutex_lock_killable(&channelPriv->msgQueueLock)) + return -ERESTARTSYS; + } + + /* Pick up the first message from the queue, making sure that the + * hwsem tasklet does not wreak havoc the queue in the meantime + */ + spin_lock_bh(&channelPriv->bh_lock); + msg = plist_first_entry(messageQueue, struct osal_msg, msg_entry); + plist_del(&msg->msg_entry, messageQueue); + spin_unlock_bh(&channelPriv->bh_lock); + + switch (msg->msg_type) { + case MSG_INTERFACE: { + + /* Check if enough space is available */ + msgSize = sizeof(msg->msg_type) + msg->d.itf.ptrSize + sizeof(os_msg->data.itf) - sizeof(os_msg->data.itf.params) ; + if (msgSize > count) { + mutex_unlock(&channelPriv->msgQueueLock); + pr_err("CM: message size bigger than buffer size silently ignored!\n"); + err = -EMSGSIZE; + goto out; + } + + /* Copy to user message type */ + err = put_user(msg->msg_type, &os_msg->type); + if (err) goto ack_evt; + + /* Copy to user the t_nmf_mpc2host_handle */ + err = put_user(msg->d.itf.skelwrap->upperLayerThis, &os_msg->data.itf.THIS); + if (err) goto ack_evt; + + /* The methodIndex */ + err = put_user(msg->d.itf.methodIdx, &os_msg->data.itf.methodIndex); + if (err) goto ack_evt; + + /* And the parameters */ + err = copy_to_user(os_msg->data.itf.params, msg->d.itf.anyPtr, msg->d.itf.ptrSize); + + ack_evt: + /* This call is void */ + /* Note: that we cannot release the lock before having called this function + as acknowledgements MUST be executed in the same order as their + respective messages have arrived! */ + CM_ENGINE_AcknowledgeEvent(msg->d.itf.skelwrap->mpc2hostId); + + mutex_unlock(&channelPriv->msgQueueLock); + break; + } + case MSG_SERVICE: { + mutex_unlock(&channelPriv->msgQueueLock); + msgSize = sizeof(msg->msg_type) + sizeof(msg->d.srv.srvType) + + sizeof(msg->d.srv.srvData); + if (count < msgSize) { + pr_err("CM: service message size bigger than buffer size - silently ignored!\n"); + err = -EMSGSIZE; + } + + /* Copy to user message type */ + err = put_user(msg->msg_type, &os_msg->type); + if (err) goto out; + err = copy_to_user(&os_msg->data.srv, &msg->d.srv, + sizeof(msg->d.srv.srvType) + sizeof(msg->d.srv.srvData)); + break; + } + default: + mutex_unlock(&channelPriv->msgQueueLock); + pr_err("CM: invalid message type %d discarded\n", msg->msg_type); + goto wait; + } +out: + /* Destroy the message */ + kfree(msg); + + return err ? err : msgSize; +} + +/** Part of driver's release method. (ie userspace close()) + * It wakes up all waiter. + * + * \return POSIX error code + */ +static int cmld_flush(struct file *file, fl_owner_t id) +{ + if (iminor(file->f_dentry->d_inode) != 0) { + struct cm_channel_priv* channelPriv = (struct cm_channel_priv*)(file->private_data); + file->f_flags |= O_FLUSH; + wake_up_interruptible(&channelPriv->waitq); + } + return 0; +} + +static long cmld_channel_ctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct cm_channel_priv *channelPriv = file->private_data; + + switch(cmd) { + /* + * All channel CM SYSCALL + */ + case CM_BINDCOMPONENTTOCMCORE: + return cmld_BindComponentToCMCore(channelPriv, (CM_BindComponentToCMCore_t *)arg); + case CM_FLUSHCHANNEL: + return cmld_flush(file, 0); + default: + pr_err("CM(%s): unsupported command %i\n", __func__, cmd); + return -EINVAL; + } + return 0; +} + +static long cmld_control_ctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct cm_process_priv* procPriv = file->private_data; + switch(cmd) { + /* + * All wrapped CM SYSCALL + */ + case CM_INSTANTIATECOMPONENT: + return cmld_InstantiateComponent(procPriv, + (CM_InstantiateComponent_t *)arg); + + case CM_BINDCOMPONENTFROMCMCORE: + return cmld_BindComponentFromCMCore(procPriv, + (CM_BindComponentFromCMCore_t *)arg); + + case CM_UNBINDCOMPONENTFROMCMCORE: + return cmld_UnbindComponentFromCMCore((CM_UnbindComponentFromCMCore_t *)arg); + + case CM_UNBINDCOMPONENTTOCMCORE: + return cmld_UnbindComponentToCMCore(procPriv, (CM_UnbindComponentToCMCore_t *)arg); + + case CM_BINDCOMPONENTASYNCHRONOUS: + return cmld_BindComponentAsynchronous(procPriv, (CM_BindComponentAsynchronous_t *)arg); + + case CM_UNBINDCOMPONENTASYNCHRONOUS: + return cmld_UnbindComponentAsynchronous(procPriv, (CM_UnbindComponentAsynchronous_t *)arg); + + case CM_BINDCOMPONENT: + return cmld_BindComponent(procPriv, (CM_BindComponent_t *)arg); + + case CM_UNBINDCOMPONENT: + return cmld_UnbindComponent(procPriv, (CM_UnbindComponent_t *)arg); + + case CM_BINDCOMPONENTTOVOID: + return cmld_BindComponentToVoid(procPriv, (CM_BindComponentToVoid_t *)arg); + + case CM_DESTROYCOMPONENT: + return cmld_DestroyComponent(procPriv, (CM_DestroyComponent_t *)arg); + + case CM_CREATEMEMORYDOMAIN: + return cmld_CreateMemoryDomain(procPriv, (CM_CreateMemoryDomain_t *)arg); + + case CM_CREATEMEMORYDOMAINSCRATCH: + return cmld_CreateMemoryDomainScratch(procPriv, (CM_CreateMemoryDomainScratch_t *)arg); + + case CM_DESTROYMEMORYDOMAIN: + return cmld_DestroyMemoryDomain((CM_DestroyMemoryDomain_t *)arg); + + case CM_GETDOMAINCOREID: + return cmld_GetDomainCoreId((CM_GetDomainCoreId_t *)arg); + + case CM_ALLOCMPCMEMORY: + return cmld_AllocMpcMemory(procPriv, (CM_AllocMpcMemory_t *)arg); + + case CM_FREEMPCMEMORY: + return cmld_FreeMpcMemory(procPriv, (CM_FreeMpcMemory_t *)arg); + + case CM_GETMPCMEMORYSTATUS: + return cmld_GetMpcMemoryStatus((CM_GetMpcMemoryStatus_t *)arg); + + case CM_STARTCOMPONENT: + return cmld_StartComponent(procPriv, (CM_StartComponent_t *)arg); + + case CM_STOPCOMPONENT: + return cmld_StopComponent(procPriv, (CM_StopComponent_t *)arg); + + case CM_GETMPCLOADCOUNTER: + return cmld_GetMpcLoadCounter((CM_GetMpcLoadCounter_t *)arg); + + case CM_GETCOMPONENTDESCRIPTION: + return cmld_GetComponentDescription(procPriv, (CM_GetComponentDescription_t *)arg); + + case CM_GETCOMPONENTLISTHEADER: + return cmld_GetComponentListHeader(procPriv, (CM_GetComponentListHeader_t *)arg); + + case CM_GETCOMPONENTLISTNEXT: + return cmld_GetComponentListNext(procPriv, (CM_GetComponentListNext_t *)arg); + + case CM_GETCOMPONENTREQUIREDINTERFACENUMBER: + return cmld_GetComponentRequiredInterfaceNumber(procPriv, + (CM_GetComponentRequiredInterfaceNumber_t *)arg); + + case CM_GETCOMPONENTREQUIREDINTERFACE: + return cmld_GetComponentRequiredInterface(procPriv, + (CM_GetComponentRequiredInterface_t *)arg); + + case CM_GETCOMPONENTREQUIREDINTERFACEBINDING: + return cmld_GetComponentRequiredInterfaceBinding(procPriv, + (CM_GetComponentRequiredInterfaceBinding_t *)arg); + + case CM_GETCOMPONENTPROVIDEDINTERFACENUMBER: + return cmld_GetComponentProvidedInterfaceNumber(procPriv, + (CM_GetComponentProvidedInterfaceNumber_t *)arg); + + case CM_GETCOMPONENTPROVIDEDINTERFACE: + return cmld_GetComponentProvidedInterface(procPriv, + (CM_GetComponentProvidedInterface_t *)arg); + + case CM_GETCOMPONENTPROPERTYNUMBER: + return cmld_GetComponentPropertyNumber(procPriv, + (CM_GetComponentPropertyNumber_t *)arg); + + case CM_GETCOMPONENTPROPERTYNAME: + return cmld_GetComponentPropertyName(procPriv, + (CM_GetComponentPropertyName_t *)arg); + + case CM_GETCOMPONENTPROPERTYVALUE: + return cmld_GetComponentPropertyValue(procPriv, + (CM_GetComponentPropertyValue_t *)arg); + + case CM_READCOMPONENTATTRIBUTE: + return cmld_ReadComponentAttribute(procPriv, + (CM_ReadComponentAttribute_t *)arg); + + case CM_GETEXECUTIVEENGINEHANDLE: + return cmld_GetExecutiveEngineHandle(procPriv, + (CM_GetExecutiveEngineHandle_t *)arg); + + case CM_SETMODE: + return cmld_SetMode((CM_SetMode_t *)arg); + + case CM_GETREQUIREDCOMPONENTFILES: + return cmld_GetRequiredComponentFiles(procPriv, + (CM_GetRequiredComponentFiles_t *)arg); + + case CM_MIGRATE: + return cmld_Migrate((CM_Migrate_t *)arg); + + case CM_UNMIGRATE: + return cmld_Unmigrate((CM_Unmigrate_t *)arg); + + case CM_SETUPRELINKAREA: + return cmld_SetupRelinkArea(procPriv, + (CM_SetupRelinkArea_t *)arg); + + case CM_PUSHCOMPONENT: + return cmld_PushComponent((CM_PushComponent_t *)arg); + + case CM_RELEASECOMPONENT: + return cmld_ReleaseComponent((CM_ReleaseComponent_t *)arg); + + /* + * NMF CALLS (Host->MPC bindings) + */ + case CM_PUSHEVENTWITHSIZE: { + CM_PushEventWithSize_t data; + t_event_params_handle event; + + /* coverity[tainted_data_argument : FALSE] */ + if (copy_from_user(&data.in, (CM_PushEventWithSize_t*)arg, sizeof(data.in))) + return -EFAULT; + + /* Take the lock to synchronize CM_ENGINE_AllocEvent() + * and CM_ENGINE_PushEvent() + */ + if (mutex_lock_killable(&procPriv->host2mpcLock)) + return -ERESTARTSYS; + + event = CM_ENGINE_AllocEvent(data.in.host2mpcId); + if (event == NULL) { + mutex_unlock(&procPriv->host2mpcLock); + return put_user(CM_PARAM_FIFO_OVERFLOW, + &((CM_PushEventWithSize_t*)arg)->out.error); + } + if (data.in.size != 0) + /* coverity[tainted_data : FALSE] */ + if (copy_from_user(event, data.in.h, data.in.size)) { + mutex_unlock(&procPriv->host2mpcLock); + return -EFAULT; // TODO: what about the already allocated and acknowledged event!?! + } + + data.out.error = CM_ENGINE_PushEvent(data.in.host2mpcId, event, data.in.methodIndex); + mutex_unlock(&procPriv->host2mpcLock); + + /* copy error value back */ + return put_user(data.out.error, &((CM_PushEventWithSize_t*)arg)->out.error); + } + + /* + * All private (internal) commands + */ + case CM_PRIVGETMPCMEMORYDESC: + return cmld_PrivGetMPCMemoryDesc(procPriv, (CM_PrivGetMPCMemoryDesc_t *)arg); + + case CM_PRIVRESERVEMEMORY: + return cmld_PrivReserveMemory(procPriv, arg); + + case CM_GETVERSION: { + t_uint32 nmfversion = NMF_VERSION; + return copy_to_user((void*)arg, &nmfversion, sizeof(nmfversion)); + } + case CM_PRIV_GETBOARDVERSION: + if (cpu_is_u8500v20_or_later()) { + enum board_version v = U8500_V2; + return copy_to_user((void*)arg, &v, sizeof(v)); + } else + return -EINVAL; + case CM_PRIV_ISCOMPONENTCACHEEMPTY: + if (CM_ENGINE_IsComponentCacheEmpty()) + return 0; + else + return -ENOENT; + case CM_PRIV_DEBUGFS_READY: +#ifdef CONFIG_DEBUG_FS + cmld_user_has_debugfs = true; +#endif + return 0; + case CM_PRIV_DEBUGFS_DUMP_DONE: + case CM_PRIV_DEBUGFS_WAIT_DUMP: + return 0; + default: + pr_err("CM(%s): unsupported command %i\n", __func__, cmd); + return -EINVAL; + } + + return 0; +} + +/** Driver's ioctl method + * Implements user/kernel crossing for SYSCALL API. + * + * \return POSIX error code + */ +static long cmld_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ +#ifdef CONFIG_DEBUG_FS + if (cmd == CM_PRIV_DEBUGFS_DUMP_DONE) { + cmld_dump_ongoing = false; + wake_up_interruptible(&dump_waitq); + return 0; + } else if (wait_event_interruptible(dump_waitq, (!cmld_dump_ongoing))) + return -ERESTARTSYS; +#endif + + if (iminor(filp->f_dentry->d_inode) == 0) { + return cmld_control_ctl(filp, cmd, arg); + } else { + return cmld_channel_ctl(filp, cmd, arg); + } +} + +/** VMA open callback function + */ +static void cmld_vma_open(struct vm_area_struct* vma) { + struct memAreaDesc_t* curr = (struct memAreaDesc_t*)vma->vm_private_data; + + atomic_inc(&curr->count); +} + +/** VMA close callback function + */ +static void cmld_vma_close(struct vm_area_struct* vma) { + struct memAreaDesc_t* curr = (struct memAreaDesc_t*)vma->vm_private_data; + + atomic_dec(&curr->count); +} + +static struct vm_operations_struct cmld_remap_vm_ops = { + .open = cmld_vma_open, + .close = cmld_vma_close, +}; + +/** mmap implementation. + * Remaps just once. + * + * \return POSIX error code + */ +static int cmld_mmap(struct file* file, struct vm_area_struct* vma) +{ + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + struct list_head* listHead; + struct list_head* cursor; + struct cm_process_priv* procPriv = file->private_data; + struct memAreaDesc_t* curr = NULL; + unsigned int vma_size = vma->vm_end-vma->vm_start; + + listHead = &procPriv->memAreaDescList; + + if (lock_process(procPriv)) return -ERESTARTSYS; + /* Make sure the memory area has not already been remapped */ + list_for_each(cursor, listHead) { + curr = list_entry(cursor, struct memAreaDesc_t, list); + /* For now, the user space aligns any requested physaddr to a page-size limit + This is not safe and must be fixed. But this is the only way to + minimize the allocated TCM memory, needed because of low amount of + TCM memory + Another way is to add some more check before doing this mmap() + to allow this mmap, for example. + NOTE: this memory must be first reserved via the CM_PRIVRESERVEMEMORY ioctl() + */ + if ((curr->physAddr&PAGE_MASK) == offset && + curr->tid == current->pid) { + if (curr->userLogicalAddr) { + unlock_process(procPriv); + return -EINVAL; // already mapped! + } + /* reset the thread id value, to not confuse any further mmap() */ + curr->tid = 0; + break; + } + } + + if (cursor == listHead) { + unlock_process(procPriv); + return -EINVAL; // no matching memory area descriptor found! + } + + /* Very, very important to have consistent buffer transition */ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTEXPAND | VM_DONTCOPY; + + if (remap_pfn_range(vma, vma->vm_start, offset>>PAGE_SHIFT, + vma_size, vma->vm_page_prot)) { + unlock_process(procPriv); + return -EAGAIN; + } + + /* Offset represents the physical address. + * Update the list entry filling in the logical address assigned to the user + */ + /* + * NOTE: here the useLogicalAddr is page-aligned, but not necessaly the + * phycical address. We mmap() more than originaly requested by the + * user, see in CM User Proxy (file cmsyscallwrapper.c) + */ + curr->userLogicalAddr = vma->vm_start; + + /* increment reference counter */ + atomic_inc(&curr->count); + + unlock_process(procPriv); + + /* set private data structure and callbacks */ + vma->vm_private_data = (void *)curr; + vma->vm_ops = &cmld_remap_vm_ops; + + return 0; +} + +/** MPC Events tasklet + * The parameter is used to know from which interrupts we're comming + * and which core to pass to CM_ProcessMpcEvent(): + * 0 means HSEM => ARM_CORE_ID + * otherwise, it gives the index+1 of MPC within osalEnv.mpc table + */ +static void mpc_events_tasklet_handler(unsigned long core) +{ + /* This serves internal events directly. No propagation to user space. + * Calls OSAL_PostDfc implementation for user interface events */ + if (core == 0) { + CM_ProcessMpcEvent(ARM_CORE_ID); + enable_irq(IRQ_DB8500_HSEM); + } else { + --core; + CM_ProcessMpcEvent(osalEnv.mpc[core].coreId); + enable_irq(osalEnv.mpc[core].interrupt0); + } +} + +/** Hardware semaphore and MPC interrupt handler + * 'data' param is the one given when registering the IRQ hanlder, + * contains the source core (ARM or MPC), and follows the same logic + * as for mpc_events_tasklet_handler() + * This handler is used for all IRQ handling some com (ie HSEM or + * all MPC IRQ line0) + */ +static irqreturn_t mpc_events_irq_handler(int irq, void *data) +{ + unsigned core = (unsigned)data; + + if (core != 0) + --core; + disable_irq_nosync(irq); + tasklet_schedule(&osalEnv.mpc[core].tasklet); + + return IRQ_HANDLED; +} + +/** MPC panic handler + * 'idx' contains the index of the core within the osalEnv.mpc table. + * This handler is used for all MPC IRQ line1 + */ +static irqreturn_t panic_handler(int irq, void *idx) +{ + set_bit((int)idx, &service_tasklet_data); + disable_irq_nosync(irq); + tasklet_schedule(&cmld_service_tasklet); + return IRQ_HANDLED; +} + +/** Driver's operations + */ +static struct file_operations cmld_fops = { + .owner = THIS_MODULE, + .read = cmld_read, + .unlocked_ioctl = cmld_ioctl, + .mmap = cmld_mmap, + .open = cmld_open, + .flush = cmld_flush, + .release = cmld_release, +}; + +/** + * Configure a MPC, called for each MPC to configure + * + * \param i index of the MPC to configure (refer to the index + * of the MPC within the osalEnvironment.mpc table) + * \param dataAllocId allocId of the data segment, passed through each call of + * this function, and initialized at the first call in case + * shared data segment + */ +static int configureMpc(unsigned i, t_cfg_allocator_id *dataAllocId) +{ + int err; + t_cm_system_address mpcSystemAddress; + t_nmf_memory_segment codeSegment, dataSegment; + t_cfg_allocator_id codeAllocId; + t_cm_domain_id eeDomainId; + t_cm_domain_memory eeDomain = INIT_DOMAIN; + char regulator_name[14]; + + getMpcSystemAddress(i, &mpcSystemAddress); + getMpcSdramSegments(i, &codeSegment, &dataSegment); + + /* Create code segment */ + err = CM_ENGINE_AddMpcSdramSegment(&codeSegment, &codeAllocId, "Code"); + if (err != CM_OK) { + pr_err("CM_ENGINE_AddMpcSdramSegment() error code: %d\n", err); + return -EAGAIN; + } + + /* Create data segment + * NOTE: in case of shared data segment, all MPC point to the same data segment + * (see in remapRegions()) and we need to create the segment only at first call. + * => we reuse the same allocId for the following MPCs + */ + if ((osalEnv.mpc[i].sdram_data.data != osalEnv.mpc[0].sdram_data.data) + || *dataAllocId == -1) { + err = CM_ENGINE_AddMpcSdramSegment(&dataSegment, dataAllocId, "Data"); + if (err != CM_OK) { + pr_err("CM_ENGINE_AddMpcSdramSegment() error code: %d\n", err); + return -EAGAIN; + } + } + + /* create default domain for the given coreId + * this serves for instanciating EE and the LoadMap, only sdram segment is present + * this domain will probably overlap with other user domains + */ + eeDomain.coreId = osalEnv.mpc[i].coreId; + eeDomain.sdramCode.offset = 0x0; + eeDomain.sdramData.offset = 0x0; + eeDomain.sdramCode.size = 0x8000; + eeDomain.sdramData.size = 0x40000; + eeDomain.esramCode.size = 0x4000; + eeDomain.esramData.size = 0x40000; + err = CM_ENGINE_CreateMemoryDomain(NMF_CORE_CLIENT, &eeDomain, &eeDomainId); + if (err != CM_OK) { + pr_err("Create EE domain on %s failed with error code: %d\n", osalEnv.mpc[i].name, err); + return -EAGAIN; + } + + err = CM_ENGINE_ConfigureMediaProcessorCore( + osalEnv.mpc[i].coreId, + osalEnv.mpc[i].eeId, + (cfgSemaphoreTypeHSEM ? SYSTEM_SEMAPHORES : LOCAL_SEMAPHORES), + osalEnv.mpc[i].nbYramBanks, + &mpcSystemAddress, + eeDomainId, + codeAllocId, + *dataAllocId); + + if (err != CM_OK) { + pr_err("CM_ConfigureMediaProcessorCore failed with error code: %d\n", err); + return -EAGAIN; + } + + // Communication channel + if (! cfgSemaphoreTypeHSEM) { + tasklet_init(&osalEnv.mpc[i].tasklet, mpc_events_tasklet_handler, i+1); + err = request_irq(osalEnv.mpc[i].interrupt0, mpc_events_irq_handler, IRQF_DISABLED, osalEnv.mpc[i].name, (void*)(i+1)); + if (err != 0) { + pr_err("CM: request_irq failed to register irq0 %i for %s (%i)\n", osalEnv.mpc[i].interrupt0, osalEnv.mpc[i].name, err); + return err; + } + } + + // Panic channel + err = request_irq(osalEnv.mpc[i].interrupt1, panic_handler, IRQF_DISABLED, osalEnv.mpc[i].name, (void*)i); + if (err != 0) { + pr_err("CM: request_irq failed to register irq1 %i for %s (%i)\n", osalEnv.mpc[i].interrupt1, osalEnv.mpc[i].name, err); + free_irq(osalEnv.mpc[i].interrupt0, (void*)(i+1)); + return err; + } + + // Retrieve the regulators used for this MPCs + sprintf(regulator_name, "%s-mmdsp", osalEnv.mpc[i].name); + osalEnv.mpc[i].mmdsp_regulator = regulator_get(cmld_dev[0], regulator_name); + if (IS_ERR(osalEnv.mpc[i].mmdsp_regulator)) { + long err = PTR_ERR(osalEnv.mpc[i].mmdsp_regulator); + pr_err("CM: Error while retrieving the regulator %s: %ld\n", regulator_name, err); + osalEnv.mpc[i].mmdsp_regulator = NULL; + return err; + } + sprintf(regulator_name, "%s-pipe", osalEnv.mpc[i].name); + osalEnv.mpc[i].pipe_regulator = regulator_get(cmld_dev[0], regulator_name); + if (IS_ERR(osalEnv.mpc[i].pipe_regulator)) { + long err = PTR_ERR(osalEnv.mpc[i].pipe_regulator); + pr_err("CM: Error while retrieving the regulator %s: %ld\n", regulator_name, err); + osalEnv.mpc[i].pipe_regulator = NULL; + return err; + } +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&osalEnv.mpc[i].wakelock, WAKE_LOCK_SUSPEND, osalEnv.mpc[i].name); +#endif + return 0; +} + +/* Free all used MPC irqs and clocks. + * max_mpc allows it to be called from init_module and free + * only the already configured irqs. + */ +static void free_mpc_irqs(int max_mpc) +{ + int i; + for (i=0; i<max_mpc; i++) { + if (! cfgSemaphoreTypeHSEM) + free_irq(osalEnv.mpc[i].interrupt0, (void*)(i+1)); + free_irq(osalEnv.mpc[i].interrupt1, (void*)i); + if (osalEnv.mpc[i].mmdsp_regulator) + regulator_put(osalEnv.mpc[i].mmdsp_regulator); + if (osalEnv.mpc[i].pipe_regulator) + regulator_put(osalEnv.mpc[i].pipe_regulator); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&osalEnv.mpc[i].wakelock); +#endif + } +} + +/** Module entry point + * Allocate memory chunks. Register hardware semaphore, SIA and SVA interrupts. + * Initialize Component Manager. Register hotplug for components download. + * + * \return POSIX error code + */ +static int __init cmld_init_module(void) +{ + int err; + unsigned i=0; + dev_t dev; + t_cfg_allocator_id dataAllocId = -1; + void *htim_base=NULL; + + /* Component manager initialization descriptors */ + t_nmf_hw_mapping_desc nmfHwMappingDesc; + t_nmf_config_desc nmfConfigDesc = { cfgCommunicationLocationInSDRAM ? COMS_IN_SDRAM : COMS_IN_ESRAM }; + + /* OSAL_*Resources() assumes the following, so check that it is correct */ + if (SVA != COREIDX((int)SVA_CORE_ID)) { + pr_err("SVA and (SVA_CORE_ID-1) differs : code must be fixed !\n"); + return -EIO; + } + if (SIA != COREIDX((int)SIA_CORE_ID)) { + pr_err("SIA and (SIA_CORE_ID-1) differs : code must be fixed !\n"); + return -EIO; + } + +#ifdef CM_DEBUG_ALLOC + init_debug_alloc(); +#endif + + err = -EIO; + prcmu_base = __io_address(U8500_PRCMU_BASE); + if (cpu_is_u8500v20_or_later()) { + /* power on a clock/timer 90KHz used on SVA */ + htim_base = ioremap_nocache(U8500_CR_BASE /*0xA03C8000*/, SZ_4K); + prcmu_tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE); + } else { + pr_err("CM: Unsupported chip version\n"); + goto out; + } + + /* Activate SVA 90 KHz timer */ + if (htim_base == NULL) + goto out; + iowrite32((1<<26) | ioread32(htim_base), htim_base); + iounmap(htim_base); + + /*i = ioread32(PRCM_SVAMMDSPCLK_MGT) & 0xFF; + if (i != 0x22) + pr_alert("CM: Looks like SVA is not clocked at 200MHz (PRCM_SVAMMDSPCLK_MGT=%x)\n", i); + i = ioread32(PRCM_SIAMMDSPCLK_MGT) & 0xFF; + if (i != 0x22) + pr_alert("CM: Looks like SIA is not clocked at 200MHz (PRCM_SIAMMDSPCLK_MGT=%x)\n", i); + + i = 0;*/ + err = init_config(); + if (err) + goto out; + + /* Remap all needed regions and store in osalEnv base addresses */ + err = remapRegions(); + if (err != 0) + goto out; + + /* Initialize linux devices */ + err = class_register(&cmld_class); + if (err) { + pr_err("CM: class_register failed (%d)\n", err); + goto out; + } + + /* Register char device */ + err = alloc_chrdev_region(&dev, 0, ARRAY_SIZE(cmld_devname), "cm"); + if (err) { + pr_err("CM: alloc_chrdev_region failed (%d)\n", err); + goto out_destroy_class; + } + cmld_major = MAJOR(dev); + + cdev_init(&cmld_cdev, &cmld_fops); + cmld_cdev.owner = THIS_MODULE; + err = cdev_add (&cmld_cdev, dev, ARRAY_SIZE(cmld_devname)); + if (err) { + pr_err("CM: cdev_add failed (%d)\n", err); + goto out_destroy_chrdev; + } + + for (i=0; i<ARRAY_SIZE(cmld_devname); i++) { + cmld_dev[i] = device_create(&cmld_class, NULL, MKDEV(cmld_major, i), NULL, + "%s", cmld_devname[i]); + if (IS_ERR(cmld_dev[i])) { + err = PTR_ERR(cmld_dev[i]); + pr_err("CM: device_create failed (%d)\n", err); + goto out_destroy_device; + } + } + + osalEnv.esram_regulator[ESRAM_12] = regulator_get(cmld_dev[0], "esram12"); + if (IS_ERR(osalEnv.esram_regulator[ESRAM_12])) { + err = PTR_ERR(osalEnv.esram_regulator[ESRAM_12]); + pr_err("CM: Error while retrieving the regulator for esram12: %d\n", err); + osalEnv.esram_regulator[ESRAM_12] = NULL; + goto out_destroy_device; + } + osalEnv.esram_regulator[ESRAM_34] = regulator_get(cmld_dev[0], "esram34"); + if (IS_ERR(osalEnv.esram_regulator[ESRAM_34])) { + err = PTR_ERR(osalEnv.esram_regulator[ESRAM_34]); + pr_err("CM: Error while retrieving the regulator for esram34: %d\n", err); + osalEnv.esram_regulator[ESRAM_34] = NULL; + goto out_destroy_device; + } + + /* Fill in the descriptors needed by CM_ENGINE_Init() */ + getNmfHwMappingDesc(&nmfHwMappingDesc); + + /* Initialize Component Manager */ + err = CM_ENGINE_Init(&nmfHwMappingDesc, &nmfConfigDesc); + if (err != CM_OK) { + pr_err("CM: CM_Init failed with error code: %d\n", err); + err = -EAGAIN; + goto out_destroy_device; + } else { + pr_info("Initialize NMF %d.%d.%d Component Manager......\n", + VERSION_MAJOR(NMF_VERSION), + VERSION_MINOR(NMF_VERSION), + VERSION_PATCH(NMF_VERSION)); + pr_info("[ CM Linux Driver %d.%d.%d ]\n", + VERSION_MAJOR(NMF_VERSION), + VERSION_MINOR(NMF_VERSION), + CMDRIVER_PATCH_VERSION); + } + + cm_debug_init(); + if (osal_debug_ops.domain_create) { + osal_debug_ops.domain_create(DEFAULT_SVA_DOMAIN); + osal_debug_ops.domain_create(DEFAULT_SIA_DOMAIN); + } + + /* Configure MPC Cores */ + for (i=0; i<NB_MPC; i++) { + err = configureMpc(i, &dataAllocId); + if (err) + goto out_all; + } + /* End of Component Manager initialization phase */ + + + if (cfgSemaphoreTypeHSEM) { + /* We use tasklet of mpc[0]. See comments above osalEnvironnent struct */ + tasklet_init(&osalEnv.mpc[0].tasklet, mpc_events_tasklet_handler, 0); + err = request_irq(IRQ_DB8500_HSEM, mpc_events_irq_handler, IRQF_DISABLED, + "hwsem", 0); + if (err) { + pr_err("CM: request_irq failed to register hwsem irq %i (%i)\n", + IRQ_DB8500_HSEM, err); + goto out_all; + } + } + + err = cmdma_init(); + if (err == 0) + return 0; + +out_all: + cm_debug_exit(); + free_mpc_irqs(i); + CM_ENGINE_Destroy(); + i=ARRAY_SIZE(cmld_devname); +out_destroy_device: + if (osalEnv.esram_regulator[ESRAM_12]) + regulator_put(osalEnv.esram_regulator[ESRAM_12]); + if (osalEnv.esram_regulator[ESRAM_34]) + regulator_put(osalEnv.esram_regulator[ESRAM_34]); + while (i--) + device_destroy(&cmld_class, MKDEV(cmld_major, i)); + cdev_del(&cmld_cdev); +out_destroy_chrdev: + unregister_chrdev_region(dev, ARRAY_SIZE(cmld_devname)); +out_destroy_class: + class_unregister(&cmld_class); +out: + unmapRegions(); +#ifdef CM_DEBUG_ALLOC + cleanup_debug_alloc(); +#endif + return err; +} + +/** Module exit point + * Unregister the driver. This will lead to a 'remove' call. + */ +static void __exit cmld_cleanup_module(void) +{ + unsigned i; + + if (!list_empty(&channel_list)) + pr_err("CM Driver ending with non empty channel list\n"); + if (!list_empty(&process_list)) + pr_err("CM Driver ending with non empty process list\n"); + + if (cfgSemaphoreTypeHSEM) + free_irq(IRQ_DB8500_HSEM, NULL); + free_mpc_irqs(NB_MPC); + tasklet_kill(&cmld_service_tasklet); + + if (osalEnv.esram_regulator[ESRAM_12]) + regulator_put(osalEnv.esram_regulator[ESRAM_12]); + if (osalEnv.esram_regulator[ESRAM_34]) + regulator_put(osalEnv.esram_regulator[ESRAM_34]); + for (i=0; i<ARRAY_SIZE(cmld_devname); i++) + device_destroy(&cmld_class, MKDEV(cmld_major, i)); + cdev_del(&cmld_cdev); + unregister_chrdev_region(MKDEV(cmld_major, 0), ARRAY_SIZE(cmld_devname)); + class_unregister(&cmld_class); + + CM_ENGINE_Destroy(); + + cmdma_destroy(); + unmapRegions(); +#ifdef CM_DEBUG_ALLOC + cleanup_debug_alloc(); +#endif + cm_debug_exit(); +} +module_init(cmld_init_module); +module_exit(cmld_cleanup_module); + +MODULE_AUTHOR("David Siorpaes"); +MODULE_AUTHOR("Wolfgang Betz"); +MODULE_AUTHOR("Pierre Peiffer"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Nomadik Multiprocessing Framework Component Manager Linux driver"); diff --git a/drivers/staging/nmf-cm/cmld.h b/drivers/staging/nmf-cm/cmld.h new file mode 100644 index 00000000000..4c5a5bed7e6 --- /dev/null +++ b/drivers/staging/nmf-cm/cmld.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#ifndef CMLD_H +#define CMLD_H + +#include <linux/kref.h> +#include <linux/mutex.h> +#include <linux/version.h> +#include <linux/wait.h> +#include <inc/nmf-limits.h> +#include "cmioctl.h" + +/** Channel state used within the per-channel private structure 'cm_channel_priv' + */ +enum channel_state { + CHANNEL_CLOSED = 0, /**< Channel already closed */ + CHANNEL_OPEN, /**< Channel still open */ +}; + +/** Component Manager per-process private structure + * It is created the first time a process opens /dev/cm0 or /dev/cm1 + */ +struct cm_process_priv +{ + struct kref ref; /**< ref count */ + struct list_head entry; /**< This entry */ + pid_t pid; /**< pid of process owner */ + struct mutex mutex; /**< per process mutex: protect memAreaDescList */ + struct list_head memAreaDescList; /**< memAreaDesc_t list */ + struct mutex host2mpcLock; /**< used to synchronize each AllocEvent + PushEvent */ +#ifdef CONFIG_DEBUG_FS + struct dentry *dir; /**< debugfs dir entry under nmf-cm/proc */ + struct dentry *comp_dir; /**< debugfs dir entry under nmf-cm/proc/..%components */ + struct dentry *domain_dir; /**< debugfs dir entry under nmf-cm/proc/..%domains */ +#endif +}; + +/** Component Manager per-channel private structure + * It is created when a user opens /dev/cm1 + */ +struct cm_channel_priv +{ + enum channel_state state; /**< Channel state */ + struct list_head entry; /**< This entry */ + struct cm_process_priv *proc; /**< Pointer to the owner process structure */ + struct list_head skelList; /**< t_skelwrapper list */ + struct mutex skelListLock; /**< skelList mutex */ + struct plist_head messageQueue; /**< queueelem_t list */ + struct mutex msgQueueLock; /**< lock used to synchronize MPC to HOST bindings + in case of multiple read (see cmld_read comments) */ + spinlock_t bh_lock; /**< lock used to synchronize add/removal of element in/from + the message queue in both user context and tasklet */ + wait_queue_head_t waitq; /**< wait queue used to block read() call */ +}; + +/** Memory area descriptor. + */ +struct memAreaDesc_t { + struct list_head list; /**< Doubly linked list descriptor */ + atomic_t count; /**< Reference counter */ + pid_t tid; /**< tid of the process this area is reserved for */ + t_cm_memory_handle handle; /**< Component Manager handle */ + unsigned int size; /**< Size */ + unsigned int physAddr; /**< Physical address */ + unsigned int kernelLogicalAddr; /**< Logical address as seen by kernel */ + unsigned int userLogicalAddr; /**< Logical address as seen by user */ + unsigned int mpcPhysAddr; /**< Physicaladdress as seen by MPC */ + struct cm_process_priv* procPriv; /**< link to per process private structure */ +}; + +extern struct list_head channel_list; /**< List of all allocated channel structures */ +extern struct list_head process_list; /**< List of all allocated process private structure */ +#ifdef CONFIG_DEBUG_FS +extern bool cmld_user_has_debugfs; /**< Whether user side has proper support of debugfs to take a dump */ +extern bool cmld_dump_ongoing; /**< Whether a dump is on-going */ +#endif + +/** Lock/unlock per process mutex + * + * \note Must be taken before tasklet_disable (if necessary)! + */ +#define lock_process_uninterruptible(proc) (mutex_lock(&proc->mutex)) +#define lock_process(proc) (mutex_lock_killable(&proc->mutex)) +#define unlock_process(proc) (mutex_unlock(&proc->mutex)) + + + +int cmld_InstantiateComponent(struct cm_process_priv *, CM_InstantiateComponent_t __user *); +int cmld_BindComponentFromCMCore(struct cm_process_priv *, + CM_BindComponentFromCMCore_t __user *); +int cmld_UnbindComponentFromCMCore(CM_UnbindComponentFromCMCore_t __user *); +int cmld_BindComponentToCMCore(struct cm_channel_priv *, CM_BindComponentToCMCore_t __user *); +int cmld_UnbindComponentToCMCore(struct cm_process_priv*, CM_UnbindComponentToCMCore_t __user *); +int cmld_BindComponentAsynchronous(struct cm_process_priv*, CM_BindComponentAsynchronous_t __user *); +int cmld_UnbindComponentAsynchronous(struct cm_process_priv*, CM_UnbindComponentAsynchronous_t __user *); +int cmld_BindComponent(struct cm_process_priv*, CM_BindComponent_t __user *); +int cmld_UnbindComponent(struct cm_process_priv*, CM_UnbindComponent_t __user *); +int cmld_BindComponentToVoid(struct cm_process_priv*, CM_BindComponentToVoid_t __user *); +int cmld_DestroyComponent(struct cm_process_priv*, CM_DestroyComponent_t __user *); +int cmld_CreateMemoryDomain(struct cm_process_priv*, CM_CreateMemoryDomain_t __user *); +int cmld_CreateMemoryDomainScratch(struct cm_process_priv*, CM_CreateMemoryDomainScratch_t __user *); +int cmld_DestroyMemoryDomain(CM_DestroyMemoryDomain_t __user *); +int cmld_GetDomainCoreId(CM_GetDomainCoreId_t __user *); +int cmld_AllocMpcMemory(struct cm_process_priv *, CM_AllocMpcMemory_t __user *); +int cmld_FreeMpcMemory(struct cm_process_priv *, CM_FreeMpcMemory_t __user *); +int cmld_GetMpcMemoryStatus(CM_GetMpcMemoryStatus_t __user *); +int cmld_StartComponent(struct cm_process_priv *, CM_StartComponent_t __user *); +int cmld_StopComponent(struct cm_process_priv *, CM_StopComponent_t __user *); +int cmld_GetMpcLoadCounter(CM_GetMpcLoadCounter_t __user *); +int cmld_GetComponentDescription(struct cm_process_priv *, CM_GetComponentDescription_t __user *); +int cmld_GetComponentListHeader(struct cm_process_priv *, CM_GetComponentListHeader_t __user *); +int cmld_GetComponentListNext(struct cm_process_priv *, CM_GetComponentListNext_t __user *); +int cmld_GetComponentRequiredInterfaceNumber(struct cm_process_priv *, + CM_GetComponentRequiredInterfaceNumber_t __user *); +int cmld_GetComponentRequiredInterface(struct cm_process_priv *, + CM_GetComponentRequiredInterface_t __user *); +int cmld_GetComponentRequiredInterfaceBinding(struct cm_process_priv *, + CM_GetComponentRequiredInterfaceBinding_t __user *); +int cmld_GetComponentProvidedInterfaceNumber(struct cm_process_priv *, + CM_GetComponentProvidedInterfaceNumber_t __user *); +int cmld_GetComponentProvidedInterface(struct cm_process_priv *, + CM_GetComponentProvidedInterface_t __user *); +int cmld_GetComponentPropertyNumber(struct cm_process_priv *, + CM_GetComponentPropertyNumber_t __user *); +int cmld_GetComponentPropertyName(struct cm_process_priv *, CM_GetComponentPropertyName_t __user *); +int cmld_GetComponentPropertyValue(struct cm_process_priv *, CM_GetComponentPropertyValue_t __user *); +int cmld_ReadComponentAttribute(struct cm_process_priv *, CM_ReadComponentAttribute_t __user *); +int cmld_GetExecutiveEngineHandle(struct cm_process_priv *, CM_GetExecutiveEngineHandle_t __user *); +int cmld_SetMode(CM_SetMode_t __user *); +int cmld_GetRequiredComponentFiles(struct cm_process_priv *cmPriv, + CM_GetRequiredComponentFiles_t __user *); +int cmld_Migrate(CM_Migrate_t __user *); +int cmld_Unmigrate(CM_Unmigrate_t __user *); +int cmld_SetupRelinkArea(struct cm_process_priv *, CM_SetupRelinkArea_t __user *); +int cmld_PushComponent(CM_PushComponent_t __user *); +int cmld_ReleaseComponent(CM_ReleaseComponent_t __user *); +int cmld_PrivGetMPCMemoryDesc(struct cm_process_priv *, CM_PrivGetMPCMemoryDesc_t __user *); +int cmld_PrivReserveMemory(struct cm_process_priv *, unsigned int); +#endif diff --git a/drivers/staging/nmf-cm/configuration.c b/drivers/staging/nmf-cm/configuration.c new file mode 100644 index 00000000000..b61ab509d94 --- /dev/null +++ b/drivers/staging/nmf-cm/configuration.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +/** \file configuration.c + * + * Nomadik Multiprocessing Framework Linux Driver + * + */ + +#include <cm/engine/api/configuration_engine.h> +#include <cm/engine/configuration/inc/configuration_status.h> +#include <cm/engine/power_mgt/inc/power.h> +#include "osal-kernel.h" + +/* Per-driver environment */ +struct OsalEnvironment osalEnv = +{ + .mpc = { + { + .coreId = SVA_CORE_ID, + .name = "sva", + .base_phys = (void*)U8500_SVA_BASE, + .interrupt0 = IRQ_DB8500_SVA, + .interrupt1 = IRQ_DB8500_SVA2, + .mmdsp_regulator = NULL, + .pipe_regulator = NULL, + .monitor_tsk = NULL, + .hwmem_code = NULL, + .hwmem_data = NULL, + }, + { + .coreId = SIA_CORE_ID, + .name = "sia", + .base_phys = (void*)U8500_SIA_BASE, + .interrupt0 = IRQ_DB8500_SIA, + .interrupt1 = IRQ_DB8500_SIA2, + .mmdsp_regulator = NULL, + .pipe_regulator = NULL, + .monitor_tsk = NULL, + .hwmem_code = NULL, + .hwmem_data = NULL, + } + }, + .esram_regulator = { NULL, NULL}, + .dsp_sleep = { + .sia_auto_pm_enable = PRCMU_AUTO_PM_OFF, + .sia_power_on = 0, + .sia_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF, + .sva_auto_pm_enable = PRCMU_AUTO_PM_OFF, + .sva_power_on = 0, + .sva_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF, + }, + .dsp_idle = { + .sia_auto_pm_enable = PRCMU_AUTO_PM_OFF, + .sia_power_on = 0, + .sia_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF, + .sva_auto_pm_enable = PRCMU_AUTO_PM_OFF, + .sva_power_on = 0, + .sva_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF, + }, +}; + +module_param_call(cm_debug_level, param_set_int, param_get_int, + &cm_debug_level, S_IWUSR|S_IRUGO); +MODULE_PARM_DESC(cm_debug_level, "Debug level of NMF Core"); + +module_param_call(cm_error_break, param_set_bool, param_get_bool, + &cm_error_break, S_IWUSR|S_IRUGO); +MODULE_PARM_DESC(cm_error_break, "Stop on error (in an infinite loop, for debugging purpose)"); + +module_param_call(cmIntensiveCheckState, param_set_bool, param_get_bool, + &cmIntensiveCheckState, S_IWUSR|S_IRUGO); +MODULE_PARM_DESC(cmIntensiveCheckState, "Add additional intensive checks"); + +DECLARE_MPC_PARAM(SVA, SDRAM_DATA_SIZE, "", 1); + +DECLARE_MPC_PARAM(SIA, 0, "\n\t\t\t(0 means shared with SVA)", 2); + +bool cfgCommunicationLocationInSDRAM = true; +module_param(cfgCommunicationLocationInSDRAM, bool, S_IRUGO); +MODULE_PARM_DESC(cfgCommunicationLocationInSDRAM, "Location of communications (SDRAM or ESRAM)"); + +bool cfgSemaphoreTypeHSEM = true; +module_param(cfgSemaphoreTypeHSEM, bool, S_IRUGO); +MODULE_PARM_DESC(cfgSemaphoreTypeHSEM, "Semaphore used (HSEM or LSEM)"); + +int cfgESRAMSize = ESRAM_SIZE; +module_param(cfgESRAMSize, uint, S_IRUGO); +MODULE_PARM_DESC(cfgESRAMSize, "Size of ESRAM used in the CM (in Kb)"); + +static int set_param_powerMode(const char *val, const struct kernel_param *kp) +{ + /* No equals means "set"... */ + if (!val) val = "1"; + + /* One of =[yYnN01] */ + switch (val[0]) { + case 'y': case 'Y': case '1': + CM_ENGINE_SetMode(CM_CMD_DBG_MODE, 0); + break; + case 'n': case 'N': case '0': + CM_ENGINE_SetMode(CM_CMD_DBG_MODE, 1); + break; + default: + return -EINVAL; + } + return 0; +} + +module_param_call(powerMode, set_param_powerMode, param_get_bool, &powerMode, S_IWUSR|S_IRUGO); +MODULE_PARM_DESC(powerMode, "DSP power mode enable"); + +int init_config(void) +{ + if (cfgMpcSDRAMCodeSize_SVA == 0 || cfgMpcSDRAMCodeSize_SIA == 0) { + pr_err("SDRAM code size must be greater than 0\n"); + return -EINVAL; + } + + if (cfgMpcSDRAMDataSize_SVA == 0) { + pr_err("SDRAM data size for SVA must be greater than 0\n"); + return -EINVAL; + } + osalEnv.mpc[SVA].nbYramBanks = cfgMpcYBanks_SVA; + osalEnv.mpc[SVA].eeId = cfgSchedulerTypeHybrid_SVA ? HYBRID_EXECUTIVE_ENGINE : SYNCHRONOUS_EXECUTIVE_ENGINE; + osalEnv.mpc[SVA].sdram_code.size = cfgMpcSDRAMCodeSize_SVA * ONE_KB; + osalEnv.mpc[SVA].sdram_data.size = cfgMpcSDRAMDataSize_SVA * ONE_KB; + osalEnv.mpc[SVA].base.size = 128*ONE_KB; //we expose only TCM24 + osalEnv.mpc[SIA].nbYramBanks = cfgMpcYBanks_SIA; + osalEnv.mpc[SIA].eeId = cfgSchedulerTypeHybrid_SIA ? HYBRID_EXECUTIVE_ENGINE : SYNCHRONOUS_EXECUTIVE_ENGINE; + osalEnv.mpc[SIA].sdram_code.size = cfgMpcSDRAMCodeSize_SIA * ONE_KB; + osalEnv.mpc[SIA].sdram_data.size = cfgMpcSDRAMDataSize_SIA * ONE_KB; + osalEnv.mpc[SIA].base.size = 128*ONE_KB; //we expose only TCM24 + + return 0; +} diff --git a/drivers/staging/nmf-cm/configuration.h b/drivers/staging/nmf-cm/configuration.h new file mode 100644 index 00000000000..35072831a13 --- /dev/null +++ b/drivers/staging/nmf-cm/configuration.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + +/** Peripherals description. + * Some of these values are taken from kernel header description (which should be the + * right place of these definition); the missing ones are defined here. + */ + +#include <linux/version.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) +#include <generated/autoconf.h> +#else +#include <linux/autoconf.h> +#endif + +/* Embedded Static RAM base address */ +#define ESRAM_BASE (U8500_ESRAM_BASE + 0x10000) // V1/V2 config: 0-64k: secure; + +/* + * Embedded ram size for CM (in Kb) + * 5 banks of 128k: skip the first half bank (secure) and the last + * one (used for MCDE/B2R2), but include DMA part (4k after the secure part) + */ +#define ESRAM_SIZE 448 +enum { + ESRAM_12, + ESRAM_34, + NB_ESRAM, +}; + +/** MPCs */ +enum { + SVA, + SIA, + NB_MPC, +}; +#define COREIDX(id) (id-1) + +/** Base address of shared SDRAM: use upper SDRAM. We reserve a rather */ +#define SDRAM_CODE_SIZE_SVA (2*ONE_KB) +#define SDRAM_CODE_SIZE_SIA (2*ONE_KB) +#define SDRAM_DATA_SIZE (8*ONE_KB) + +extern bool cfgCommunicationLocationInSDRAM; +extern bool cfgSemaphoreTypeHSEM; +extern int cfgESRAMSize; + +int init_config(void); + +#define DECLARE_MPC_PARAM(mpc, sdramDataSize, extension, ybank) \ + static unsigned int cfgMpcYBanks_##mpc = ybank; \ + module_param(cfgMpcYBanks_##mpc, uint, S_IRUGO); \ + MODULE_PARM_DESC(cfgMpcYBanks_##mpc, "Nb of Y-Ram banks used on " #mpc); \ + \ + static int cfgSchedulerTypeHybrid_##mpc = 1; \ + module_param(cfgSchedulerTypeHybrid_##mpc, bool, S_IRUGO); \ + MODULE_PARM_DESC(cfgSchedulerTypeHybrid_##mpc, "Scheduler used on " #mpc " (Hybrid or Synchronous)"); \ + \ + static unsigned int cfgMpcSDRAMCodeSize_##mpc = SDRAM_CODE_SIZE_##mpc; \ + module_param(cfgMpcSDRAMCodeSize_##mpc, uint, S_IRUGO); \ + MODULE_PARM_DESC(cfgMpcSDRAMCodeSize_##mpc, "Size of code segment on " #mpc " (in Kb)"); \ + \ + static unsigned int cfgMpcSDRAMDataSize_##mpc = sdramDataSize; \ + module_param(cfgMpcSDRAMDataSize_##mpc, uint, S_IRUGO); \ + MODULE_PARM_DESC(cfgMpcSDRAMDataSize_##mpc, "Size of data segment on " #mpc " (in Kb)" extension) + +#endif diff --git a/drivers/staging/nmf-cm/ee/api/panic.idt b/drivers/staging/nmf-cm/ee/api/panic.idt new file mode 100644 index 00000000000..f971fdad8c5 --- /dev/null +++ b/drivers/staging/nmf-cm/ee/api/panic.idt @@ -0,0 +1,74 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \defgroup NMF_EE_TYPE Execution Engine Common Type Definitions + * \ingroup COMMON + */ + +#ifndef __INC_PANIC_IDT +#define __INC_PANIC_IDT + +/*! + * \brief Panic reason type + * + * For values, see \ref t_panic_reasonDescription. + * + * \ingroup NMF_EE_TYPE + */ +typedef t_uint8 t_panic_reason; + +/*! + * \brief The different panic reasons + * + * \verbatim + * Reason | Information | Behavior + * ------------------------------------------------------------------- + * INTERNAL_PANIC | Not interpreted | Fatal panic, stop MPC + * MPC_NOT_RESPONDING_PANIC | Not interpreted | Fatal panic, stop MPC + * USER_STACK_OVERFLOW | Faulting address & SPu | Fatal panic, stop MPC + * SYSTEM_STACK_OVERFLOW | Faulting address & SPu | Fatal panic, stop MPC + * UNALIGNED_LONG_ACCESS | Indicative Faulting address & SPu | Fatal panic, stop MPC + * EVENT_FIFO_OVERFLOW | 0 | Abort current task, stop MPC + * PARAM_FIFO_OVERFLOW | 0 | idem + * INTERFACE_NOT_BINDED | 0 | idem + * USER_PANIC | Not interpreted | idem + * UNBIND_INTERRUPT | Interrupt number | Do nothing, just return from interrupt. + * EVENT_FIFO_IN_USE | Destroy event Fifo while event already schedule (only for HostEE) + * \endverbatim + * + * \ingroup NMF_EE_TYPE + */ +typedef enum { + INTERNAL_PANIC = 1, + MPC_NOT_RESPONDING_PANIC = 2, + USER_STACK_OVERFLOW = 3, + SYSTEM_STACK_OVERFLOW = 4, + UNALIGNED_LONG_ACCESS = 5, + EVENT_FIFO_OVERFLOW = 6, + PARAM_FIFO_OVERFLOW = 7, + INTERFACE_NOT_BINDED = 8, + USER_PANIC = 9, + UNBIND_INTERRUPT = 10, + EVENT_FIFO_IN_USE = 11 +} t_panic_reasonDescription; + +/*! + * \brief Define the source of the panic + * + * It indicates the source core of the panic message.\n + * It gives the member to use within \ref t_nmf_panic_data (which is a member of the t_nmf_service_data service data structure). + + * \ingroup NMF_EE_TYPE + */ +typedef enum { + HOST_EE, //!< If the source is the Executive Engine running on the ARM Core + MPC_EE //!< If the source is the Executive Engine running on one of the MPC Core +} t_panic_source; + +#endif diff --git a/drivers/staging/nmf-cm/inc/nmf-def.h b/drivers/staging/nmf-cm/inc/nmf-def.h new file mode 100644 index 00000000000..e671198f7b9 --- /dev/null +++ b/drivers/staging/nmf-cm/inc/nmf-def.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) ST-Ericsson SA 2010. All rights reserved. + * This code is ST-Ericsson proprietary and confidential. + * Any use of the code for whatever purpose is subject to + * specific written permission of ST-Ericsson SA. + */ + /*! + * \brief NMF Version. + * + * This file contains the NMF Version. + * + * \defgroup NMF_VERSION NMF Version + * \ingroup COMMON + */ + +#ifndef __INC_NMF_DEF_H +#define __INC_NMF_DEF_H + +/*! + * \brief Current NMF version number + * + * \ingroup NMF_VERSION + */ +#define NMF_VERSION ((2 << 16) | (10 << 8) | (118)) + +/*! + * \brief Get NMF major version corresponding to NMF version number + * \ingroup NMF_VERSION + */ +#define VERSION_MAJOR(version) (((version) >> 16) & 0xFF) +/*! + * \brief Get NMF minor version corresponding to NMF version number + * \ingroup NMF_VERSION + */ +#define VERSION_MINOR(version) (((version) >> 8) & 0xFF) +/*! + * \brief Get NMF patch version corresponding to NMF version number + * \ingroup NMF_VERSION + */ +#define VERSION_PATCH(version) (((version) >> 0) & 0xFF) + +#endif /* __INC_NMF_DEF_H */ diff --git a/drivers/staging/nmf-cm/inc/nmf-limits.h b/drivers/staging/nmf-cm/inc/nmf-limits.h new file mode 100644 index 00000000000..a942e542233 --- /dev/null +++ b/drivers/staging/nmf-cm/inc/nmf-limits.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Common Nomadik Multiprocessing Framework limits definition + * + * This file contains the limit definitions used into NMF. + * + * \warning Don't modify it since it is also hardcoded in tools + * + * \defgroup NMF_LIMITS NMF limits definition + * \ingroup COMMON + */ +#ifndef __INC_NMF_LIMITS_H +#define __INC_NMF_LIMITS_H + +/*! + * \brief Maximum interface name length + * + * Define the maximum interface name length allowed by NMF. + * + * \ingroup NMF_LIMITS + */ +#define MAX_INTERFACE_NAME_LENGTH 32 + +/*! + * \brief Maximum interface method name length + * + * Define the maximum interface method name length allowed by NMF. + * + * \ingroup NMF_LIMITS + */ +#define MAX_INTERFACE_METHOD_NAME_LENGTH 64 + +/*! + * \brief Maximum interface type name length + * + * Define the maximum interface type name length allowed by NMF. + * + * \ingroup NMF_LIMITS + */ +#define MAX_INTERFACE_TYPE_NAME_LENGTH 128 + + +/*! + * \brief Maximum template name length + * + * Define the maximum template name length allowed by NMF. + * + * \ingroup NMF_LIMITS + */ +#define MAX_TEMPLATE_NAME_LENGTH 128 + +/*! + * \brief Maximum component local name length + * + * Define the maximum component local name length inside a composite component allowed by NMF. + * + * \ingroup NMF_LIMITS + */ +#define MAX_COMPONENT_NAME_LENGTH 32 + +/*! + * \brief Maximum property name length + * + * Define the maximum property name length allowed by NMF. + * + * \ingroup NMF_LIMITS + */ +#define MAX_PROPERTY_NAME_LENGTH 32 + +/*! + * \brief Maximum property value length + * + * Define the maximum property value length allowed by NMF. + * + * \ingroup NMF_LIMITS + */ +#define MAX_PROPERTY_VALUE_LENGTH 128 + +/*! + * \brief Maximum attribute name length + * + * Define the maximum attribute name length allowed by NMF. + * + * \ingroup NMF_LIMITS + */ +#define MAX_ATTRIBUTE_NAME_LENGTH 32 + +/*! + * \brief Maximum fifo size allowed for binding component + * + * Define the maximum fifo size allowed for binding component allowed by NMF when calling + * CM_BindComponentFromHost and CM_BindComponentAsynchronous. + * + * \ingroup NMF_LIMITS + */ +#define MAX_COMMUNICATION_FIFO_SIZE 256 + +#define MAX_COMPONENT_FILE_PATH_LENGTH 1024 + +#endif /* __INC_NMF_LIMITS_H */ diff --git a/drivers/staging/nmf-cm/inc/nmf-tracedescription.h b/drivers/staging/nmf-cm/inc/nmf-tracedescription.h new file mode 100644 index 00000000000..9611803b978 --- /dev/null +++ b/drivers/staging/nmf-cm/inc/nmf-tracedescription.h @@ -0,0 +1,324 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief NMF xti/stm trace format description + * + * \defgroup NMF_TRACE_FORMAT NMF xti/stm trace format description + * + * The NMF trace is output by either xti ip on 8815 or stm ip on 8820 and 8500. + * Each type of trace is output on a dedicated channel. Following is a description + * of each of this traces. + * + * Traces have generally a timestamp added by hardware but is not described here. + * \ingroup NMF_ABI + */ +#ifndef TRACE_FORMAT_H_ +#define TRACE_FORMAT_H_ + +#include <inc/nmf-limits.h> + +/*! + * \brief XTI/STM Channel where trace are dumped + * + * \note This type is only for defining constants, please not reference it. + * + * \note Ever if this format is able to be generated on same channel, Host EE & CM channel are separated + * in order to avoir concurrency and access STM IP without require mutual exclusion. + * + * \ingroup NMF_TRACE_FORMAT + */ +typedef enum { + MPC_EE_CHANNEL = 100, //!< MPC EE channel (MPC activity) in 32bits bundle + CM_CHANNEL = 101, //!< CM channel (MPC deployment) in 64bits bundle + HOST_EE_CHANNEL = 151 //!< Host EE channel (deployment & activity) in 64bits bundle +} t_nmfTraceChannelDescription; + +/*! + * \brief Message trace type + * + * \note This type is only for defining constants, please not reference it. + * + * \ingroup NMF_TRACE_FORMAT + */ +typedef enum { + TRACE_TYPE_RESET = 1, //!< Reset trace type + TRACE_TYPE_COMPONENT = 2, //!< Component instantiate trace type + TRACE_TYPE_BIND = 3, //!< Component bind trace type + TRACE_TYPE_METHOD = 4, //!< Component method trace type + TRACE_TYPE_ACTIVITY = 5, //!< Activity trace type + TRACE_TYPE_PANIC = 6, //!< Panic trace type + TRACE_TYPE_COMMUNICATION = 7, //!< Communication trace type + TRACE_TYPE_ALLOCATOR = 8, //!< Allocator trace type + TRACE_TYPE_ALLOC = 9, //!< Alloc trace type + TRACE_TYPE_USER = 10 //!< User trace type +} t_nmfTraceTypeDescription; + +#define TRACE_MAJOR_VERSION 1 //!< Current major trace version number \ingroup NMF_TRACE_FORMAT +#define TRACE_MINOR_VERSION 2 //!< Current minor trace version number \ingroup NMF_TRACE_FORMAT + +/*! + * \brief Trace header description. + * + * \note XTI will add 64bits time-stamp in first field of this structure, but not generated by us ! + * + * \ingroup NMF_TRACE_FORMAT + */ +struct t_nmfTraceChannelHeader { + // t_uint64 timeStamp; + t_uint8 traceType; //!< Trace type + t_uint8 reserved; + t_uint16 traceSize; //!< Trace size (depending on trace type description) +}; + +/*! + * \brief Trace header union + * + * The purpose of this is to optimize header setting in one instruction. + * + * \ingroup NMF_TRACE_FORMAT + */ +typedef union { + struct t_nmfTraceChannelHeader s; + t_uint32 v; +} t_nmfTraceChannelHeaderUnion; + + +/*! + * \brief Trace reset description + * + * Inform tools to reset their internal state because network will be dumped new time. + * + * \ingroup NMF_TRACE_FORMAT + */ +struct t_nmfTraceReset { + t_nmfTraceChannelHeaderUnion header; //!< Trace header + + t_uint16 minorVersion; //!< NMF trace format minor version + t_uint16 majorVersion; //!< NMF trace format major version +}; + +/** + * \brief Component instantiation trace command description + * + * \ingroup NMF_TRACE_FORMAT + */ +typedef enum { + TRACE_COMPONENT_COMMAND_ADD = 0x1, + TRACE_COMPONENT_COMMAND_REMOVE = 0x2 +} t_nmfTraceComponentCommandDescription; + + +/*! + * \brief Component instantiation trace description + * + * Component instantiation trace is generated each time an instance of a component is added or removed. + * + * \ingroup NMF_TRACE_FORMAT + */ +struct t_nmfTraceComponent { + t_nmfTraceChannelHeaderUnion header; //!< Trace header + + t_uint16 command; //!< See \ref t_nmfTraceComponentCommandDescription + t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA, in SMPEE: 0x1 + t_uint32 componentContext; //!< Component context belonging domain (DSP this or ARM class this) + t_uint32 componentUserContext; //!< User friendly component Id belonging the channel (CM handle or ARM class this) + t_uint8 componentLocalName[MAX_COMPONENT_NAME_LENGTH]; //!< local name of component as given by user (null terminated) + t_uint8 componentTemplateName[MAX_TEMPLATE_NAME_LENGTH];//!< template name of component (null terminated) +}; + +/** + * \brief Component binding trace command description + * + * \ingroup NMF_TRACE_FORMAT + */ +typedef enum { + TRACE_BIND_COMMAND_BIND_SYNCHRONOUS = 0x1, + TRACE_BIND_COMMAND_UNBIND_SYNCHRONOUS = 0x2, + TRACE_BIND_COMMAND_BIND_ASYNCHRONOUS = 0x3, + TRACE_BIND_COMMAND_UNBIND_ASYNCHRONOUS = 0x4 +} t_nmfTraceBindCommandDescription; + +/** + * \brief Component binding trace description + * + * \note clientComponentContext & serverComponentContext take value 0xffffffff when client or server are Component Manager. + * \note serverComponentContext take value 0x00000000 when binding to void. + * + * \ingroup NMF_TRACE_FORMAT + */ +struct t_nmfTraceBind { + t_nmfTraceChannelHeaderUnion header; //!< Trace header + + t_uint16 command; //!< See \ref t_nmfTraceBindCommandDescription + t_uint16 reserved; + t_uint16 clientDomainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA, in SMPEE: 0x1 + t_uint16 serverDomainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA, in SMPEE: 0x1 + t_uint32 clientComponentContext; //!< Component context belonging domain (DSP this or ARM class this) + t_uint32 serverComponentContext; //!< Component context belonging domain (DSP this or ARM class this) + t_uint8 requiredItfName[MAX_INTERFACE_NAME_LENGTH]; //!< Required interface name + t_uint8 providedItfName[MAX_INTERFACE_NAME_LENGTH]; //!< Provided interface name +}; + +/*! + * \brief Component interface method name trace description + * + * For each methods of each interfaces provided by a component, one such trace is dumped. + * + * \ingroup NMF_TRACE_FORMAT + */ +struct t_nmfTraceMethod { + t_nmfTraceChannelHeaderUnion header; //!< Trace header + + t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA, in SMPEE: 0x1 + t_uint16 reserved; + t_uint32 methodId; //!< Unique Method Id belonging the component + t_uint32 componentContext; //!< Component context belonging domain (DSP this or ARM class this) + t_uint8 methodName[MAX_INTERFACE_METHOD_NAME_LENGTH]; //!< Symbolic method name +}; + +/** + * \brief Activity trace trace command description + * + * \ingroup NMF_TRACE_FORMAT + */ +typedef enum { + TRACE_ACTIVITY_START = 0x1, //!< Start method + TRACE_ACTIVITY_END = 0x2, //!< End method + TRACE_ACTIVITY_POST = 0x3, //!< Post method + TRACE_ACTIVITY_CALL = 0x4, //!< Synchronous call method + TRACE_ACTIVITY_RETURN = 0x5 //!< Synchronous return method +} t_nmfTraceActivityCommandDescription; + +/*! + * \brief Execution Engine scheduling activity trace description + * + * \ingroup NMF_TRACE_FORMAT + */ +struct t_nmfTraceActivity { + t_nmfTraceChannelHeaderUnion header; //!< Trace header + + t_uint16 command; //!< See \ref t_nmfTraceActivityCommandDescription + t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA + t_uint32 componentContext; //!< Unique component Id (Component Handle for CM, Component this for EE) + t_uint32 methodId; //!< Unique Method Id belonging the component +}; + +/** + * \brief Component instantiation trace command description + * + * \ingroup NMF_TRACE_FORMAT + */ +typedef enum { + TRACE_COMMUNICATION_COMMAND_SEND = 0x1, + TRACE_COMMUNICATION_COMMAND_RECEIVE = 0x2 +} t_nmfTraceCommunicationCommandDescription; + +/** + * \brief Inter-processor communication signaling trace description + * + * Use when trigging interrupt through core. + * + * \note Not used on SMP EE + * + * \ingroup NMF_TRACE_FORMAT + */ +struct t_nmfTraceCommunication { + t_nmfTraceChannelHeaderUnion header; //!< Trace header + + t_uint16 command; //!< See \ref t_nmfTraceCommunicationCommandDescription + t_uint16 reserved_0; + t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA + t_uint16 remoteDomainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA +}; + +/** + * \brief Component instantiation trace command description + * + * \ingroup NMF_TRACE_FORMAT + */ +typedef enum { + TRACE_ALLOCATOR_COMMAND_CREATE = 0x1, + TRACE_ALLOCATOR_COMMAND_DESTROY = 0x2 +} t_nmfTraceAllocatorCommandDescription; + +/*! + * \brief Panic trace description + * + * \ingroup NMF_TRACE_FORMAT + */ +struct t_nmfTraceAllocator { + t_nmfTraceChannelHeaderUnion header; //!< Trace header + + t_uint16 command; //!< See \ref t_nmfTraceAllocatorCommandDescription + t_uint16 allocId; //!< Memory allocator ID + t_uint32 size; //!< Memory allocator size + t_uint8 name[32]; //!< Memory allocator name +}; + +/** + * \brief Component instantiation trace command description + * + * \ingroup NMF_TRACE_FORMAT + */ +typedef enum { + TRACE_ALLOC_COMMAND_ALLOC = 0x1, + TRACE_ALLOC_COMMAND_FREE = 0x2 +} t_nmfTraceAllocCommandDescription; + +/*! + * \brief Panic trace description + * + * \ingroup NMF_TRACE_FORMAT + */ +struct t_nmfTraceAlloc { + t_nmfTraceChannelHeaderUnion header; //!< Trace header + + t_uint16 command; //!< See \ref t_nmfTraceAllocatorCommandDescription + t_uint16 allocId; //!< Memory allocator ID + t_uint32 offset; //!< Memory chunk offet + t_uint32 size; //!< Memory chunk size +}; + +/*! + * \brief Panic trace description + * + * \ingroup NMF_TRACE_FORMAT + */ +struct t_nmfTracePanic { + t_nmfTraceChannelHeaderUnion header; //!< Trace header + + t_uint16 reason; //!< See \ref t_panic_reason for description + t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA + t_uint32 componentContext; //!< Unique component Id (Component Handle for CM, Component this for EE) + t_uint32 information1; //!< Reason dependent information 1st + t_uint32 information2; //!< Reason dependent information 2nd +}; + +/*! + * \brief User trace description + * + * \ingroup NMF_TRACE_FORMAT + */ +struct t_nmfTraceUser { + t_nmfTraceChannelHeaderUnion header; //!< Trace header + + t_uint32 key; //!< User key + t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA + t_uint16 reserved; + t_uint32 componentContext; //!< Unique component Id (Component Handle for CM, Component this for EE) + t_uint32 callerAddress; //!< Unique code address belonging the component +}; + +/* +struct t_nmfTracePower{ + struct t_nmfTraceChannelHeader header; +}; +*/ + +#endif /* TRACE_FORMAT_H_ */ diff --git a/drivers/staging/nmf-cm/inc/nmf_type.idt b/drivers/staging/nmf-cm/inc/nmf_type.idt new file mode 100644 index 00000000000..e8cc4e09946 --- /dev/null +++ b/drivers/staging/nmf-cm/inc/nmf_type.idt @@ -0,0 +1,64 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +#ifndef NMF_TYPE_H_ +#define NMF_TYPE_H_ + +/*! + * \defgroup NMF_COMMON_TYPE NMF Common Type + * \ingroup COMMON + */ + +/*! + * \brief Error type returned by NMF API routines + * + * Possible value describe by \ref t_nmf_errorDescription + * + * \ingroup NMF_COMMON_TYPE + */ +typedef t_sint8 t_nmf_error; + +/*! + * \brief Error type values + * + * \ingroup NMF_COMMON_TYPE + */ +typedef enum { + NMF_OK = 0, //!< No error + NMF_INVALID_PARAMETER = -2, //!< Invalid parameter + NMF_NO_MORE_MEMORY = -30, //!< Out of memory + NMF_INTERFACE_NOT_BINDED = -59, //!< Try to unbind not binded interface + NMF_INTERFACE_ALREADY_BINDED = -60, //!< Try to bind already binded interface + NMF_NO_SUCH_REQUIRED_INTERFACE = -63, //!< Interface name not required by a component + NMF_NO_SUCH_PROVIDED_INTERFACE = -64, //!< Interface name not provided by a component + NMF_COMPONENT_NOT_STOPPED = -80, //!< Component must be stopped to perform operation + NMF_INVALID_COMPONENT_STATE_TRANSITION = -81, //!< Invalid component state transition caused by user action + NMF_NO_SUCH_PROPERTY = -87, //!< Property name doesn't exported by the underlying component + NMF_NO_SUCH_ATTRIBUTE = -88, //!< Attribute name not shared (exported) by a component + NMF_NO_MESSAGE = -103, //!< No message available + NMF_FLUSH_MESSAGE = -106, //!< Message send after call to EE_FlushChannel() + NMF_INTEGRATION_ERROR0 = -112, //!< OS dependent integration Error [-112 -> -121] + NMF_INTEGRATION_ERROR1 = -113, //!< OS dependent integration Error [-112 -> -121] + NMF_INTEGRATION_ERROR2 = -114, //!< OS dependent integration Error [-112 -> -121] + NMF_INTEGRATION_ERROR3 = -115, //!< OS dependent integration Error [-112 -> -121] + NMF_INTEGRATION_ERROR4 = -116, //!< OS dependent integration Error [-112 -> -121] + NMF_INTEGRATION_ERROR5 = -117, //!< OS dependent integration Error [-112 -> -121] + NMF_INTEGRATION_ERROR6 = -118, //!< OS dependent integration Error [-112 -> -121] + NMF_INTEGRATION_ERROR7 = -119, //!< OS dependent integration Error [-112 -> -121] + NMF_INTEGRATION_ERROR8 = -120, //!< OS dependent integration Error [-112 -> -121] + NMF_INTEGRATION_ERROR9 = -121 //!< OS dependent integration Error [-112 -> -121] +} t_nmf_errorDescription; + +/*! + * \brief Define t_nmf_channel type that identify a communication channel between nmf and user. + * + * \ingroup NMF_COMMON_TYPE + */ +typedef t_uint32 t_nmf_channel; + +#endif /* NMF_TYPE_H_ */ diff --git a/drivers/staging/nmf-cm/inc/type.h b/drivers/staging/nmf-cm/inc/type.h new file mode 100644 index 00000000000..d6eafe9aea5 --- /dev/null +++ b/drivers/staging/nmf-cm/inc/type.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/* inc/type.h - Programming Model. + * + * Copyright (c) 2006, 2007, 2008 STMicroelectronics. + * + * Reproduction and Communication of this document is strictly prohibited + * unless specifically authorized in writing by STMicroelectronics. + * + * Written by NMF team. + */ +#ifndef _NMF_TYPE_H_ +#define _NMF_TYPE_H_ + +#include <inc/typedef.h> + +PUBLIC IMPORT_SHARED void NMF_LOG(const char* fmt, ...); +PUBLIC IMPORT_SHARED void NMF_PANIC(const char* fmt, ...); + +#define NMF_ASSERT(cond) do { if(!(cond)) NMF_PANIC("NMF_ASSERT at %s:%d\n", (int)__FILE__, (int)__LINE__); } while(0) + +#ifndef EXPORT_NMF_COMPONENT + #define EXPORT_NMF_COMPONENT EXPORT_SHARED +#endif + +#ifndef IMPORT_NMF_COMPONENT + #define IMPORT_NMF_COMPONENT IMPORT_SHARED +#endif + +#endif /* _NMF_TYPE_H_ */ diff --git a/drivers/staging/nmf-cm/inc/typedef.h b/drivers/staging/nmf-cm/inc/typedef.h new file mode 100644 index 00000000000..97af9dec2c2 --- /dev/null +++ b/drivers/staging/nmf-cm/inc/typedef.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \defgroup COMMON Common types and definitions + * + * \defgroup NMF_COMMON NMF common definition + * \ingroup COMMON + * + * \defgroup NMF_ABI NMF ABI specification + * \warning This page is not for multimedia developers ! + */ +/*! + * \brief Primitive Type Definition + * + * \defgroup NMF_PRIMITIVE_TYPE Primitive type definition + * \ingroup COMMON + */ + +#ifndef NMF_TYPEDEF_H_ +#define NMF_TYPEDEF_H_ + +#undef PRIVATE +#define PRIVATE static //!< Private macro declaration \ingroup NMF_PRIMITIVE_TYPE + +#undef PUBLIC +#ifdef __cplusplus +#define PUBLIC extern "C" //!< Public macro declaration \ingroup NMF_PRIMITIVE_TYPE +#else +#define PUBLIC extern //!< Public macro declaration \ingroup NMF_PRIMITIVE_TYPE +#endif + +#if defined(__SYMBIAN32__) +/*! + * \brief Declared IMPORT_SHARED to allow dll/shared library creation + * + * \note Value depend on OS. + * + * \ingroup NMF_PRIMITIVE_TYPE + */ + #ifndef IMPORT_SHARED + #define IMPORT_SHARED IMPORT_C + #endif +/*! + * \brief Declared EXPORT_SHARED to allow dll/shared library creation + * + * \note Value depend on OS. + * + * \ingroup NMF_PRIMITIVE_TYPE + */ + #ifndef EXPORT_SHARED + #define EXPORT_SHARED EXPORT_C + #endif +#elif defined(LINUX) + #ifndef IMPORT_SHARED + #define IMPORT_SHARED + #endif + #ifndef EXPORT_SHARED + #define EXPORT_SHARED __attribute__ ((visibility ("default"))) + #endif +#else + #ifndef IMPORT_SHARED + #define IMPORT_SHARED + #endif + + #ifndef EXPORT_SHARED + #define EXPORT_SHARED + #endif +#endif + +/* + * Definition of type that are used by interface. + */ + +typedef unsigned int t_uword; +typedef signed int t_sword; + +#ifdef __flexcc2__ + +typedef unsigned char t_bool; + +#ifdef __mode16__ + +typedef signed char t_sint8; +typedef signed int t_sint16; +typedef signed long t_sint24; +typedef signed long t_sint32; +typedef signed long long t_sint40; +// bigger type are not handle on this mode + +typedef unsigned char t_uint8; +typedef unsigned int t_uint16; +typedef unsigned long t_uint24; +typedef unsigned long t_uint32; +typedef unsigned long long t_uint40; +// bigger type are not handle on this mode + +// shared addr type definition +//typedef __SHARED16 t_uint16 * t_shared_addr; +typedef void * t_shared_field; + +#else /* __mode16__ -> __mode24__ */ + +typedef signed char t_sint8; +typedef signed short t_sint16; +typedef signed int t_sint24; +typedef signed long t_sint32; +typedef signed long t_sint40; +typedef signed long t_sint48; +typedef signed long long t_sint56; + +typedef unsigned char t_uint8; +typedef unsigned short t_uint16; +typedef unsigned int t_uint24; +typedef unsigned long t_uint32; +typedef unsigned long t_uint40; +typedef unsigned long t_uint48; +typedef unsigned long long t_uint56; + +// shared addr type definition +//typedef __SHARED16 t_uint16 * t_shared_addr; +typedef t_uint24 t_shared_field; + +#endif /* MMDSP mode24 */ + +// shared register (ARM world) type definition +#if 0 +typedef struct { + t_uint16 lsb; + t_uint16 msb; +} t_shared_reg; +#endif +typedef t_uint32 t_shared_reg; + +typedef t_uint32 t_physical_address; + +#include <stwdsp.h> + +#else /* __flexcc2__ -> RISC 32 Bits */ + +#ifndef _HCL_DEFS_H +typedef unsigned char t_bool; //!< Boolean primitive type \ingroup NMF_PRIMITIVE_TYPE + +typedef unsigned char t_uint8; //!< Unsigned 8 bits primitive type \ingroup NMF_PRIMITIVE_TYPE +typedef signed char t_sint8; //!< Signed 8 bits primitive type \ingroup NMF_PRIMITIVE_TYPE +typedef unsigned short t_uint16; //!< Unsigned 16 bits primitive type \ingroup NMF_PRIMITIVE_TYPE +typedef signed short t_sint16; //!< Signed 16 bits primitive type \ingroup NMF_PRIMITIVE_TYPE +typedef unsigned long t_uint32; //!< Unsigned 32 bits primitive type \ingroup NMF_PRIMITIVE_TYPE +typedef signed long t_sint32; //!< Signed 32 bits primitive type \ingroup NMF_PRIMITIVE_TYPE +typedef unsigned long long t_uint64; //!< Unsigned 64 bits primitive type \ingroup NMF_PRIMITIVE_TYPE +typedef signed long long t_sint64; //!< Signed 64 bits primitive type \ingroup NMF_PRIMITIVE_TYPE + +typedef t_uint32 t_physical_address; +#endif /* _HCL_DEFS_H */ + +typedef unsigned long t_uint24; +typedef signed long t_sint24; +typedef unsigned long long t_uint48; +typedef signed long long t_sint48; + +// shared addr type definition +typedef t_uint32 t_shared_addr; + +// shared register (ARM world) type definition +typedef t_uint32 t_shared_reg; +typedef t_uint32 t_shared_field; + +#endif /* RISC 32 Bits */ + +/* + * Define boolean type + */ +#undef FALSE +#define FALSE 0 //!< Boolean FALSE value +#undef TRUE +#define TRUE 1 //!< Boolean TRUE value + +#ifndef NULL + #if defined __flexcc2__ || defined __SYMBIAN32__ + #define NULL (0x0) //!< Null type \ingroup NMF_PRIMITIVE_TYPE + #else + #define NULL ((void*)0x0) //!< Null type \ingroup NMF_PRIMITIVE_TYPE + #endif +#endif + +typedef t_uint32 t_nmf_component_handle; + +#endif /* NMF_TYPEDEF_H_ */ diff --git a/drivers/staging/nmf-cm/nmf/inc/channel_type.h b/drivers/staging/nmf-cm/nmf/inc/channel_type.h new file mode 100644 index 00000000000..7d439ebf0cf --- /dev/null +++ b/drivers/staging/nmf-cm/nmf/inc/channel_type.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Common Nomadik Multiprocessing Framework type definition + * + * This file contains the shared between cm and ee type definitions used into NMF for callback. + */ +/*! + * \defgroup _t_nmf_channel_flag t_nmf_channel_flag + * \ingroup NMF_COMMON + */ + +#ifndef __INC_CHANNEL_TYPE_H +#define __INC_CHANNEL_TYPE_H + +#include <inc/typedef.h> +#include <inc/nmf_type.idt> + +/*! + * \brief Define t_nmf_channel_flag type that allow to control if/how a new communication channel is created. + * \ingroup _t_nmf_channel_flag + */ +typedef t_uint32 t_nmf_channel_flag; + +#define NMF_CHANNEL_SHARED ((t_nmf_channel_flag)0) //!< \ingroup _t_nmf_channel_flag +#define NMF_CHANNEL_PRIVATE ((t_nmf_channel_flag)1) //!< \ingroup _t_nmf_channel_flag + +/*! + * \brief Define t_nmf_virtualInterruptHandler function type to allow to dispatch virtual interrupt + * \ingroup VIRTUAL_INTERRUPT + */ +typedef void (*t_nmf_virtualInterruptHandler)(void *interruptContext); + +#endif + diff --git a/drivers/staging/nmf-cm/nmf/inc/component_type.h b/drivers/staging/nmf-cm/nmf/inc/component_type.h new file mode 100644 index 00000000000..08b63b21225 --- /dev/null +++ b/drivers/staging/nmf-cm/nmf/inc/component_type.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Common Nomadik Multiprocessing Framework type definition + * + * This file contains the shared between cm and ee type definitions used into NMF for callback. + */ +#ifndef __INC_COMPONENT_TYPE_H +#define __INC_COMPONENT_TYPE_H + +#include <inc/typedef.h> + +/*! + * \brief Identifier of a component instance handle + * + * \ingroup NMF_COMMON + */ +typedef t_nmf_component_handle t_cm_instance_handle; + +#endif + diff --git a/drivers/staging/nmf-cm/nmf/inc/service_type.h b/drivers/staging/nmf-cm/nmf/inc/service_type.h new file mode 100644 index 00000000000..3e5473338ee --- /dev/null +++ b/drivers/staging/nmf-cm/nmf/inc/service_type.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Service type and data used through service callback. + * \defgroup NMF_SERVICE NMF Service Callback types and data definition + * \ingroup NMF_COMMON + */ +#ifndef SERVICE_TYPE_H +#define SERVICE_TYPE_H + +#include <ee/api/panic.idt> +#include <nmf/inc/component_type.h> +#include <share/inc/nmf.h> + +/*! + * \brief Define t_nmf_service_type type + * + * It gives the type of service message passed to service callback. + * \ingroup NMF_SERVICE + */ +typedef t_uint32 t_nmf_service_type; +#define NMF_SERVICE_PANIC ((t_nmf_service_type)0) //!< \ingroup NMF_SERVICE +#define NMF_SERVICE_SHUTDOWN ((t_nmf_service_type)1) //!< \ingroup NMF_SERVICE + +/* + * The following structured define each data structure used for each service type + * and given to each serviceCallback + */ + +/*! + * \brief Define t_nmf_panic_data type + * + * This is the data structure passed to the service callback (inside \ref t_nmf_service_data) + * when t_nmf_service_type == NMF_SERVICE_PANIC + * \ingroup NMF_SERVICE + */ +typedef struct { + t_panic_reason panicReason; //!< The reason of the panic + t_panic_source panicSource; //!< THe source of the panic (One of the MPC or the ARM-EE) + /*! + * union of structures containing specific info, depending on the panicSource + */ + union { + struct { + t_nmf_core_id coreid; //!< The coreId of the MPC on which the panic occured + t_cm_instance_handle faultingComponent; //!< The faulting component handle + t_uint32 panicInfo1; //!< First info (depend on \ref panicReason) + t_uint32 panicInfo2; //!< Second info (depend on \ref panicReason) + } mpc; //!< member to use if panicSource == MPC_EE + struct { + void * faultingComponent; //!< The faulting component handle + t_uint32 panicInfo1; //!< First info (depend on \ref panicReason) + t_uint32 panicInfo2; //!< Second info (depend on \ref panicReason) + } host; //!< member to use if panicSource == HOST_EE + } info; //!< union of structures containing specific info, depending on the panicSource +} t_nmf_panic_data; + +/*! + * \brief Define t_nmf_shutdown_data type + * + * This is the data structure passed to the service callback (inside \ref t_nmf_service_data) + * when t_nmf_service_type == NMF_SERVICE_SHUTDOWN + * \ingroup NMF_SERVICE + */ +typedef struct { + t_nmf_core_id coreid; //!< The coreId of the MPC on which has been shutdown +} t_nmf_shutdown_data; + +/*! + * \brief Define t_nmf_service_data type + * + * It gives the data passed to the service callbacks for each service type + * This is an union whose member to use is defined by the given \ref t_nmf_service_type + * + * \ingroup NMF_SERVICE + */ +typedef union { + t_nmf_panic_data panic; //!< if service_type == NMF_SERVICE_PANIC + t_nmf_shutdown_data shutdown; //!< if service_type == NMF_SERVICE_SHUTDOWN +} t_nmf_service_data; + +/*! + * \brief Define t_nmf_serviceCallback function type to allow to dispatch service message to user. + * \ingroup NMF_SERVICE + */ +typedef void (*t_nmf_serviceCallback)(void *contextHandler, t_nmf_service_type serviceType, t_nmf_service_data *serviceData); + +#endif //SERVICE_TYPE_H diff --git a/drivers/staging/nmf-cm/osal-kernel.c b/drivers/staging/nmf-cm/osal-kernel.c new file mode 100644 index 00000000000..0b0eeac4ce3 --- /dev/null +++ b/drivers/staging/nmf-cm/osal-kernel.c @@ -0,0 +1,1215 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +/** \file osal-kernel.c + * + * Implements NMF OSAL for Linux kernel-space environment + */ + +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kthread.h> +#include <linux/mm.h> +#include <linux/semaphore.h> +#include <linux/slab.h> +#include <linux/timer.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> + +#ifdef CONFIG_STM_TRACE +#include <trace/stm.h> +#endif + +#include <cm/engine/configuration/inc/configuration_status.h> + +#include "cmioctl.h" +#include "osal-kernel.h" +#include "cm_service.h" +#include "cmld.h" +#include "cm_debug.h" +#include "cm_dma.h" + +__iomem void *prcmu_base = NULL; +__iomem void *prcmu_tcdm_base = NULL; + +/* DSP Load Monitoring */ +#define FULL_OPP 100 +#define HALF_OPP 50 +static unsigned long running_dsp = 0; +static unsigned int dspLoadMonitorPeriod = 1000; +module_param(dspLoadMonitorPeriod, uint, S_IWUSR|S_IRUGO); +MODULE_PARM_DESC(dspLoadMonitorPeriod, "Period of the DSP-Load monitoring in ms"); +static unsigned int dspLoadHighThreshold = 85; +module_param(dspLoadHighThreshold, uint, S_IWUSR|S_IRUGO); +MODULE_PARM_DESC(dspLoadHighThreshold, "Threshold above which 100 APE OPP is requested"); +static unsigned int dspLoadLowThreshold = 35; +module_param(dspLoadLowThreshold, uint, S_IWUSR|S_IRUGO); +MODULE_PARM_DESC(dspLoadLowThreshold, "Threshold below which 100 APE OPP request is removed"); + +/** \defgroup ENVIRONMENT_INITIALIZATION Environment initialization + * Includes functions that initialize the Linux OSAL itself plus functions that + * are responsible to factor configuration objects needed to initialize Component Manager library + */ + +/** \defgroup OSAL_IMPLEMENTATION OSAL implementation + * Linux-specific implementation of the Component Manager OSAL interface. + */ + + +/** \ingroup ENVIRONMENT_INITIALIZATION + * Remaps IO, SDRAM and ESRAM regions + * + * \osalEnvironment NMF-Osal descriptor + * \return POSIX error code + */ +int remapRegions(void) +{ + unsigned i; + + /* Remap DSP base areas */ + for (i=0; i<NB_MPC; i++) { + osalEnv.mpc[i].base.data = ioremap_nocache((int)osalEnv.mpc[i].base_phys, ONE_MB); + if(osalEnv.mpc[i].base.data == NULL){ + pr_err("%s: could not remap base address for %s\n", __func__, osalEnv.mpc[i].name); + return -ENOMEM; + } + } + + /* Remap hardware semaphores */ + osalEnv.hwsem_base = ioremap_nocache(U8500_HSEM_BASE, (4*ONE_KB)); + if(osalEnv.hwsem_base == NULL){ + pr_err("%s: could not remap HWSEM Base\n", __func__); + return -ENOMEM; + } + + /* Remap _all_ ESRAM banks */ + osalEnv.esram_base = ioremap_nocache(ESRAM_BASE, cfgESRAMSize*ONE_KB); + if(osalEnv.esram_base == NULL){ + pr_err("%s: could not remap ESRAM Base\n", __func__); + return -ENOMEM; + } + + /* Allocate code and data sections for MPC (SVA, SIA) */ + for (i=0; i<NB_MPC; i++) { + /* Allocate MPC SDRAM code area */ + struct hwmem_mem_chunk mem_chunk; + size_t mem_chunk_length; + osalEnv.mpc[i].hwmem_code = hwmem_alloc(osalEnv.mpc[i].sdram_code.size, + //HWMEM_ALLOC_HINT_CACHE_WB, + HWMEM_ALLOC_HINT_WRITE_COMBINE | HWMEM_ALLOC_HINT_UNCACHED, + HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE, + HWMEM_MEM_CONTIGUOUS_SYS); + if (IS_ERR(osalEnv.mpc[i].hwmem_code)) { + int err = PTR_ERR(osalEnv.mpc[i].hwmem_code); + osalEnv.mpc[i].hwmem_code = NULL; + pr_err("%s: could not allocate SDRAM Code for %s\n", + __func__, osalEnv.mpc[i].name); + return err; + } + osalEnv.mpc[i].sdram_code.data = hwmem_kmap(osalEnv.mpc[i].hwmem_code); + if (IS_ERR(osalEnv.mpc[i].sdram_code.data)) { + int err = PTR_ERR(osalEnv.mpc[i].sdram_code.data); + osalEnv.mpc[i].sdram_code.data = NULL; + pr_err("%s: could not map SDRAM Code for %s\n", __func__, osalEnv.mpc[i].name); + return err; + } + mem_chunk_length = 1; + (void)hwmem_pin(osalEnv.mpc[i].hwmem_code, &mem_chunk, &mem_chunk_length); + osalEnv.mpc[i].sdram_code_phys = mem_chunk.paddr; + /* Allocate MPC SDRAM data area by taking care wether the data are shared or not */ + if (osalEnv.mpc[i].sdram_data.size == 0) { + /* size of 0 means shared data segment, reuse the same param as for first MPC */ + osalEnv.mpc[i].sdram_data_phys = osalEnv.mpc[0].sdram_data_phys; + osalEnv.mpc[i].sdram_data.data = osalEnv.mpc[0].sdram_data.data; + osalEnv.mpc[i].sdram_data.size = osalEnv.mpc[0].sdram_data.size; + } else { + /* If we do not share the data segment or if this is the first MPC */ + osalEnv.mpc[i].hwmem_data = hwmem_alloc(osalEnv.mpc[i].sdram_data.size, + HWMEM_ALLOC_HINT_WRITE_COMBINE | HWMEM_ALLOC_HINT_UNCACHED, + HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE, + HWMEM_MEM_CONTIGUOUS_SYS); + if (IS_ERR(osalEnv.mpc[i].hwmem_data)) { + int err = PTR_ERR(osalEnv.mpc[i].hwmem_data); + osalEnv.mpc[i].hwmem_data = NULL; + pr_err("%s: could not allocate SDRAM Data for %s\n", + __func__, osalEnv.mpc[i].name); + return err; + } + mem_chunk_length = 1; + (void)hwmem_pin(osalEnv.mpc[i].hwmem_data, + &mem_chunk, &mem_chunk_length); + osalEnv.mpc[i].sdram_data_phys = mem_chunk.paddr; + osalEnv.mpc[i].sdram_data.data = hwmem_kmap(osalEnv.mpc[i].hwmem_data); + if (IS_ERR(osalEnv.mpc[i].sdram_data.data)) { + int err = PTR_ERR(osalEnv.mpc[i].sdram_data.data); + osalEnv.mpc[i].sdram_data.data = NULL; + pr_err("%s: could not map SDRAM Data for %s\n", + __func__, osalEnv.mpc[i].name); + return err; + } + } + } + + return 0; +} + +/** \ingroup ENVIRONMENT_INITIALIZATION + * Unmaps IO, SDRAM and ESRAM regions + * + * \return POSIX error code + */ +void unmapRegions(void) +{ + unsigned i; + + /* Release SVA, SIA, Hardware sempahores and embedded SRAM mappings */ + for (i=0; i<NB_MPC; i++) { + if(osalEnv.mpc[i].base.data != NULL) + iounmap(osalEnv.mpc[i].base.data); + } + + if(osalEnv.hwsem_base != NULL) + iounmap(osalEnv.hwsem_base); + + if(osalEnv.esram_base != NULL) + iounmap(osalEnv.esram_base); + + /* + * Free SVA and SIA code and data sections or release their mappings + * according on how memory allocations has been achieved + */ + for (i=0; i<NB_MPC; i++) { + if (osalEnv.mpc[i].sdram_code.data != NULL) { + hwmem_unpin(osalEnv.mpc[i].hwmem_code); + hwmem_kunmap(osalEnv.mpc[i].hwmem_code); + if (osalEnv.mpc[i].hwmem_code != NULL) + hwmem_release(osalEnv.mpc[i].hwmem_code); + } + + /* If data segment is shared, we must free only the first data segment */ + if (((i == 0) || (osalEnv.mpc[i].sdram_data.data != osalEnv.mpc[0].sdram_data.data)) + && (osalEnv.mpc[i].sdram_data.data != NULL)) { + hwmem_unpin(osalEnv.mpc[i].hwmem_data); + hwmem_kunmap(osalEnv.mpc[i].hwmem_data); + if (osalEnv.mpc[i].hwmem_data != NULL) + hwmem_release(osalEnv.mpc[i].hwmem_data); + } + } +} + + +/** \ingroup ENVIRONMENT_INITIALIZATION + * Fills a t_nmf_hw_mapping_desc object + * + * \param nmfHwMappingDesc Pointer to a t_nmf_hw_mapping_desc object + * \return POSIX error code + */ +int getNmfHwMappingDesc(t_nmf_hw_mapping_desc* nmfHwMappingDesc) +{ + + if (nmfHwMappingDesc == NULL) + return -ENXIO; + + nmfHwMappingDesc->esramDesc.systemAddr.physical = ESRAM_BASE; + nmfHwMappingDesc->esramDesc.systemAddr.logical = (t_cm_logical_address)osalEnv.esram_base; + nmfHwMappingDesc->esramDesc.size = cfgESRAMSize*ONE_KB; + + nmfHwMappingDesc->hwSemaphoresMappingBaseAddr.physical = U8500_HSEM_BASE; + nmfHwMappingDesc->hwSemaphoresMappingBaseAddr.logical = (t_cm_logical_address)osalEnv.hwsem_base; + + return 0; +} + +/** \ingroup ENVIRONMENT_INITIALIZATION + * Fills a t_cm_system_address object + * + * \param mpcSystemAddress Pointer to a t_cm_system_address object + * \return POSIX error code + */ +void getMpcSystemAddress(unsigned i, t_cm_system_address* mpcSystemAddress) +{ + mpcSystemAddress->physical = (t_cm_physical_address)osalEnv.mpc[i].base_phys; + mpcSystemAddress->logical = (t_cm_logical_address)osalEnv.mpc[i].base.data; +} + + +/** \ingroup ENVIRONMENT_INITIALIZATION + * Fills t_nmf_memory_segment objects for MPC code and data segments + * + * \param i Index of the MPC to initialize + * \param codeSegment Pointer to a t_nmf_memory_segment (code segment) + * \param dataSegment Pointer to a t_nmf_memory_segment (data segment) + * \return Always 0 + */ +void getMpcSdramSegments(unsigned i, t_nmf_memory_segment* codeSegment, t_nmf_memory_segment* dataSegment) +{ + codeSegment->systemAddr.logical = (t_cm_logical_address)osalEnv.mpc[i].sdram_code.data; + codeSegment->systemAddr.physical = osalEnv.mpc[i].sdram_code_phys; + codeSegment->size = osalEnv.mpc[i].sdram_code.size; + + dataSegment->systemAddr.logical = (t_cm_logical_address)osalEnv.mpc[i].sdram_data.data; + dataSegment->systemAddr.physical = osalEnv.mpc[i].sdram_data_phys; + dataSegment->size = osalEnv.mpc[i].sdram_data.size; +} + +#ifdef CM_DEBUG_ALLOC +#include <linux/kallsyms.h> +struct cm_alloc cm_alloc; + +/** + * These routines initializes the structures used to trace all alloc/free. + * These are used in debug mode to track all memory leak about the allocations + * done through the OSAL. + */ +void init_debug_alloc(void) +{ + INIT_LIST_HEAD(&cm_alloc.chain); + spin_lock_init(&cm_alloc.lock); +} + +void cleanup_debug_alloc(void) +{ + struct cm_alloc_elem *entry, *next; + char buffer[128]; + + list_for_each_entry_safe(entry, next, &cm_alloc.chain, elem) { + sprint_symbol(buffer, (int)entry->caller); + pr_err("/!\\ ALLOC(size=%d) not freed from: 0x%p (%s)\n", + entry->size, entry->caller, buffer); + list_del(&entry->elem); + if ((void*)entry >= (void*)VMALLOC_START + && (void*)entry < (void*)VMALLOC_END) + vfree(entry); + else + kfree(entry); + } +} + +void dump_debug_alloc(void) +{ + struct cm_alloc_elem *entry, *next; + char buffer[128]; + + pr_err("Current allocated memory:\n"); + list_for_each_entry_safe(entry, next, &cm_alloc.chain, elem) { + sprint_symbol(buffer, (int)entry->caller); + pr_err("=> Alloc of size=%d from: 0x%p (%s)\n", + entry->size, entry->caller, buffer); + } +} +#endif + + +/** \ingroup OSAL_IMPLEMENTATION + * Called by CM_ProcessMpcEvent in interrupt/tasklet context. Schedules the DFC. + * Enqueues the new event in the process' message queue. + * + * \note This is _not_ called in response to internal events such as in + * response to a CM_InstantiateComponent. It is called when user-defined + * functions need to be called in skeletons. This behavior is different + * from 0.8.1 version. + */ +void OSAL_PostDfc(t_nmf_mpc2host_handle upLayerTHIS, t_uint32 methodIndex, t_event_params_handle ptr, t_uint32 size) +{ + /* skelwrapper has been created in CM_SYSCALL_BindComponentToCMCore and conveys per-process private data */ + t_skelwrapper* skelwrapper = (t_skelwrapper*)upLayerTHIS; + struct osal_msg* message; + + /* If the clannel has been closed, no more reader exists + => discard the message */ + if (skelwrapper->channelPriv->state == CHANNEL_CLOSED) { + pr_warning("%s: message discarded (channel closed)\n", + __func__ ); + return; + } + + /* Create a new message */ + message = kmalloc(sizeof(*message), GFP_ATOMIC); + if (!message) { + pr_err("%s: message discarded (alloc failed)\n", __func__ ); + return; + } + + /* Stuff it */ + plist_node_init(&message->msg_entry, 0); + message->msg_type = MSG_INTERFACE; + message->d.itf.skelwrap = skelwrapper; + message->d.itf.methodIdx = methodIndex; + message->d.itf.anyPtr = ptr; + message->d.itf.ptrSize = size; + + /* Enqueue it */ + /* Should be protected with the cmPriv->msgQueueLock held + But we know by design that we are safe here. (Alone here in + tasklet (soft-interrupt) context. + When accessed in process context, soft-irq are disable) + */ + spin_lock_bh(&skelwrapper->channelPriv->bh_lock); + plist_add(&message->msg_entry, &skelwrapper->channelPriv->messageQueue); + spin_unlock_bh(&skelwrapper->channelPriv->bh_lock); + + /* Wake up process' wait queue */ + wake_up_interruptible(&skelwrapper->channelPriv->waitq); +} + + +#define MAX_LOCKS 8 // max number of locks/semaphores creatable +static unsigned long semused = 0; // bit field for used semaphores +static unsigned long lockused = 0; // bit field for used mutexes +static struct mutex cmld_locks[MAX_LOCKS]; + +/** \ingroup OSAL_IMPLEMENTATION + */ +t_nmf_osal_sync_handle OSAL_CreateLock(void) +{ + int i; + + for (i=0; i<MAX_LOCKS; i++) + if (!test_and_set_bit(i, &lockused)) { + struct mutex* mutex = &cmld_locks[i]; + mutex_init(mutex); + return (t_nmf_osal_sync_handle)mutex; + } + + return (t_nmf_osal_sync_handle)NULL; +} + + +/** \ingroup OSAL_IMPLEMENTATION + */ +void OSAL_Lock(t_nmf_osal_sync_handle handle) +{ + // unfortunately there is no return value to this function + // so we cannot use 'mutex_lock_killable()' + mutex_lock((struct mutex*)handle); +} + + +/** \ingroup OSAL_IMPLEMENTATION + */ +void OSAL_Unlock(t_nmf_osal_sync_handle handle) +{ + mutex_unlock((struct mutex*)handle); +} + + +/** \ingroup OSAL_IMPLEMENTATION + */ +void OSAL_DestroyLock(t_nmf_osal_sync_handle handle) +{ + int i; + + // clear the bit in the bits field about used locks + i = ((struct mutex*)handle - cmld_locks); + + clear_bit(i, &lockused); +} + +static struct semaphore cmld_semaphores[MAX_LOCKS]; +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * Goal: Use by CM to allow to synchronize with code running on mpc side. + * + * \param[in] value : Initial value of semaphore. + * + * \return handle of the Semaphore created + * + * Called by: + * - any CM API call + * + * \ingroup OSAL + */ +t_nmf_osal_sem_handle OSAL_CreateSemaphore(t_uint32 value) +{ + int i; + + for (i=0; i<MAX_LOCKS; i++) + if (!test_and_set_bit(i, &semused)) { + struct semaphore* sem = &cmld_semaphores[i]; + sema_init(sem, value); + return (t_nmf_osal_sem_handle)sem; + } + + return (t_nmf_osal_sem_handle)NULL; +} + +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * Goal: Use by CM to allow to synchronize with code running on mpc side. This function can be called under + * Irq context by CM. + * + * param[in] : handle of the Semaphore for which we increase value and so potentially wake up thread. + * + * param[in] : aCtx is a hint to indicate to os that we are in a none normal context (e.g under interruption). + * + * Called by: + * - any CM API call + * + * \ingroup OSAL + */ +void OSAL_SemaphorePost(t_nmf_osal_sem_handle handle, t_uint8 aCtx) +{ + up((struct semaphore*)handle); +} + +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * Goal: Use by CM to allow to synchronize with code running on mpc side. + * + * param[in] : handle of the Semaphore for which we decrease value and so potentially block current thread. + * + * param[in] : maximun time in ms after which the block thread is wake up. In this case function return SYNC_ERROR_TIMEOUT value. + * + * \return error number: SYNC_ERROR_TIMEOUT in case semaphore is not release withing timeOutInMs. + * + * Called by: + * - any CM API call + * + * \ingroup OSAL + */ +t_nmf_osal_sync_error OSAL_SemaphoreWaitTimed(t_nmf_osal_sem_handle handle, + t_uint32 timeOutInMs) +{ + if (down_timeout((struct semaphore*)handle, msecs_to_jiffies(timeOutInMs))) + return SYNC_ERROR_TIMEOUT; + else + return SYNC_OK; +} + +/*! + * \brief Description of the Synchronization part of the OS Adaptation Layer + * + * Goal: Use by CM to allow to synchronize with code running on mpc side. + * + * param[in] : handle of the Semaphore to be destroyed + * + * Called by: + * - any CM API call + * + * \ingroup OSAL + */ +void OSAL_DestroySemaphore(t_nmf_osal_sem_handle handle) +{ + int i; + + // clear the bit in the bits field about used locks + i = ((struct semaphore*)handle - cmld_semaphores); + + clear_bit(i, &semused); +} + +/** \ingroup OSAL_IMPLEMENTATION + * OSAL alloc implementation + * + * In both OSAL_Alloc() and OSAL_Alloc_Zero() function, the strategy is to use + * kmalloc() as it is the more efficient and most common way to allocate memory. + * For big allocation, kmalloc may fail because memory is very fragmented + * (kmalloc() allocates contiguous memory). In that case, we fall to vmalloc() + * instead. + * In OSAL_Free(), we rely on the virtual address to know which of kfree() or + * vfree() to use (vmalloc() use its own range of virtual addresses) + */ +void* OSAL_Alloc(t_cm_size size) +{ +#ifdef CM_DEBUG_ALLOC + struct cm_alloc_elem *entry; + + if (size == 0) + return NULL; + + entry = kmalloc(size + sizeof(*entry), GFP_KERNEL); + + if (entry == NULL) { + entry = vmalloc(size + sizeof(*entry)); + + if (entry == NULL) { + pr_alert("%s: kmalloc(%d) and vmalloc(%d) failed\n", + __func__, (int)size, (int)size); + dump_debug_alloc(); + return NULL; + } + } + /* return address of the caller */ + entry->caller = __builtin_return_address(0); + entry->size = size; + + spin_lock(&cm_alloc.lock); + list_add_tail(&entry->elem, &cm_alloc.chain); + spin_unlock(&cm_alloc.lock); + + return entry->addr; +#else + void* mem; + + if (size == 0) + return NULL; + mem = kmalloc(size, GFP_KERNEL); + if (mem == NULL) { + mem = vmalloc(size); + if (mem == NULL) + pr_alert("CM (%s): No more memory (requested " + "size=%d) !!!\n", __func__, (int)size); + } + return mem; +#endif +} + + +/** \ingroup OSAL_IMPLEMENTATION + * OSAL alloc implementation + */ +void* OSAL_Alloc_Zero(t_cm_size size) +{ +#ifdef CM_DEBUG_ALLOC + struct cm_alloc_elem *entry; + + if (size == 0) + return NULL; + + entry = kzalloc(size + sizeof(*entry), GFP_KERNEL); + if (entry == NULL) { + entry = vmalloc(size + sizeof(*entry)); + if (entry == NULL) { + pr_alert("%s: kmalloc(%d) and vmalloc(%d) failed\n", + __func__, (int)size, (int)size); + dump_debug_alloc(); + return NULL; + } else { + memset(entry, 0, size + sizeof(*entry)); + } + } + + /* return address of the caller */ + entry->caller = __builtin_return_address(0); + entry->size = size; + + spin_lock(&cm_alloc.lock); + list_add_tail(&entry->elem, &cm_alloc.chain); + spin_unlock(&cm_alloc.lock); + + return entry->addr; +#else + void* mem; + + if (size == 0) + return NULL; + mem = kzalloc(size, GFP_KERNEL); + if (mem == NULL) { + mem = vmalloc(size); + if (mem == NULL) + pr_alert("CM (%s): No more memory (requested " + "size=%d) !!!\n", __func__, (int)size); + else + memset(mem, 0, size); + } + + return mem; +#endif +} + + +/** \ingroup OSAL_IMPLEMENTATION + * OSAL free implementation + */ +void OSAL_Free(void* mem) +{ +#ifdef CM_DEBUG_ALLOC + struct cm_alloc_elem *entry = container_of(mem, struct cm_alloc_elem, addr); + unsigned int i; + char pattern[4] = { 0xEF, 0xBE, 0xAD, 0xDE }; + + if (mem == NULL) + return; + + /* fill with a pattern to detect bad re-use of this area */ + for (i=0; i<entry->size; i++) + entry->addr[i] = pattern[i%4]; + + spin_lock(&cm_alloc.lock); + list_del(&entry->elem); + spin_unlock(&cm_alloc.lock); + + if ((void*)entry >= (void*)VMALLOC_START + && (void*)entry < (void*)VMALLOC_END) + vfree(entry); + else + kfree(entry); +#else + if (mem >= (void*)VMALLOC_START && mem < (void*)VMALLOC_END) + vfree(mem); + else + kfree(mem); +#endif +} + +/** \ingroup OSAL_IMPLEMENTATION + * OSAL Copy implementation + * This copy some data from userspace (address to kernel space. + * This implementation differs on Symbian. + */ +t_cm_error OSAL_Copy(void *dst, const void *src, t_cm_size size) +{ + if (copy_from_user(dst, src, size)) + return CM_UNKNOWN_MEMORY_HANDLE; + return CM_OK; +} + +/** \ingroup OSAL_IMPLEMENTATION + * OSAL write64 function implementation + */ +void OSAL_Write64(t_nmf_trace_channel channel, t_uint8 isTimestamped, t_uint64 value) +{ +#ifdef CONFIG_STM_TRACE + if (isTimestamped) + stm_tracet_64(channel, value); + else + stm_trace_64(channel, value); +#endif +} + + +/** \ingroup OSAL_IMPLEMENTATION + * OSAL log function implementation + */ +void OSAL_Log(const char *format, int param1, int param2, int param3, int param4, int param5, int param6) +{ + printk(format, param1, param2, param3, param4, param5, param6); +} + +/** + * compute the dsp load + * + * return -1 if in case of failure, a value between 0 and 100 otherwise + */ +static s8 computeDspLoad(t_cm_mpc_load_counter *oldCounter, t_cm_mpc_load_counter *counter) +{ + u32 t, l; + + if ((oldCounter->totalCounter == 0) && (oldCounter->loadCounter == 0)) + return -1; // Failure or not started ? + if ((counter->totalCounter == 0) && (counter->loadCounter == 0)) + return -1; // Failure or already stopped ? + + if (counter->totalCounter < oldCounter->totalCounter) + t = (u32)((((u64)-1) - oldCounter->totalCounter) + + counter->totalCounter + 1); + else + t = (u32)(counter->totalCounter - oldCounter->totalCounter); + + if (counter->loadCounter < oldCounter->loadCounter) + l = (u32)((((u64)-1) - oldCounter->loadCounter) + + counter->loadCounter + 1); + else + l = (u32)(counter->loadCounter - oldCounter->loadCounter); + + if (t == 0) // not significant + return -1; + + if (l > t) // not significant + return -1; + + return (l*100) / t; +} + +static void wakeup_process(unsigned long data) +{ + wake_up_process((struct task_struct *)data); +} + +/** + * Thread function entry for monitorin the CPU load + */ +static int dspload_monitor(void *idx) +{ + int i = (int)idx; + unsigned char current_opp_request = FULL_OPP; + struct mpcConfig *mpc = &osalEnv.mpc[i]; + struct timer_list timer; + + timer.function = wakeup_process; + timer.data = (unsigned long)current; + init_timer_deferrable(&timer); + +#ifdef CONFIG_DEBUG_FS + mpc->opp_request = current_opp_request; +#endif + if (prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, + (char*)mpc->name, + current_opp_request)) + pr_err("CM Driver: Add QoS failed\n"); + + /* + * Wait for 500ms before initializing the counter, + * to let the DSP boot (init of counter will failed if + * DSP is not booted). + */ + schedule_timeout_uninterruptible(msecs_to_jiffies(500)); + + /* init counter */ + if (CM_GetMpcLoadCounter(mpc->coreId, + &mpc->oldLoadCounter) != CM_OK) + pr_err("CM Driver: Failed to init load counter for %s\n", + mpc->name); + + while (!kthread_should_stop()) { + t_cm_mpc_load_counter loadCounter; + s8 load = -1; + unsigned long expire; + + __set_current_state(TASK_UNINTERRUPTIBLE); + + expire = msecs_to_jiffies(dspLoadMonitorPeriod) + jiffies; + + mod_timer(&timer, expire); + schedule(); + /* We can be woken up before the expiration of the timer + but we don't need to handle that case as the + computation of the DSP load takes that into account */ + + if (!test_bit(i, &running_dsp)) + continue; + + if (CM_GetMpcLoadCounter(mpc->coreId, + &loadCounter) != CM_OK) + loadCounter = mpc->oldLoadCounter; + +#ifdef CONFIG_DEBUG_FS + mpc->load = +#endif + load = computeDspLoad(&mpc->oldLoadCounter, &loadCounter); + mpc->oldLoadCounter = loadCounter; + + if (load == -1) + continue; + /* check if we must request more opp */ + if ((current_opp_request == HALF_OPP) + && (load > dspLoadHighThreshold)) { +#ifdef CONFIG_DEBUG_FS + mpc->opp_request = +#endif + current_opp_request = FULL_OPP; + if (cm_debug_level) + pr_info("CM Driver: Request QoS OPP %d for %s\n", + current_opp_request, mpc->name); + prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, + (char*)mpc->name, + current_opp_request); + } + /* check if we can request less opp */ + else if ((current_opp_request == FULL_OPP) + && (load < dspLoadLowThreshold)) { +#ifdef CONFIG_DEBUG_FS + mpc->opp_request = +#endif + current_opp_request = HALF_OPP; + if (cm_debug_level) + pr_info("CM Driver: Request QoS OPP %d for %s\n", + current_opp_request, mpc->name); + prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, + (char*)mpc->name, + current_opp_request); + } + } + +#ifdef CONFIG_DEBUG_FS + mpc->opp_request = mpc->load = 0; +#endif + del_singleshot_timer_sync(&timer); + if (cm_debug_level) + pr_info("CM Driver: Remove QoS OPP for %s\n", mpc->name); + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, + (char*)mpc->name); + return 0; +} + +static int enable_auto_pm = 1; +module_param(enable_auto_pm, bool, S_IWUSR|S_IRUGO); + +/** \ingroup OSAL_IMPLEMENTATION + * Used by CM to disable a power resource + */ +void OSAL_DisablePwrRessource(t_nmf_power_resource resource, t_uint32 firstParam, t_uint32 secondParam) +{ + switch (resource) { + case CM_OSAL_POWER_SxA_CLOCK: { + unsigned idx = COREIDX(firstParam); + struct osal_msg msg; + + if (idx >= NB_MPC) { + pr_err("CM Driver(%s(res=%d)): core %u unknown\n", + __func__, (int)resource, (unsigned)firstParam); + return; + } + + cm_debug_destroy_tcm_file(idx); + + /* Stop the DSP load monitoring */ + clear_bit(idx, &running_dsp); + if (osalEnv.mpc[idx].monitor_tsk) { + kthread_stop(osalEnv.mpc[idx].monitor_tsk); + osalEnv.mpc[idx].monitor_tsk = NULL; + } + + /* Stop the DMA (normally done on DSP side, but be safe) */ + if (firstParam == SIA_CORE_ID) + cmdma_stop_dma(); + + /* Stop the DSP */ + if (regulator_disable(osalEnv.mpc[idx].mmdsp_regulator) < 0) + pr_err("CM Driver(%s): can't disable regulator %s-mmsdp\n", + __func__, osalEnv.mpc[idx].name); +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(&osalEnv.mpc[idx].wakelock); +#endif + + /* Create and dispatch a shutdown service message */ + msg.msg_type = MSG_SERVICE; + msg.d.srv.srvType = NMF_SERVICE_SHUTDOWN; + msg.d.srv.srvData.shutdown.coreid = firstParam; + dispatch_service_msg(&msg); + break; + } + case CM_OSAL_POWER_SxA_AUTOIDLE: + switch (firstParam) { + case SVA_CORE_ID: + osalEnv.dsp_sleep.sva_auto_pm_enable = PRCMU_AUTO_PM_OFF; + osalEnv.dsp_sleep.sva_power_on = 0; + osalEnv.dsp_sleep.sva_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF; + break; + case SIA_CORE_ID: + osalEnv.dsp_sleep.sia_auto_pm_enable = PRCMU_AUTO_PM_OFF; + osalEnv.dsp_sleep.sia_power_on = 0; + osalEnv.dsp_sleep.sia_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF; + break; + default: + pr_err("CM Driver(%s(res=%d)): core %u unknown\n", __func__, (int)resource, (unsigned)firstParam); + return; + } + if (enable_auto_pm) + prcmu_configure_auto_pm(&osalEnv.dsp_sleep, &osalEnv.dsp_idle); + break; + case CM_OSAL_POWER_SxA_HARDWARE: { + unsigned idx = COREIDX(firstParam); + if (idx >= NB_MPC) { + pr_err("CM Driver(%s(res=%d)): core %u unknown\n", + __func__, (int)resource, (unsigned)firstParam); + return; + } + if (regulator_disable(osalEnv.mpc[idx].pipe_regulator) < 0) + pr_err("CM Driver(%s): can't disable regulator %s-pipe\n", + __func__, osalEnv.mpc[idx].name); + break; + } + case CM_OSAL_POWER_HSEM: + break; + case CM_OSAL_POWER_SDRAM: + break; + case CM_OSAL_POWER_ESRAM: { + int i; + /* firstParam: base address; secondParam: size + U8500_ESRAM_BASE is the start address of BANK 0, + BANK size=0x20000 */ + + /* Compute the relative end address of the range, + relative to base address of BANK1 */ + secondParam = (firstParam+secondParam-(U8500_ESRAM_BASE+0x20000)-1); + + /* if end is below base address of BANK1, it means that full + range of addresses is on Bank0 */ + if (((int)secondParam) < 0) + break; + /* Compute the index of the last bank accessed among + esram 1+2 and esram 3+4 banks */ + secondParam /= 0x40000; + WARN_ON(secondParam > 1); + + /* Compute the index of the first bank accessed among esram 1+2 + and esram 3+4 banks + Do not manage Bank 0 (secured, must be always ON) */ + if (firstParam < (U8500_ESRAM_BASE+0x20000)) + firstParam = 0; + else + firstParam = (firstParam-(U8500_ESRAM_BASE+0x20000))/0x40000; + + /* power off the banks 1+2 and 3+4 if accessed. */ + for (i=firstParam; i<=secondParam; i++) { + if (regulator_disable(osalEnv.esram_regulator[i]) < 0) + pr_err("CM Driver(%s): can't disable regulator" + "for esram bank %s\n", __func__, + i ? "34" : "12"); + } + break; + } + default: + pr_err("CM Driver(%s): resource %d unknown/not supported\n", + __func__, (int)resource); + } +} + +/** \ingroup OSAL_IMPLEMENTATION + * Used by CM to enable a power resource + */ +t_cm_error OSAL_EnablePwrRessource(t_nmf_power_resource resource, t_uint32 firstParam, t_uint32 secondParam) +{ + switch (resource) { + case CM_OSAL_POWER_SxA_CLOCK: { + unsigned idx = COREIDX(firstParam); + + if (idx > NB_MPC) { + pr_err("CM Driver(%s(res=%d)): core %u unknown\n", __func__, (int)resource, (unsigned)firstParam); + return CM_INVALID_PARAMETER; + } + + /* Start the DSP */ +#ifdef CONFIG_HAS_WAKELOCK + wake_lock(&osalEnv.mpc[idx].wakelock); +#endif + if (regulator_enable(osalEnv.mpc[idx].mmdsp_regulator) < 0) + pr_err("CM Driver(%s): can't enable regulator %s-mmsdp\n", __func__, osalEnv.mpc[idx].name); + + /* Start the DSP load monitoring for this dsp */ + set_bit(idx, &running_dsp); + osalEnv.mpc[idx].monitor_tsk = kthread_run(&dspload_monitor, + (void*)idx, + "%s-loadd", + osalEnv.mpc[idx].name); + if (IS_ERR(osalEnv.mpc[idx].monitor_tsk)) { + pr_err("CM Driver: failed to start dspmonitord " + "thread: %ld\n", PTR_ERR(osalEnv.mpc[idx].monitor_tsk)); + osalEnv.mpc[idx].monitor_tsk = NULL; + } + + cm_debug_create_tcm_file(idx); + break; + } + case CM_OSAL_POWER_SxA_AUTOIDLE: + switch (firstParam) { + case SVA_CORE_ID: + osalEnv.dsp_sleep.sva_auto_pm_enable = PRCMU_AUTO_PM_ON; + osalEnv.dsp_sleep.sva_power_on = PRCMU_AUTO_PM_POWER_ON_HSEM | PRCMU_AUTO_PM_POWER_ON_ABB_FIFO_IT; + osalEnv.dsp_sleep.sva_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_RAMRET_HWP_OFF; + break; + case SIA_CORE_ID: + osalEnv.dsp_sleep.sia_auto_pm_enable = PRCMU_AUTO_PM_ON; + osalEnv.dsp_sleep.sia_power_on = PRCMU_AUTO_PM_POWER_ON_HSEM | PRCMU_AUTO_PM_POWER_ON_ABB_FIFO_IT; + osalEnv.dsp_sleep.sia_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_RAMRET_HWP_OFF; + break; + default: + pr_err("CM Driver(%s(res=%d)): core %u unknown\n", __func__, (int)resource, (unsigned)firstParam); + return CM_INVALID_PARAMETER; + } + if (enable_auto_pm) + prcmu_configure_auto_pm(&osalEnv.dsp_sleep, &osalEnv.dsp_idle); + break; + case CM_OSAL_POWER_SxA_HARDWARE: { + unsigned idx = COREIDX(firstParam); + + if (idx > NB_MPC) { + pr_err("CM Driver(%s(res=%d)): core %u unknown\n", __func__, (int)resource, (unsigned)firstParam); + return CM_INVALID_PARAMETER; + } + if (regulator_enable(osalEnv.mpc[idx].pipe_regulator) < 0) + pr_err("CM Driver(%s): can't enable regulator %s-pipe\n", __func__, osalEnv.mpc[idx].name); + break; + } + case CM_OSAL_POWER_HSEM: + return CM_OK; + case CM_OSAL_POWER_SDRAM: + break; + case CM_OSAL_POWER_ESRAM: + { + int i; + /* firstParam: base address; secondParam: size + U8500_ESRAM_BASE is the start address of BANK 0, + BANK size=0x20000 */ + + /* Compute the relative end address of the range, relative + to base address of BANK1 */ + secondParam = (firstParam+secondParam-(U8500_ESRAM_BASE+0x20000)-1); + + /* if end is below base address of BANK1, it means that full + range of addresses is on Bank0 */ + if (((int)secondParam) < 0) + break; + /* Compute the index of the last bank accessed among esram 1+2 + and esram 3+4 banks */ + secondParam /= 0x40000; + WARN_ON(secondParam > 1); + + /* Compute the index of the first bank accessed among esram 1+2 + and esram 3+4 banks + Do not manage Bank 0 (secured, must be always ON) */ + if (firstParam < (U8500_ESRAM_BASE+0x20000)) + firstParam = 0; + else + firstParam = (firstParam-(U8500_ESRAM_BASE+0x20000))/0x40000; + + /* power on the banks 1+2 and 3+4 if accessed. */ + for (i=firstParam; i<=secondParam; i++) { + if (regulator_enable(osalEnv.esram_regulator[i]) < 0) + pr_err("CM Driver(%s): can't enable regulator " + "for esram bank %s\n", __func__, + i ? "34" : "12"); + } + break; + } + default: + pr_err("CM Driver(%s): resource %x unknown/not supported\n", + __func__, (int)resource); + return CM_INVALID_PARAMETER; + } + + return CM_OK; +} + +/*! + * \brief Generate 'software' panic to notify cm users + * that a problem occurs but no dsp panic has been sent yet + * (for example a dsp crash) + * \ingroup CM_ENGINE_OSAL_API + */ +void OSAL_GeneratePanic(t_nmf_core_id coreId, t_uint32 reason) +{ + struct osal_msg msg; + + /* Create and dispatch a shutdown service message */ + msg.msg_type = MSG_SERVICE; + msg.d.srv.srvType = NMF_SERVICE_PANIC; + msg.d.srv.srvData.panic.panicReason = MPC_NOT_RESPONDING_PANIC; + msg.d.srv.srvData.panic.panicSource = MPC_EE; + msg.d.srv.srvData.panic.info.mpc.coreid = coreId; + msg.d.srv.srvData.panic.info.mpc.faultingComponent = 0; + msg.d.srv.srvData.panic.info.mpc.panicInfo1 = reason; + msg.d.srv.srvData.panic.info.mpc.panicInfo2 = 0; + dispatch_service_msg(&msg); +} + +/*! + * \brief Generate an OS-Panic. Called in from CM_ASSERT(). + * \ingroup CM_ENGINE_OSAL_API + */ +void OSAL_Panic(void) +{ + panic("FATAL ISSUE IN THE CM DRIVER !!"); +} +#include <mach/dcache.h> +/*! + * \brief Clean data cache in DDR in order to be accessible from peripheral. + * + * \ingroup CM_ENGINE_OSAL_API + */ +void OSAL_CleanDCache(t_uint32 startAddr, t_uint32 size) +{ +#if 0 + /* + * Currently, the code sections are non-cached/buffered, + * which normally doesn't required the maintenance done below. + * As the cost is low (doesn't do much thing), I keep it in case + * of the memory settings are changed later. + */ + + struct hwmem_region region; + struct mpcConfig *mpc; + t_uint32 endAddr = startAddr + size; + + if (startAddr >= (u32)osalEnv.mpc[0].sdram_code.data + && endAddr <= (u32)(osalEnv.mpc[0].sdram_code.data + + osalEnv.mpc[0].sdram_code.size)) { + mpc = &osalEnv.mpc[0]; + } else if (startAddr >= (u32)osalEnv.mpc[1].sdram_code.data + && endAddr <= (u32)(osalEnv.mpc[1].sdram_code.data + + osalEnv.mpc[1].sdram_code.size)) { + mpc = &osalEnv.mpc[1]; + } else { + /* The code may be in esram, in that case, nothing to do */ + return; + } + + region.offset = startAddr - (u32)mpc->sdram_code.data; + region.count = 1; + region.start = 0; + region.end = size; + region.size = size; + hwmem_set_domain(mpc->hwmem_code, HWMEM_ACCESS_READ, + HWMEM_DOMAIN_SYNC, ®ion); + /* + * The hwmem keep track of region being sync or not. + * Mark the region as being write-accessed here right now + * to let following clean being done as expected. Today, + * there is no other place to do that in CM Core right now + */ + hwmem_set_domain(mpc->hwmem_code, HWMEM_ACCESS_WRITE, + HWMEM_DOMAIN_CPU, ®ion); +#else + dsb(); + outer_cache.sync(); +#endif +} + +/*! + * \brief Flush write-buffer of L2 cache + * + * \ingroup CM_ENGINE_OSAL_API + */ +void OSAL_mb(void) +{ + mb(); +} + +/*! + * \brief return prcmu timer value. + * + * This is need for perfmeter api (see \ref t_nmf_power_resource) + * + * \ingroup CM_ENGINE_OSAL_API + */ +t_uint64 OSAL_GetPrcmuTimer() +{ + t_uint64 msbBefore; + t_uint32 lsb; + t_uint64 msbAfter; + + /* read prcmu timers */ + msbBefore = ~ioread32(prcmu_tcdm_base+0xDE4); + lsb = ~ioread32(prcmu_base+0x454); + msbAfter = ~ioread32(prcmu_tcdm_base+0xDE4); + + /* handle rollover test case */ + // NOTE : there is still a window in prcmu side between counter rollover + // and prcmu interrupt handling + // to update msb register => this can lead to erroneous value return here + if (msbBefore == msbAfter || lsb >= 0x80000000UL) + return (((msbBefore & 0xffffffUL) << 32) + lsb); + else + return (((msbAfter & 0xffffffUL) << 32) + lsb); +} + +/*! + * \brief Disable the service message handling (panic, etc) + * + * It must disable the handling of all service messages + * If a service message is currently handled, it must wait till the end + * of its managment before returning. + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_DisableServiceMessages(void) { + tasklet_disable(&cmld_service_tasklet); +} + +/*! + * \brief Enable the service message handling (panic, etc) + * + * It enables the handling of all service messages + * + * \ingroup CM_ENGINE_OSAL_API + */ +PUBLIC void OSAL_EnableServiceMessages(void) { + tasklet_enable(&cmld_service_tasklet); +} diff --git a/drivers/staging/nmf-cm/osal-kernel.h b/drivers/staging/nmf-cm/osal-kernel.h new file mode 100644 index 00000000000..7a71bdd591d --- /dev/null +++ b/drivers/staging/nmf-cm/osal-kernel.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#ifndef OSAL_KERNEL_H +#define OSAL_KERNEL_H + +#include <linux/debugfs.h> +#include <linux/interrupt.h> +#include <linux/hwmem.h> +#include <linux/regulator/consumer.h> +#ifdef CONFIG_HAS_WAKELOCK +#include <linux/wakelock.h> +#endif +#include <linux/mfd/dbx500-prcmu.h> +#include <cm/engine/api/channel_engine.h> +#include <cm/engine/api/control/configuration_engine.h> +#include <cm/engine/api/perfmeter_engine.h> +/* + * Do not include ELF definition from cm/engine/elf/inc/elfapi.h file + * because it conflicts with definition from linux/elf.h file + */ +#define _CM_ELF_H +#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h> +#include <linux/plist.h> + +#include "configuration.h" + +/* + * Per-MPC configuration structure + * Use struct debugfs_blob_wrapper to store pointer and size of section + * to allow easy re-use of data through debugfs + */ +struct mpcConfig { + const t_nmf_core_id coreId; /**< MPC coreId */ + const char *name; /**< MPC name */ + t_uint8 nbYramBanks; /**< number of TCM ram banks to reserve for y memory */ + t_nmf_executive_engine_id eeId; /**< Type of Executive Engine */ + const void *base_phys; /**< Physical base address of the MPC */ + struct debugfs_blob_wrapper base;/**< Remapped base address of the MPC and size of TCM24 */ + struct hwmem_alloc *hwmem_code; /**< hwmem code segment */ + u32 sdram_code_phys; /**< Physical base address for MPC SDRAM Code region */ + struct debugfs_blob_wrapper sdram_code; /**< Remapped base address and size for MPC SDRAM Code */ + struct hwmem_alloc *hwmem_data; /**< hwmem data segment */ + u32 sdram_data_phys; /**< Physical base address for MPC SDRAM Data region */ + struct debugfs_blob_wrapper sdram_data; /**< Remapped base address and size for MPC SDRAM Data */ + const unsigned int interrupt0; /**< interrupt line triggered by the MPC, for MPC events (if HSEM not used) */ + const unsigned int interrupt1; /**< interrupt line triggered by the MPC, for PANIC events */ + struct tasklet_struct tasklet; /**< taskket used to process MPC events */ + struct regulator *mmdsp_regulator; /**< mmdsp regulator linked to this MPC */ + struct regulator *pipe_regulator; /**< hardware pipe linked to this MPC */ +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock wakelock; /**< wakelock for this MPC to prevent ARM to go in APSLEEP state */ +#endif + struct task_struct *monitor_tsk; /**< task to monitor the dsp load; */ + t_cm_mpc_load_counter oldLoadCounter; /**< previous load counter of the DSP */ +#ifdef CONFIG_DEBUG_FS + struct dentry *dir; /**< debugfs dir entry */ + struct dentry *comp_dir; /**< debugfs component dir entry */ + struct dentry *domain_dir; /**< debugfs domain dir entry */ + struct dentry *snapshot_dir; /**< debugfs snapshot dir entry */ + struct dentry *mem_file; /**< debugfs meminfo file entry */ + struct dentry *tcm_file; /**< debugfs meminfo file entry */ + struct dentry *esram_file; /**< debugfs meminfo file entry */ + s8 load; /**< current load of the DSP */ + s8 opp_request; /**< current requested opp of the DSP */ +#endif +}; + +/** Describes current Kernel OSAL environment + * + * Note about mpc.tasklet : we declare one tasklet per MPC but their usage depends + * on cfgSemaphoreTypeHSEM. + * + * This tasklet is scheduled by the interrupt handler to process MPC Events. + * - If we use Hardware Semaphore, there is only one interrupt handler used + * and thus only one tasklet, tasklet of MPC 0 (ie osalEnv.mpc[0].tasklet) + * - If we use local semaphore, there is one interrupt handler and tasklet per mpc + */ +struct OsalEnvironment +{ + struct mpcConfig mpc[NB_MPC]; + void* hwsem_base; /** < Remapped base address of the hardware semaphores */ + void* esram_base; /**< Remapped base address embedded RAM used within the CM */ + struct regulator *esram_regulator[NB_ESRAM]; /**< regulator for ESRAM bank 1+2 and 3+4 */ + struct prcmu_auto_pm_config dsp_sleep; + struct prcmu_auto_pm_config dsp_idle; +}; + + +/** Structure used to store the skeleton related data. + * It is used for communicattion from a MPC to a user process (=host) + */ +typedef struct { + struct list_head entry; /**< Doubly linked list descriptor */ + t_cm_bf_mpc2host_handle mpc2hostId; /**< mpc2host ID */ + t_nmf_mpc2host_handle upperLayerThis;/**< upper-layer handle */ + struct cm_channel_priv* channelPriv; /**< Per-channel private data. The actual message queue is hold here */ +} t_skelwrapper; + +/** Message description for MPC to HOST communication + */ +struct osal_msg { + struct { + struct plist_node entry; /**< Doubly linked list descriptor */ + t_message_type type; /**< Type of message (callback, service or interrupt for now) */ + } hdr; /**< Header of the message */ +#define msg_entry hdr.entry +#define msg_type hdr.type + union { + struct { + t_skelwrapper *skelwrap; /**< Link to the skelwrapper, to retrieve the channel on which this message has to be forwarded */ + t_uint32 methodIdx; /**< callback data: method index*/ + t_event_params_handle anyPtr; /**< callback data: method parameters */ + t_uint32 ptrSize; /**< size of the parameters */ + } itf; /**< structure holding callback data */ + struct { + t_nmf_service_type srvType; /**< Type of the service */ + t_nmf_service_data srvData; /**< Data of the service */ + } srv; /**< structure holding service data */ + } d; /**< data */ +}; + +extern struct OsalEnvironment osalEnv; + +/** Environment initialization/deinitialization */ +int remapRegions(void); +void unmapRegions(void); + +/** Component manager configuration getters for CM_ENGINE_Init() */ +int getNmfHwMappingDesc(t_nmf_hw_mapping_desc* nmfHwMappingDesc); + +/** Component manager configuration getters for CM_ConfigureMediaProcessorCore (SVA and SIA) */ +void getMpcSystemAddress(unsigned i, t_cm_system_address* mpcSystemAddress); +void getMpcSdramSegments(unsigned i, t_nmf_memory_segment* codeSegment, t_nmf_memory_segment* dataSegment); + +#ifdef CM_DEBUG_ALLOC +struct cm_alloc { + spinlock_t lock; + struct list_head chain; +}; + +struct cm_alloc_elem { + struct list_head elem; + void *caller; + size_t size; + char addr[0]; +}; + +void init_debug_alloc(void); +void cleanup_debug_alloc(void); +#endif /* CM_DEBUG_ALLOC */ + +/* TODO: To remove later */ +extern __iomem void *prcmu_base; +extern __iomem void *prcmu_tcdm_base; +extern const char *cmld_devname[]; + +#define PRCM_SVAMMDSPCLK_MGT (prcmu_base + 0x008) +#define PRCM_SIAMMDSPCLK_MGT (prcmu_base + 0x00c) + +#endif /* OSAL_KERNEL_H */ diff --git a/drivers/staging/nmf-cm/share/communication/inc/communication_fifo.h b/drivers/staging/nmf-cm/share/communication/inc/communication_fifo.h new file mode 100644 index 00000000000..ea24e82ceae --- /dev/null +++ b/drivers/staging/nmf-cm/share/communication/inc/communication_fifo.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) ST-Ericsson SA 2010. All rights reserved. + * This code is ST-Ericsson proprietary and confidential. + * Any use of the code for whatever purpose is subject to + * specific written permission of ST-Ericsson SA. + */ + +#ifndef __INC_NMF_COM_FIFO +#define __INC_NMF_COM_FIFO + +#include <inc/typedef.h> + +#define EVENT_ELEM_METHOD_IDX 0 +#define EVENT_ELEM_PARAM_IDX 1 +#define EVENT_ELEM_EXTFIELD_IDX 2 + +#define EVENT_ELEM_SIZE_IN_BYTE (3 * sizeof(t_shared_field)) + +#endif /* __INC_NMF_COM_FIFO */ diff --git a/drivers/staging/nmf-cm/share/communication/inc/initializer.h b/drivers/staging/nmf-cm/share/communication/inc/initializer.h new file mode 100644 index 00000000000..10985c3981a --- /dev/null +++ b/drivers/staging/nmf-cm/share/communication/inc/initializer.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) ST-Ericsson SA 2010. All rights reserved. + * This code is ST-Ericsson proprietary and confidential. + * Any use of the code for whatever purpose is subject to + * specific written permission of ST-Ericsson SA. + */ + +#ifndef __INC_SHARE_INITIALIZER +#define __INC_SHARE_INITIALIZER + +#define NMF_CONSTRUCT_INDEX 0 +#define NMF_START_INDEX 1 +#define NMF_STOP_INDEX 2 +#define NMF_DESTROY_INDEX 3 +#define NMF_UPDATE_STACK 4 +#define NMF_LOCK_CACHE 5 +#define NMF_UNLOCK_CACHE 6 +#define NMF_ULP_FORCEWAKEUP 7 +#define NMF_ULP_ALLOWSLEEP 8 +#define NMF_CONSTRUCT_SYNC_INDEX 9 +#define NMF_START_SYNC_INDEX 10 +#define NMF_STOP_SYNC_INDEX 11 + +/* + * Index of datas in command parameter format + */ +#define INIT_COMPONENT_CMD_HANDLE_INDEX 0 +#define INIT_COMPONENT_CMD_THIS_INDEX 2 +#define INIT_COMPONENT_CMD_METHOD_INDEX 4 +#define INIT_COMPONENT_CMD_SIZE 6 + +/* + * Index of datas in acknowledge parameter format + */ +#define INIT_COMPONENT_ACK_HANDLE_INDEX 0 +#define INIT_COMPONENT_ACK_SIZE 2 + +#endif /* __INC_SHARE_INITIALIZER */ diff --git a/drivers/staging/nmf-cm/share/communication/inc/nmf_fifo_desc.h b/drivers/staging/nmf-cm/share/communication/inc/nmf_fifo_desc.h new file mode 100644 index 00000000000..99caa48b05c --- /dev/null +++ b/drivers/staging/nmf-cm/share/communication/inc/nmf_fifo_desc.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) ST-Ericsson SA 2010. All rights reserved. + * This code is ST-Ericsson proprietary and confidential. + * Any use of the code for whatever purpose is subject to + * specific written permission of ST-Ericsson SA. + */ + +#ifndef __INC_NMF_FIFO_DESC +#define __INC_NMF_FIFO_DESC + +#include <inc/typedef.h> +#include <share/semaphores/inc/semaphores.h> + +/* + * SHOULD be mapped onto a AHB burst (16 bytes=8x16-bit) + */ +typedef struct { + t_semaphore_id semId; + + t_uint16 elemSize; + t_uint16 fifoFullValue; + t_uint16 readIndex; + t_uint16 writeIndex; + t_uint16 wrappingValue; + + t_uint32 extendedField; /* in DSP 24 memory when to MPC in Logical Host when to ARM */ +} t_nmf_fifo_desc; + +#define EXTENDED_FIELD_BCTHIS_OR_TOP 0 //<! This field will be used: + //<! - as hostBCThis for DSP->HOST binding + //<! - as TOP else +#define EXTENDED_FIELD_BCDESC 1 //<! This field will be used for: + //<! - interface method address for ->MPC binding + //<! - for params size for ->Host binding (today only [0] is used as max size) + +#endif /* __INC_NMF_FIFO */ diff --git a/drivers/staging/nmf-cm/share/communication/inc/nmf_service.h b/drivers/staging/nmf-cm/share/communication/inc/nmf_service.h new file mode 100644 index 00000000000..5ec08434492 --- /dev/null +++ b/drivers/staging/nmf-cm/share/communication/inc/nmf_service.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) ST-Ericsson SA 2010. All rights reserved. + * This code is ST-Ericsson proprietary and confidential. + * Any use of the code for whatever purpose is subject to + * specific written permission of ST-Ericsson SA. + */ + +#ifndef __INC_NMF_SERVICE_H +#define __INC_NMF_SERVICE_H + +/* 1 - 0xff Reserved for Panic Reason */ +#define MPC_SERVICE_NONE 0 +#define MPC_SERVICE_BOOT 0xB001 +#define MPC_SERVICE_PRINT 0x1234 + +#endif diff --git a/drivers/staging/nmf-cm/share/inc/macros.h b/drivers/staging/nmf-cm/share/inc/macros.h new file mode 100644 index 00000000000..c96fe031c25 --- /dev/null +++ b/drivers/staging/nmf-cm/share/inc/macros.h @@ -0,0 +1,214 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief NMF Macro API. + */ + +#ifndef _COMMON_MACROS_H_ +#define _COMMON_MACROS_H_ + +#undef ALIGN_VALUE +#define ALIGN_VALUE(value, alignment) (((value) + (alignment - 1)) & ~(alignment - 1)) + +#undef MIN +#define MIN(a,b) (((a)>(b))?(b):(a)) + +#undef MAX +#define MAX(a,b) (((a)<(b))?(b):(a)) + +/*----------------------------------------------------------------------------- + * endianess switch macros (32 bits and 16 bits) + *---------------------------------------------------------------------------*/ +#define ENDIANESS_32_SWITCH(value) ( \ + (((value) & MASK_BYTE3) >> SHIFT_BYTE3) | \ + (((value) & MASK_BYTE2) >> SHIFT_BYTE1) | \ + (((value) & MASK_BYTE1) << SHIFT_BYTE1) | \ + (((value) & MASK_BYTE0) << SHIFT_BYTE3) \ + ) + +#define ENDIANESS_16_SWITCH(value) ( \ + (((value) & MASK_BYTE0) << SHIFT_BYTE1) | \ + (((value) & MASK_BYTE1) >> SHIFT_BYTE1) \ + ) + +/*----------------------------------------------------------------------------- + * field offset extraction from a structure + *---------------------------------------------------------------------------*/ +#undef FIELD_OFFSET +#define FIELD_OFFSET(typeName, fieldName) ((t_uint32)(&(((typeName *)0)->fieldName))) + +#undef MASK_BIT +#define MASK_BIT(n) (1UL << ((n) - 1)) + +/*----------------------------------------------------------------------------- + * Misc definition + *---------------------------------------------------------------------------*/ + +#undef ONE_KB +#define ONE_KB (1024) +#undef ONE_MB +#define ONE_MB (ONE_KB * ONE_KB) + +/*----------------------------------------------------------------------------- + * Bit mask definition + *---------------------------------------------------------------------------*/ +#undef MASK_NULL8 +#define MASK_NULL8 0x00U +#undef MASK_NULL16 +#define MASK_NULL16 0x0000U +#undef MASK_NULL32 +#define MASK_NULL32 0x00000000UL +#undef MASK_ALL8 +#define MASK_ALL8 0xFFU +#undef MASK_ALL16 +#define MASK_ALL16 0xFFFFU +#undef MASK_ALL32 +#define MASK_ALL32 0xFFFFFFFFUL + +#undef MASK_BIT0 +#define MASK_BIT0 (1UL<<0) +#undef MASK_BIT1 +#define MASK_BIT1 (1UL<<1) +#undef MASK_BIT2 +#define MASK_BIT2 (1UL<<2) +#undef MASK_BIT3 +#define MASK_BIT3 (1UL<<3) +#undef MASK_BIT4 +#define MASK_BIT4 (1UL<<4) +#undef MASK_BIT5 +#define MASK_BIT5 (1UL<<5) +#undef MASK_BIT6 +#define MASK_BIT6 (1UL<<6) +#undef MASK_BIT7 +#define MASK_BIT7 (1UL<<7) +#undef MASK_BIT8 +#define MASK_BIT8 (1UL<<8) +#undef MASK_BIT9 +#define MASK_BIT9 (1UL<<9) +#undef MASK_BIT10 +#define MASK_BIT10 (1UL<<10) +#undef MASK_BIT11 +#define MASK_BIT11 (1UL<<11) +#undef MASK_BIT12 +#define MASK_BIT12 (1UL<<12) +#undef MASK_BIT13 +#define MASK_BIT13 (1UL<<13) +#undef MASK_BIT14 +#define MASK_BIT14 (1UL<<14) +#undef MASK_BIT15 +#define MASK_BIT15 (1UL<<15) +#undef MASK_BIT16 +#define MASK_BIT16 (1UL<<16) +#undef MASK_BIT17 +#define MASK_BIT17 (1UL<<17) +#undef MASK_BIT18 +#define MASK_BIT18 (1UL<<18) +#undef MASK_BIT19 +#define MASK_BIT19 (1UL<<19) +#undef MASK_BIT20 +#define MASK_BIT20 (1UL<<20) +#undef MASK_BIT21 +#define MASK_BIT21 (1UL<<21) +#undef MASK_BIT22 +#define MASK_BIT22 (1UL<<22) +#undef MASK_BIT23 +#define MASK_BIT23 (1UL<<23) +#undef MASK_BIT24 +#define MASK_BIT24 (1UL<<24) +#undef MASK_BIT25 +#define MASK_BIT25 (1UL<<25) +#undef MASK_BIT26 +#define MASK_BIT26 (1UL<<26) +#undef MASK_BIT27 +#define MASK_BIT27 (1UL<<27) +#undef MASK_BIT28 +#define MASK_BIT28 (1UL<<28) +#undef MASK_BIT29 +#define MASK_BIT29 (1UL<<29) +#undef MASK_BIT30 +#define MASK_BIT30 (1UL<<30) +#undef MASK_BIT31 +#define MASK_BIT31 (1UL<<31) + +/*----------------------------------------------------------------------------- + * quartet shift definition + *---------------------------------------------------------------------------*/ +#undef MASK_QUARTET +#define MASK_QUARTET (0xFUL) +#undef SHIFT_QUARTET0 +#define SHIFT_QUARTET0 0 +#undef SHIFT_QUARTET1 +#define SHIFT_QUARTET1 4 +#undef SHIFT_QUARTET2 +#define SHIFT_QUARTET2 8 +#undef SHIFT_QUARTET3 +#define SHIFT_QUARTET3 12 +#undef SHIFT_QUARTET4 +#define SHIFT_QUARTET4 16 +#undef SHIFT_QUARTET5 +#define SHIFT_QUARTET5 20 +#undef SHIFT_QUARTET6 +#define SHIFT_QUARTET6 24 +#undef SHIFT_QUARTET7 +#define SHIFT_QUARTET7 28 +#undef MASK_QUARTET0 +#define MASK_QUARTET0 (MASK_QUARTET << SHIFT_QUARTET0) +#undef MASK_QUARTET1 +#define MASK_QUARTET1 (MASK_QUARTET << SHIFT_QUARTET1) +#undef MASK_QUARTET2 +#define MASK_QUARTET2 (MASK_QUARTET << SHIFT_QUARTET2) +#undef MASK_QUARTET3 +#define MASK_QUARTET3 (MASK_QUARTET << SHIFT_QUARTET3) +#undef MASK_QUARTET4 +#define MASK_QUARTET4 (MASK_QUARTET << SHIFT_QUARTET4) +#undef MASK_QUARTET5 +#define MASK_QUARTET5 (MASK_QUARTET << SHIFT_QUARTET5) +#undef MASK_QUARTET6 +#define MASK_QUARTET6 (MASK_QUARTET << SHIFT_QUARTET6) +#undef MASK_QUARTET7 +#define MASK_QUARTET7 (MASK_QUARTET << SHIFT_QUARTET7) + +/*----------------------------------------------------------------------------- + * Byte shift definition + *---------------------------------------------------------------------------*/ +#undef MASK_BYTE +#define MASK_BYTE (0xFFUL) +#undef SHIFT_BYTE0 +#define SHIFT_BYTE0 0U +#undef SHIFT_BYTE1 +#define SHIFT_BYTE1 8U +#undef SHIFT_BYTE2 +#define SHIFT_BYTE2 16U +#undef SHIFT_BYTE3 +#define SHIFT_BYTE3 24U +#undef MASK_BYTE0 +#define MASK_BYTE0 (MASK_BYTE << SHIFT_BYTE0) +#undef MASK_BYTE1 +#define MASK_BYTE1 (MASK_BYTE << SHIFT_BYTE1) +#undef MASK_BYTE2 +#define MASK_BYTE2 (MASK_BYTE << SHIFT_BYTE2) +#undef MASK_BYTE3 +#define MASK_BYTE3 (MASK_BYTE << SHIFT_BYTE3) + +/*----------------------------------------------------------------------------- + * Halfword shift definition + *---------------------------------------------------------------------------*/ +#undef MASK_HALFWORD +#define MASK_HALFWORD (0xFFFFUL) +#undef SHIFT_HALFWORD0 +#define SHIFT_HALFWORD0 0U +#undef SHIFT_HALFWORD1 +#define SHIFT_HALFWORD1 16U +#undef MASK_HALFWORD0 +#define MASK_HALFWORD0 (MASK_HALFWORD << SHIFT_HALFWORD0) +#undef MASK_HALFWORD1 +#define MASK_HALFWORD1 (MASK_HALFWORD << SHIFT_HALFWORD1) + +#endif /* _COMMON_MACROS_H_ */ + diff --git a/drivers/staging/nmf-cm/share/inc/nmf.h b/drivers/staging/nmf-cm/share/inc/nmf.h new file mode 100644 index 00000000000..8be8b41e5e3 --- /dev/null +++ b/drivers/staging/nmf-cm/share/inc/nmf.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2, with + * user space exemption described in the top-level COPYING file in + * the Linux kernel source tree. + */ + +/*! + * \brief Common Nomadik Multiprocessing Framework type definition + * + * This file contains the shared type definitions used into NMF. + */ + +#ifndef __INC_NMF_H +#define __INC_NMF_H + +#include <inc/typedef.h> + +/*! + * \brief Identification of the various cores (host cpu and Media Processors) into Nomadik Platform + * In order to improve performance, these ids are those used to interconnect HW Semaphores IP with Cores (Interrupt lines) + * \ingroup NMF_COMMON + */ +#if defined(__STN_8500) + //#warning "TODO : mapping below is not correct, need to think how to change it" +#endif +typedef t_uint8 t_nmf_core_id; +#define ARM_CORE_ID ((t_nmf_core_id)0) //!< HOST CPU Id +#define SVA_CORE_ID ((t_nmf_core_id)1) //!< Smart Video Accelerator Media Processor Code Id +#define SIA_CORE_ID ((t_nmf_core_id)2) //!< Smart Imaging Accelerator Media Processor Code Id +#define NB_CORE_IDS ((t_nmf_core_id)3) + +#define FIRST_CORE_ID ((t_nmf_core_id)ARM_CORE_ID) +#define FIRST_MPC_ID ((t_nmf_core_id)SVA_CORE_ID) +#define LAST_CORE_ID ((t_nmf_core_id)SIA_CORE_ID) +#define LAST_MPC_ID ((t_nmf_core_id)SIA_CORE_ID) + + +/*! + * \brief Define minimal stack size use by execution engine + */ +#define MIN_STACK_SIZE 128 + + + +#endif /* __INC_NMF_H */ diff --git a/drivers/staging/nmf-cm/share/inc/nomadik_mapping.h b/drivers/staging/nmf-cm/share/inc/nomadik_mapping.h new file mode 100644 index 00000000000..bec221aa111 --- /dev/null +++ b/drivers/staging/nmf-cm/share/inc/nomadik_mapping.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) ST-Ericsson SA 2010. All rights reserved. + * This code is ST-Ericsson proprietary and confidential. + * Any use of the code for whatever purpose is subject to + * specific written permission of ST-Ericsson SA. + */ + +#ifndef __INC_NOMADIK_MAPPING_H +#define __INC_NOMADIK_MAPPING_H + +/*--------------------------------------------------------------------------*/ +#if defined(__STN_8810) + +/* XTI (CPU OSMO/OSMOT address space) */ +#define XTI_CPU_BASE_ADDR 0x10000000 +#define XTI_CPU_END_ADDR 0x100FFFFF + +/* XTI configuration registers */ +#define XTI_CFG_REG_BASE_ADDR 0x101A0000 +#define XTI_CFG_REG_END_ADDR 0x101AFFFF + +/* Core APB Peripherals */ +#define CORE_APB_BASE_ADDR 0x101E0000 +#define CORE_APB_END_ADDR 0x101EFFFF + +/* DMA APB Peripherals */ +#define DMA_APB_BASE_ADDR 0x101F0000 +#define DMA_APB_END_ADDR 0x101FFFFF + +/* XTI (DSP OSMO/OSMOT address space) */ +#define XTI_DSP_BASE_ADDR 0x10200000 +#define XTI_DSP_END_ADDR 0x1020FFFF + +#endif /* defined(__STN_8810) */ + +/*--------------------------------------------------------------------------*/ +#if defined(__STN_8815) + +/* XTI (CPU OSMO/OSMOT address space) */ +#define XTI_CPU_BASE_ADDR 0x10000000 +#define XTI_CPU_END_ADDR 0x100FFFFF + +/* XTI configuration registers */ +#define XTI_CFG_REG_BASE_ADDR 0x101A0000 +#define XTI_CFG_REG_END_ADDR 0x101AFFFF + +/* Core APB Peripherals */ +#define CORE_APB_BASE_ADDR 0x101E0000 +#define CORE_APB_END_ADDR 0x101EFFFF + +/* DMA APB Peripherals */ +#define DMA_APB_BASE_ADDR 0x101F0000 +#define DMA_APB_END_ADDR 0x101FFFFF + +/* XTI (DSP OSMO/OSMOT address space) */ +#define XTI_DSP_BASE_ADDR 0x10220000 +#define XTI_DSP_END_ADDR 0x1022FFFF + +#endif /* defined(__STN_8815) */ + + +/*--------------------------------------------------------------------------*/ +#if defined(__STN_8820) + +/* STM (System Trace Module address space) */ +#define STM_BASE_ADDR 0x700F0000 +#define STM_END_ADDR 0x700FFFFF + +/* AHB2 Peripherals */ +#define AHB2_PERIPH_BASE_ADDR 0x70100000 +#define AHB2_PERIPH_END_ADDR 0x7010FFFF + +/* APB2 Peripherals */ +#define APB2_PERIPH_BASE_ADDR 0x70110000 +#define APB2_PERIPH_END_ADDR 0x7011FFFF + +/* APB1 Peripherals */ +#define APB1_PERIPH_BASE_ADDR 0x70120000 +#define APB1_PERIPH_END_ADDR 0x7012FFFF + +#endif /* defined(__STN_8820) */ + +/*--------------------------------------------------------------------------*/ +#if defined(__STN_8500) +/* STM (System Trace Module address space) */ +#define STM_BASE_ADDR 0x80100000 +#define STM_END_ADDR 0x8010FFFF + +#define HSEM_BASE_ADDR 0x80140000 +#define HSEM_END_ADDR 0x8014FFFF + +#define DMA_CTRL_BASE_ADDR 0x801C0000 +#define DMA_CTRL_END_ADDR 0x801C0FFF + + +#endif /* defined(__STN_8500) */ + +#endif /*__INC_NOMADIK_MAPPING_H */ diff --git a/drivers/staging/nmf-cm/share/semaphores/inc/hwsem_hwp.h b/drivers/staging/nmf-cm/share/semaphores/inc/hwsem_hwp.h new file mode 100644 index 00000000000..b573627beae --- /dev/null +++ b/drivers/staging/nmf-cm/share/semaphores/inc/hwsem_hwp.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) ST-Ericsson SA 2010. All rights reserved. + * This code is ST-Ericsson proprietary and confidential. + * Any use of the code for whatever purpose is subject to + * specific written permission of ST-Ericsson SA. + */ + +#ifndef __INC_HWSEM_HWP_H +#define __INC_HWSEM_HWP_H + +#include <share/semaphores/inc/semaphores.h> + +#define CORE_ID_2_HW_CORE_ID(coreId) (1U << (coreId)) + +/* + * Definition of the number of hw semaphores into the Nomadik IP + */ +#define NUM_HW_SEMAPHORES 32 + + +/* + * Definition of how HSEM IP interrupts are interconnected with cores + */ +typedef enum { + HSEM_FIRST_INTR = 0, + HSEM_INTRA = HSEM_FIRST_INTR, + HSEM_INTRB = 1, + HSEM_INTRC = 2, + HSEM_INTRD = 3, + HSEM_INTRE = 4, + HSEM_MAX_INTR +} t_hw_semaphore_irq_id; + +/* + * Description of the registers of the HW Sem IP + */ +#define HSEM_INTRA_MASK (1<<(4+HSEM_INTRA)) +#define HSEM_INTRB_MASK (1<<(4+HSEM_INTRB)) +#define HSEM_INTRC_MASK (1<<(4+HSEM_INTRC)) +#define HSEM_INTRD_MASK (1<<(4+HSEM_INTRD)) +#define HSEM_INTRE_MASK (1<<(4+HSEM_INTRE)) + +typedef struct { + t_shared_reg imsc; + t_shared_reg ris; + t_shared_reg mis; + t_shared_reg icr; +} t_hsem_it_regs; + +typedef volatile struct { +#if defined(__STN_8500) + t_shared_reg cr; + t_shared_reg dummy; +#endif + t_shared_reg sem[NUM_HW_SEMAPHORES]; +#if defined(__STN_8820) + t_shared_reg RESERVED1[(0x90 - 0x80)>>2]; +#elif defined(__STN_8500) + t_shared_reg RESERVED1[(0x90 - 0x88)>>2]; +#else /* __STN_8820 or __STN_8500 -> _STN_8815 */ + t_shared_reg RESERVED1[(0x90 - 0x40)>>2]; +#endif /* __STN_8820 or __STN_8500 -> _STN_8815 */ + t_shared_reg icrall; + t_shared_reg RESERVED2[(0xa0 - 0x94)>>2]; + t_hsem_it_regs it[HSEM_MAX_INTR]; +#if defined(__STN_8820) || defined(__STN_8500) + t_shared_reg RESERVED3[(0x100 - 0xf0)>>2]; +#else /* __STN_8820 or __STN_8500 -> _STN_8815 */ + t_shared_reg RESERVED3[(0x100 - 0xe0)>>2]; +#endif /* __STN_8820 or __STN_8500 -> _STN_8815 */ + t_shared_reg itcr; + t_shared_reg RESERVED4; + t_shared_reg itop; + t_shared_reg RESERVED5[(0xfe0 - 0x10c)>>2]; + t_shared_reg pid0; + t_shared_reg pid1; + t_shared_reg pid2; + t_shared_reg pid3; + t_shared_reg pcid0; + t_shared_reg pcid1; + t_shared_reg pcid2; + t_shared_reg pcid3; +} t_hw_semaphore_regs, *tp_hw_semaphore_regs; + +#endif /* __INC_HWSEM_HWP_H */ diff --git a/drivers/staging/nmf-cm/share/semaphores/inc/semaphores.h b/drivers/staging/nmf-cm/share/semaphores/inc/semaphores.h new file mode 100644 index 00000000000..c72b64cd709 --- /dev/null +++ b/drivers/staging/nmf-cm/share/semaphores/inc/semaphores.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) ST-Ericsson SA 2010. All rights reserved. + * This code is ST-Ericsson proprietary and confidential. + * Any use of the code for whatever purpose is subject to + * specific written permission of ST-Ericsson SA. + */ + +#ifndef __INC_SHARED_SEMAPHORE_H +#define __INC_SHARED_SEMAPHORE_H + +#include <share/inc/nmf.h> + +typedef t_uint16 t_semaphore_id; + +/* + * HW semaphore allocation + * ----------------------- + * We want to optimize interrupt demultiplexing at dsp interrupt handler level + * so a good solution would be to have sequentially the semaphores for each neighbors + * + * STn8500 : + * --------- + * ARM <- SVA COMS => 0 + * ARM <- SIA COMS => 1 + * SVA <- ARM COMS => 2 + * SVA <- SIA COMS => 3 + * SIA <- ARM COMS => 4 + * SIA <- SVA COMS => 5 + + * The first neighbor is always the ARM, then the other ones (SVA,SIA) + */ + +/* + * Local semaphore allocation + * ----------------------- + * 0 : ARM <- DSP + * 1 : DSP <- ARM + */ + +#define NB_USED_HSEM_PER_CORE (NB_CORE_IDS - 1) +#define FIRST_NEIGHBOR_SEMID(coreId) ((coreId)*NB_USED_HSEM_PER_CORE) + +#endif /* __INC_SHARED_SEMAPHORE_H */ diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c index 36f4cb77567..f4b43e55eba 100644 --- a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c +++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c @@ -5,7 +5,7 @@ * * Author: Js HA <js.ha@stericsson.com> for ST-Ericsson * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson - * Copyright 2010 (c) ST-Ericsson AB + * Copyright 2010 (c) ST-Ericsson SA */ /* * This file is licensed under the GPL2 license. @@ -27,6 +27,7 @@ #include <linux/input.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/regulator/consumer.h> @@ -35,8 +36,10 @@ /* TODO: for multiple device support will need a per-device mutex */ #define DRIVER_NAME "synaptics_rmi4_i2c" +#define DELTA 8 #define MAX_ERROR_REPORT 6 -#define MAX_TOUCH_MAJOR 15 +#define TIMEOUT_PERIOD 1 +#define MAX_WIDTH_MAJOR 255 #define MAX_RETRY_COUNT 5 #define STD_QUERY_LEN 21 #define PAGE_LEN 2 @@ -44,6 +47,7 @@ #define BUF_LEN 37 #define QUERY_LEN 9 #define DATA_LEN 12 +#define RESUME_DELAY 100 /* msecs */ #define HAS_TAP 0x01 #define HAS_PALMDETECT 0x01 #define HAS_ROTATE 0x02 @@ -163,6 +167,8 @@ struct synaptics_rmi4_device_info { * @regulator: pointer to the regulator structure * @wait: wait queue structure variable * @touch_stopped: flag to stop the thread function + * @enable: flag to enable/disable the driver event. + * @resume_wq_handler: work queue for resume the device * * This structure gives the device data information. */ @@ -183,6 +189,8 @@ struct synaptics_rmi4_data { struct regulator *regulator; wait_queue_head_t wait; bool touch_stopped; + bool enable; + struct work_struct resume_wq_handler; }; /** @@ -290,6 +298,133 @@ exit: } /** + * synaptics_rmi4_enable() - enable the touchpad driver event + * @pdata: pointer to synaptics_rmi4_data structure + * + * This function is to enable the touchpad driver event and returns integer. + */ +static int synaptics_rmi4_enable(struct synaptics_rmi4_data *pdata) +{ + int retval; + unsigned char intr_status; + + if (pdata->board->regulator_en) + regulator_enable(pdata->regulator); + enable_irq(pdata->board->irq_number); + pdata->touch_stopped = false; + + msleep(RESUME_DELAY); + retval = synaptics_rmi4_i2c_block_read(pdata, + pdata->fn01_data_base_addr + 1, + &intr_status, + pdata->number_of_interrupt_register); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_byte_write(pdata, + pdata->fn01_ctrl_base_addr + 1, + (intr_status | TOUCHPAD_CTRL_INTR)); + if (retval < 0) + return retval; + + return 0; +} + +/** + * synaptics_rmi4_disable() - disable the touchpad driver event + * @pdata: pointer to synaptics_rmi4_data structure + * + * This function is to disable the driver event and returns integer. + */ + +static int synaptics_rmi4_disable(struct synaptics_rmi4_data *pdata) +{ + int retval; + unsigned char intr_status; + + pdata->touch_stopped = true; + disable_irq(pdata->board->irq_number); + + retval = synaptics_rmi4_i2c_block_read(pdata, + pdata->fn01_data_base_addr + 1, + &intr_status, + pdata->number_of_interrupt_register); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_byte_write(pdata, + pdata->fn01_ctrl_base_addr + 1, + (intr_status & ~TOUCHPAD_CTRL_INTR)); + if (retval < 0) + return retval; + if (pdata->board->regulator_en) + regulator_disable(pdata->regulator); + + return 0; +} + +/** + * synaptics_rmi4_show_attr_enable() - show the touchpad enable value + * @dev: pointer to device data structure + * @attr: pointer to attribute structure + * @buf: pointer to character buffer + * + * This function is to show the touchpad enable value and returns ssize_t. + */ +static ssize_t synaptics_rmi4_show_attr_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *pdata = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", pdata->enable); +} + +/** + * synaptics_rmi4_store_attr_enable() - store the touchpad enable value + * @dev: pointer to device data structure + * @attr: pointer to attribute structure + * @buf: pointer to character buffer + * @count: number fo arguments + * + * This function is to store the touchpad enable value and returns ssize_t. + */ +static ssize_t synaptics_rmi4_store_attr_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *pdata = dev_get_drvdata(dev); + unsigned long val; + int retval = 0; + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + if (pdata->enable != val) { + pdata->enable = val ? true : false; + if (pdata->enable) + retval = synaptics_rmi4_enable(pdata); + else + retval = synaptics_rmi4_disable(pdata); + + } + return ((retval < 0) ? retval : count); +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + synaptics_rmi4_show_attr_enable, synaptics_rmi4_store_attr_enable); + +static struct attribute *synaptics_rmi4_attrs[] = { + &dev_attr_enable.attr, + NULL, +}; + +static struct attribute_group synaptics_rmi4_attr_group = { + .attrs = synaptics_rmi4_attrs, +}; + +/** * synpatics_rmi4_touchpad_report() - reports for the rmi4 touchpad device * @pdata: pointer to synaptics_rmi4_data structure * @rfi: pointer to synaptics_rmi4_fn structure @@ -315,8 +450,9 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, unsigned char data[DATA_LEN]; int x[RMI4_NUMBER_OF_MAX_FINGERS]; int y[RMI4_NUMBER_OF_MAX_FINGERS]; - int wx[RMI4_NUMBER_OF_MAX_FINGERS]; - int wy[RMI4_NUMBER_OF_MAX_FINGERS]; + int w[RMI4_NUMBER_OF_MAX_FINGERS]; + static int prv_x[RMI4_NUMBER_OF_MAX_FINGERS]; + static int prv_y[RMI4_NUMBER_OF_MAX_FINGERS]; struct i2c_client *client = pdata->i2c_client; /* get 2D sensor finger data */ @@ -375,11 +511,7 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, y[touch_count] = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT); - wy[touch_count] = - (data[3] >> 4) & MASK_4BIT; - wx[touch_count] = - (data[3] & MASK_4BIT); - + w[touch_count] = data[3]; if (pdata->board->x_flip) x[touch_count] = pdata->sensor_max_x - @@ -388,6 +520,25 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, y[touch_count] = pdata->sensor_max_y - y[touch_count]; + if (x[touch_count] < 0) + x[touch_count] = 0; + else if (x[touch_count] >= pdata->sensor_max_x) + x[touch_count] = + pdata->sensor_max_x - 1; + + if (y[touch_count] < 0) + y[touch_count] = 0; + else if (y[touch_count] >= pdata->sensor_max_y) + y[touch_count] = + pdata->sensor_max_y - 1; + } + if ((abs(x[finger] - prv_x[finger]) < DELTA) && + (abs(y[finger] - prv_y[finger]) < DELTA)) { + x[finger] = prv_x[finger]; + y[finger] = prv_y[finger]; + } else { + prv_x[finger] = x[finger]; + prv_y[finger] = y[finger]; } /* number of active touch points */ touch_count++; @@ -398,7 +549,9 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, if (touch_count) { for (finger = 0; finger < touch_count; finger++) { input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR, - max(wx[finger] , wy[finger])); + max(x[finger] , y[finger])); + input_report_abs(pdata->input_dev, ABS_MT_WIDTH_MAJOR, + w[finger]); input_report_abs(pdata->input_dev, ABS_MT_POSITION_X, x[finger]); input_report_abs(pdata->input_dev, ABS_MT_POSITION_Y, @@ -501,7 +654,7 @@ static irqreturn_t synaptics_rmi4_irq(int irq, void *data) touch_count = synaptics_rmi4_sensor_report(pdata); if (touch_count) wait_event_timeout(pdata->wait, pdata->touch_stopped, - msecs_to_jiffies(1)); + msecs_to_jiffies(TIMEOUT_PERIOD)); else break; } while (!pdata->touch_stopped); @@ -880,9 +1033,27 @@ static int synaptics_rmi4_i2c_query_device(struct synaptics_rmi4_data *pdata) } /** + * synaptics_rmi4_resume_handler() - work queue for resume handler + * @work:work_struct structure pointer + * + * This work queue handler used to resume the device and returns none + */ +static void synaptics_rmi4_resume_handler(struct work_struct *work) +{ + struct synaptics_rmi4_data *prmi4_data = container_of(work, + struct synaptics_rmi4_data, resume_wq_handler); + struct i2c_client *client = prmi4_data->i2c_client; + int retval; + + retval = synaptics_rmi4_enable(prmi4_data); + if (retval < 0) + dev_err(&client->dev, "%s: resume failed\n", __func__); +} + +/** * synaptics_rmi4_probe() - Initialze the i2c-client touchscreen driver - * @i2c: i2c client structure pointer - * @id:i2c device id pointer + * @client: i2c client structure pointer + * @dev_id:i2c device id pointer * * This function will allocate and initialize the instance * data and request the irq and set the instance data as the clients @@ -926,19 +1097,17 @@ static int __devinit synaptics_rmi4_probe goto err_input; } - rmi4_data->regulator = regulator_get(&client->dev, "vdd"); - if (IS_ERR(rmi4_data->regulator)) { - dev_err(&client->dev, "%s:get regulator failed\n", - __func__); - retval = PTR_ERR(rmi4_data->regulator); - goto err_get_regulator; - } - retval = regulator_enable(rmi4_data->regulator); - if (retval < 0) { - dev_err(&client->dev, "%s:regulator enable failed\n", - __func__); - goto err_regulator_enable; + if (platformdata->regulator_en) { + rmi4_data->regulator = regulator_get(&client->dev, "vdd"); + if (IS_ERR(rmi4_data->regulator)) { + dev_err(&client->dev, "%s:get regulator failed\n", + __func__); + retval = PTR_ERR(rmi4_data->regulator); + goto err_regulator; + } + regulator_enable(rmi4_data->regulator); } + init_waitqueue_head(&rmi4_data->wait); /* * Copy i2c_client pointer into RTID's i2c_client pointer for @@ -986,7 +1155,16 @@ static int __devinit synaptics_rmi4_probe input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, 0, rmi4_data->sensor_max_y, 0, 0); input_set_abs_params(rmi4_data->input_dev, ABS_MT_TOUCH_MAJOR, 0, - MAX_TOUCH_MAJOR, 0, 0); + max(rmi4_data->sensor_max_y, rmi4_data->sensor_max_y), + 0, 0); + input_set_abs_params(rmi4_data->input_dev, ABS_MT_WIDTH_MAJOR, 0, + MAX_WIDTH_MAJOR, 0, 0); + + retval = input_register_device(rmi4_data->input_dev); + if (retval) { + dev_err(&client->dev, "%s:input register failed\n", __func__); + goto err_input_register; + } /* Clear interrupts */ synaptics_rmi4_i2c_block_read(rmi4_data, @@ -999,24 +1177,34 @@ static int __devinit synaptics_rmi4_probe if (retval) { dev_err(&client->dev, "%s:Unable to get attn irq %d\n", __func__, platformdata->irq_number); - goto err_query_dev; + goto err_request_irq; } - retval = input_register_device(rmi4_data->input_dev); + INIT_WORK(&rmi4_data->resume_wq_handler, synaptics_rmi4_resume_handler); + + /* sysfs implementation for dynamic enable/disable the input event */ + retval = sysfs_create_group(&client->dev.kobj, + &synaptics_rmi4_attr_group); if (retval) { - dev_err(&client->dev, "%s:input register failed\n", __func__); - goto err_free_irq; + dev_err(&client->dev, "failed to create sysfs entries\n"); + goto err_sysfs; } - + rmi4_data->enable = true; return retval; -err_free_irq: +err_sysfs: + cancel_work_sync(&rmi4_data->resume_wq_handler); +err_request_irq: free_irq(platformdata->irq_number, rmi4_data); + input_unregister_device(rmi4_data->input_dev); +err_input_register: + i2c_set_clientdata(client, NULL); err_query_dev: - regulator_disable(rmi4_data->regulator); -err_regulator_enable: - regulator_put(rmi4_data->regulator); -err_get_regulator: + if (platformdata->regulator_en) { + regulator_disable(rmi4_data->regulator); + regulator_put(rmi4_data->regulator); + } +err_regulator: input_free_device(rmi4_data->input_dev); rmi4_data->input_dev = NULL; err_input: @@ -1036,12 +1224,16 @@ static int __devexit synaptics_rmi4_remove(struct i2c_client *client) struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client); const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; + sysfs_remove_group(&client->dev.kobj, &synaptics_rmi4_attr_group); rmi4_data->touch_stopped = true; wake_up(&rmi4_data->wait); + cancel_work_sync(&rmi4_data->resume_wq_handler); free_irq(pdata->irq_number, rmi4_data); input_unregister_device(rmi4_data->input_dev); - regulator_disable(rmi4_data->regulator); - regulator_put(rmi4_data->regulator); + if (pdata->regulator_en) { + regulator_disable(rmi4_data->regulator); + regulator_put(rmi4_data->regulator); + } kfree(rmi4_data); return 0; @@ -1058,31 +1250,11 @@ static int __devexit synaptics_rmi4_remove(struct i2c_client *client) static int synaptics_rmi4_suspend(struct device *dev) { /* Touch sleep mode */ - int retval; - unsigned char intr_status; struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); - const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; - rmi4_data->touch_stopped = true; - disable_irq(pdata->irq_number); - - retval = synaptics_rmi4_i2c_block_read(rmi4_data, - rmi4_data->fn01_data_base_addr + 1, - &intr_status, - rmi4_data->number_of_interrupt_register); - if (retval < 0) - return retval; - - retval = synaptics_rmi4_i2c_byte_write(rmi4_data, - rmi4_data->fn01_ctrl_base_addr + 1, - (intr_status & ~TOUCHPAD_CTRL_INTR)); - if (retval < 0) - return retval; - - regulator_disable(rmi4_data->regulator); - - return 0; + return synaptics_rmi4_disable(rmi4_data); } + /** * synaptics_rmi4_resume() - resume the touch screen controller * @dev: pointer to device structure @@ -1092,28 +1264,9 @@ static int synaptics_rmi4_suspend(struct device *dev) */ static int synaptics_rmi4_resume(struct device *dev) { - int retval; - unsigned char intr_status; struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); - const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; - - regulator_enable(rmi4_data->regulator); - enable_irq(pdata->irq_number); - rmi4_data->touch_stopped = false; - - retval = synaptics_rmi4_i2c_block_read(rmi4_data, - rmi4_data->fn01_data_base_addr + 1, - &intr_status, - rmi4_data->number_of_interrupt_register); - if (retval < 0) - return retval; - - retval = synaptics_rmi4_i2c_byte_write(rmi4_data, - rmi4_data->fn01_ctrl_base_addr + 1, - (intr_status | TOUCHPAD_CTRL_INTR)); - if (retval < 0) - return retval; + schedule_work(&rmi4_data->resume_wq_handler); return 0; } diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h index 384436ef806..973abc97374 100644 --- a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h +++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h @@ -42,6 +42,7 @@ struct synaptics_rmi4_platform_data { int irq_type; bool x_flip; bool y_flip; + bool regulator_en; }; #endif |