diff options
-rw-r--r-- | arch/arm/configs/u8500_defconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-ux500/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-ux500/board-mop500-mmio.c | 509 | ||||
-rw-r--r-- | arch/arm/mach-ux500/board-mop500-pins.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-ux500/board-mop500.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-ux500/board-mop500.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-ux500/include/mach/devices.h | 1 | ||||
-rw-r--r-- | drivers/staging/Kconfig | 4 | ||||
-rw-r--r-- | drivers/staging/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/mmio/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/mmio/st_mmio.c | 1128 | ||||
-rw-r--r-- | include/linux/mmio.h | 175 |
12 files changed, 1829 insertions, 1 deletions
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig index b86ff2d6433..10d8667e5fb 100644 --- a/arch/arm/configs/u8500_defconfig +++ b/arch/arm/configs/u8500_defconfig @@ -221,6 +221,7 @@ CONFIG_RTC_DRV_AB8500=y CONFIG_DMADEVICES=y CONFIG_STE_DMA40=y CONFIG_STAGING=y +CONFIG_U8500_MMIO=y # CONFIG_STAGING_EXCLUDE_BUILD is not set CONFIG_AUTOFS_FS=m CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile index 2d13aa051fc..b065d2b57ca 100644 --- a/arch/arm/mach-ux500/Makefile +++ b/arch/arm/mach-ux500/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_MACH_U8500) += board-mop500.o board-mop500-sdi.o \ obj-$(CONFIG_MACH_U5500) += board-u5500.o board-u5500-sdi.o \ board-u5500-mcde.o board-u5500-regulators.o \ board-u5500-pins.o +obj-$(CONFIG_U8500_MMIO) += board-mop500-mmio.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o diff --git a/arch/arm/mach-ux500/board-mop500-mmio.c b/arch/arm/mach-ux500/board-mop500-mmio.c new file mode 100644 index 00000000000..b86b9031d48 --- /dev/null +++ b/arch/arm/mach-ux500/board-mop500-mmio.c @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2011 ST-Ericsson + * Author: Joakim Axelsson <joakim.axelsson@stericsson.com> for ST-Ericsson + * Author: Rajat Verma <rajat.verma@stericsson.com> for ST-Ericsson. + * + * 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 <linux/kernel.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/mmio.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/vmalloc.h> +#include <asm/mach-types.h> +#include <plat/pincfg.h> +#include <mach/gpio.h> +#include <mach/devices.h> +#include <mach/hardware.h> +#include "pins-db8500.h" +#include "pins.h" +#include "board-mop500.h" + +static pin_cfg_t i2c2_pins[] = { + GPIO8_I2C2_SDA, + GPIO9_I2C2_SCL +}; +static pin_cfg_t ipi2c_pins[] = { + GPIO8_IPI2C_SDA, + GPIO9_IPI2C_SCL +}; +static pin_cfg_t i2c_disable_pins[] = { + GPIO8_GPIO, + GPIO9_GPIO +}; +static pin_cfg_t xshutdown_host[] = { + GPIO141_GPIO, + GPIO142_GPIO +}; +static pin_cfg_t xshutdown_fw[] = { + GPIO141_IP_GPIO2, + GPIO142_IP_GPIO3 +}; +static pin_cfg_t xshutdown_disable[] = { + GPIO141_GPIO, + GPIO142_GPIO +}; + +struct mmio_board_data{ + int number_of_regulators; + struct regulator **mmio_regulators; + /* Pin configs */ + int xenon_charge; + struct mmio_gpio xshutdown_pins[CAMERA_SLOT_END]; + /* Internal clocks */ + struct clk *clk_ptr_bml; + struct clk *clk_ptr_ipi2c; + /* External clocks */ + struct clk *clk_ptr_ext[CAMERA_SLOT_END]; +}; + +/* Fill names of regulators required for powering up the + * camera sensor in below array */ +static char *regulator_names[] = {"v-mmio-camera" , "v-ana"}; + +/* This function is used to translate the physical GPIO used for reset GPIO + * to logical IPGPIO that needs to be communicated to Firmware. so that + * firmware can control reset GPIO of a RAW Bayer sensor */ +static int mmio_get_ipgpio(struct mmio_platform_data *pdata, int gpio, + int *ip_gpio) +{ + int err = 0; + dev_dbg(pdata->dev, "%s() : IPGPIO requested for %d", __func__, gpio); + switch (gpio) { + case 67: + case 140: + *ip_gpio = 7; + break; + case 5: + case 66: + *ip_gpio = 6; + break; + case 81: + case 65: + *ip_gpio = 5; + break; + case 80: + case 64: + *ip_gpio = 4; + break; + case 10: + case 79: + case 142: + *ip_gpio = 3; + break; + case 11: + case 78: + case 141: + *ip_gpio = 2; + break; + case 7: + case 150: + *ip_gpio = 1; + break; + case 6: + case 149: + *ip_gpio = 0; + break; + default: + *ip_gpio = -1; + err = -1; + break; + } + return err; +} + +static int mmio_clock_init(struct mmio_platform_data *pdata) +{ + int err; + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + + extra->clk_ptr_bml = clk_get_sys("bml", NULL); + if (IS_ERR(extra->clk_ptr_bml)) { + err = PTR_ERR(extra->clk_ptr_bml); + dev_err(pdata->dev, "Error %d getting clock 'bml'\n", err); + goto err_bml_clk; + } + extra->clk_ptr_ipi2c = clk_get_sys("ipi2", NULL); + if (IS_ERR(extra->clk_ptr_ipi2c)) { + err = PTR_ERR(extra->clk_ptr_ipi2c); + dev_err(pdata->dev, "Error %d getting clock 'ipi2'\n", err); + goto err_ipi2c_clk; + } + extra->clk_ptr_ext[PRIMARY_CAMERA] = clk_get_sys("pri-cam", NULL); + if (IS_ERR(extra->clk_ptr_ext[PRIMARY_CAMERA])) { + err = PTR_ERR(extra->clk_ptr_ext[PRIMARY_CAMERA]); + dev_err(pdata->dev, "Error %d getting clock 'pri-cam'\n", err); + goto err_pri_ext_clk; + } + extra->clk_ptr_ext[SECONDARY_CAMERA] = clk_get_sys("sec-cam", NULL); + if (IS_ERR(extra->clk_ptr_ext[SECONDARY_CAMERA])) { + err = PTR_ERR(extra->clk_ptr_ext[SECONDARY_CAMERA]); + dev_err(pdata->dev, "Error %d getting clock 'sec-cam'\n", err); + goto err_sec_ext_clk; + } + dev_dbg(pdata->dev , "Board %s() Exit\n", __func__); + return 0; +err_sec_ext_clk: + clk_put(extra->clk_ptr_ext[PRIMARY_CAMERA]); +err_pri_ext_clk: + clk_put(extra->clk_ptr_ipi2c); +err_ipi2c_clk: + clk_put(extra->clk_ptr_bml); +err_bml_clk: + return err; +} +static void mmio_clock_exit(struct mmio_platform_data *pdata) +{ + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + clk_put(extra->clk_ptr_bml); + clk_put(extra->clk_ptr_ipi2c); + clk_put(extra->clk_ptr_ext[PRIMARY_CAMERA]); + clk_put(extra->clk_ptr_ext[SECONDARY_CAMERA]); +} + + +static int mmio_pin_cfg_init(struct mmio_platform_data *pdata) +{ + int err; + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + + extra->xshutdown_pins[PRIMARY_CAMERA].gpio = XSHUTDOWN_PRIMARY_SENSOR; + extra->xshutdown_pins[PRIMARY_CAMERA].active_high = 0; + extra->xshutdown_pins[PRIMARY_CAMERA].udelay = 500; + + extra->xshutdown_pins[SECONDARY_CAMERA].active_high = 0; + extra->xshutdown_pins[SECONDARY_CAMERA].udelay = 500; + + /* Update GPIO mappings according to board */ + if (machine_is_hrefv60()) { + extra->xenon_charge = HREFV60_MMIO_XENON_CHARGE; + xshutdown_host[SECONDARY_CAMERA] = GPIO140_GPIO; + xshutdown_fw[SECONDARY_CAMERA] = GPIO140_IP_GPIO7; + xshutdown_disable[SECONDARY_CAMERA] = GPIO140_GPIO; + extra->xshutdown_pins[SECONDARY_CAMERA].gpio = 140; + } else { + extra->xenon_charge = GPIO_MMIO_XENON_CHARGE; + xshutdown_host[SECONDARY_CAMERA] = GPIO142_GPIO; + xshutdown_fw[SECONDARY_CAMERA] = GPIO142_IP_GPIO3; + xshutdown_disable[SECONDARY_CAMERA] = GPIO142_GPIO; + extra->xshutdown_pins[SECONDARY_CAMERA].gpio = 142; + } + /* Setup Xenon Charge */ + err = gpio_request(extra->xenon_charge, "xenon charge"); + if (err) { + dev_err(pdata->dev, "Error %d while requesting xenon charge\n", + err); + goto err_xenon_gpio_req; + } + err = gpio_direction_output(extra->xenon_charge, 0); + if (err) { + dev_err(pdata->dev, "Error %d while setting xenon charge in" + "output mode\n", err); + goto err_xenon_gpio_set_dir; + } + dev_dbg(pdata->dev , "Board %s() Exit\n", __func__); + return 0; +err_xenon_gpio_set_dir: + gpio_free(extra->xenon_charge); +err_xenon_gpio_req: + return err; +} + +static void mmio_pin_cfg_exit(struct mmio_platform_data *pdata) +{ + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + gpio_free(extra->xenon_charge); +} + +/* For now, both sensors on HREF have some power up sequence. If different + * sequences are needed for primary and secondary sensors, it can be + * implemented easily. Just use camera_slot field of mmio_platform_data + * to determine which camera needs to be powered up */ +static int mmio_power_init(struct mmio_platform_data *pdata) +{ + int err = 0, i = 0; + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + extra->number_of_regulators = sizeof(regulator_names)/ + sizeof(regulator_names[0]); + extra->mmio_regulators = + kzalloc(sizeof(struct regulator *) * extra->number_of_regulators, + GFP_KERNEL); + if (!extra->mmio_regulators) { + dev_err(pdata->dev , "Error while allocating memory for mmio" + "regulators\n"); + err = -ENOMEM; + goto err_no_mem_reg; + } + for (i = 0; i < + extra->number_of_regulators; i++) { + extra->mmio_regulators[i] = + regulator_get(pdata->dev, regulator_names[i]); + if (IS_ERR(extra->mmio_regulators[i])) { + err = PTR_ERR(extra->mmio_regulators[i]); + dev_err(pdata->dev , "Error %d getting regulator '%s'" + "\n", err, regulator_names[i]); + goto err_regulator; + } + } + dev_dbg(pdata->dev , "Board %s() Exit\n", __func__); + return 0; +err_regulator: + /* Return regulators we have already requested */ + while (i--) + regulator_put(extra->mmio_regulators[i]); + kfree(extra->mmio_regulators); +err_no_mem_reg: + return err; +} +static void mmio_power_exit(struct mmio_platform_data *pdata) +{ + int i = 0; + struct mmio_board_data *extra = pdata->extra;; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + for (i = 0; i < extra->number_of_regulators; i++) + regulator_put(extra->mmio_regulators[i]); + kfree(extra->mmio_regulators); +} + +static int mmio_platform_init(struct mmio_platform_data *pdata) +{ + int err = 0; + struct mmio_board_data *extra = NULL; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + /* Alloc memory for our own extra data */ + extra = kzalloc(sizeof(struct mmio_board_data), GFP_KERNEL); + if (!extra) { + dev_err(pdata->dev, "%s: memory alloc failed for " + "mmio_board_data\n", __func__); + err = -ENOMEM; + goto err_no_mem_extra; + } + /* Hook the data for other callbacks to use */ + pdata->extra = extra; + + pdata->camera_slot = -1; + + err = mmio_power_init(pdata); + if (err) + goto err_regulator; + err = mmio_clock_init(pdata); + if (err) + goto err_clock; + err = mmio_pin_cfg_init(pdata); + if (err) + goto err_pin_cfg; + /* Store logical IPGPIO for physical reset GPIOs used */ + err = mmio_get_ipgpio(pdata, + extra->xshutdown_pins[PRIMARY_CAMERA].gpio, + &(pdata->reset_ipgpio[PRIMARY_CAMERA])); + if (err) { + dev_err(pdata->dev, "Error getting ipgpio for pri cam\n"); + goto err_ipgpio; + } + err = mmio_get_ipgpio(pdata, + extra->xshutdown_pins[SECONDARY_CAMERA].gpio, + &(pdata->reset_ipgpio[SECONDARY_CAMERA])); + if (err) { + dev_err(pdata->dev, "Error getting ipgpio for sec cam\n"); + goto err_ipgpio; + } + dev_dbg(pdata->dev , "Board %s() Exit\n", __func__); + return 0; +err_ipgpio: + mmio_pin_cfg_exit(pdata); +err_pin_cfg: + mmio_clock_exit(pdata); +err_clock: + mmio_power_exit(pdata); +err_regulator: + kfree(extra); +err_no_mem_extra: + return err; +} +static void mmio_platform_exit(struct mmio_platform_data *pdata) +{ + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + mmio_power_exit(pdata); + mmio_clock_exit(pdata); + mmio_pin_cfg_exit(pdata); + kfree(extra); + pdata->extra = NULL; +} + +static int mmio_power_enable(struct mmio_platform_data *pdata) +{ + int err = 0, i = 0; + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + /* Enable the regulators */ + for (i = 0; i < extra->number_of_regulators; i++) { + err = regulator_enable(extra->mmio_regulators[i]); + if (IS_ERR(extra->mmio_regulators[i])) { + err = PTR_ERR(extra->mmio_regulators[i]); + dev_err(pdata->dev , "Error %d enabling regulator '%s'" + "\n", err, regulator_names[i]); + goto err_regulator; + } + } + /* Set Xenon Charge */ + gpio_set_value(extra->xenon_charge, 1); + dev_dbg(pdata->dev , "Board %s() Exit\n", __func__); + return 0; +err_regulator: + /* Disable regulators we already enabled */ + while (i--) + regulator_disable(extra->mmio_regulators[i]); + return err; +} + +static void mmio_power_disable(struct mmio_platform_data *pdata) +{ + int i; + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + /* Disable the regulators */ + for (i = 0; i < extra->number_of_regulators; i++) + regulator_disable(extra->mmio_regulators[i]); + /* Disable Xenon Charge */ + gpio_set_value(extra->xenon_charge, 0); +} +static int mmio_clock_enable(struct mmio_platform_data *pdata) +{ + int err = 0; + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + /* Enable internal clocks */ + err = clk_enable(extra->clk_ptr_bml); + if (err) { + dev_err(pdata->dev, "Error activating bml clock %d\n", err); + goto err_bml_clk; + } + err = clk_enable(extra->clk_ptr_ipi2c); + if (err) { + dev_err(pdata->dev, "Error activating i2c2 clock %d\n", err); + goto err_ipi2c_clk; + } + /* Enable appropriate external clock */ + err = clk_enable(extra->clk_ptr_ext[pdata->camera_slot]); + if (err) { + dev_err(pdata->dev, "Error activating clock for sensor %d, err" + "%d\n", pdata->camera_slot, err); + goto err_ext_clk; + } + dev_dbg(pdata->dev , "Board %s() Exit\n", __func__); + return 0; +err_ext_clk: + clk_disable(extra->clk_ptr_ipi2c); +err_ipi2c_clk: + clk_disable(extra->clk_ptr_bml); +err_bml_clk: + return err; +} + +static void mmio_clock_disable(struct mmio_platform_data *pdata) +{ + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + clk_disable(extra->clk_ptr_bml); + clk_disable(extra->clk_ptr_ipi2c); + clk_disable(extra->clk_ptr_ext[pdata->camera_slot]); +} + + +static int mmio_config_xshutdown_pins(struct mmio_platform_data *pdata, + enum mmio_select_xshutdown_t select, + int is_active_high) +{ + int err = 0; + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + switch (select) { + case MMIO_ENABLE_XSHUTDOWN_HOST: + extra->xshutdown_pins[pdata->camera_slot].active_high = + is_active_high; + err = nmk_config_pin(xshutdown_host[pdata->camera_slot] | + (is_active_high ? PIN_OUTPUT_LOW : PIN_OUTPUT_HIGH), + 0); + break; + case MMIO_ENABLE_XSHUTDOWN_FW: + err = nmk_config_pin(xshutdown_fw[pdata->camera_slot], 0); + break; + case MMIO_DISABLE_XSHUTDOWN: + err = nmk_config_pin(xshutdown_disable[pdata->camera_slot], + 0); + break; + default: + break; + } + if (err) + dev_dbg(pdata->dev , "Error configuring xshutdown, err = %d\n", + err); + return err; +} +static void mmio_set_xshutdown(struct mmio_platform_data *pdata) +{ + struct mmio_board_data *extra = pdata->extra; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + gpio_set_value(extra->xshutdown_pins[pdata->camera_slot].gpio , + (extra->xshutdown_pins[pdata->camera_slot].active_high ? 1 : + 0)); + udelay(extra->xshutdown_pins[pdata->camera_slot].udelay); +} +static int mmio_config_i2c_pins(struct mmio_platform_data *pdata, + enum mmio_select_i2c_t select) +{ + int err = 0; + dev_dbg(pdata->dev , "Board %s() Enter\n", __func__); + switch (select) { + case MMIO_ACTIVATE_I2C_HOST: + err = nmk_config_pins(i2c2_pins, ARRAY_SIZE(i2c2_pins)); + break; + case MMIO_ACTIVATE_IPI2C2: + err = nmk_config_pins(ipi2c_pins, ARRAY_SIZE(ipi2c_pins)); + break; + case MMIO_DEACTIVATE_I2C: + err = nmk_config_pins(i2c_disable_pins, + ARRAY_SIZE(i2c_disable_pins)); + break; + default: + break; + } + + return err; +} +static struct mmio_platform_data mmio_config = { + .platform_init = mmio_platform_init, + .platform_exit = mmio_platform_exit, + .power_enable = mmio_power_enable, + .power_disable = mmio_power_disable, + .clock_enable = mmio_clock_enable, + .clock_disable = mmio_clock_disable, + .config_i2c_pins = mmio_config_i2c_pins, + .config_xshutdown_pins = mmio_config_xshutdown_pins, + .set_xshutdown = mmio_set_xshutdown, + .sia_base = U8500_SIA_BASE, + .cr_base = U8500_CR_BASE +}; + +struct platform_device ux500_mmio_device = { + .name = MMIO_NAME, + .id = -1, + .dev = { + .platform_data = &mmio_config, + } +}; diff --git a/arch/arm/mach-ux500/board-mop500-pins.c b/arch/arm/mach-ux500/board-mop500-pins.c index 3fa197648fd..667de8a9f51 100644 --- a/arch/arm/mach-ux500/board-mop500-pins.c +++ b/arch/arm/mach-ux500/board-mop500-pins.c @@ -113,7 +113,6 @@ static pin_cfg_t mop500_pins_hrefv60[] = { /* XENON Flashgun INTERFACE */ GPIO6_IP_GPIO0 | PIN_INPUT_PULLUP,/* XENON_FLASH_ID */ GPIO7_IP_GPIO1 | PIN_INPUT_PULLUP,/* XENON_READY */ - GPIO170_GPIO | PIN_OUTPUT_LOW, /* XENON_CHARGE */ /* Assistant LED INTERFACE */ GPIO21_GPIO | PIN_OUTPUT_LOW, /* XENON_EN1 */ diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 85ebae2a0a6..164884c452a 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -642,6 +642,9 @@ static struct hsi_board_info __initdata u8500_hsi_devices[] = { /* add any platform devices here - TODO */ static struct platform_device *mop500_platform_devs[] __initdata = { &u8500_shrm_device, +#ifdef CONFIG_U8500_MMIO + &ux500_mmio_device, +#endif &ux500_hwmem_device, &u8500_mcde_device, &u8500_b2r2_device, diff --git a/arch/arm/mach-ux500/board-mop500.h b/arch/arm/mach-ux500/board-mop500.h index 8e9285b3741..08869354bcf 100644 --- a/arch/arm/mach-ux500/board-mop500.h +++ b/arch/arm/mach-ux500/board-mop500.h @@ -27,6 +27,10 @@ #define HREFV60_MAGNET_DRDY_GPIO 32 #define HREFV60_DISP1_RST_GPIO 65 #define HREFV60_DISP2_RST_GPIO 66 +#define HREFV60_MMIO_XENON_CHARGE 170 +#define HREFV60_XSHUTDOWN_SECONDARY_SENSOR 140 +#define XSHUTDOWN_PRIMARY_SENSOR 141 +#define XSHUTDOWN_SECONDARY_SENSOR 142 /* MOP500 generic GPIOs */ #define MOP500_HDMI_RST_GPIO 196 @@ -35,6 +39,7 @@ #define MOP500_EGPIO(x) (NOMADIK_NR_GPIO + (x)) #define GPIO_MAGNET_DRDY MOP500_EGPIO(1) #define GPIO_SDMMC_CD MOP500_EGPIO(3) +#define GPIO_MMIO_XENON_CHARGE MOP500_EGPIO(5) #define GPIO_PROX_SENSOR MOP500_EGPIO(7) #define GPIO_HAL_SENSOR MOP500_EGPIO(8) #define GPIO_ACCEL_INT1 MOP500_EGPIO(10) diff --git a/arch/arm/mach-ux500/include/mach/devices.h b/arch/arm/mach-ux500/include/mach/devices.h index a1ae8a2a702..33d29990680 100644 --- a/arch/arm/mach-ux500/include/mach/devices.h +++ b/arch/arm/mach-ux500/include/mach/devices.h @@ -30,6 +30,7 @@ extern struct platform_device u8500_thsens_device; extern struct platform_device u8500_dma40_device; extern struct platform_device ux500_ske_keypad_device; extern struct platform_device u8500_hsi_device; +extern struct platform_device ux500_mmio_device; void dma40_u8500ed_fixup(void); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index bd1b283a343..92298414c4a 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -24,6 +24,10 @@ menuconfig STAGING if STAGING +config U8500_MMIO + bool "ST-Ericsson MMIO (Camera) Driver" + depends on ARCH_U8500 + config STAGING_EXCLUDE_BUILD bool "Exclude Staging drivers from being built" if STAGING default y diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 2a5e26aa7cd..a0fb3ef3acd 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -72,3 +72,4 @@ obj-$(CONFIG_SPEAKUP) += speakup/ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ obj-$(CONFIG_CW1200) += cw1200/ +obj-$(CONFIG_U8500_MMIO) += mmio/ 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/st_mmio.c b/drivers/staging/mmio/st_mmio.c new file mode 100644 index 00000000000..1dc82a9671b --- /dev/null +++ b/drivers/staging/mmio/st_mmio.c @@ -0,0 +1,1128 @@ +/* + * 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/mmio.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> +#include <linux/workqueue.h> +#include <mach/prcmu.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; + +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 (size of the code =64KB) */ + writew(0x0, 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 int mmio_ioctl(struct inode *node, 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/include/linux/mmio.h b/include/linux/mmio.h new file mode 100644 index 00000000000..9dee32055ef --- /dev/null +++ b/include/linux/mmio.h @@ -0,0 +1,175 @@ +/* + * 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" + +#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 */ |