From cf21836d0de958989c9a6213da7a88c0557c6555 Mon Sep 17 00:00:00 2001 From: zwx305167 Date: Wed, 7 Mar 2018 14:12:31 +0800 Subject: [PATCH] Drivers/IPU: add IPU driver for hikey970 Add IPU device driver and device tree source for HiKey970. Signed-off-by: zwx305167 --- .../boot/dts/hisilicon/kirin970-hikey970.dts | 2 + .../boot/dts/hisilicon/kirin970_ics_cs.dtsi | 141 + arch/arm64/configs/hikey970_defconfig | 1 + drivers/clk/hisilicon/clk-kirin970.c | 1 + drivers/hisi/Kconfig | 1 + drivers/hisi/Makefile | 2 +- drivers/hisi/ics/Kconfig | 40 + drivers/hisi/ics/Makefile | 14 + drivers/hisi/ics/cambricon_ipu.c | 2843 +++++++++++++++++ drivers/hisi/ics/cambricon_ipu.h | 251 ++ drivers/hisi/ics/ics_debug.c | 803 +++++ drivers/hisi/ics/ics_debug.h | 6 + drivers/hisi/ics/ics_debug_proxy.c | 77 + drivers/hisi/ics/ics_debug_proxy.h | 17 + drivers/hisi/ics/ipu_clock.c | 233 ++ drivers/hisi/ics/ipu_clock.h | 48 + drivers/hisi/ics/ipu_mntn.c | 433 +++ drivers/hisi/ics/ipu_mntn.h | 69 + drivers/hisi/ics/ipu_smmu_drv.c | 1052 ++++++ drivers/hisi/ics/ipu_smmu_drv.h | 122 + include/dt-bindings/clock/kirin970-clock.h | 2 +- 21 files changed, 6156 insertions(+), 2 deletions(-) mode change 100644 => 100755 arch/arm64/boot/dts/hisilicon/kirin970-hikey970.dts create mode 100644 arch/arm64/boot/dts/hisilicon/kirin970_ics_cs.dtsi create mode 100644 drivers/hisi/ics/Kconfig create mode 100644 drivers/hisi/ics/Makefile create mode 100644 drivers/hisi/ics/cambricon_ipu.c create mode 100644 drivers/hisi/ics/cambricon_ipu.h create mode 100644 drivers/hisi/ics/ics_debug.c create mode 100644 drivers/hisi/ics/ics_debug.h create mode 100644 drivers/hisi/ics/ics_debug_proxy.c create mode 100644 drivers/hisi/ics/ics_debug_proxy.h create mode 100644 drivers/hisi/ics/ipu_clock.c create mode 100644 drivers/hisi/ics/ipu_clock.h create mode 100644 drivers/hisi/ics/ipu_mntn.c create mode 100644 drivers/hisi/ics/ipu_mntn.h create mode 100644 drivers/hisi/ics/ipu_smmu_drv.c create mode 100644 drivers/hisi/ics/ipu_smmu_drv.h diff --git a/arch/arm64/boot/dts/hisilicon/kirin970-hikey970.dts b/arch/arm64/boot/dts/hisilicon/kirin970-hikey970.dts old mode 100644 new mode 100755 index 7249fbccbf70..b350ed53176f --- a/arch/arm64/boot/dts/hisilicon/kirin970-hikey970.dts +++ b/arch/arm64/boot/dts/hisilicon/kirin970-hikey970.dts @@ -18,6 +18,8 @@ #include "kirin970_ipc.dtsi" #include "kirin970-coresight.dtsi" #include "kirin970-drm.dtsi" +#include "kirin970_ics_cs.dtsi" + / { model = "HiKey970"; compatible = "Hisilicon,kirin970-hikey970", "Hisilicon,kirin970"; diff --git a/arch/arm64/boot/dts/hisilicon/kirin970_ics_cs.dtsi b/arch/arm64/boot/dts/hisilicon/kirin970_ics_cs.dtsi new file mode 100644 index 000000000000..a4f55587a2e9 --- /dev/null +++ b/arch/arm64/boot/dts/hisilicon/kirin970_ics_cs.dtsi @@ -0,0 +1,141 @@ +/* + * Hisilicon Ltd. Kirin970 SoC + * + * Copyright (C) 2014- Hisilicon Ltd. + * + * 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 + * publishhed by the Free Software Foundation. + */ + +/ { + cambricon-ipu@ff400000{ + // vipu-supply = <&ics>; + compatible = "hisilicon,cambricon-ipu"; + reg = <0x0 0xff400000 0x0 0x100000>,<0x0 0xff500000 0x0 0x100000>; + interrupts = <0 293 4>; + interrupt-names = "ipu_dma_irq"; + ics-platform = "kirin970_cs"; + // clocks = <&clk_gate_ics>; + clocks = <&media2_crg KIRIN970_CLK_GATE_ICSFREQ>; + clock-names = "clk-ics"; + status = "ok"; + ipu-and-vcodecbus-clock-rate { + start-rate = <900000000>; + stop-rate = <332000000>; + ipu-low = <384000000>; + ipu-middle = <600000000>; + ipu-high = <900000000>; + ipu-low-temperature = <600000000>; + vcodecbus-low = <185000000>; + vcodecbus-middle = <332000000>; + vcodecbus-high = <450000000>; + vcodecbus-default = <415000000>; + vcodecbus-high2default = <225000000>; + }; + iommu_info { + start-addr = <0x40000>; + iova-align = <0x1000>; + size = <0x80000000>; + }; + smmu_master { + smmu-mstr-base-addr = <0xff4a0000>; + smmu-mstr-glb-bypass = <0x0000>; + smmu-mstr-end-ack = <0x001c>; + smmu-mstr-smrx-start = <0x0028>; + smmu-mstr-inpt-sel = <0x0034>; + smmu-mstr-intmask = <0x0040>; + smmu-mstr-intstat = <0x0048>; + smmu-mstr-intclr = <0x004c>; + smmu-mstr-dbg-port-in-0 = <0x0070>; + smmu-mstr-dbg-port-out = <0x0078>; + smmu-mstr-smrx-0-stream-0 = <0x0100>; + smmu-mstr-smrx-0-stream-1 = <0x0104>; + smmu-mstr-smrx-0-stream-2 = <0x0108>; + smmu-mstr-smrx-0-stream-3 = <0x010c>; + read-cmd-total-cnt-stream-0 = <0x0600>; + read-cmd-total-cnt-stream-1 = <0x0604>; + read-cmd-total-cnt-stream-2 = <0x0608>; + read-cmd-miss-cnt-stream-0 = <0x0760>; + read-cmd-miss-cnt-stream-1 = <0x0764>; + read-cmd-miss-cnt-stream-2 = <0x0768>; + read-data-total-cnt-stream-0 = <0x08c0>; + read-data-total-cnt-stream-1 = <0x08c4>; + read-data-total-cnt-stream-2 = <0x08c8>; + read-cmd-case-cnt-stream-0 = <0x0a20>; + read-cmd-case-cnt-stream-1 = <0x0a24>; + read-cmd-case-cnt-stream-2 = <0x0a28>; + read-cmd-case-cnt-stream-3 = <0x0a2c>; + read-cmd-case-cnt-stream-4 = <0x0a30>; + read-cmd-case-cnt-stream-5 = <0x0a34>; + read-cmd-trans-latency = <0x0a38>; + write-cmd-total-cnt = <0x0b00>; + write-cmd-miss-cnt = <0x0c60>; + write-data-total-cnt = <0x0dc0>; + write-cmd-case-cnt-stream-0 = <0x0f20>; + write-cmd-case-cnt-stream-1 = <0x0f24>; + write-cmd-case-cnt-stream-2 = <0x0f28>; + write-cmd-case-cnt-stream-3 = <0x0f2c>; + write-cmd-case-cnt-stream-4 = <0x0f30>; + write-cmd-case-cnt-stream-5 = <0x0f34>; + write-cmd-trans-latency = <0x0f38>; + }; + smmu_common { + smmu-common-base-addr = <0xff480000>; + smmu-scr = <0x0000>; + smmu-intmask-ns = <0x0010>; + smmu-intstat-ns = <0x0018>; + smmu-intclr-ns = <0x001c>; + smmu-cb-ttbr0 = <0x0204>; + smmu-cb-ttbcr = <0x020c>; + smmu-scachei-all = <0x0214>; + smmu-fama-ctrl1-ns = <0x0224>; + smmu-addr-msb = <0x0300>; + smmu-err-rdaddr = <0x0304>; + smmu-err-wraddr = <0x0308>; + smmu-opref-addr = <0x03a4>; + smmu-opref-ctrl = <0x03a8>; + }; + ics_irq { + ics-irq-base-addr = <0xff4a2000>; + ics-irq-mask-ns = <0x0000>; + ics-irq-status-ns = <0x0008>; + ics-irq-clr-ns = <0x000c>; + }; + ics_noc_bus { + base-addr = <0xe8950000>; + qos-type = <0x000c>; + factor = <0x0010>; + saturation = <0x0014>; + qos_extcontrol = <0x0018>; + }; + pmctrl { + base-addr = <0xfff31000>; + noc-power-idle-req = <0x0380>; + noc-power-idle-ack = <0x0384>; + noc-power-idle-stat = <0x0388>; + }; + pctrl { + base-addr = <0xe8a09000>; + peri-stat3 = <0x00a0>; + }; + media2 { + base-addr = <0xe8900000>; + peren0 = <0x000>; + perdis0 = <0x004>; + perclken0 = <0x008>; + perstat0 = <0x00c>; + perrsten0 = <0x030>; + perrstdis0 = <0x034>; + perrststat0 = <0x038>; + }; + peri { + base-addr = <0xfff35000>; + clkdiv8 = <0xc8>; + clkdiv18 = <0xf0>; + perpwrstat = <0x158>; + perpwrack = <0x15c>; + peristat7 = <0x50c>; + }; + }; +}; diff --git a/arch/arm64/configs/hikey970_defconfig b/arch/arm64/configs/hikey970_defconfig index ffd2ae18a48a..c77d7ca03ddb 100644 --- a/arch/arm64/configs/hikey970_defconfig +++ b/arch/arm64/configs/hikey970_defconfig @@ -260,6 +260,7 @@ CONFIG_VIRTIO_BLK=y CONFIG_SRAM=y CONFIG_UID_SYS_STATS=y CONFIG_HISI_HIKEY_USB=y +CONFIG_HISI_ICS_IPU=y CONFIG_MEMORY_STATE_TIME=y CONFIG_TI_ST=y CONFIG_ST_HCI=y diff --git a/drivers/clk/hisilicon/clk-kirin970.c b/drivers/clk/hisilicon/clk-kirin970.c index 2d222a7940ca..85ab78781a21 100644 --- a/drivers/clk/hisilicon/clk-kirin970.c +++ b/drivers/clk/hisilicon/clk-kirin970.c @@ -561,6 +561,7 @@ static const struct hisi_divider_clock kirin970_media1_divider_clks[] = { static const struct hisi_gate_clock kirin970_media2_gate_sep_clks[] = { { KIRIN970_CLK_GATE_VDECFREQ, "clk_gate_vdecfreq", "clk_div_vdec", CLK_SET_RATE_PARENT, 0x00, 8, 0, }, { KIRIN970_CLK_GATE_VENCFREQ, "clk_gate_vencfreq", "clk_div_venc", CLK_SET_RATE_PARENT, 0x00, 5, 0, }, + { KIRIN970_CLK_GATE_ICSFREQ, "clk_gate_icsfreq", "clk_div_ics", CLK_SET_RATE_PARENT, 0x00, 2, 0, }, }; static void kirin970_clk_crgctrl_init(struct device_node *np) diff --git a/drivers/hisi/Kconfig b/drivers/hisi/Kconfig index b75cccc5a038..b90af265cd4d 100644 --- a/drivers/hisi/Kconfig +++ b/drivers/hisi/Kconfig @@ -11,6 +11,7 @@ if HISILICON_PLATFORM source "drivers/hisi/mailbox/Kconfig" source "drivers/hisi/hifi_dsp/Kconfig" source "drivers/hisi/hifi_mailbox/Kconfig" +source "drivers/hisi/ics/Kconfig" endif #HISILICON_PLATFORM diff --git a/drivers/hisi/Makefile b/drivers/hisi/Makefile index c2f75581457a..c67b752ec738 100644 --- a/drivers/hisi/Makefile +++ b/drivers/hisi/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_HISILICON_PLATFORM_MAILBOX) += mailbox/ obj-$(CONFIG_HIFI_DSP_ONE_TRACK) += hifi_dsp/ obj-$(CONFIG_HIFI_MAILBOX) += hifi_mailbox/ - +obj-$(CONFIG_HISI_ICS_IPU) += ics/ diff --git a/drivers/hisi/ics/Kconfig b/drivers/hisi/ics/Kconfig new file mode 100644 index 000000000000..6e83ea93720d --- /dev/null +++ b/drivers/hisi/ics/Kconfig @@ -0,0 +1,40 @@ +# +# Cambricon IPU Platform Specific Drivers +# + +menuconfig HISI_ICS_IPU_PLATFORM + bool "Platform supoort for Cambricon IPU Device" + default y + help + Say Y here to get to see options for device drivers of various + Cambricon IPU platforms. This option itself does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if HISI_ICS_IPU_PLATFORM + +config HISI_ICS_IPU + tristate "Cambricon IPU Driver" + default y + help + Cambricon IPU Platform driver. + +config HISI_IPU_SET_VCODECBUS + tristate "Allow IPU Driver to set VCODEC bus clock rate" + default n + help + Allow IPU Driver to set VCODEC bus clock rate. + +config HISI_IPU_MNTN + tristate "Enable IPU Driver maintain function" + default n + help + Enable IPU Driver maintain function. + +config HISI_IPU_REGULATOR + tristate "Enable IPU Driver regulator control" + default n + help + Enable IPU Driver regulator control. + +endif # HISI_ICS_IPU_PLATFORM diff --git a/drivers/hisi/ics/Makefile b/drivers/hisi/ics/Makefile new file mode 100644 index 000000000000..1d7bedca60e1 --- /dev/null +++ b/drivers/hisi/ics/Makefile @@ -0,0 +1,14 @@ +ifeq ($(es_low_freq),true) +EXTRA_CFLAGS += -DCONFIG_ES_VDEC_LOW_FREQ +endif + +obj-$(CONFIG_HISI_ICS_IPU) += cambricon_ipu.o +obj-$(CONFIG_HISI_ICS_IPU) += ipu_smmu_drv.o +obj-$(CONFIG_HISI_ICS_IPU) += ipu_clock.o +obj-$(CONFIG_HISI_IPU_MNTN) += ipu_mntn.o +ifneq ($(TARGET_BUILD_VARIANT),user) +obj-$(CONFIG_HISI_ICS_IPU) += ics_debug_proxy.o +obj-y += ics_debug.o +endif + +EXTRA_CFLAGS += -I$(srctree)/drivers/hisi/ap/platform/kirin970 diff --git a/drivers/hisi/ics/cambricon_ipu.c b/drivers/hisi/ics/cambricon_ipu.c new file mode 100644 index 000000000000..975336430e54 --- /dev/null +++ b/drivers/hisi/ics/cambricon_ipu.c @@ -0,0 +1,2843 @@ +/* + * Generic driver for the cambricon ipu device. + * + * Copyright (C) 2016 Cambricon Limited + * + * Licensed under the GPL v2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_HUAWEI_DSM +#include +#endif /* CONFIG_HUAWEI_DSM */ +#include "ipu_smmu_drv.h" +#include "ipu_clock.h" +#include "cambricon_ipu.h" +// #include "ipu_mntn.h" + +#define COMP_CAMBRICON_IPU_DRV_NAME "hisilicon,cambricon-ipu" + +/* ipu base address */ +#define IPU_NAME "ipu" +#define UNUSED_PARAMETER(x) ((void)(x)) + +/* configure registers info */ +#define IPU_CONF_REG_BASE (IPU_BASE_ADDRESS + 0x00000000) +#define IPU_CONF_REG_SIZE (0x00100000) +/* instruction RAM info */ +#define IPU_INST_RAM_BASE (IPU_BASE_ADDRESS + 0x00100000) +#define IPU_INST_RAM_SIZE (0x00100000) + +/* ipu configure register offset */ +#define IPU_TO_STOP (0x0) +#define IPU_TO_START (0x1) +#define IPU_STATUS_UNFINISH (0x0) +#define IPU_STATUS_FINISH (0x1) + +#define IPU_START_REG (0x18) /* IPU start up reg */ +#define IPU_STATUS_REG (0x20) /* IPU payload finish status reg */ +#define IPU_BASE_ADDR_REG (0x28) /* IPU access external DDR address */ +#define IPU_SRAM_CTRL_REG (0x30) /* IPU internal SRAM configure reg */ +#define IPU_VERSION_REG (0x40) /* IPU Version reg */ + +/* reserved DDR memory info */ +#define DMA_BUFFER_START (0x20c00000) +#define DMA_BUFFER_SIZE (500 * 1024 * 1024) + +/* Configuration Registers Operations: ioctl */ +#define MAGIC_NUM 100 +#define RDCONFIG_DWORD _IOR(MAGIC_NUM, 3, unsigned int) +#define WRCONFIG_DWORD _IOW(MAGIC_NUM, 6, unsigned int*)//_IOW(MAGIC_NUM, 6, unsigned long[2]) +#define IN_TASKQUEUE _IOW(MAGIC_NUM, 9, unsigned int*)//_IOW(MAGIC_NUM, 9, taskElement) +#define RDCONFIG_QWORD _IOR(MAGIC_NUM, 10, unsigned int) +#define WRCONFIG_QWORD _IOW(MAGIC_NUM, 11, unsigned int*) +#define SETCONFIG_MAP _IOW(MAGIC_NUM, 37, struct map_data) +#define SETCONFIG_UNMAP _IOW(MAGIC_NUM, 38, struct map_data) +#define SETCONFIG_RESET_STATISTIC _IOW(MAGIC_NUM, 30, unsigned int) +#define SETCONFIG_REPORT_STATISTIC _IOW(MAGIC_NUM, 31, struct smmu_statistic) +#define SETCONFIG_UPDATE_PTE _IOW(MAGIC_NUM, 32, unsigned int) +#define SETCONFIG_RESET_VIRT_ADDR _IOW(MAGIC_NUM, 33, struct map_data) +#define SETCONFIG_IPU_PROFILE _IOW(MAGIC_NUM, 34, unsigned long) + +#define IPU_IOC_MAXNR (38) + +#define ICS_IRQ_UNMASK_NO_SECURITY (0x00000000) +#define ICS_IRQ_MASK_NO_SECURITY (0x00000001) +#define ICS_IRQ_CLEAR_IRQ_NS (0x00000001) +#define ICS_SMMU_WR_ERR_BUFF_LEN (128) +#define ICS_NOC_BUS_CONFIG_QOS_TYPE (0x1) +#define ICS_NOC_BUS_QOS_EXTCONTROL_ENABLE (0x1) +#define ICS_NOC_BUS_CONFIG_FACTOR (0x0) +#define ICS_NOC_BUS_CONFIG_SATURATION_RESET (0x0) +#define ICS_NOC_BUS_CONFIG_SATURATION_NORMAL (0x10) +#define CONFIG_NOC_POWER_IDLEREQ_0 (0x02000200) +#define CONFIG_NOC_POWER_IDLEACK_0_BIT9 (0X200) +#define CONFIG_NOC_POWER_IDLE_0_BIT9 (0X200) +#define CONFIG_PCTRL_PERI_STAT3_BIT22 (0x400000) +#define CONFIG_MEDIA2_REG_PERDIS0_ICS (0x7) +#define CONFIG_MEDIA2_REG_PEREN0_ICS (0x7) +#define CONFIG_MEDIA2_REG_PERRSTEN0_ICS (0x38) +#define CONFIG_SC_GT_CLK_ICS_DIS (0x40000000) +#define CONFIG_SC_GT_CLK_ICS_EN (0x40004000) +#define CONFIG_NOC_ICS_POWER_IDLEREQ_DIS (0x02000000) +#define TASKQUEUE_SIZE (64) +#define NORMAL_START_BIT (0) +#define NORMAL_START_MASK (0x1 << NORMAL_START_BIT) + +#define WATCHDOG_TIMEOUT_THD_MS (2000) + +#define SMMU_OUTSTANDING (4) +#define IPU_BASELINE_RATE_MHZ (960) +#define IPU_BASELINE_LMT_OSD (32) +#define IPU_BASELINE_LMT_BANDWIDTH_MHZ (7500) +#define IPU_RATE_HZ_TO_MHZ (1000000) +#define IPU_STATUS_REG_FINISH (0xA0) +#define IPU_INTERRUPT_INST_REG (0xAC) +#define ICS_IRQ_CLEAR_IRQ_LEVEL1_NS (0x2) +#define ICS_SOFT_RST_REQ_REG (0x74) +#define ICS_SOFT_RST_ACK_REG (0x78) +#define ICS_SOFT_RST_ACK (0x1) +#define IPU_ID_REG (0x40000) +#define IPU_CONTROL_ID_REG (0x200) +#define IPU_WATCH_DOG_REG (0x20C) +#define PERF_CNT_CLEAR_REG (0x84) +#define PERF_CNT_CLK_GT_REG (0x88) +#define FRAME_CYC_CNT_REG (0x8C) +#define FU_IDLE_CNT_REG (0x90) +#define IO_IDLE_CNT_REG (0x94) +#define ALL_IDLE_CNT_REG (0x98) +#define ALL_BUSY_CNT_REG (0x9C) +#define ICS_MAX_OSD_REG (0xA0) +#define ICS_OSD_CNT_REG (0xA4) +#define ICS_FRAME_CNT_REG (0xA8) +#define FRAME_CNT_CLEAR_REG (0xAC) +#define PERF_CNT_CLEAR (0x1) +#define FRAME_START_CNT_CLEAR (0x1) +#define FRAME_FINISH_CNT_CLEAR (0x10) +#define PERF_CNT_CLK_GT_CLOSE (0x0) +#define PERF_CNT_CLK_GT_ENABLE (0x1) +#define INTERRUPTINST_INTERRUPT_BIT (7) +#define INTERRUPTINST_INTERRUPT_MASK (0x1UL<(IPU_TASK_ENUM_END - IPU_TASK_ENUM_START - 1)) \ + __ret = 1; \ + __ret; \ +}) \ + + +typedef enum { + DEV_UNSET_PROFILE = 0, + DEV_LOW_PROFILE, + DEV_NORMAL_PROFILE, + DEV_HIGH_PROFILE, +} dev_perf; + +enum ipu_wtd_status { + IPU_WTD_UNINIT = 0, + IPU_WTD_IDLE, + IPU_WTD_RUNNING, +}; + +/* global variables */ +static unsigned int ipu_major = 0; +static unsigned int ipu_minor = 0; +struct cambricon_ipu_private *adapter = NULL; +static struct class *dev_class = NULL; + +#ifdef CONFIG_HUAWEI_DSM +/* DSM information about ai module */ +static struct dsm_dev dsm_ai = { + .name = "dsm_ai", + .device_name = "ai", + .ic_name = NULL, + .module_name = NULL, + .fops = NULL, + .buff_size = 1024, +}; + +/* AI client handle for DSM */ +static struct dsm_client *dsm_ai_client = NULL; + +/* Register ai client to DSM, if register fail, continue and does't return */ +#define DSM_AI_REGISTER()\ +do {\ + if (NULL == dsm_ai_client)\ + {\ + dsm_ai_client = dsm_register_client(&dsm_ai);\ + }\ +} while(0) + +/* Unregister ai client to DSM */ +#define DSM_AI_UNREGISTER()\ +do {\ + if (NULL != dsm_ai_client)\ + {\ + dsm_unregister_client(dsm_ai_client, &dsm_ai);\ + dsm_ai_client = NULL;\ + }\ +} while(0) + +/* Report to DSM */ +#define DSM_AI_KERN_ERROR_REPORT(error_no, fmt, args...)\ +do {\ + if (NULL != dsm_ai_client && !dsm_client_ocuppy(dsm_ai_client))\ + {\ + dsm_client_record(dsm_ai_client, fmt, ##args);\ + dsm_client_notify(dsm_ai_client, error_no);\ + }\ +} while(0) + +#else +#define DSM_AI_REGISTER() +#define DSM_AI_UNREGISTER() +#define DSM_AI_KERN_ERROR_REPORT(error_no, fmt, args...) + +#endif /* CONFIG_HUAWEI_DSM */ + +struct ioctl_out_params { + bool ret_directly; + void *memory_node; + //TODO: add more out params here +}; + +typedef long (*ioctl_cb)(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); + +/* IOCTL map */ +struct ipu_ioctl_map { + unsigned int cmd; + ioctl_cb func; + const char *name; +}; + +/* FIFO_TaskElements which holds ipu tasks */ +static DECLARE_KFIFO_PTR(FIFO_TaskElements, taskElement); +/* wait queue holds sync task which make user process sleep */ +wait_queue_head_t sync_wq; + +/* ipu char device ops declaration */ +static int ipu_open(struct inode *inode, struct file *filp); +static int ipu_release(struct inode *inode, struct file *filp); +static ssize_t ipu_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); +static long ipu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +static long ipu_ioctl32(struct file *fd, unsigned int cmd, unsigned long arg); +#endif +static loff_t ipu_llseek(struct file *filp, loff_t off, int whence); +static long ipu_write_dword(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); +static long ipu_set_map(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); +static long ipu_set_unmap(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); +static long ipu_set_reset_virt_addr(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); +static long ipu_set_reset_statistic(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); +static long ipu_set_report_statistic(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); +static long ipu_set_update_pte(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); +static long ipu_process_workqueue(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); +static long ipu_read_dword(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); +static int ipu_power_up(void); +static int ipu_power_down(void); +long ipu_set_profile(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); +static bool cambricon_ipu_workqueue(void); +static void performance_monitor_open(void); + + +#define PRINT_TASK(element) \ + printk(KERN_INFO "[%s:%d]: PRINT_TASK id: %ld, type: %x, instAddr: %lx, prior: %d\n", __func__, __LINE__, element.taskId, element.taskType, element.offchipInstAddr, element.prior) + +static long ipu_write_qword(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + unsigned long off_val[2]; + + if (!adapter->feature_tree.wr_qword) { + printk(KERN_ERR"[%s]: IPU_ERROR:unsupport op in this platform!\n", __func__); + return -EINVAL; + } + + if (copy_from_user(off_val, (void *)arg, sizeof(off_val))) { + printk(KERN_ERR"[%s]: IPU_ERROR:Copy data from user failed!\n", __func__); + return -EFAULT; + } else { + unsigned int *offset = (unsigned int *)off_val; + unsigned long *data = (unsigned long *)off_val+1; + + printk(KERN_DEBUG"[%s]: Write CONFIG REG qword offset 0x%pK, value is 0x%lu\n", __func__, (void *)off_val[0], (unsigned long)*data); + iowrite32(((*data) & 0xffffffff), (void *)((unsigned long)adapter->config_reg_virt_addr + *offset)); + iowrite32(((*data >> 32) & 0xffffffff), (void *)((unsigned long)adapter->config_reg_virt_addr + (*offset + 4))); + } + return 0; +} + +static long ipu_read_qword(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + unsigned long ret_value; + + if (!adapter->feature_tree.wr_qword) { + printk(KERN_ERR"[%s]: IPU_ERROR:unsupport op in this platform!\n", __func__); + return 0; + } + + ret_value = ioread32((void *)((unsigned long)adapter->config_reg_virt_addr + arg)); + ret_value += ((unsigned long)ioread32((void *)((unsigned long)adapter->config_reg_virt_addr + arg + 4))) << 32; + printk(KERN_DEBUG"[%s]: Read CONFIG REG dword offset 0x%x, read value=0x%lx\n", __func__, (unsigned int)arg, ret_value); + return ret_value; +} + +#define FUNC_AND_NAME(func) func, #func + +static const struct ipu_ioctl_map ipu_ioctl_maps[] = { + {RDCONFIG_DWORD, FUNC_AND_NAME(ipu_read_dword)}, + {WRCONFIG_DWORD, FUNC_AND_NAME(ipu_write_dword)}, + {SETCONFIG_MAP, FUNC_AND_NAME(ipu_set_map)}, + {SETCONFIG_UNMAP, FUNC_AND_NAME(ipu_set_unmap)}, + {SETCONFIG_RESET_VIRT_ADDR, FUNC_AND_NAME(ipu_set_reset_virt_addr)}, + {SETCONFIG_RESET_STATISTIC, FUNC_AND_NAME(ipu_set_reset_statistic)}, + {SETCONFIG_REPORT_STATISTIC,FUNC_AND_NAME(ipu_set_report_statistic)}, + {SETCONFIG_UPDATE_PTE, FUNC_AND_NAME(ipu_set_update_pte)}, + {IN_TASKQUEUE, FUNC_AND_NAME(ipu_process_workqueue)}, + {SETCONFIG_IPU_PROFILE, FUNC_AND_NAME(ipu_set_profile)}, + {RDCONFIG_QWORD, FUNC_AND_NAME(ipu_read_qword)}, + {WRCONFIG_QWORD, FUNC_AND_NAME(ipu_write_qword)}, +}; + +static void ipu_watchdog_init(struct ipu_wtd *watchdog, bool enable, void *callback) +{ + watchdog->enable = enable; + watchdog->status = IPU_WTD_IDLE; + + if (enable) { + mutex_init(&watchdog->timer_mutex); + setup_timer(&watchdog->timer, callback, 0); + } +} + +static void ipu_watchdog_start(struct ipu_wtd *watchdog, unsigned long watchdog_length_ms) +{ + if (!watchdog->enable) { + return; + } + + mutex_lock(&watchdog->timer_mutex); + + if (IPU_WTD_RUNNING == watchdog->status) { + /* here use del_timer, not use del_timer_sync, because mutex can guarantee the wtd is run serially */ + del_timer(&watchdog->timer); + } + + watchdog->timer.expires = jiffies + msecs_to_jiffies(watchdog_length_ms); + add_timer(&watchdog->timer); + watchdog->status = IPU_WTD_RUNNING; + mutex_unlock(&watchdog->timer_mutex); +} + +static void ipu_watchdog_stop(struct ipu_wtd *watchdog) +{ + if (!watchdog->enable) { + return; + } + + mutex_lock(&watchdog->timer_mutex); + + if (IPU_WTD_RUNNING == watchdog->status) { + /* here use del_timer, not use del_timer_sync, because mutex can guarantee the wtd is run serially */ + del_timer(&watchdog->timer); + watchdog->status = IPU_WTD_IDLE; + } + mutex_unlock(&watchdog->timer_mutex); +} + +int set_offchip_inst_addr(unsigned long addr) { + unsigned int i; + unsigned int tmp; + unsigned long adjusted_addr; + unsigned int ipu_base_reg; + uint32_t inst_addr; + + printk(KERN_INFO"[%s]: addr is %lx ++++\n", __func__, addr); + + /* write offchip inst addr to instram */ + inst_addr = (uint32_t) (addr + 384); + inst_addr = (inst_addr | 0x80000000) >> 1; + ipu_base_reg = ioread32((void *)((unsigned long)adapter->config_reg_virt_addr + IPU_BASE_ADDR_REG)); + adjusted_addr = ((ipu_base_reg & 0xfff) << 20) + addr; + + if (ipu_mem_mngr_check_valid((unsigned int)adjusted_addr)) { + printk(KERN_ERR"[%s]: IPU_ERROR:fail to seek in malloc memory, input addr=0x%pK ipu_base_reg=%u, adjusted_addr=0x%pK \n", + __func__, (void *)(unsigned long)addr, ipu_base_reg, (void *)adjusted_addr); + ipu_mem_mngr_dump(); + return -EINVAL; + } + + if(down_interruptible(&adapter->inst_ram_sem)) { + printk(KERN_ERR"[%s]:IPU_ERROR: down_interruptible inst_ram_sem fail!\n", __func__); + return -ERESTARTSYS; + } + + /* write boot inst to instram */ + for (i = 0; i < adapter->boot_inst_set.boot_inst_size; i += 4) { + tmp = *(unsigned int *)&adapter->boot_inst_set.boot_inst[i]; + iowrite32(tmp, (void *)((unsigned long)adapter->inst_ram_virt_addr + i)); + } + + iowrite8((inst_addr&0x000000ff) , (void *)((unsigned long)adapter->inst_ram_virt_addr + 53)); + iowrite8((inst_addr&0x0000ff00)>>8 , (void *)((unsigned long)adapter->inst_ram_virt_addr + 54)); + iowrite8((inst_addr&0x00ff0000)>>16, (void *)((unsigned long)adapter->inst_ram_virt_addr + 55)); + iowrite8((inst_addr&0xff000000)>>24, (void *)((unsigned long)adapter->inst_ram_virt_addr + 56)); + + up(&adapter->inst_ram_sem); + + printk(KERN_INFO"[%s]: addr is %lx ----\n", __func__, addr); + return 0; +} + +static int start_ipu(void) +{ + int ret; + + /* power up ipu if ipu is power-off */ + mutex_lock(&adapter->boot_inst_set.boot_mutex); + + if (!adapter->ipu_power_up) { + if (!adapter->boot_inst_set.access_ddr_addr_is_config) { + printk(KERN_ERR"[%s]: IPU_ERROR:boot_inst_set.access_ddr_addr_is_config is not configed, FATAL\n", __func__); + mutex_unlock(&adapter->boot_inst_set.boot_mutex); + return -EFAULT; + } + + if (!adapter->boot_inst_set.boot_inst_recorded_is_config) { + printk(KERN_ERR"[%s]: IPU_ERROR:boot_inst_set is not configed, check whether [ipu_write] is called, FATAL\n", __func__); + mutex_unlock(&adapter->boot_inst_set.boot_mutex); + return -EFAULT; + } + + if ((0 == adapter->boot_inst_set.boot_inst_size) || (0 != (adapter->boot_inst_set.boot_inst_size % 4))) { + printk(KERN_ERR"[%s]: IPU_ERROR:invalid boot inst size=0x%x, FATAL\n", __func__, adapter->boot_inst_set.boot_inst_size); + mutex_unlock(&adapter->boot_inst_set.boot_mutex); + return -EFAULT; + } + + ret = ipu_power_up(); + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu_power_up fail\n", __func__); + /* IPU power up fail, report to DSM */ + DSM_AI_KERN_ERROR_REPORT(DSM_AI_KERN_POWER_UP_ERR_NO, "IPU power up fail, ttbr0=%x, ret=%d.\n", adapter->smmu_ttbr0, ret); + mutex_unlock(&adapter->boot_inst_set.boot_mutex); + return -EBUSY; + } + + /* set base address */ + iowrite32(adapter->boot_inst_set.ipu_access_ddr_addr, (void *)((unsigned long)adapter->config_reg_virt_addr + IPU_BASE_ADDR_REG)); + } + + /* prepare data for ipu */ + if(IPU_TO_START!= ioread32((void *)((unsigned long)adapter->config_reg_virt_addr + IPU_START_REG))) { + taskElement head; + + if (kfifo_peek(&FIFO_TaskElements, &head)) { + PRINT_TASK(head); + ret = set_offchip_inst_addr(head.offchipInstAddr); + printk(KERN_DEBUG"[%s]: START_COMPUTE_1, offchipInstAddr=0x%lx\n", __func__, head.offchipInstAddr); + if (0 == ret) { + /* update pte */ + ipu_smmu_pte_update(); + + /* start ipu */ + iowrite32(IPU_TO_STOP, (void *)((unsigned long)adapter->config_reg_virt_addr + IPU_START_REG)); + iowrite32(IPU_STATUS_UNFINISH, (void *)((unsigned long)adapter->config_reg_virt_addr + IPU_STATUS_REG)); + + printk(KERN_DEBUG"[%s]: START_COMPUTE_2, offchipInstAddr=0x%lx\n", __func__, head.offchipInstAddr); + if (adapter->feature_tree.performance_monitor) { + performance_monitor_open(); + } + + iowrite32(IPU_TO_START, (void *)((unsigned long)adapter->config_reg_virt_addr + IPU_START_REG)); + udelay(3); + printk(KERN_DEBUG"[%s]: START_COMPUTE_3, offchipInstAddr=0x%lx\n", __func__, head.offchipInstAddr); + + ipu_watchdog_start(&adapter->reset_wtd, WATCHDOG_TIMEOUT_THD_MS); + } else { + printk(KERN_ERR"[%s]: IPU_ERROR:set_offchip_inst_addr fail, ret=%d\n", __func__, ret); + mutex_unlock(&adapter->boot_inst_set.boot_mutex); + return ret; + } + } else { + printk(KERN_ERR"[%s]: IPU_ERROR:kfifo_peek empty\n", __func__); + } + } + + /* workqueue task_fifo_sem is not up here */ + mutex_unlock(&adapter->boot_inst_set.boot_mutex); + + printk(KERN_DEBUG"[%s]: success!\n", __func__); + return 0; +} + +static void start_ipu_workqueue(void) +{ + while (start_ipu()) { + printk(KERN_ERR"[%s]: IPU_ERROR:start ipu fail, FATAL\n", __func__); + if (!cambricon_ipu_workqueue()) { + printk(KERN_ERR"[%s]: IPU_ERROR:cambricon_ipu_workqueue false when kill head task\n", __func__); + } + + if (kfifo_is_empty(&FIFO_TaskElements)) { + break; + } + } + printk(KERN_DEBUG"[%s]: start_ipu done!\n", __func__); +} + +static int stop_ipu(void) +{ + int ret = 0; + + if (adapter->ipu_power_up) { + ret = ipu_power_down(); + } + + return ret; +} + +/* global variable declaration */ +static const struct file_operations ipu_fops = { + .owner = THIS_MODULE,/*lint !e64*/ + .open = ipu_open, + .release = ipu_release, + .write = ipu_write, + .unlocked_ioctl = ipu_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ipu_ioctl32, +#endif + .llseek = ipu_llseek, +};/*lint !e785*/ +/* ipu platform device */ +static struct platform_device cambricon_ipu_device = { + .name = "cambricon-ipu", + .id = -1, +};/*lint !e785*/ + +bool ipu_get_irq_offset (struct device *dev) +{ + int property_rd; + struct irq_reg_offset *offset = &adapter->irq_reg_offset; + struct device_node *node = of_find_node_by_name(dev->of_node, "ics_irq"); + if(!node) { + printk(KERN_ERR"[%s]: IPU_ERROR:find ics_irq node error\n", __func__); + return false; + } + memset(offset, 0, sizeof(*offset));// coverity[secure_coding] + property_rd = of_property_read_u32(node, "ics-irq-base-addr", &offset->ics_irq_base_addr); + property_rd |= of_property_read_u32(node, "ics-irq-mask-ns", &offset->ics_irq_mask_ns); + property_rd |= of_property_read_u32(node, "ics-irq-clr-ns", &offset->ics_irq_clr_ns); + if (property_rd) { + printk(KERN_ERR"[%s]: IPU_ERROR:read property of irq offset error\n", __func__); + return false; + } + + return true; +} + +bool ipu_get_bandwidth_lmt_offset (struct device *dev) +{ + int property_rd; + struct ics_noc_bus_reg_offset *ics_noc_bus = &adapter->ics_noc_bus_reg_offset; + struct device_node *node = of_find_node_by_name(dev->of_node, "ics_noc_bus"); + + if(!node) { + printk(KERN_ERR"[%s]: IPU_ERROR:find ics_noc_bus node error\n", __func__); + return false; + } + + property_rd = of_property_read_u32(node, "base-addr", &ics_noc_bus->base_addr); + property_rd |= of_property_read_u32(node, "qos-type", &ics_noc_bus->qos_type); + property_rd |= of_property_read_u32(node, "factor", &ics_noc_bus->factor); + property_rd |= of_property_read_u32(node, "saturation", &ics_noc_bus->saturation); + property_rd |= of_property_read_u32(node, "qos_extcontrol", &ics_noc_bus->qos_extcontrol); + if (property_rd) { + printk(KERN_ERR"[%s]: IPU_ERROR:read property of ics_noc_bus offset error\n", __func__); + return false; + } + + return true; +} + +bool ipu_get_reset_offset (struct device *dev) +{ + int property_rd; + struct pmctrl_reg_offset *pmctrl = &adapter->pmctrl_reg_offset; + struct pctrl_reg_offset *pctrl = &adapter->pctrl_reg_offset; + struct media2_reg_offset *media2 = &adapter->media2_reg_offset; + struct peri_reg_offset *peri = &adapter->peri_reg_offset; + struct device_node *node = of_find_node_by_name(dev->of_node, "pmctrl"); + if(!node) { + printk(KERN_ERR"[%s]: IPU_ERROR:find pmctrl node error\n", __func__); + return false; + } + memset(pmctrl, 0, sizeof(*pmctrl)); + property_rd = of_property_read_u32(node, "base-addr", &pmctrl->base_addr); + property_rd |= of_property_read_u32(node, "noc-power-idle-req", &pmctrl->noc_power_idle_req); + property_rd |= of_property_read_u32(node, "noc-power-idle-ack", &pmctrl->noc_power_idle_ack); + property_rd |= of_property_read_u32(node, "noc-power-idle-stat", &pmctrl->noc_power_idle_stat); + if (property_rd) { + printk(KERN_ERR"[%s]: IPU_ERROR:read property of pmctrl offset error\n", __func__); + return false; + } + + node = of_find_node_by_name(dev->of_node, "pctrl"); + if(!node) { + printk(KERN_ERR"[%s]: IPU_ERROR:find pctrl node error\n", __func__); + return false; + } + memset(pctrl, 0, sizeof(*pctrl)); + property_rd = of_property_read_u32(node, "base-addr", &pctrl->base_addr); + property_rd |= of_property_read_u32(node, "peri-stat3", &pctrl->peri_stat3); + if (property_rd) { + printk(KERN_ERR"[%s]: IPU_ERROR:read property of pctrl offset error\n", __func__); + return false; + } + + node = of_find_node_by_name(dev->of_node, "media2"); + if(!node) { + printk(KERN_ERR"[%s]: IPU_ERROR:find media2 node error\n", __func__); + return false; + } + memset(media2, 0, sizeof(*media2)); + property_rd = of_property_read_u32(node, "base-addr", &media2->base_addr); + property_rd |= of_property_read_u32(node, "peren0", &media2->peren0); + property_rd |= of_property_read_u32(node, "perdis0", &media2->perdis0); + property_rd |= of_property_read_u32(node, "perclken0", &media2->perclken0);; + property_rd |= of_property_read_u32(node, "perstat0", &media2->perstat0); + property_rd |= of_property_read_u32(node, "perrsten0", &media2->perrsten0); + property_rd |= of_property_read_u32(node, "perrstdis0", &media2->perrstdis0); + property_rd |= of_property_read_u32(node, "perrststat0", &media2->perrststat0); + if (property_rd) { + printk(KERN_ERR"[%s]: IPU_ERROR:read property of media2 offset error\n", __func__); + return false; + } + + node = of_find_node_by_name(dev->of_node, "peri"); + if(!node) { + printk(KERN_ERR"[%s]: IPU_ERROR:find peri node error\n", __func__); + return false; + } + memset(peri, 0, sizeof(*peri));// coverity[secure_coding] + property_rd = of_property_read_u32(node, "base-addr", &peri->base_addr); + property_rd |= of_property_read_u32(node, "clkdiv18", &peri->clkdiv18); + property_rd |= of_property_read_u32(node, "clkdiv8", &peri->clkdiv8); + property_rd |= of_property_read_u32(node, "perpwrstat", &peri->perpwrstat); + property_rd |= of_property_read_u32(node, "perpwrack", &peri->perpwrack); + property_rd |= of_property_read_u32(node, "peristat7", &peri->peristat7); + if (property_rd) { + printk(KERN_ERR"[%s]: IPU_ERROR:read property of crg_periph offset error\n", __func__); + return false; + } + + return true; +} + +bool ipu_get_feature_tree (struct device *dev) +{ + /* get platform information */ + const char *str; + int ret = of_property_read_string(dev->of_node, "ics-platform", &str); + + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:fatal err, of_property_read_string fail\n", __func__); + return false; + } + + memset(&adapter->feature_tree, 0, sizeof(adapter->feature_tree));// coverity[secure_coding] + if (strncmp(str, "kirin970_es", sizeof("kirin970_es")) == 0) { + adapter->feature_tree.finish_irq_expand_ns = false; + adapter->feature_tree.finish_irq_expand_p = false; + adapter->feature_tree.finish_irq_expand_s = false; + adapter->feature_tree.finish_irq_to_hifi = false; + adapter->feature_tree.finish_irq_to_ivp = false; + adapter->feature_tree.finish_irq_to_isp = false; + adapter->feature_tree.finish_irq_to_lpm3 = false; + adapter->feature_tree.finish_irq_to_iocmu = false; + adapter->feature_tree.smmu_port_select = false; + adapter->feature_tree.soft_watchdog_enable = false; + adapter->feature_tree.ipu_reset_when_in_error = IPU_RESET_UNSUPPORT; + adapter->feature_tree.ipu_bandwidth_lmt = IPU_BANDWIDTH_LMT_UNSUPPORT; + } else if (strncmp(str, "kirin970_cs", sizeof("kirin970_cs")) == 0) { + adapter->feature_tree.finish_irq_expand_ns = true; + adapter->feature_tree.finish_irq_expand_p = true; + adapter->feature_tree.finish_irq_expand_s = true; + adapter->feature_tree.finish_irq_to_hifi = true; + adapter->feature_tree.finish_irq_to_ivp = true; + adapter->feature_tree.finish_irq_to_isp = true; + adapter->feature_tree.finish_irq_to_lpm3 = true; + adapter->feature_tree.finish_irq_to_iocmu = true; + adapter->feature_tree.smmu_port_select = false; + adapter->feature_tree.soft_watchdog_enable = true; + adapter->feature_tree.ipu_reset_when_in_error = IPU_RESET_BY_CONFIG_NOC_BUS; + adapter->feature_tree.ipu_bandwidth_lmt = IPU_BANDWIDTH_LMT_UNSUPPORT;//fixme: IPU_BANDWIDTH_LMT_BY_QOS; + adapter->feature_tree.performance_monitor = true; // for DEBUG + } else { + printk(KERN_ERR"[%s]: IPU_ERROR:fatal err, platform is unsupported\n", __func__); + return false; + } + + printk(KERN_DEBUG"[%s]: the platform is %s\n", __func__, str); + + return true; +} + +int regulator_ip_vipu_enable(void) +{ +#ifdef CONFIG_HISI_IPU_REGULATOR + int ret; + + ret = regulator_is_enabled(adapter->vipu_ip); + if (ret) { + printk(KERN_ERR"[%s]:IPU_ERROR:regulator_is_enabled: %d\n", __func__, ret); + // rdr_system_error((unsigned int)MODID_NPU_EXC_SET_POWER_UP_STATUS_FAULT, 0, 0); + return -EBUSY; + } + + ret = regulator_enable(adapter->vipu_ip); + if (0 != ret) { + printk(KERN_ERR"[%s]:IPU_ERROR:Failed to enable: %d\n", __func__, ret); + // rdr_system_error((unsigned int)MODID_NPU_EXC_SET_POWER_UP_FAIL, 0, 0); + return ret; + } +#endif + return 0; +} + +int regulator_ip_vipu_disable(void) +{ +#ifdef CONFIG_HISI_IPU_REGULATOR + int ret; + + ret = regulator_disable(adapter->vipu_ip); + if (ret != 0) { + printk(KERN_ERR"[%s]:IPU_ERROR:Failed to disable: %d\n", __func__, ret); + // rdr_system_error((unsigned int)MODID_NPU_EXC_SET_POWER_DOWN_FAIL, 0, 0); + return ret; + } +#endif + return 0; +} + +int ipu_bandwidth_lmt_ioremap_addr(void) +{ + adapter->noc_bus_io_addr = ioremap((unsigned long)adapter->ics_noc_bus_reg_offset.base_addr, (unsigned long)0xff); + if (!adapter->noc_bus_io_addr) { + printk(KERN_ERR"[%s]:IPU_ERROR:noc_bus_io_addr ioremap fail\n", __func__); + return -ENOMEM; + } + + return 0; +} + +void ipu_bandwidth_lmt_unremap_addr(void) +{ + if (adapter->noc_bus_io_addr) { + iounmap(adapter->noc_bus_io_addr); + adapter->noc_bus_io_addr = NULL; + } +} + +int ipu_bandwidth_lmt_init(struct device *dev) +{ + int ret; + + /* ioremap reg addr for reset */ + if (!(IPU_RESET_UNSUPPORT == adapter->feature_tree.ipu_reset_when_in_error && + IPU_BANDWIDTH_LMT_UNSUPPORT == adapter->feature_tree.ipu_bandwidth_lmt)) { + + if (!ipu_get_bandwidth_lmt_offset(dev)) { + printk(KERN_ERR"[%s]: fatal err, error bandwidth lmt reg offset\n", __func__); + return -ENOENT; + } + + ret = ipu_bandwidth_lmt_ioremap_addr(); + if (ret) { + printk(KERN_ERR"[%s]: fatal err, ipu_bandwidth_lmt_ioremap_addr\n", __func__); + return ret; + } + } + + return 0; +} + +/* IPU bandwidth lmt, to avoid block video work */ +static unsigned int ipu_bandwidth_get_lmt(unsigned int ipu_rate, unsigned int bandwidth_lmt_trategy) +{ + unsigned int noc_bandwidth_lmt; + unsigned int vcodec_rate; + unsigned int bandwidth; + unsigned int ipu_rate_MHz; + unsigned int read_outstanding; + unsigned int write_outstanding; + unsigned int ret; + + if (ipu_rate == adapter->clk.ipu_high) { + vcodec_rate = adapter->clk.vcodecbus_high; + } else if (ipu_rate == adapter->clk.ipu_middle) { + vcodec_rate = adapter->clk.vcodecbus_middle; + } else if (ipu_rate == adapter->clk.ipu_low) { + vcodec_rate = adapter->clk.vcodecbus_low; + } else { + printk(KERN_ERR"[%s]: IPU_ERROR:error ipu_rate=%u, ignore\n", __func__, ipu_rate); + return 0; + } + + ipu_rate_MHz = ipu_rate / IPU_RATE_HZ_TO_MHZ; + + if (IPU_BANDWIDTH_LMT_BY_RW_OSD == bandwidth_lmt_trategy) { + /* for ipu_rate=960M, assume read/write osd is 32 */ + read_outstanding = ((IPU_BASELINE_LMT_OSD * ipu_rate_MHz) / IPU_BASELINE_RATE_MHZ + SMMU_OUTSTANDING) & 0xff; + write_outstanding = ((IPU_BASELINE_LMT_OSD * ipu_rate_MHz) / IPU_BASELINE_RATE_MHZ) & 0xff; + ret = (read_outstanding << 8) + write_outstanding; + printk(KERN_DEBUG"[%s]:read/write OSD=%u/%u\n", __func__, read_outstanding, write_outstanding); + } else { + /* for ipu_rate=960M, bandwidth is 7500Mbps, equal proportion for other ipu_rate (7500 * (MAX/1000000)=7200000ics_irq_io_addr) { + printk(KERN_ERR"[%s]:IPU_ERROR:adapter->ics_irq_io_addr is NULL\n", __func__); + return; + } + + iowrite32(reg_bandwidth, (void *)((unsigned long)adapter->ics_irq_io_addr + ICS_MAX_OSD_REG)); + } else { + if (0 == adapter->noc_bus_io_addr) { + printk(KERN_ERR"[%s]:IPU_ERROR:adapter->noc_bus_io_addr is NULL\n", __func__); + return; + } + + iowrite32(ICS_NOC_BUS_CONFIG_QOS_TYPE, (void *)((unsigned long)adapter->noc_bus_io_addr + adapter->ics_noc_bus_reg_offset.qos_type)); + iowrite32(reg_bandwidth, (void *)((unsigned long)adapter->noc_bus_io_addr + adapter->ics_noc_bus_reg_offset.factor)); + iowrite32(saturation, (void *)((unsigned long)adapter->noc_bus_io_addr + adapter->ics_noc_bus_reg_offset.saturation)); + iowrite32(ICS_NOC_BUS_QOS_EXTCONTROL_ENABLE, (void *)((unsigned long)adapter->noc_bus_io_addr + adapter->ics_noc_bus_reg_offset.qos_extcontrol)); + } +} + +int ipu_reset_ioremap_addr(void) +{ + adapter->pmctrl_io_addr = ioremap((unsigned long)adapter->pmctrl_reg_offset.base_addr, (unsigned long)0xfff); + if (!adapter->pmctrl_io_addr) { + printk(KERN_ERR"[%s]:IPU_ERROR:pmctrl_io_addr ioremap fail\n", __func__); + return -ENOMEM; + } + + adapter->pctrl_io_addr = ioremap((unsigned long)adapter->pctrl_reg_offset.base_addr, (unsigned long)0xff); + if (!adapter->pctrl_io_addr) { + printk(KERN_ERR"[%s]:IPU_ERROR:pctrl_io_addr ioremap fail\n", __func__); + return -ENOMEM; + } + + adapter->media2_io_addr = ioremap((unsigned long)adapter->media2_reg_offset.base_addr, (unsigned long)0xff); + if (!adapter->media2_io_addr) { + printk(KERN_ERR"[%s]:IPU_ERROR:media2_io_addr ioremap fail\n", __func__); + return -ENOMEM; + } + + adapter->peri_io_addr = ioremap((unsigned long)adapter->peri_reg_offset.base_addr, (unsigned long)0xfff); + if (!adapter->peri_io_addr) { + printk(KERN_ERR"[%s]:IPU_ERROR:peri_io_addr ioremap fail\n", __func__); + return -ENOMEM; + } + + return 0; +} + +void ipu_reset_unremap_addr(void) +{ + if (adapter->pmctrl_io_addr) { + iounmap(adapter->pmctrl_io_addr); + adapter->pmctrl_io_addr = NULL; + } + + if (adapter->pctrl_io_addr) { + iounmap(adapter->pctrl_io_addr); + adapter->pctrl_io_addr = NULL; + } + + if (adapter->media2_io_addr) { + iounmap(adapter->media2_io_addr); + adapter->media2_io_addr = NULL; + } + + if (adapter->peri_io_addr) { + iounmap(adapter->peri_io_addr); + adapter->peri_io_addr = NULL; + } +} + +int ipu_reset_init(struct device *dev) +{ + int ret; + + if (adapter->feature_tree.ipu_reset_when_in_error) { + if (!ipu_get_reset_offset(dev)) { + printk(KERN_ERR"[%s]: IPU_ERROR:fatal err, error reset reg offset\n", __func__); + return -ENOENT; + } + + /* ioremap reg addr for reset */ + ret = ipu_reset_ioremap_addr(); + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:fatal err, ipu_reset_ioremap_addr fail\n", __func__); + return ret; + } + } + + return 0; +} + +static void performance_monitor_open(void) +{ + if (!adapter->feature_tree.performance_monitor) { + printk(KERN_ERR"[%s]: IPU_ERROR:unsupport op in this platform!\n", __func__); + return; + } + + /* PERF_CNT_CLK_GT.perf_cnt_clk_gt = 0x1, to OPEN performance monitor */ + iowrite32(PERF_CNT_CLK_GT_ENABLE, (void *)((unsigned long)adapter->ics_irq_io_addr + PERF_CNT_CLK_GT_REG)); + + /* PERF_CNT_CLEAR.perf_cnt_clear = 0x1, FRAME_CNT_CLEAR = 0x11, CLEAR old values */ + iowrite32(PERF_CNT_CLEAR, (void *)((unsigned long)adapter->ics_irq_io_addr + PERF_CNT_CLEAR_REG)); + iowrite32(FRAME_START_CNT_CLEAR | FRAME_FINISH_CNT_CLEAR, (void *)((unsigned long)adapter->ics_irq_io_addr + FRAME_CNT_CLEAR_REG)); +} + +static void performance_monitor_get_stat(void) +{ + unsigned int frame_cycle_cnt; + unsigned int fu_idle_cnt; + unsigned int io_idle_cnt; + unsigned int all_idle_cnt; + unsigned int all_busy_cnt; + unsigned int ics_frame_cnt; + unsigned int vcodecbus_clk; + + if (!adapter->feature_tree.performance_monitor) { + printk(KERN_ERR"[%s]: IPU_ERROR:unsupport op in this platform!\n", __func__); + return; + } + + /* after ICS finish, record value of FRAME_CYC_CNT, FU_IDLE_CNT, IO_IDLE_CNT, ALL_IDLE_CNT, ALL_BUSY_CNT, ICS_FRAME_CNT */ + frame_cycle_cnt = ioread32((void *)((unsigned long)adapter->ics_irq_io_addr + FRAME_CYC_CNT_REG)); + fu_idle_cnt = ioread32((void *)((unsigned long)adapter->ics_irq_io_addr + FU_IDLE_CNT_REG)); + io_idle_cnt = ioread32((void *)((unsigned long)adapter->ics_irq_io_addr + IO_IDLE_CNT_REG)); + all_idle_cnt = ioread32((void *)((unsigned long)adapter->ics_irq_io_addr + ALL_IDLE_CNT_REG)); + all_busy_cnt = ioread32((void *)((unsigned long)adapter->ics_irq_io_addr + ALL_BUSY_CNT_REG)); + ics_frame_cnt = ioread32((void *)((unsigned long)adapter->ics_irq_io_addr + ICS_FRAME_CNT_REG)); + + // todo: here you can config PERF_CNT_CLEAR.perf_cnt_clear = 0x1 if want to clear current value + +#ifdef CONFIG_HISI_IPU_SET_VCODECBUS + vcodecbus_clk = clk_get_rate(adapter->clk.vcodecbus_clk_ptr); + vcodecbus_clk = vcodecbus_clk / 1000000; /* convert vcodecbus clk working rate from bps to Mbps */ +#else + vcodecbus_clk = 450000000; + vcodecbus_clk = vcodecbus_clk / 1000000; /* convert vcodecbus clk working rate from bps to Mbps */ +#endif + if (0 != vcodecbus_clk) { + printk(KERN_DEBUG"[%s]: frame_cycle: %d | fu/io/all idle: %d/%d/%d | all_busy: %d | ics_frame_cnt: %d; hw time: %d @ %dM", + __func__, frame_cycle_cnt, fu_idle_cnt, io_idle_cnt, all_idle_cnt, all_busy_cnt, ics_frame_cnt & 0xffff, + frame_cycle_cnt / vcodecbus_clk, vcodecbus_clk); + } else { + printk(KERN_DEBUG"[%s]: frame_cycle: %d | fu/io/all idle: %d/%d/%d | all_busy: %d | ics_frame_cnt: %d", + __func__, frame_cycle_cnt, fu_idle_cnt, io_idle_cnt, all_idle_cnt, all_busy_cnt, ics_frame_cnt & 0xffff); + } +} + +static void performance_monitor_close(void) +{ + if (!adapter->feature_tree.performance_monitor) { + printk(KERN_ERR"[%s]: IPU_ERROR:unsupport op in this platform!\n", __func__); + return; + } + + /* PERF_CNT_CLK_GT.perf_cnt_clk_gt = 0x0, to CLOSE performance monitor */ + iowrite32(PERF_CNT_CLK_GT_CLOSE, (void *)((unsigned long)adapter->ics_irq_io_addr + PERF_CNT_CLK_GT_REG)); + + /* PERF_CNT_CLEAR.perf_cnt_clear = 0x1, FRAME_CNT_CLEAR = 0x11, to clear current value */ + iowrite32(PERF_CNT_CLEAR, (void *)((unsigned long)adapter->ics_irq_io_addr + PERF_CNT_CLEAR_REG)); + iowrite32(FRAME_START_CNT_CLEAR | FRAME_FINISH_CNT_CLEAR, (void *)((unsigned long)adapter->ics_irq_io_addr + FRAME_CNT_CLEAR_REG)); + + // todo: here you can calc by this data, e.g. you can calc the accurate time clapse by (FRAME_CYC_CNT * BUS clock) +} + +#ifdef CAMBRICON_IPU_IRQ +/* ipu interrupt init, including update ipu status, clear interrupt, and unmask interrupt */ +void ipu_interrupt_init(void) +{ + unsigned long irq_io_addr = (unsigned long)adapter->ics_irq_io_addr; + + /* clear ipu status to unfinished */ + iowrite32(IPU_STATUS_UNFINISH, (void *)((unsigned long)adapter->config_reg_virt_addr + IPU_STATUS_REG)); + + /* clear ns interrupt */ + if (adapter->feature_tree.finish_irq_expand_ns) { + iowrite32(ICS_IRQ_CLEAR_IRQ_NS, (void *)(irq_io_addr + adapter->irq_reg_offset.ics_irq_clr_ns)); + } + + /* unmask interrupt */ + iowrite32(ICS_IRQ_UNMASK_NO_SECURITY, (void *)(irq_io_addr + adapter->irq_reg_offset.ics_irq_mask_ns)); +} + +/* to mask ipu interrupt and will not receive it */ +void ipu_interrupt_deinit(void) +{ + iowrite32(ICS_IRQ_MASK_NO_SECURITY, + (void *)((unsigned long)adapter->ics_irq_io_addr + adapter->irq_reg_offset.ics_irq_mask_ns)); +} +#endif + +void ipu_reset_proc(unsigned int addr) +{ + unsigned int loop_cnt; + int noc_power_idle_ack; + int noc_power_idle_stat; + int noc_peri_status; + int noc_idle = 0; + unsigned int reg_bandwidth; + + // IOREAD_RANGE(adapter->ics_irq_io_addr, 0xc); + if ((adapter->feature_tree.ipu_reset_when_in_error != IPU_RESET_BY_CONFIG_NOC_BUS) && + (adapter->feature_tree.ipu_reset_when_in_error != IPU_SOFT_RESET)) { + printk(KERN_ERR"[%s]: IPU_ERROR:unsupported ipu reset trategy=%u!\n", __func__, adapter->feature_tree.ipu_reset_when_in_error); + return; + } + + mutex_lock(&adapter->reset_mutex); + + printk(KERN_DEBUG"[%s]: ipu_recover start!\n", __func__); + + if (ipu_clock_set_rate(&adapter->clk, adapter->clk.stop_rate)) { + /* not return even set rate error, because reset process should finish */ + printk(KERN_DEBUG"[%s]: ipu_clock_set_rate fail, ignore\n", __FUNCTION__); + } else { + printk(KERN_DEBUG"[%s]: ipu_clock_set_rate ok\n", __FUNCTION__); + } + + if (IPU_SOFT_RESET == adapter->feature_tree.ipu_reset_when_in_error) { + /* set ICS_SOFT_RST_REQ */ + iowrite32(1, (void *)((unsigned long)adapter->ics_irq_io_addr + ICS_SOFT_RST_REQ_REG)); + + /* loop wait ICS_SOFT_RST_ACK (0xFF4A2078) == 0x1 */ + for (loop_cnt = 0; loop_cnt < 100; loop_cnt++) { + if (ICS_SOFT_RST_ACK == ioread32((void *)((unsigned long)adapter->ics_irq_io_addr + ICS_SOFT_RST_ACK_REG))) { + break; + } + udelay(1); + } + + printk(KERN_DEBUG"[%s]: loop wait ICS_SOFT_RST_ACK ok\n", __FUNCTION__); + + } else { + /* config NOC register, to enter stream limited mode, stream is limited to 0 */ + mutex_lock(&adapter->bandwidth_lmt_mutex); + + /* config NOC register, to enter stream limited mode, stream is limited to 0; SOC suggest here use NOC-BUS trategy for bandwidth lmt */ + ipu_bandwidth_set_lmt(ICS_NOC_BUS_CONFIG_FACTOR, ICS_NOC_BUS_CONFIG_SATURATION_RESET, IPU_RESET_BY_CONFIG_NOC_BUS); + mutex_unlock(&adapter->bandwidth_lmt_mutex); + + ipu_smmu_override_prefetch_addr(addr); + + udelay(10); + } + + iowrite32(CONFIG_NOC_POWER_IDLEREQ_0, (void *)((unsigned long)adapter->pmctrl_io_addr + adapter->pmctrl_reg_offset.noc_power_idle_req)); + + loop_cnt = 0; + while(!noc_idle) { + if (loop_cnt > 10) { + printk(KERN_ERR"[%s]: IPU_ERROR:loop timeout", __func__); + break; + } + noc_power_idle_ack = ioread32((void *)((unsigned long)adapter->pmctrl_io_addr + adapter->pmctrl_reg_offset.noc_power_idle_ack)) & CONFIG_NOC_POWER_IDLEACK_0_BIT9; + noc_power_idle_stat = ioread32((void *)((unsigned long)adapter->pmctrl_io_addr + adapter->pmctrl_reg_offset.noc_power_idle_stat)) & CONFIG_NOC_POWER_IDLE_0_BIT9; + noc_peri_status = ioread32((void *)((unsigned long)adapter->pctrl_io_addr + adapter->pctrl_reg_offset.peri_stat3)) & CONFIG_PCTRL_PERI_STAT3_BIT22; + noc_idle = noc_power_idle_ack && noc_power_idle_stat && noc_peri_status; + printk(KERN_DEBUG"[%s]: noc_power_idle_ack:%d, noc_power_idle_stat:%d, noc_peri_status:%d\n", + __func__, noc_power_idle_ack, noc_power_idle_stat, noc_peri_status); + + udelay(1); + loop_cnt++; + } + + iowrite32(CONFIG_MEDIA2_REG_PERDIS0_ICS, (void *)((unsigned long)adapter->media2_io_addr + adapter->media2_reg_offset.perdis0)); + iowrite32(CONFIG_MEDIA2_REG_PERRSTEN0_ICS, (void *)((unsigned long)adapter->media2_io_addr + adapter->media2_reg_offset.perrsten0)); + iowrite32(CONFIG_MEDIA2_REG_PEREN0_ICS, (void *)((unsigned long)adapter->media2_io_addr + adapter->media2_reg_offset.peren0)); + iowrite32(CONFIG_MEDIA2_REG_PERDIS0_ICS, (void *)((unsigned long)adapter->media2_io_addr + adapter->media2_reg_offset.perdis0)); + iowrite32(CONFIG_SC_GT_CLK_ICS_DIS, (void *)((unsigned long)adapter->peri_io_addr + adapter->peri_reg_offset.clkdiv18)); + + /* restart ipu */ + iowrite32(CONFIG_SC_GT_CLK_ICS_EN, (void *)((unsigned long)adapter->peri_io_addr + adapter->peri_reg_offset.clkdiv18)); + iowrite32(CONFIG_MEDIA2_REG_PEREN0_ICS, (void *)((unsigned long)adapter->media2_io_addr + adapter->media2_reg_offset.peren0)); + iowrite32(CONFIG_MEDIA2_REG_PERDIS0_ICS, (void *)((unsigned long)adapter->media2_io_addr + adapter->media2_reg_offset.perdis0)); + iowrite32(CONFIG_MEDIA2_REG_PERRSTEN0_ICS, (void *)((unsigned long)adapter->media2_io_addr + adapter->media2_reg_offset.perrstdis0)); + iowrite32(CONFIG_MEDIA2_REG_PEREN0_ICS, (void *)((unsigned long)adapter->media2_io_addr + adapter->media2_reg_offset.peren0)); + iowrite32(CONFIG_NOC_ICS_POWER_IDLEREQ_DIS, (void *)((unsigned long)adapter->pmctrl_io_addr + adapter->pmctrl_reg_offset.noc_power_idle_req)); + + noc_power_idle_ack = ioread32((void *)((unsigned long)adapter->pmctrl_io_addr + adapter->pmctrl_reg_offset.noc_power_idle_ack)) & CONFIG_NOC_POWER_IDLEACK_0_BIT9; + noc_power_idle_stat = ioread32((void *)((unsigned long)adapter->pmctrl_io_addr + adapter->pmctrl_reg_offset.noc_power_idle_stat)) & CONFIG_NOC_POWER_IDLE_0_BIT9; + + printk(KERN_DEBUG"[%s]: noc_power_idle_ack:%d, noc_power_idle_stat:%d\n", __func__, noc_power_idle_ack, noc_power_idle_stat); + + /* unmask irq*/ + iowrite32(ICS_IRQ_UNMASK_NO_SECURITY, (void *)((unsigned long)(adapter->ics_irq_io_addr) + adapter->irq_reg_offset.ics_irq_mask_ns)); + + if (ipu_clock_set_rate(&adapter->clk, adapter->clk.start_rate)) { + /* not return even set rate error, because reset process should finish */ + printk(KERN_DEBUG"[%s]: ipu_clock_set_rate fail, ignore\n", __FUNCTION__); + } + + mutex_lock(&adapter->bandwidth_lmt_mutex); + reg_bandwidth = ipu_bandwidth_get_lmt(adapter->clk.curr_rate, adapter->feature_tree.ipu_bandwidth_lmt); + if (reg_bandwidth) { + ipu_bandwidth_set_lmt(reg_bandwidth, ICS_NOC_BUS_CONFIG_SATURATION_NORMAL, adapter->feature_tree.ipu_bandwidth_lmt); + } + mutex_unlock(&adapter->bandwidth_lmt_mutex); + +#ifdef IPU_SMMU_ENABLE + ipu_smmu_init(adapter->smmu_ttbr0, + (unsigned long)adapter->smmu_rw_err_phy_addr, adapter->feature_tree.smmu_port_select, adapter->feature_tree.smmu_mstr_hardware_start); +#endif + +#ifdef CAMBRICON_IPU_IRQ + ipu_interrupt_init(); +#endif + + printk(KERN_DEBUG"[%s]: ipu_recover finished!\n", __func__); + mutex_unlock(&adapter->reset_mutex); +} + +#ifdef CAMBRICON_IPU_IRQ + +static bool cambricon_ipu_workqueue(void) { + taskElement Element; + struct timeval tv1, tv2; + unsigned int elapse; + + /* fetch a compute_task in the head of kfifo, need update computed_task_cnt */ + adapter->computed_task_cnt++; + + /* ipu irq comes means the first element of FIFO_TaskElements is finished + and this task must be compute task, so remove it from queue. */ + if (kfifo_get(&FIFO_TaskElements, &Element)) { + PRINT_TASK(Element); + up(&(adapter->task_fifo_sem)); + if (CHECK_TASK_TYPE(Element.taskType)) { + printk(KERN_ERR "[%s]: IPU_ERROR:Get error type of first task from kfifo\n", __func__); + return false; + } + } else { + printk(KERN_ERR "[%s]: IPU_ERROR:Get element from kfifo faild.\n", __func__); + return false; + } + + if (!kfifo_is_empty(&FIFO_TaskElements)) { + if (kfifo_peek(&FIFO_TaskElements, &Element)) {/*to check next taskType*/ + PRINT_TASK(Element); + if (CHECK_TASK_TYPE(Element.taskType)) { + printk(KERN_ERR "[%s]: IPU_ERROR:Get error type of next task from kfifo\n", __func__); + return false; + } + do_gettimeofday(&tv1); + /* handle sync task, until queue head is compute task or queue is empty */ + while (IPU_SYNC_TASK == Element.taskType) { + /* wakeup user process */ + if (NULL != Element.ptaskFlag) + *Element.ptaskFlag = IPU_TASK_FLAG_SYNC_DONE; + wake_up_interruptible(&sync_wq); + if (!kfifo_get(&FIFO_TaskElements,&Element)) + printk(KERN_ERR "[%s]: IPU_ERROR:FIFO_TaskElements is empty\n", __func__); + up(&(adapter->task_fifo_sem)); + /*to check next taskType*/ + if (!kfifo_peek(&FIFO_TaskElements, &Element)) { + break; + } + PRINT_TASK(Element); + + if (CHECK_TASK_TYPE(Element.taskType)) { + printk(KERN_ERR "[%s]: IPU_ERROR:Get error sync task type from kfifo\n", __func__); + return false; + } + } + + do_gettimeofday(&tv2); + + elapse = (tv2.tv_sec - tv1.tv_sec)*1000 + (tv2.tv_usec - tv1.tv_usec)/1000; + if (elapse > 1) { + printk(KERN_DEBUG"[%s]: elapse=%d\n",__func__ , elapse); + } + } else { + printk(KERN_ERR "[%s]: IPU_ERROR:Peek element from kfifo failed while fifo is not empty\n", __func__); + return false; + } + } + return true; +} + +static void ipu_level1_irq_handler(void) +{ + unsigned int lv1_int_status; + unsigned int fault_status; + int loop_cnt = 0; + unsigned int detect_fault = 0; + + if (!adapter->feature_tree.level1_irq) { + printk(KERN_ERR"[%s]: IPU_ERROR:unsupport op in this platform!\n", __func__); + return; + } + + lv1_int_status = ioread32((void *)((unsigned long)adapter->config_reg_virt_addr + IPU_STATUS_REG_FINISH)); + + while(lv1_int_status){ + if (loop_cnt > 3) { + printk(KERN_ERR"[%s]: lv1_int loop timeout\n", __func__); + break; + } + printk(KERN_DEBUG"[%s]: lv1_int_status=0x%x\n", __func__, lv1_int_status); + + if (lv1_int_status & INTERRUPTINST_INTERRUPT_MASK) { + detect_fault = detect_fault | INTERRUPTINST_INTERRUPT_MASK; + fault_status = ioread32((void *)((unsigned long)adapter->config_reg_virt_addr + IPU_INTERRUPT_INST_REG)); + iowrite32(0, (void *)((unsigned long)(adapter->config_reg_virt_addr) + IPU_INTERRUPT_INST_REG)); + printk(KERN_ERR"[%s]: IPU_ERROR:INTERRUPTINST_INTERRUPT error, fault_status=0x%x\n", __func__, fault_status); + } + if (lv1_int_status & CT_INTERRUPT_MASK) { + /* do_interruptinst_interrupt_service(); */ + detect_fault = detect_fault | CT_INTERRUPT_MASK; + fault_status = ioread32((void *)((unsigned long)adapter->config_reg_virt_addr + IPU_ID_REG)); + iowrite32(0, (void *)((unsigned long)(adapter->config_reg_virt_addr) + IPU_ID_REG)); + printk(KERN_ERR"[%s]: IPU_ERROR:CT_INTERRUPT error, fault_status=0x%x\n", __func__, fault_status); + } + if (lv1_int_status & IPU_CONTROL_INTERRUPT_MASK) { + /* do_ipu_control_interrupt_service(); */ + detect_fault = detect_fault | IPU_CONTROL_INTERRUPT_MASK; + fault_status = ioread32((void *)((unsigned long)adapter->config_reg_virt_addr + IPU_CONTROL_ID_REG)); + iowrite32(0, (void *)((unsigned long)(adapter->config_reg_virt_addr) + IPU_CONTROL_ID_REG)); + printk(KERN_ERR"[%s]: IPU_ERROR:IPU_CONTROL_INTERRUPT error, fault_status=0x%x\n", __func__, fault_status); + } + /* WATCH_DOG */ + if (lv1_int_status & WATCH_DOG_MASK) { + /* do_watch_dog_interrupt_service();*/ + detect_fault = detect_fault | WATCH_DOG_MASK; + fault_status = ioread32((void *)((unsigned long)adapter->config_reg_virt_addr + IPU_WATCH_DOG_REG)); + iowrite32(0, (void *)((unsigned long)(adapter->config_reg_virt_addr) + IPU_WATCH_DOG_REG)); + printk(KERN_ERR"[%s]: IPU_ERROR:WATCH_DOG error, fault_status=0x%x\n", __func__, fault_status); + } + if (lv1_int_status & IS_NORMAL_FINISH_MASK) { + iowrite32(IPU_STATUS_UNFINISH, (void *)((unsigned long)adapter->config_reg_virt_addr + IPU_STATUS_REG)); + } + iowrite32(lv1_int_status, (void *)((unsigned long)adapter->config_reg_virt_addr + IPU_STATUS_REG_FINISH)); + iowrite32(0, (void *)((unsigned long)adapter->config_reg_virt_addr + (IPU_STATUS_REG_FINISH + 0x4))); /* to clear finish reg high 32bit */ + lv1_int_status = ioread32((void *)((unsigned long)adapter->config_reg_virt_addr + IPU_STATUS_REG_FINISH)); + + loop_cnt++; + } + + if (detect_fault) { + if (adapter->feature_tree.ipu_reset_when_in_error && adapter->reset_va) { + ipu_reset_proc((unsigned int)adapter->reset_va); + } + } +} + +static void ipu_finish_irq_handler(void) +{ + unsigned long reg_virt_addr = (unsigned long)adapter->config_reg_virt_addr; + + /* clear ipu finished status */ + iowrite32(0, (void *)(reg_virt_addr + IPU_STATUS_REG)); + if (adapter->feature_tree.finish_irq_expand_ns) { + /* clear ipu finished status non-security irq, which is copied from ipu finished irq in non-security mode if unmask*/ + // fixme: add security and protected mode here in furture + if (adapter->feature_tree.level1_irq) { + iowrite32(ICS_IRQ_CLEAR_IRQ_LEVEL1_NS | ICS_IRQ_CLEAR_IRQ_NS, + (void *)((unsigned long)adapter->ics_irq_io_addr + adapter->irq_reg_offset.ics_irq_clr_ns)); + } else { + iowrite32(ICS_IRQ_CLEAR_IRQ_NS, (void *)((unsigned long)adapter->ics_irq_io_addr + adapter->irq_reg_offset.ics_irq_clr_ns)); + } + } +} + +static void ipu_core_irq_handler(void) +{ + ipu_finish_irq_handler(); + + if (adapter->feature_tree.level1_irq) { + ipu_level1_irq_handler(); + } else { + /* clear ipu finished status */ + iowrite32(IPU_STATUS_UNFINISH, (void *)((unsigned long)adapter->config_reg_virt_addr + IPU_STATUS_REG)); + } +} + +/* + * A very tiny interrupt handler. It runs with interrupts disabled, + * but there is possibility of conflicting with operating register + * at the same time in two different CPUs. So we need to serialize + * accesses to the chip with the ipu_lock spinlock. + */ +static irqreturn_t ipu_interrupt_handler(int irq, void *dev) +{ + bool ipu_smmu_err_isr; + struct cambricon_ipu_private *pdev; + + printk(KERN_DEBUG"[%s]: start\n", __func__); + if (dev == NULL) { + printk(KERN_ERR"[%s]: IPU_ERROR:no device\n", __func__); + return IRQ_HANDLED; + } + + pdev = container_of((struct cdev *)dev, struct cambricon_ipu_private, cdev); + if (!pdev || pdev != adapter) { + printk(KERN_ERR"[%s]: IPU_ERROR:error dev\n", __func__); + return IRQ_HANDLED; + } + + mutex_lock(&adapter->power_mutex); + if (!adapter->ipu_power_up) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu is power down, ignore\n", __func__); + mutex_unlock(&adapter->power_mutex); + return IRQ_HANDLED; + } + + ipu_watchdog_stop(&adapter->reset_wtd); + if (adapter->feature_tree.performance_monitor) { + performance_monitor_get_stat(); + performance_monitor_close(); + } + + mutex_lock(&adapter->stat_mutex); + ipu_smmu_err_isr = ipu_smmu_interrupt_handler(&adapter->stat.smmu_irq_count); + mutex_unlock(&adapter->stat_mutex); + + if (ipu_smmu_err_isr) { + if (adapter->feature_tree.ipu_reset_when_in_error && adapter->reset_va) { + // rdr_system_error((unsigned int)MODID_NPU_EXC_INTERRUPT_ABNORMAL, 0, 0); + ipu_reset_proc((unsigned int)adapter->reset_va); //lint !e570 + } + } else { + ipu_core_irq_handler(); + if (!cambricon_ipu_workqueue()) { + printk(KERN_ERR"[%s]: IPU_ERROR:cambricon_ipu_workqueue return false\n", __func__); + mutex_unlock(&adapter->power_mutex); + return IRQ_HANDLED; + } + } + + /*Start next task*/ + if (!kfifo_is_empty(&FIFO_TaskElements)) { + /* handle compute task */ + printk(KERN_DEBUG"[%s]: to start ipu\n", __func__); + start_ipu_workqueue(); + } else { + printk(KERN_DEBUG"[%s]: to stop ipu\n", __func__); + if (stop_ipu()) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu_power_down fail, FATAL\n", __func__); + } + } + mutex_unlock(&adapter->power_mutex); + + printk(KERN_DEBUG"[%s]: done\n", __func__); + return IRQ_HANDLED; +} +#endif + +/* here void (*function)(unsigned long) is a standard Linux timer callback function, + it will register as a callback func for ipu resume watchdog */ +static int ipu_reset(void *arg) +{ + static unsigned long last_computed_task = 0; + + #ifdef CONFIG_HUAWEI_DSM + char register_info_flag[] = "NULL"; + char *perr = register_info_flag; + #endif /* CONFIG_HUAWEI_DSM */ + + UNUSED_PARAMETER(arg); + + while (1) { + if (down_interruptible(&adapter->reset_wtd.sem)) { + continue; + } + + mutex_lock(&adapter->power_mutex); + + /* WTD time out, report to DSM */ + #ifdef CONFIG_HUAWEI_DSM + if (last_computed_task != adapter->computed_task_cnt){ + ipu_smmu_dump_strm(); + perr = register_info; + } + #endif /* CONFIG_HUAWEI_DSM */ + + DSM_AI_KERN_ERROR_REPORT(DSM_AI_KERN_WTD_TIMEOUT_ERR_NO, + "IPU soft watchdog timeout, ipu_status=%d, ttbr0=%x, inst_set=%d, offchip{set=%x, base=%x}, last_computed_task=%d, register_info=%s.\n", + adapter->ipu_power_up, adapter->smmu_ttbr0, adapter->boot_inst_set.boot_inst_recorded_is_config, adapter->boot_inst_set.access_ddr_addr_is_config, + adapter->boot_inst_set.ipu_access_ddr_addr, adapter->computed_task_cnt, perr); + if (false == adapter->ipu_power_up) { + printk(KERN_ERR"[%s]: IPU_ERROR: ipu is power off, can not resume\n", __func__); + mutex_unlock(&adapter->power_mutex); + continue; + } + + if (last_computed_task != adapter->computed_task_cnt) { + /* get IPU status in adapter */ + printk(KERN_ERR"[%s]: ipu(status=%d) dead, ttbr0=%lx, inst_set=%d, offchip{set=%x, base=%x}, curr_frame=0x%lx\n", + __func__, adapter->ipu_power_up, adapter->smmu_ttbr0, adapter->boot_inst_set.boot_inst_recorded_is_config, + adapter->boot_inst_set.access_ddr_addr_is_config, adapter->boot_inst_set.ipu_access_ddr_addr, adapter->computed_task_cnt); + +#ifdef CONFIG_HISI_IPU_MNTN + /* get clock and power status in register */ + ipu_reg_info.peri_reg.peri_stat = ioread32((void *)adapter->peri_io_addr + adapter->peri_reg_offset.peristat7); + ipu_reg_info.peri_reg.ppll_select = ioread32((void *)adapter->peri_io_addr + adapter->peri_reg_offset.clkdiv8); + ipu_reg_info.peri_reg.power_stat = ioread32((void *)adapter->peri_io_addr + adapter->peri_reg_offset.perpwrstat); + ipu_reg_info.peri_reg.power_ack = ioread32((void *)adapter->peri_io_addr + adapter->peri_reg_offset.perpwrack); + ipu_reg_info.peri_reg.reset_stat = ioread32((void *)adapter->media2_io_addr + adapter->media2_reg_offset.perrststat0); + ipu_reg_info.peri_reg.perclken0 = ioread32((void *)adapter->media2_io_addr + adapter->media2_reg_offset.perclken0); + ipu_reg_info.peri_reg.perstat0 = ioread32((void *)adapter->media2_io_addr + adapter->media2_reg_offset.perstat0); + + printk(KERN_ERR"[%s]: peri_stat=%x, ppll_select=%x, power_stat=%x, power_ack=%x, reset_stat=%x, perclken=%x, perstat=%x\n", + __func__, + ipu_reg_info.peri_reg.peri_stat, + ipu_reg_info.peri_reg.ppll_select, + ipu_reg_info.peri_reg.power_stat, + ipu_reg_info.peri_reg.power_ack, + ipu_reg_info.peri_reg.reset_stat, + ipu_reg_info.peri_reg.perclken0, + ipu_reg_info.peri_reg.perstat0); +#endif + + /* get memory info in register */ + ipu_smmu_dump_strm(); + // rdr_system_error((unsigned int)MODID_NPU_EXC_DEAD, 0, 0); + } + + /* reset ipu */ + ipu_reset_proc((unsigned int)adapter->reset_va); + + /* ipu watchdog comes means the first element of FIFO_TaskElements is invalid and abandoned. + so remove it from queue and prepare for next element. */ + if (last_computed_task == adapter->computed_task_cnt) { + printk(KERN_ERR"[%s]: IPU_ERROR:current frame=0x%lx can not resume, abandon\n", __func__, last_computed_task); + if (!cambricon_ipu_workqueue()) { + printk(KERN_ERR"[%s]: IPU_ERROR:cambricon_ipu_workqueue return false\n", __func__); + mutex_unlock(&adapter->power_mutex); + continue; + } + } + + /* start next frame */ + if (!kfifo_is_empty(&FIFO_TaskElements)) { + /* handle compute task */ + printk(KERN_DEBUG"[%s]: handle compute task", __func__); + start_ipu_workqueue(); + } else { + printk(KERN_DEBUG"[%s]: kfifo is empty, stop ipu\n", __func__); + if (stop_ipu()) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu_power_down fail, FATAL\n", __func__); + } + } + + last_computed_task = adapter->computed_task_cnt; + mutex_unlock(&adapter->power_mutex); + } + + printk(KERN_DEBUG"[%s]: done\n", __func__); + return 0; /*lint !e527*/ +} + +void ipu_reset_irq(unsigned long para) +{ + UNUSED_PARAMETER(para); + if (adapter->reset_wtd.task) { + up(&adapter->reset_wtd.sem); + } +} + +/* this func not use power mutex, so its caller must use power mutex */ +int ipu_power_up(void) +{ + struct timeval tv1, tv2, tv3, tv4, tv5; + int ret; + unsigned int reg_bandwidth; + +#ifdef IPU_SMMU_ENABLE + if (0 == adapter->smmu_ttbr0) { + printk(KERN_ERR"[%s]: IPU_ERROR:FATAL, adapter->smmu_ttbr0 is NULL\n", __func__); + return -EFAULT; + } +#endif + + if (adapter->ipu_power_up) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu is already power-up\n", __func__); + return -EBUSY; + } + + do_gettimeofday(&tv1); + + /*ICS power on*/ + ret = regulator_ip_vipu_enable(); + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:regulator_ip_vipu_enable failed\n", __func__); + return ret; + } + + do_gettimeofday(&tv2); + + /* start ipu clock */ + ret = ipu_clock_start(&adapter->clk); + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:IPU clock start failed, power down ipu\n", __func__); + if (regulator_ip_vipu_disable()) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu power down fail\n", __func__); + } + + /* if more return add in this func, add ipu power-down function before "return" */ + return ret; + } + + if (adapter->feature_tree.ipu_bandwidth_lmt) { + mutex_lock(&adapter->bandwidth_lmt_mutex); + reg_bandwidth = ipu_bandwidth_get_lmt(adapter->clk.curr_rate, adapter->feature_tree.ipu_bandwidth_lmt); + if (reg_bandwidth) { + ipu_bandwidth_set_lmt(reg_bandwidth, ICS_NOC_BUS_CONFIG_SATURATION_NORMAL, adapter->feature_tree.ipu_bandwidth_lmt); + } + mutex_unlock(&adapter->bandwidth_lmt_mutex); + } + + do_gettimeofday(&tv3); + +#ifdef IPU_SMMU_ENABLE + ipu_smmu_init(adapter->smmu_ttbr0, + (unsigned long)adapter->smmu_rw_err_phy_addr, adapter->feature_tree.smmu_port_select, adapter->feature_tree.smmu_mstr_hardware_start); + + mutex_lock(&adapter->stat_mutex); + if (adapter->smmu_stat_en) { + ipu_smmu_reset_statistic(); + } + mutex_unlock(&adapter->stat_mutex); + + do_gettimeofday(&tv4); +#endif + +#ifdef CAMBRICON_IPU_IRQ + ipu_interrupt_init(); + + do_gettimeofday(&tv5); +#endif + + if (((tv5.tv_sec - tv1.tv_sec)*1000000 + (tv5.tv_usec - tv1.tv_usec)) > 1000) { + printk(KERN_WARNING"[%s]: IPU_WARN: ipu PU/CLK/SMMU_INIT/ISR_INIT elapse is %ld/%ld/%ld/%ld usec\n", __func__, + (tv2.tv_sec - tv1.tv_sec)*1000000 + (tv2.tv_usec - tv1.tv_usec), + (tv3.tv_sec - tv2.tv_sec)*1000000 + (tv3.tv_usec - tv2.tv_usec), + (tv4.tv_sec - tv3.tv_sec)*1000000 + (tv4.tv_usec - tv3.tv_usec), + (tv5.tv_sec - tv4.tv_sec)*1000000 + (tv5.tv_usec - tv4.tv_usec)); + } + + adapter->ipu_power_up = true; + + return 0; +} + +/* ipu char device ops function implementation, inode:node of file, filp: pointer of file */ +static int ipu_open(struct inode *inode, struct file *filp) +{ + struct cambricon_ipu_private *dev; + + if (!inode || !filp){ + printk(KERN_ERR"[%s]: IPU_ERROR:invalid input parameter !\n", __func__); + return -EINVAL; + } + + if (!adapter) { + printk(KERN_ERR"[%s]: IPU_ERROR:FATAL: adapter is NULL\n", __func__); + return -EFAULT; + } + + mutex_lock(&adapter->open_mutex); + + if (adapter->ipu_device_opened) { + printk(KERN_ERR"[%s]: IPU_ERROR:IPU device has already opened !\n", __func__); + mutex_unlock(&adapter->open_mutex); + return -EBUSY; + } else { + adapter->ipu_device_opened = true; + } + + /* find offset of "cdev" in "struct cambricon_ipu_private" */ + dev = container_of(inode->i_cdev, struct cambricon_ipu_private, cdev);/*lint !e826*/ + + /* save dev info to "filp->private_data", it will be used later */ + filp->private_data = dev; + + ipu_mem_mngr_init(); + + /* set IPU clock to 900M */ + iowrite32(0xF0001000, (void *)((unsigned long)adapter->peri_io_addr + adapter->peri_reg_offset.clkdiv8)); + + /* clk ics gt */ + iowrite32(0x40004000, (void *)((unsigned long)adapter->peri_io_addr + adapter->peri_reg_offset.clkdiv18)); + + /* select clk div of 1 */ + iowrite32(0x7E000000, (void *)((unsigned long)adapter->peri_io_addr + 0xE4)); + + /* clk ics gt in media2 CRG */ + iowrite32(0x00000004, (void *)((unsigned long)adapter->media2_io_addr + adapter->media2_reg_offset.peren0)); + + /* set VCODECBUS to 450M */ + iowrite32(0x003F0001, (void *)((unsigned long)adapter->peri_io_addr + 0xBC)); + iowrite32(0x000F0001, (void *)((unsigned long)adapter->peri_io_addr + adapter->peri_reg_offset.clkdiv8)); + + mutex_lock(&adapter->stat_mutex); + adapter->smmu_stat_en = false; + memset(&adapter->stat, 0, sizeof(adapter->stat)); + + mutex_unlock(&adapter->stat_mutex); + mutex_unlock(&adapter->open_mutex); + + ipu_reset_proc((unsigned int)adapter->reset_va); // reset when ipu device open + printk(KERN_DEBUG"[%s]: IPU device open success!\n", __func__); + // IOREAD_RANGE(adapter->ics_irq_io_addr, 0xff); + return 0;/*lint !e454*/ +} + +/* this func not use power mutex, so its caller must use power mutex */ +int ipu_power_down(void) +{ + int ret; + struct timeval tv1, tv2, tv3, tv4, tv5; + + if (!adapter->ipu_power_up) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu is already power-down, ignore\n", __func__); + return -EBUSY; + } + + do_gettimeofday(&tv1); +#ifdef IPU_SMMU_ENABLE + ipu_smmu_deinit(); + + mutex_lock(&adapter->stat_mutex); + if (adapter->smmu_stat_en) { + ipu_smmu_record_statistic(&adapter->stat); + } + mutex_unlock(&adapter->stat_mutex); + +#endif + + do_gettimeofday(&tv2); +#ifdef CAMBRICON_IPU_IRQ + ipu_interrupt_deinit(); +#endif + + /* Set clock rate to default(which should always be generated from PPLL0) */ + do_gettimeofday(&tv3); + if (ipu_clock_set_rate(&adapter->clk, adapter->clk.stop_rate)) { + /* not return even set rate error, because power down process should finish */ + printk(KERN_WARNING"[%s]: ipu_clock_set_rate fail, ignore\n", __FUNCTION__); + } + + /* STOP ipu clock */ + ipu_clock_stop(&adapter->clk); + + /*ICS power down*/ + do_gettimeofday(&tv4); + ret = regulator_ip_vipu_disable(); + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu power down fail\n", __func__); + return -EBUSY; + } + + do_gettimeofday(&tv5); + + if (((tv5.tv_sec - tv1.tv_sec)*1000000 + (tv5.tv_usec - tv1.tv_usec)) > 1000) { + printk(KERN_WARNING"[%s]: IPU_WARN: ipu SMMU_DEINT/INTR CLR/CLK STOP/PD elapse is %ld/%ld/%ld/%ld usec\n", __func__, + (tv2.tv_sec - tv1.tv_sec)*1000000 + (tv2.tv_usec - tv1.tv_usec), + (tv3.tv_sec - tv2.tv_sec)*1000000 + (tv3.tv_usec - tv2.tv_usec), + (tv4.tv_sec - tv3.tv_sec)*1000000 + (tv4.tv_usec - tv3.tv_usec), + (tv5.tv_sec - tv4.tv_sec)*1000000 + (tv5.tv_usec - tv4.tv_usec)); + } + + adapter->ipu_power_up = false; + + return 0; +} + +/* ipu device release and power down */ +static int ipu_release(struct inode *inode, struct file *filp) +{ + struct cambricon_ipu_private *dev; + int ret; + struct irq_data *desc = irq_get_irq_data(adapter->irq); + struct irq_common_data *d = desc ? desc->common : NULL; + + UNUSED_PARAMETER(inode); + + if (!inode || !filp){ + printk(KERN_ERR"[%s]: IPU_ERROR:input parameter inode or filp is invalid !\n", __func__); + return -EINVAL; + } + + if (!adapter) { + printk(KERN_ERR"[%s]: IPU_ERROR:FATAL: adapter is NULL\n", __func__); + return -EFAULT; + } + + mutex_lock(&adapter->open_mutex); + if (!adapter->ipu_device_opened) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu device is already closed\n", __func__); + mutex_unlock(&adapter->open_mutex); + return -EBUSY; + } + + dev = filp->private_data; + + if (adapter == dev) { + kfifo_reset(&FIFO_TaskElements); + if (d && (d->state_use_accessors & IRQD_IRQ_DISABLED)) { + enable_irq(adapter->irq); + } + } else { + printk(KERN_ERR"[%s]: IPU_ERROR:No IPU device! input dev is %pK\n", __func__, (void *)dev); + mutex_unlock(&adapter->open_mutex); + return -EINVAL; + } + + ipu_watchdog_stop(&adapter->reset_wtd); + + ret = stop_ipu(); + if (ret) { + /* not return here, ignore, to release other resource */ + printk(KERN_ERR"[%s]: IPU_ERROR:ipu_power_down fail\n", __func__); + } + + ipu_mem_mngr_deinit(&adapter->reset_va); + + mutex_lock(&adapter->boot_inst_set.boot_mutex); + adapter->boot_inst_set.access_ddr_addr_is_config = false; + adapter->boot_inst_set.boot_inst_recorded_is_config = false; + mutex_unlock(&adapter->boot_inst_set.boot_mutex); + + adapter->ipu_device_opened = false; + mutex_unlock(&adapter->open_mutex);/*lint !e455*/ + + printk(KERN_DEBUG"[%s]: ipu release succeed\n", __func__); + return 0; +} + +static long ipu_read_dword(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + long read_value; + + UNUSED_PARAMETER(out_params); + + if (arg != IPU_VERSION_REG) { + printk(KERN_ERR"[%s]: IPU_ERROR:Error can not read offset=0x%pK, it is an invalid offset to read\n", __func__, (void *)arg); + return -EINVAL; + } + + mutex_lock(&adapter->power_mutex); + + if (!adapter->ipu_power_up) { + printk(KERN_ERR"[%s]: IPU_ERROR:this work is not allowed when IPU is power-down, FATAL and ignore\n", __func__); + mutex_unlock(&adapter->power_mutex); + return -EBUSY; + } + + read_value = ioread32((void *)((unsigned long)dev->config_reg_virt_addr + arg)); + + printk(KERN_ERR"[%s]: Read CONFIG REG dword offset 0x%p, value is 0x%x\n", + __func__, (void *)arg, (unsigned int)read_value); + + mutex_unlock(&adapter->power_mutex); + return read_value; +} + +static long ipu_write_dword(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + const int offset = 0; + const int data = 1; + unsigned long in[2]; + + UNUSED_PARAMETER(out_params); + + if (!arg) { + printk(KERN_ERR"[%s]: IPU_ERROR:input parameter arg is NULL, FATAL arg and ignore\n", __func__); + return -EINVAL; + } + + memset(in, 0, sizeof(in)); /* [false alarm]:memset an array */ + + if (copy_from_user(in, (void __user *)arg, sizeof(in))) { + printk(KERN_ERR"[%s]: IPU_ERROR:copy arg failed!\n", __func__); + return -EFAULT; + } + + printk(KERN_DEBUG"[%s]: Write CONFIG REG dword offset 0x%pK, value is 0x%lx\n", __func__, (void *)in[offset], in[data]); + + mutex_lock(&adapter->power_mutex); + if (adapter->ipu_power_up) { + if ((IPU_START_REG == in[offset]) || (IPU_STATUS_REG == in[offset]) || (IPU_BASE_ADDR_REG == in[offset])) { + if (IPU_START_REG == in[offset] && IPU_TO_START == in[data]) { + if (adapter->feature_tree.performance_monitor) { + performance_monitor_open(); + } + ipu_watchdog_start(&adapter->reset_wtd, WATCHDOG_TIMEOUT_THD_MS); + } + iowrite32(in[data], (void *)((unsigned long)dev->config_reg_virt_addr + in[offset])); + } else { + printk(KERN_ERR"[%s]: IPU_ERROR:error offset when ipu on, offset=0x%pK, data=0x%lx\n", __func__, (void *)in[offset], in[data]); + mutex_unlock(&adapter->power_mutex); + return -EFAULT; + } + } else { + if (IPU_BASE_ADDR_REG == in[offset]) { + mutex_lock(&adapter->boot_inst_set.boot_mutex); + adapter->boot_inst_set.ipu_access_ddr_addr = in[data]; + adapter->boot_inst_set.access_ddr_addr_is_config = true; + mutex_unlock(&adapter->boot_inst_set.boot_mutex); + } else { + printk(KERN_ERR"[%s]: IPU_ERROR:error offset when ipu off, offset=0x%pK, data=0x%lx\n", __func__, (void *)in[offset], in[data]); + mutex_unlock(&adapter->power_mutex); + return -EFAULT; + } + } + + mutex_unlock(&adapter->power_mutex); + return 0; +} + +static long ipu_set_map(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + long ret_value; + struct map_data map_data; + + UNUSED_PARAMETER(dev); + + if (!arg) { + printk(KERN_ERR"[%s]: IPU_ERROR:input parameter arg is NULL, FATAL arg and ignore\n", __func__); + return -EINVAL; + } + + if (copy_from_user(&map_data, (void __user *)arg, sizeof(map_data))) { + printk(KERN_ERR"[%s]: IPU_ERROR:copy arg failed!\n", __func__); + return -EFAULT; + } + + ret_value = ipu_smmu_map(&map_data); + if (ret_value) { + printk(KERN_ERR"[%s]: IPU_ERROR:IPU_ERROR:ipu_smmu_map failed!\n", __func__); + return -EFAULT; + } + + if (copy_to_user((void __user *)arg, &map_data, sizeof(map_data))) { + printk(KERN_ERR"[%s]: IPU_ERROR:copy_to_user failed!\n", __func__); + return -EFAULT; + } + + out_params->memory_node = ipu_mem_mngr_add(&map_data); + return ret_value; +} + +static long ipu_set_reset_virt_addr(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + long ret; + struct memory_manage_node *node; + + if (!arg) { + printk(KERN_ERR"[%s]: IPU_ERROR:input parameter arg is NULL, FATAL arg and ignore\n", __func__); + return -EINVAL; + } + + if (adapter->reset_va) { + printk(KERN_ERR"[%s]: IPU_ERROR:virtual address for reset is exist, ignore\n", __func__); + return -EINVAL; + } + + ret = ipu_set_map(dev, arg, out_params); + + if ((0 == ret) && out_params->memory_node) { + node = (struct memory_manage_node *)out_params->memory_node; + adapter->reset_va = node->map.format.iova_start; + } else { + printk(KERN_ERR"[%s]: IPU_ERROR:remap failed!\n", __func__); + } + + return ret; +} + +static long ipu_set_unmap(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + struct map_data map_data; + + UNUSED_PARAMETER(dev); + UNUSED_PARAMETER(out_params); + + if (!arg) { + printk(KERN_ERR"[%s]: IPU_ERROR:input parameter arg is NULL, FATAL arg and ignore\n", __func__); + return -EINVAL; + } + + if (copy_from_user(&map_data, (void __user *)arg, sizeof(map_data))) { + printk(KERN_ERR"[%s]: IPU_ERROR:copy_from_user failed!\n", __func__); + return -EFAULT; + } + + if (ipu_mem_mngr_del(&map_data)) { + printk(KERN_ERR"[%s]: IPU_ERROR:invalid map data to unmap\n", __func__); + return -EINVAL; + } + + if (ipu_smmu_unmap(&map_data)) { + printk(KERN_ERR"[%s]: IPU_ERROR:unmap failed\n", __func__); + return -EINVAL; + } + + if (adapter->reset_va == map_data.format.iova_start) { + adapter->reset_va = 0; + } + + return 0; +} + +static long ipu_set_reset_statistic(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + UNUSED_PARAMETER(dev); + UNUSED_PARAMETER(arg); + UNUSED_PARAMETER(out_params); + + mutex_lock(&adapter->stat_mutex); + adapter->smmu_stat_en = true; + mutex_unlock(&adapter->stat_mutex); + return 0; +} + +static long ipu_set_report_statistic(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + UNUSED_PARAMETER(dev); + UNUSED_PARAMETER(out_params); + + if (!arg) { + printk(KERN_ERR"[%s]: IPU_ERROR:input parameter arg is NULL, FATAL arg and ignore\n", __func__); + return -EINVAL; + } + + mutex_lock(&adapter->stat_mutex); + + if (!adapter->smmu_stat_en) { + printk(KERN_ERR"[%s]: IPU_ERROR:report statistic fail, for have not set ipu smmu stat enable\n", __func__); + mutex_unlock(&adapter->stat_mutex); + return -ENOMEM; + } + + if (copy_to_user((void __user *)arg, &adapter->stat, sizeof(adapter->stat))) { + printk(KERN_ERR"[%s]: IPU_ERROR:copy_to_user failed!\n", __func__); + mutex_unlock(&adapter->stat_mutex); + return -EFAULT; + } + + mutex_unlock(&adapter->stat_mutex); + + return 0; +} + +static long ipu_set_update_pte(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + UNUSED_PARAMETER(dev); + UNUSED_PARAMETER(arg); + UNUSED_PARAMETER(out_params); + + mutex_lock(&adapter->power_mutex); + if (!adapter->ipu_power_up) { + printk(KERN_ERR"[%s]: IPU_ERROR:this work is not allowed when IPU is power-down, FATAL and ignore\n", __func__); + mutex_unlock(&adapter->power_mutex); + return -EBUSY; + } + + ipu_smmu_pte_update(); + mutex_unlock(&adapter->power_mutex); + return 0; +} + +long ipu_set_profile(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + unsigned long profile = arg; + unsigned int clock; + + UNUSED_PARAMETER(dev); + UNUSED_PARAMETER(out_params); + + /* get rate by profile */ + switch (profile) { + case DEV_UNSET_PROFILE: + clock = adapter->clk.ipu_low; /* if profile not need IPU, set IPU clock as low */ + break; + case DEV_LOW_PROFILE: + clock = adapter->clk.ipu_low; + break; + case DEV_NORMAL_PROFILE: + clock = adapter->clk.ipu_middle; + break; + case DEV_HIGH_PROFILE: + clock = adapter->clk.ipu_high; + break; + default: + printk(KERN_ERR"[%s]: IPU_ERROR:profile=%lu error\n", __FUNCTION__, profile); + return -EINVAL; + } + + return ipu_clock_set_start_rate(&adapter->clk, clock); +} + +static ioctl_cb ipu_obtain_ioctl_callback(unsigned int cmd) +{ + unsigned int cnt; + unsigned int size = sizeof(ipu_ioctl_maps) / sizeof(ipu_ioctl_maps[0]); + + for (cnt = 0; cnt < size; cnt++) { + if (ipu_ioctl_maps[cnt].cmd == cmd) { + printk(KERN_INFO "[%s], cmd %x, func %s\n", __func__, cmd, ipu_ioctl_maps[cnt].name); + return ipu_ioctl_maps[cnt].func; + } + } + + printk(KERN_ERR"[%s]: IPU_ERROR:error cmd=0x%x\n", __func__, cmd); + + return NULL; +} + +static int input_filp_check(struct file *filp, unsigned int cmd) +{ + struct cambricon_ipu_private *dev; + if (!filp){ + printk(KERN_ERR"[%s]: IPU_ERROR:input parameter filp is invalid !\n", __func__); + return -EINVAL; + } + dev = filp->private_data; + if (!dev || dev != adapter) { + printk(KERN_ERR"[%s]: IPU_ERROR:No IPU device! input dev is %pK\n", __func__, (void *)dev); + return -EINVAL; + } + + if (!dev->config_reg_virt_addr) { + printk(KERN_ERR"[%s]: IPU_ERROR:reg_virt_addr is invalid!\n", __func__); + return -EINVAL; + } + /* check whether cmd is valid */ + if (_IOC_TYPE(cmd) != MAGIC_NUM) { + printk(KERN_ERR"[%s]: IPU_ERROR:cmd is invalid!(not a MAGIC_NUM)\n", __func__); + return -EINVAL; + } + + if (_IOC_NR(cmd) > IPU_IOC_MAXNR) { + printk(KERN_ERR"[%s]: IPU_ERROR:cmd is invalid!(%d > IPU_IOC_MAXNR)\n", __func__, _IOC_NR(cmd)); + return -EINVAL; + } + + return 0; +} + +static long ipu_process_workqueue(struct cambricon_ipu_private *dev, unsigned long arg, struct ioctl_out_params *out_params) +{ + taskElement element; + taskFlag_t taskFlag = IPU_TASK_FLAG_NONE; + long ret_value = 0; + + if (!arg) { + printk(KERN_ERR"[%s]: IPU_ERROR:input parameter arg is NULL, FATAL arg and ignore\n", __func__); + return -EINVAL; + } + + if(down_interruptible(&(dev->task_fifo_sem))){ + printk(KERN_ERR "[%s]Request task_fifo_sem failed\n", __func__); + return -EBUSY; + } + + /*Caution : Already obtain task_fifo_sem !!*/ + disable_irq(adapter->irq); + + if (!copy_from_user(&element, (void __user *)arg, sizeof(element))) { + // todo: add more input-check at here!! + + printk(KERN_INFO "task element info, id: %ld, type: %x, instAddr: %lx, prior: %d\n", element.taskId, element.taskType, element.offchipInstAddr, element.prior); + + element.ptaskFlag = NULL;/*Ensure point correct*/ + mutex_lock(&adapter->power_mutex); + if (kfifo_is_empty(&FIFO_TaskElements)) { + /*First task is SYNC while fifo is empty should return user thread at once*/ + if (element.taskType != IPU_SYNC_TASK){ + kfifo_put(&FIFO_TaskElements, element); + printk(KERN_DEBUG "[%s]: IPU_COMPUTE_TASK to start ipu\n", __func__); + PRINT_TASK(element); + start_ipu_workqueue(); + } else { + up(&(dev->task_fifo_sem)); + } + } else { + if (unlikely(IPU_SYNC_TASK == element.taskType)) { + printk(KERN_DEBUG "[%s]: IPU_SYNC_TASK is waiting\n", __func__); + /*Set sync flag as wake up condition*/ + taskFlag = IPU_TASK_FLAG_SYNC_WAITING; + element.ptaskFlag = &taskFlag; + PRINT_TASK(element); + kfifo_put(&FIFO_TaskElements, element); + mutex_unlock(&adapter->power_mutex); + enable_irq(adapter->irq); + up(&(dev->config_reg_sem)); + /* sync task sleeping */ + wait_event_interruptible(sync_wq, IPU_TASK_FLAG_SYNC_DONE == taskFlag); + out_params->ret_directly = true; + return 0; + } else { + printk(KERN_DEBUG "[%s]: IPU_COMPUTE_TASK add to queue\n", __func__); + kfifo_put(&FIFO_TaskElements, element); + PRINT_TASK(element); + } + } + mutex_unlock(&adapter->power_mutex); + } else { + printk(KERN_ERR "[%s]: IPU_ERROR: Copy_from_user failed\n", __func__); + up(&(dev->task_fifo_sem)); + ret_value = -EINVAL; + } + + enable_irq(adapter->irq); + + performance_monitor_get_stat(); + + return ret_value; +} + +static long ipu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + ioctl_cb ioctl_callback; + long ret = -EINVAL; + struct cambricon_ipu_private *dev; + struct ioctl_out_params out_params; + + if (!adapter) { + printk(KERN_ERR"[%s]: IPU_ERROR:FATAL: adapter is NULL\n", __func__); + return -EFAULT; + } + + mutex_lock(&adapter->open_mutex); + if (!adapter->ipu_device_opened) { + printk(KERN_ERR"[%s]: IPU_ERROR:receiving IOCTL attack when ipu is not open, ignore\n", __func__); + mutex_unlock(&adapter->open_mutex); + return -EINVAL; + } + + if (input_filp_check(filp, cmd)) { + printk(KERN_ERR"[%s]:IPU_ERROR: input parameter filp is invalid !\n", __func__); + mutex_unlock(&adapter->open_mutex); + return -EINVAL; + } + + dev = filp->private_data; + + /* a "mutex signal" to guarantee only one process is working */ + if (down_interruptible(&dev->config_reg_sem)) { + printk(KERN_ERR"[%s]:IPU_ERROR: down_interruptible dev->config_reg_sem fail!\n", __func__); + mutex_unlock(&adapter->open_mutex); + return -ERESTARTSYS; + } + + ioctl_callback = ipu_obtain_ioctl_callback(cmd); + + out_params.memory_node = 0; + out_params.ret_directly = false; + + if (ioctl_callback) { + ret = ioctl_callback(dev, arg, &out_params); + } else { + printk(KERN_ERR"[%s]:IPU_ERROR: unknown cmd=0x%x\n", __func__, cmd); + } + + if (!out_params.ret_directly) { + up(&(dev->config_reg_sem)); + } + + mutex_unlock(&adapter->open_mutex); + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ipu_ioctl32(struct file *fd, unsigned int cmd, unsigned long arg) +{ + void *ptr_user = compat_ptr(arg); + + printk(KERN_DEBUG"[%s]: CMD %x ...\n", __func__, cmd); + return ipu_ioctl(fd, cmd, (unsigned long)ptr_user); +} +#endif + +static ssize_t ipu_write_when_ipu_down(struct cambricon_ipu_private *dev, const char __user *buf, size_t count, loff_t *f_pos) +{ + ssize_t ret_value; + unsigned long bytes_not_copied; + unsigned max_buff_size = dev->inst_ram_size; + + /* "sizeof" not need mutex_lock */ + if (max_buff_size > sizeof(adapter->boot_inst_set.boot_inst)) { + max_buff_size = sizeof(adapter->boot_inst_set.boot_inst); + } + + /* NOTE: here max_buff_size is a 32bit length data, and above judge can guarantee (*f_pos + count) < 2*max_buff_size + so OVERFLOW in "unsigned long" is impossible here */ + if((unsigned long)*f_pos + (unsigned long)count > (unsigned long)max_buff_size) { + printk(KERN_ERR"[%s]: IPU_ERROR:FATAL, count OVERFLOW, *f_pos=0x%pK, count=0x%x\n", __func__, (void *)*f_pos, (unsigned int)count); + return -EINVAL; + } + + mutex_lock(&adapter->boot_inst_set.boot_mutex); + if (adapter->boot_inst_set.boot_inst_recorded_is_config) { + printk(KERN_ERR"[%s]: IPU_WARN:boot_inst to overwrite old data\n", __func__); + } + + /* clean-up buffer and recv new data */ + memset(&adapter->boot_inst_set.boot_inst[0], 0, sizeof(adapter->boot_inst_set.boot_inst)); + bytes_not_copied = copy_from_user((void *)((unsigned long)&adapter->boot_inst_set.boot_inst[0] + (unsigned long)(*f_pos)), buf, count); + + if (bytes_not_copied) { + printk(KERN_ERR"[%s]: IPU_ERROR:Copy data from user failed, bytes_not_copied=0x%lx\n", __func__, bytes_not_copied); + + /* here type convert has no risk of type truncation, because the caller "ipu_write" had judged the value */ + ret_value = (ssize_t)count - (ssize_t)bytes_not_copied; + } else { + adapter->boot_inst_set.boot_inst_recorded_is_config = true; + + /* because above guarentees (*f_pos + count <= dev->inst_ram_size), dev->inst_ram_size is uint16, so "+" CAN NOT get overflow */ + adapter->boot_inst_set.boot_inst_size = ((unsigned int)count + (unsigned int)(*f_pos)); + ret_value = (ssize_t)count; + } + + mutex_unlock(&adapter->boot_inst_set.boot_mutex); + return ret_value; + +} + +/* CPU write inst to IPU SRAM */ +static ssize_t ipu_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) +{ + int ipu_power_up_flag; + ssize_t ret_value = 0; + struct cambricon_ipu_private *dev; + + if (!filp || !buf || !f_pos) { + printk(KERN_ERR"[%s]: IPU_ERROR:input parameter filp or buf is invalid !\n", __func__); + return -EINVAL; + } + + dev = filp->private_data; + + if (!dev || dev != adapter) { + printk(KERN_ERR"[%s]: IPU_ERROR:No IPU device! input dev is %pK\n", __func__, (void *)dev); + return -EINVAL; + } + + if (count == 0 || count > dev->inst_ram_size) { + printk(KERN_ERR"[%s]: IPU_ERROR:input parameter count is invalid!\n", __func__); + return -EINVAL; + } + + if (*f_pos >= dev->inst_ram_size || *f_pos < 0) { + printk(KERN_ERR"[%s]: IPU_ERROR:Write file position out of range!\n", __func__); + return -EINVAL; + } + + if (!adapter->inst_ram_virt_addr) { + printk(KERN_ERR"[%s]: IPU_ERROR:inst_ram_virt_addr is NULL\n", __func__); + return -EFAULT; + } + + mutex_lock(&adapter->power_mutex); + ipu_power_up_flag = adapter->ipu_power_up; + if (adapter->ipu_power_up) { + printk(KERN_ERR"[%s]: IPU_ERROR:unsupport to write ipu SRAM when it is powered up\n", __func__); + mutex_unlock(&adapter->power_mutex); + } else { + /* ipu off, write to temp buff "adapter->boot_inst_set.boot_inst" */ + mutex_unlock(&adapter->power_mutex); + ret_value = ipu_write_when_ipu_down(dev, buf, count, f_pos); + } + + if (ret_value < 0) { + printk(KERN_DEBUG"[%s]: write 0x%lx bytes, offset=0x%pK error, return is %ld\n", __func__, count, (void *)(*f_pos), ret_value); + } else { + printk(KERN_DEBUG"[%s]: write 0x%lx bytes, offset=0x%pK when ipu_power_up flag is %d\n", + __func__, ret_value, (void *)(*f_pos), ipu_power_up_flag); + } + + return ret_value; +} + +/* set file position in IPU where CPU to write */ +static loff_t ipu_llseek(struct file *filp, loff_t off, int whence) +{ + struct cambricon_ipu_private *dev; + loff_t pos; + + if (!filp) { + printk(KERN_ERR"[%s]: IPU_ERROR:input parameter filp is invalid !\n", __func__); + return -EINVAL; + } + + if (!adapter) { + printk(KERN_ERR"[%s]: IPU_ERROR:FATAL: adapter is NULL\n", __func__); + return -EFAULT; + } + + dev = filp->private_data; + if (!dev || dev != adapter) { + printk(KERN_ERR"[%s]: IPU_ERROR:No IPU device! input dev is %pK\n", __func__, (void *)dev); + return -EINVAL; + } + + /* covert unsigned int(32bit) to long(64bit), with no risk */ + if ((off > (signed long)dev->inst_ram_size) || (off < (-1 * (signed long)dev->inst_ram_size))) { + printk(KERN_ERR"[%s]: IPU_ERROR:invalid offset=0x%pK while inst_ram_size=0x%x\n", + __func__, (void *)off, dev->inst_ram_size); + return -EINVAL; + } + + if(down_interruptible(&dev->llseek_sem)) { + printk(KERN_ERR"[%s]:IPU_ERROR: down_interruptible llseek_sem fail!\n", __func__); + return -ERESTARTSYS; + } + + pos = filp->f_pos; + switch (whence) { + /* Set f_pos */ + case 0: /* SEEK_SET */ + pos = off; + break; + /* Move f_pos forward */ + case 1: /* SEEK_CUR */ + pos += off; + break; + /* More */ + case 2: /* SEEK_END */ + break; + /* Default Operation */ + default: + up(&dev->llseek_sem); + return -EINVAL; + } + + if ((pos >= dev->inst_ram_size) || (pos < 0)) { + printk(KERN_ERR"[%s]: IPU_ERROR:Move file position out of range!", __func__); + up(&dev->llseek_sem); + return -EINVAL; + } + + filp->f_pos = pos; + + up(&dev->llseek_sem); + return pos; +} + +/* Allocate ipu chrdev region ("dev/ipu") */ +static int cambricon_ipu_chrdev_region(dev_t *chrdev) +{ + int err = -1; + + /* Allocate char device region */ + if (ipu_major) { + *chrdev = MKDEV(ipu_major, ipu_minor); + err = register_chrdev_region(*chrdev, 1, IPU_NAME); + } else { + err = alloc_chrdev_region(chrdev, 0, 1, IPU_NAME); + } + if (err < 0) { + printk(KERN_ERR"[%s]:IPU_ERROR:alloc_chrdev_region fail!\n", __func__); + return err; + } + + ipu_major = MAJOR(*chrdev); + ipu_minor = MINOR(*chrdev); + + return err; +} + +/* probe() function for platform driver */ +static int cambricon_ipu_probe(struct platform_device *pdev) +{ + int err; + dev_t chrdev = 0; + struct device *temp = NULL; + struct resource *res, *res_cfg; + unsigned long size; + + printk(KERN_DEBUG"[%s]: Initializing IPU device!\n", __func__); + + /* Register AI dsm client */ + DSM_AI_REGISTER(); + + /* Allocate char device region */ + err = cambricon_ipu_chrdev_region(&chrdev); + if (err < 0) { + printk(KERN_ERR"[%s]: Failed to allocate device ID for IPU!\n", __func__); + goto fail; + } + + /* create and init queue FIFO_TaskElements */ + err = kfifo_alloc(&FIFO_TaskElements, TASKQUEUE_SIZE * sizeof(taskElement), GFP_KERNEL); + if(err != 0) { + printk(KERN_DEBUG"[%s]: Failed to allocate kfifo for FIFO_TaskElements!\n", __func__); + goto unregister; + } + /*init fifo of task elements*/ + printk(KERN_INFO "[%s]:IPU FIFO_TaskElements init success, queue size: %u\n", __func__, kfifo_size(&FIFO_TaskElements)); + + /* init wait queue sync_wq */ + init_waitqueue_head(&sync_wq); + + /* Allocate cambricon_ipu_private struct and asigned to global variable adapter, devm_kzalloc will memset adapter to zeros */ + adapter = devm_kzalloc(&pdev->dev, sizeof(struct cambricon_ipu_private), GFP_KERNEL); + if (!adapter) { + err = -ENOMEM; + printk(KERN_ERR"[%s]: Failed to allocate memory for struct ipu!\n", __func__); + goto cleanup_kfifo; + } + + adapter->name = IPU_NAME; + adapter->irq = (unsigned int)platform_get_irq_byname(pdev, "ipu_dma_irq"); + printk(KERN_DEBUG"[%s]: platform_get_irq_byname:%d\n", __func__, adapter->irq); + + /* get regulator */ + adapter->vipu_ip = devm_regulator_get(&pdev->dev, "vipu"); + if (IS_ERR(adapter->vipu_ip)) { + printk(KERN_ERR"[%s]:Couldn't get regulator ip! \n", __func__); + goto cleanup; + } + printk(KERN_DEBUG"[%s]:Get regulator ip succuss! dev-num-resouce:%d\n", + __func__, pdev->num_resources); + + /* init mutex signals (down_interrupt/up) */ + sema_init(&(adapter->config_reg_sem), 1); + sema_init(&(adapter->inst_ram_sem), 1); + sema_init(&(adapter->llseek_sem), 1); + sema_init(&(adapter->task_fifo_sem), TASKQUEUE_SIZE); + + mutex_init(&adapter->open_mutex); + mutex_init(&adapter->power_mutex); + mutex_init(&adapter->clk.clk_mutex); + mutex_init(&adapter->stat_mutex); + mutex_init(&adapter->boot_inst_set.boot_mutex); + mutex_init(&adapter->reset_mutex); + mutex_init(&adapter->bandwidth_lmt_mutex); + + /* ipu instruction ram resource, third input para is for the number of dts */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + + if (res == NULL) { + printk(KERN_ERR"[%s]: failed to get instruction resource\n", __func__); + err = -ENXIO; + goto cleanup; + } + + size = resource_size(res); + adapter->inst_ram_phys_addr = res->start; + adapter->inst_ram_size = (unsigned int)size; + + /* the region of request must be unused */ + adapter->inst_mem = request_mem_region(res->start, size, pdev->name); + if (adapter->inst_mem == NULL) { + printk(KERN_ERR"[%s]: failed to get instruction memory region\n", __func__); + err = -ENOENT; + goto cleanup; + } + + adapter->inst_ram_virt_addr = ioremap(res->start, size); + if (!adapter->inst_ram_virt_addr) { + printk(KERN_ERR"[%s]: ioremap() of instruction resource failed\n", __func__); + err = -ENXIO; + goto release_res_inst; + } + + /* ipu configure registers resource */ + res_cfg = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res_cfg == NULL) { + printk(KERN_ERR"[%s]: failed to get configure registers resource\n", __func__); + err = -ENXIO; + goto unmap_ram; + } + if (res_cfg->start != 0xff400000) { + res_cfg->start = 0xff400000; + } + + size = resource_size(res_cfg); + adapter->config_reg_phys_addr = res_cfg->start; + adapter->config_reg_length = (unsigned int)size; + adapter->cfg_mem = request_mem_region(res_cfg->start, size, pdev->name); + if (adapter->cfg_mem == NULL) { + printk(KERN_ERR"[%s]: failed to get configure registers memory region\n", __func__); + err = -ENOENT; + goto unmap_ram; + } + + adapter->config_reg_virt_addr = ioremap(res_cfg->start, size); + if (!adapter->config_reg_virt_addr) { + printk(KERN_ERR"[%s]: ioremap() of configure registers resource failed\n", __func__); + err = -ENXIO; + goto release_res_cfg; + } + IOREAD_RANGE(adapter->config_reg_virt_addr, 0xff); + + if (!ipu_get_feature_tree(&pdev->dev)) { + printk(KERN_ERR"[%s]: fatal err, unknown feature tree\n", __func__); + goto unmap_reg; + } + +#ifdef CAMBRICON_IPU_IRQ + /* request ipu irq */ + if (request_threaded_irq(adapter->irq, NULL, ipu_interrupt_handler, + (unsigned long)(IRQF_ONESHOT | IRQF_TRIGGER_HIGH), IPU_NAME, &(adapter->cdev))) { + printk(KERN_ERR"[%s]: IPU Require IRQ failed!\n", __func__); + err = -EIO; + goto unmap_reg; + } + printk(KERN_DEBUG"[%s]: IPU Require IRQ Succeeded\n", __func__); +#endif + + /* Add ipu char device to system, udev can auto detect */ + cdev_init(&(adapter->cdev), &ipu_fops); + adapter->cdev.owner = THIS_MODULE;/*lint !e64*/ + adapter->cdev.ops = &ipu_fops; + err = cdev_add(&(adapter->cdev), chrdev, 1); + if (err) { + printk(KERN_ERR"[%s]: Failed to Add IPU to system!\n", __func__); + goto free_irq; + } + + /* Create ipu class under /sys/class */ + dev_class = class_create(THIS_MODULE, IPU_NAME);/*lint !e64*/ + if (IS_ERR(dev_class)) { + err = (int)PTR_ERR(dev_class); + printk(KERN_ERR"[%s]: Failed to create ipu class!\n", __func__); + goto destroy_cdev; + } + /* Register ipu device in sysfs, and this will cause udev to create corresponding device node */ + temp = device_create(dev_class, NULL, chrdev, NULL, "%s", IPU_NAME); + if (IS_ERR(temp)) { + err = (int)PTR_ERR(temp); + printk(KERN_ERR"[%s]: Failed to mount IPU to /dev/ipu!\n", __func__); + goto destroy_class; + } + +#ifdef CAMBRICON_IPU_IRQ + if (!ipu_get_irq_offset(&pdev->dev)) { + printk(KERN_ERR"[%s]: fatal err, ipu irq is unsupported\n", __func__); + goto destroy_device; + } + + /* ioremap irq reg addr */ + adapter->ics_irq_io_addr = ioremap((unsigned long)adapter->irq_reg_offset.ics_irq_base_addr, (unsigned long)0xff); + if (!adapter->ics_irq_io_addr) { + printk(KERN_ERR"[%s]IPU_ERROR:ics_irq_io_addr ioremap fail\n", __func__); + goto destroy_device; + } + IOREAD_RANGE(adapter->ics_irq_io_addr, 0xff); +#endif + + if (!ipu_smmu_master_get_offset(&pdev->dev)) { + printk(KERN_ERR"[%s]: fatal err, ipu_smmu_master is unsupported\n", __func__); + goto unmap_irq; + } + + if (!ipu_smmu_common_get_offset(&pdev->dev)) { + printk(KERN_ERR"[%s]: fatal err, ipu_smmu_common is unsupported\n", __func__); + goto unmap_irq; + } + + /* smmu manager init for ioremap smmu reg addr */ + if (ipu_smmu_mngr_init()) { + goto unmap_smmu; + } + + if (ipu_reset_init(&pdev->dev)) { + goto unmap_smmu; + } + + if (ipu_bandwidth_lmt_init(&pdev->dev)) { + goto unmap_reset; + } + + /* init ipu clock */ + if (ipu_clock_init(&pdev->dev, &adapter->clk, adapter->feature_tree.lpm3_set_vcodecbus)) { + printk(KERN_ERR"[%s]: Failed to init ipu clock\n", __func__); + goto unmap_bandwidth_lmt; + } + +#ifdef IPU_SMMU_ENABLE + adapter->smmu_rw_err_phy_addr = devm_kzalloc(&pdev->dev, ICS_SMMU_WR_ERR_BUFF_LEN, GFP_KERNEL); + if (!adapter->smmu_rw_err_phy_addr) { + err = -ENOMEM; + printk(KERN_ERR"[%s]: Failed to allocate memory for smmu read and write phy addr in error case\n", __func__); + goto exit_error; + } + + adapter->smmu_ttbr0 = ipu_get_smmu_base_phy(&pdev->dev); + +#endif + +#ifdef CONFIG_HISI_IPU_MNTN + err = ipu_mntn_rdr_init(); + if (err) { + printk(KERN_ERR"[%s]: Call ipu_mntn_rdr_init is failed!ret=%d\n", __func__, err); + goto exit_error; + } +#endif + + ipu_watchdog_init(&adapter->reset_wtd, adapter->feature_tree.soft_watchdog_enable, ipu_reset_irq); + + sema_init(&(adapter->reset_wtd.sem), 0); + adapter->reset_wtd.task = kthread_run(ipu_reset, NULL, "ipu_reset_task"); + + printk(KERN_DEBUG"[%s]: Succeeded to initialize ipu device.\n", __func__); + + return 0; + +exit_error: +unmap_bandwidth_lmt: + ipu_bandwidth_lmt_unremap_addr(); +unmap_reset: + ipu_reset_unremap_addr(); +unmap_smmu: + ipu_smmu_mngr_deinit(); +unmap_irq: + iounmap(adapter->ics_irq_io_addr); + adapter->ics_irq_io_addr = NULL; +destroy_device: + device_destroy(dev_class, chrdev); +destroy_class: + if (dev_class) { + class_destroy(dev_class); + } +destroy_cdev: + cdev_del(&(adapter->cdev)); +free_irq: +#ifdef CAMBRICON_IPU_IRQ + free_irq(adapter->irq, &(adapter->cdev)); +#endif +unmap_reg: + iounmap(adapter->config_reg_virt_addr); + adapter->config_reg_virt_addr = NULL; +release_res_cfg: + release_mem_region((unsigned long)res_cfg->start, size); +unmap_ram: + iounmap(adapter->inst_ram_virt_addr); + adapter->inst_ram_virt_addr = NULL; +release_res_inst: + release_mem_region((unsigned long)res->start, size); +cleanup: + devm_kfree(&pdev->dev, adapter); + adapter = NULL; +cleanup_kfifo: + kfifo_free(&FIFO_TaskElements); +unregister: + unregister_chrdev_region(chrdev, 1); +fail: + /* Unregister ai client for DSM */ + DSM_AI_UNREGISTER(); + return err; +} + +/* remove() function for platform driver */ +static int __exit cambricon_ipu_remove(struct platform_device *pdev) +{ + dev_t chrdev; + + UNUSED_PARAMETER(pdev); + + /* device has two dev number, i.e. ipu_major, ipu_minor */ + chrdev = MKDEV(ipu_major, ipu_minor); + + printk(KERN_DEBUG"[%s]: Destroying IPU device!\n", __func__); + + if (adapter->reset_wtd.task) { + kthread_stop(adapter->reset_wtd.task); + } + + ipu_smmu_mngr_deinit(); + + /* Destroy ipu class */ + if (dev_class) { + device_destroy(dev_class, chrdev); + class_destroy(dev_class); + } + + /* Destroy ipu */ + if (adapter) { + if (adapter->ics_irq_io_addr) { + iounmap(adapter->ics_irq_io_addr); + adapter->ics_irq_io_addr = NULL; + } + + cdev_del(&(adapter->cdev)); + +#ifdef CAMBRICON_IPU_IRQ + free_irq(adapter->irq, &(adapter->cdev)); +#endif + + kfifo_free(&FIFO_TaskElements); + if (adapter->config_reg_virt_addr) { + iounmap(adapter->config_reg_virt_addr); + adapter->config_reg_virt_addr = NULL; + } + + if (adapter->inst_ram_virt_addr) { + iounmap(adapter->inst_ram_virt_addr); + adapter->inst_ram_virt_addr = NULL; + } + + if (adapter->feature_tree.ipu_reset_when_in_error) { + ipu_reset_unremap_addr(); + } + + ipu_bandwidth_lmt_unremap_addr(); + + release_mem_region(adapter->inst_mem->start, resource_size(adapter->inst_mem)); + release_mem_region(adapter->cfg_mem->start, resource_size(adapter->cfg_mem)); + + + +#ifdef IPU_SMMU_ENABLE + if (adapter->smmu_rw_err_phy_addr) { + devm_kfree(&pdev->dev, adapter->smmu_rw_err_phy_addr); + adapter->smmu_rw_err_phy_addr = NULL; + } +#endif + + devm_kfree(&pdev->dev, adapter); + adapter = NULL; + } + + /* Unregister chrdev region */ + unregister_chrdev_region(chrdev, 1); + + /* Unregister ai client for DSM */ + DSM_AI_UNREGISTER(); + + printk(KERN_DEBUG"[%s]: Succeeded to destroying IPU device.\n", __func__); + + return 0; +} +/*lint -e785*/ +static const struct of_device_id cambricon_ipu_match_table[] = { + { .compatible = COMP_CAMBRICON_IPU_DRV_NAME, }, + {}, +}; + +/* to find key word "cambricon-ipu" in dts, if failed, not load driver */ +MODULE_DEVICE_TABLE(of, cambricon_ipu_match_table); +/* ipu platform drive */ +static struct platform_driver cambricon_ipu_driver = { + .driver = { + .name = "cambricon-ipu", + .owner = THIS_MODULE,/*lint !e64*/ + .of_match_table = of_match_ptr(cambricon_ipu_match_table), + }, + .probe = cambricon_ipu_probe, + .remove = cambricon_ipu_remove, +}; +/*lint +e785*/ +/* ipu platform device and driver register */ +static int __init cambricon_ipu_init(void) +{ + int ret; + + printk(KERN_DEBUG"[%s]: platform device and driver register!\n", __func__); + ret = platform_driver_register(&cambricon_ipu_driver);/*lint !e64*/ + + /* No need to "platform_device_register(&cambricon_ipu_device);", + because it did when DTS initializating, no need to register device again */ + + return ret; +} + +/* ipu platform device and driver unregister */ +static void __exit cambricon_ipu_exit(void) +{ + platform_device_unregister(&cambricon_ipu_device); + platform_driver_unregister(&cambricon_ipu_driver); +#ifdef CONFIG_HISI_IPU_MNTN + destroy_workqueue(ipu_mntn_rdr_wq); +#endif +} + +/*lint -e753 -e528*/ + +module_init(cambricon_ipu_init); +module_exit(cambricon_ipu_exit); + +MODULE_AUTHOR("Cambricon Limited"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/hisi/ics/cambricon_ipu.h b/drivers/hisi/ics/cambricon_ipu.h new file mode 100644 index 000000000000..28ec6a5d19b7 --- /dev/null +++ b/drivers/hisi/ics/cambricon_ipu.h @@ -0,0 +1,251 @@ +/* + * Generic driver head file for the cambricon ipu device. + * + * Copyright (C) 2016 Cambricon Limited + * + * Licensed under the GPL v2 or later. + */ +#ifndef _CAMBRICON_IPU_H +#define _CAMBRICON_IPU_H + +#include +#include +#include +#include +#include + +#include "ipu_smmu_drv.h" +#include "ipu_clock.h" + +struct irq_reg_offset { + unsigned int ics_irq_base_addr; + unsigned int ics_irq_mask_ns; + unsigned int ics_irq_clr_ns; +}; + +struct ics_noc_bus_reg_offset { + unsigned int base_addr; + unsigned int qos_type; + unsigned int factor; + unsigned int saturation; + unsigned int qos_extcontrol; +}; + +struct pmctrl_reg_offset { + unsigned int base_addr; + unsigned int noc_power_idle_req; + unsigned int noc_power_idle_ack; + unsigned int noc_power_idle_stat; +}; + +struct pctrl_reg_offset { + unsigned int base_addr; + unsigned int peri_stat3; +}; + +struct media2_reg_offset { + unsigned int base_addr; + unsigned int peren0; + unsigned int perdis0; + unsigned int perclken0; + unsigned int perstat0; + unsigned int perrsten0; + unsigned int perrstdis0; + unsigned int perrststat0; +}; + +struct peri_reg_offset { + unsigned int base_addr; + unsigned int clkdiv8; + unsigned int clkdiv18; + unsigned int perpwrstat; + unsigned int perpwrack; + unsigned int peristat7; +}; + +enum ipu_reset_trategy { + IPU_RESET_UNSUPPORT = 0, + IPU_RESET_BY_CONFIG_NOC_BUS, + IPU_SOFT_RESET, +}; + +enum ipu_bandwidth_lmt_trategy { + IPU_BANDWIDTH_LMT_UNSUPPORT = 0, + IPU_BANDWIDTH_LMT_BY_QOS, + IPU_BANDWIDTH_LMT_BY_RW_OSD, +}; + +struct ics_feature_tree { + bool finish_irq_expand_ns; + bool finish_irq_expand_p; + bool finish_irq_expand_s; + bool finish_irq_to_hifi; + bool finish_irq_to_ivp; + bool finish_irq_to_isp; + bool finish_irq_to_lpm3; + bool finish_irq_to_iocmu; + bool smmu_port_select; + bool level1_irq; + bool performance_monitor; + bool wr_qword; + bool soft_watchdog_enable; + bool lpm3_set_vcodecbus; + bool smmu_mstr_hardware_start; + unsigned int ipu_reset_when_in_error; + unsigned int ipu_bandwidth_lmt; +}; +typedef int Priority; + +/* the ipu task type */ +typedef enum taskType { + IPU_TASK_ENUM_START = 0, + IPU_NONE_TASK = IPU_TASK_ENUM_START, + /*First*/ + IPU_COMPUTE_TASK, + IPU_SYNC_TASK, + //Add new to here! + IPU_TASK_ENUM_END +} taskType_t; + +typedef enum taskFlag{ + IPU_TASK_FLAG_START = 0, + IPU_TASK_FLAG_NONE = IPU_TASK_FLAG_START, + /*First*/ + IPU_TASK_FLAG_SYNC_WAITING, + IPU_TASK_FLAG_SYNC_DONE, + //Add new to here! + IPU_TASK_FLAG_END +} taskFlag_t; + +struct ipu_wtd { + bool enable; + int status; + struct mutex timer_mutex; + struct timer_list timer; + struct task_struct *task; + struct semaphore sem; +}; + +/* the ipu task element */ +typedef struct taskStruct { + taskType_t taskType;//define different cmd type + taskFlag_t* ptaskFlag;//use to mark task status + unsigned long offchipInstAddr; + unsigned long taskId; + Priority prior; +} taskElement; + +#define BOOT_INST_SIZE (64) +#define BOOT_INST_NUMBER (4) + +/* this struct stores the temp data of boot instrument for IPU initialization after power-up */ +struct boot_inst_set { + /* mutex */ + struct mutex boot_mutex; + + /* store the value from IOCTL WRITE when IPU is off */ + unsigned int ipu_access_ddr_addr; + + /* boot-instrument data */ + unsigned char boot_inst[BOOT_INST_SIZE * BOOT_INST_NUMBER]; + + /* size of boot-inst data */ + unsigned int boot_inst_size; + + /* bool flag to record whether access_ddr_addr is recorded */ + bool access_ddr_addr_is_config; + + /* bool flag to record whether boot_inst is recorded */ + bool boot_inst_recorded_is_config; +}; + +/* cambricon ipu private data */ +struct cambricon_ipu_private +{ + const char *name; + unsigned int irq; + bool ipu_device_opened; + bool ipu_power_up; + struct mutex power_mutex; + struct mutex open_mutex; + struct mutex bandwidth_lmt_mutex; + struct boot_inst_set boot_inst_set; + + struct smmu_statistic stat; + bool smmu_stat_en; + struct mutex stat_mutex; + + /* config reg addr */ + unsigned int config_reg_length; + phys_addr_t config_reg_phys_addr; + void __iomem *config_reg_virt_addr; + + /* inst reg addr */ + unsigned int inst_ram_size; + phys_addr_t inst_ram_phys_addr; + void __iomem *inst_ram_virt_addr; + + /* ioremap addr */ + void __iomem *ics_irq_io_addr; + void __iomem *noc_bus_io_addr; + void __iomem *pmctrl_io_addr; + void __iomem *pctrl_io_addr; + void __iomem *media2_io_addr; + void __iomem *peri_io_addr; + + struct semaphore config_reg_sem; + struct semaphore inst_ram_sem; + struct semaphore llseek_sem; + struct semaphore task_fifo_sem; + + /* char device */ + struct cdev cdev; /* ipu char device */ + + /* platform device resource */ + struct resource *inst_mem, *cfg_mem; + struct regulator *vipu_ip; + + /* clock */ + struct ics_clock clk; + + struct mutex reset_mutex; + unsigned long reset_va; + unsigned long smmu_ttbr0; + void *smmu_rw_err_phy_addr; + + struct irq_reg_offset irq_reg_offset; + struct ics_noc_bus_reg_offset ics_noc_bus_reg_offset; + struct pmctrl_reg_offset pmctrl_reg_offset; + struct pctrl_reg_offset pctrl_reg_offset; + struct media2_reg_offset media2_reg_offset; + struct peri_reg_offset peri_reg_offset; + + struct ics_feature_tree feature_tree; + + struct ipu_wtd reset_wtd; + + unsigned long computed_task_cnt; + +}; + + +#define IOREAD_RANGE(base, size) \ +do { \ + unsigned long i; \ + DEBUG("IOREAD_RANGE(%p, %u)", (void*)(base), (u32)(size)); \ + for (i = 0; i < (size); i += 4) { \ + unsigned long addr = (unsigned long)(base) + i; \ + u32 val = ioread32((void*)addr); \ + if (val != 0) { \ + DEBUG("*(u32*)(%s + %lx): %x", #base, i, val); \ + } \ + } \ +} while (0) + +#define READ_IPU_VERSION_REGISTER() \ +do { \ + u32 version = ioread32((void*)((unsigned long)adapter->config_reg_virt_addr + IPU_VERSION_REG)); \ + DEBUG("IPU_VERSION: %x", version); \ +} while (0) + +#endif diff --git a/drivers/hisi/ics/ics_debug.c b/drivers/hisi/ics/ics_debug.c new file mode 100644 index 000000000000..2c5c0ceaf2a8 --- /dev/null +++ b/drivers/hisi/ics/ics_debug.c @@ -0,0 +1,803 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ics_debug.h" + +#define CLASS_NAME "ics_debug" +#define UNMASK_LPM3 (0) +#define UNMASK_IOMCU (1) +#define UNMASK_ISP (2) +#define UNMASK_IVP (3) +#define UNMASK_HIFI (4) + +#define PERI_OFFSET_PEREN0 (0x000) +#define PERI_OFFSET_PERDIS0 (0x004) +#define PERI_OFFSET_PEREN6 (0x410) +#define PERI_OFFSET_PERDIS6 (0x414) +#define PERI_OFFSET_PERRSTEN4 (0x090) +#define PERI_OFFSET_PERRSTDIS4 (0x094) +#define PERI_OFFSET_CLKDIV18 (0x0F0) +#define PERI_OFFSET_PERPWREN (0x150) +#define PERI_OFFSET_PERPWRDIS (0x154) +#define PERI_OFFSET_ISOEN (0x144) +#define PERI_OFFSET_ISODIS (0x148) +#define PERI_OFFSET_CLKDIV5 (0x0bc) +#define PERI_OFFSET_CLKDIV8 (0x0c8) +#define PERI_OFFSET_CLKDIV15 (0x0e4) + +#define PERI_ISOEN_ICS_ISO_EN (0x00000100) +#define PERI_ISODIS_ICS_ISO_UN (0x00000100) +#define PERI_CLKDIV8_SEL_ICS_PLL2 (0xf0004000) +#define PERI_CLKDIV8_SEL_ICS_PLL0 (0xf0002000) +#define PERI_PERPWREN_ICSPWREN_EN (0x00000100) +#define PERI_PERPWRDIS_ICS_PWR_DIS (0x00000100) +#define PERI_PERRSTDIS4_IP_RST_MEDIA2 (0x00000001) +#define PERI_CLKDIV15_SEL_FREQ_DIV4_ICS (0x7e000600) +#define PERI_PERRSTEN4_IP_RST_MEDIA_CRG (0x00000002) +#define PERI_CLKDIV8_SEL_VCODECBUS_PLL2 (0x000f0004) +#define PERI_CLKDIV8_SEL_VCODECBUS_PLL0 (0x000f0002) +#define PERI_CLKDIV15_SEL_FREQ_DIV3_ICS (0x7e000400) +#define PERI_CLKDIV15_SEL_FREQ_DIV2_ICS (0x7e000200) +#define PERI_CLKDIV18_SC_GT_CLK_ICS__EN (0x40000000) +#define PERI_PERRSTEN4_IP_RST_MEDIA2_EN (0x00000001) +#define PERI_CLKDIV18_SC_GT_CLK_VCODEBUS_EN (0x01000000) +#define PERI_CLKDIV5_SET_FREQ_DIV4_VCODECBUS (0x003f0003) +#define PERI_CLKDIV5_SET_FREQ_DIV5_VCODECBUS (0x003f0004) +#define PERI_CLKDIV5_SET_FREQ_DIV8_VCODECBUS (0x003f0007) +#define PERI_PEREN0_GT_CLK_VCODECBUS2DDRC_EN (0x00000020) +#define PERI_PERRSTDIS4_IP_RST_MEDIA2_CRG_EN (0x00000002) +#define PERI_PERDIS0_GT_CLK_VCODECBUS2DDRC_UN (0x00000020) +#define PERI_CLKDIV18_SC_GT_CLK_ICS_OPEN_AND_EN (0x40004000) +#define PERI_CLKDIV18_SC_GT_CLK_VCODEBUS_OPEN_AND_EN (0x01000100) +#define PERI_PEREN6_GT_CLK_CFGBUS_MEDIA_AND_MEDIA2_EN (0x00010200) +#define PERI_PERDIS6_GT_CLK_CFGBUS_MEDIA_AND_MEDIA2_EN (0x00010200) + +#define MEDIA2_OFFSET_PEREN0 (0x000) +#define MEDIA2_OFFSET_PERDIS0 (0x004) +#define MEDIA2_OFFSET_PERRSTEN0 (0x030) +#define MEDIA2_OFFSET_PERRSTDIS0 (0x034) + +#define MEDIA2_PEREN0_GT_CLK_VCODEBUS_EN (0x00000200) +#define MEDIA2_PERDIS0_GT_CLK_VCODEBUS_UN (0x00000200) +#define MEDIA2_PEREN0_GT_CLK_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_EN (0x00000007) +#define MEDIA2_PERDIS0_GT_CLK_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_UN (0x00000007) +#define MEDIA2_PERRSTEN0_IP_RST_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_EN (0x00000038) +#define MEDIA2_PERRSTDIS0_IP_RST_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_UN (0x00000038) + +#define PMCTRL_OFFSET_NOC_POWER_IDLEREQ_0 (0x380) +#define PMCTRL_OFFSET_NOC_POWER_IDLEACK_0 (0x384) +#define PMCTRL_OFFSET_NOC_POWER_IDLE_0 (0x388) + +#define PMCTRL_NOC_POWER_IDLE_0_NOC_ICS_POWER_IDLE (0x200) +#define PMCTRL_NOC_POWER_IDLE_0_NOC_ICS_POWER_IDLE_UN (0x0) +#define PMCTRL_NOC_POWER_IDLE_0_NOC_ICS_POWER_IDLE_EN (0x200) +#define PMCTRL_NOC_POWER_IDLEACK_0_NOC_ICS_POWER_IDLEACK (0x200) +#define PMCTRL_NOC_POWER_IDLEACK_0_NOC_ICS_POWER_IDLEACK_UN (0x0) +#define PMCTRL_NOC_POWER_IDLEACK_0_NOC_ICS_POWER_IDLEACK_EN (0x200) +#define PMCTRL_NOC_POWER_IDLEACK_0_NOC_VCODEC_BUS_POWER_IDLEACK (0x10) +#define PMCTRL_NOC_POWER_IDLE_0_NOC_VCODEC_BUS_POWER_IDLE_STATUS (0x10) +#define PMCTRL_NOC_POWER_IDLEACK_0_NOC_VCODEC_BUS_POWER_IDLEACK_UN (0x0) +#define PMCTRL_NOC_POWER_IDLEREQ_0_NOC_ICS_POWER_IDLEREQ_EN (0x02000000) +#define PMCTRL_NOC_POWER_IDLEACK_0_NOC_VCODEC_BUS_POWER_IDLEACK_EN (0x10) +#define PMCTRL_NOC_POWER_IDLE_0_NOC_VCODEC_BUS_POWER_IDLE_STATUS_UN (0x0) +#define PMCTRL_NOC_POWER_IDLE_0_NOC_VCODEC_BUS_POWER_IDLE_STATUS_EN (0x10) +#define PMCTRL_NOC_POWER_IDLEREQ_0_NOC_ICS_POWER_IDLEREQ_REQ_AND_EN (0x02000200) +#define PMCTRL_NOC_NOC_POWER_IDLEREQ_0_NOC_VCODEC_BUS_POWER_IDLEREQ_EN (0x00100000) +#define PMCTRL_NOC_NOC_POWER_IDLEREQ_0_NOC_VCODEC_BUS_POWER_IDLEREQ_REQ_AND_EN (0x00100010) + +#define CONFIG_REG_VIRT_ADDR_OFFSET_ICS_VERSION (0x40) +#define CONFIG_REG_VIRT_ADDR_OFFSET_ICS_BASE_ADDR (0x28) + +#define ICS_IRQ_OFFSET_ICS_IRQ_MASK_LPMCU_FINISH (0x20) +#define ICS_IRQ_OFFSET_ICS_IRQ_MASK_IOMCU_FINISH (0x30) +#define ICS_IRQ_OFFSET_ICS_IRQ_MASK_ISPCPU_FINISH (0x40) +#define ICS_IRQ_OFFSET_ICS_IRQ_MASK_IVP_FINISH (0x50) +#define ICS_IRQ_OFFSET_ICS_IRQ_MASK_HIFI_FINISH (0x60) + +static struct class *ics_class; + +struct cambricon_ipu_private *ics_adapter; + +static uint32_t setclkrate = 0; +static uint32_t setprofile = 0; +static uint32_t ipuopen = 0; +static uint32_t ipurelease = 0; +static uint32_t ipuversion = 0; +static uint32_t resetproc = 0; +static uint32_t ipurstcrtenv = 0; +static uint32_t ipurstdstenv = 0; +static uint32_t pusetreg = 0; +static uint32_t pdsetreg = 0; +static uint32_t rdcorereg = 0; +static uint32_t wrcorereg = 0; +static uint32_t wrregvbusclk = 0; +static uint32_t wrregcnnclk = 0; +static uint32_t wrreglmt = 0; + +struct ics_test_iomap_addr { + void __iomem *pmctrl_io_addr; + void __iomem *pctrl_io_addr; + void __iomem *sctrl_io_addr; + void __iomem *media_io_addr; + void __iomem *peri_io_addr; +}; +struct ics_test_iomap_addr ics_test_iomap_addr; + +unsigned long smmu_ttbr0_bk = 0; + +static ssize_t setclkrate_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret = 0; + ret = snprintf(buf, PAGE_SIZE, "setclkrate:0x%x!\n", setclkrate); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo clockRate>setclkrate\n"); + return ret; +} + +static ssize_t setclkrate_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + int ret = 0; + + if (sscanf(buf, "%d", &setclkrate) != 1) + return -EINVAL; + printk(KERN_DEBUG"[%s]: setclkrate_store begin\n", __FUNCTION__); + ics_adapter->clk.start_rate = setclkrate; + if (ret) { + printk(KERN_ERR"[%s]: call_ipu_clock_start failed\n", __FUNCTION__); + return -EINVAL; + } + printk(KERN_DEBUG"[%s]: setclkrate_store end\n", __FUNCTION__); + + return (ssize_t)size; +} + +static ssize_t setprofile_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret = 0; + ret = snprintf(buf, PAGE_SIZE, "setprofile:0x%x!\n", setprofile); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo profile>setprofile\n"); + return ret; +} + +static ssize_t setprofile_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + int ret = 0; + + if (sscanf(buf, "%d", &setprofile) != 1) + return -EINVAL; + printk(KERN_DEBUG"[%s]: setclkrate_store begin\n", __FUNCTION__); + call_ipu_set_profile(setprofile); + if (ret) { + printk(KERN_ERR"[%s]: ipu_clock_set_profile failed\n", __FUNCTION__); + return -EINVAL; + } + printk(KERN_DEBUG"[%s]: setprofile_store end\n", __FUNCTION__); + + return (ssize_t)size; +} + +static ssize_t ipuopen_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret =0; + ret = snprintf(buf, PAGE_SIZE, "ipuopen:0x%x!\n", ipuopen); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>ipuopen\n"); + return ret; +} + +static ssize_t ipuopen_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + int ret = 0; + + printk(KERN_DEBUG"[%s]: ipuopen_store begin\n", __FUNCTION__); + + ret = call_regulator_ip_vipu_enable(); + if (ret) { + printk(KERN_ERR"[%s]: call_regulator_ip_vipu_enable failed\n", __FUNCTION__); + return -EINVAL; + } + + printk(KERN_DEBUG"[%s]: call_regulator_ip_vipu_enable ok\n", __FUNCTION__); + + call_ipu_clock_start(&ics_adapter->clk); + if (ret) { + printk(KERN_ERR"[%s]: IPU clock start failed\n", __FUNCTION__); + return -EINVAL; + } + printk(KERN_DEBUG"[%s]: call_ipu_clock_start ok\n", __FUNCTION__); + + call_ipu_smmu_init(ics_adapter->smmu_ttbr0, + (unsigned long)ics_adapter->smmu_rw_err_phy_addr, ics_adapter->feature_tree.smmu_port_select, ics_adapter->feature_tree.smmu_mstr_hardware_start); + printk(KERN_DEBUG"[%s]: call_ipu_smmu_init ok\n", __FUNCTION__); + + call_ipu_interrupt_init(); + printk(KERN_DEBUG"[%s]: call_ipu_interrupt_init ok\n", __FUNCTION__); + + printk(KERN_DEBUG"[%s]: ipuopen_store end\n", __FUNCTION__); + + return (ssize_t)size; +} + +static ssize_t ipurelease_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret =0; + ret = snprintf(buf, PAGE_SIZE, "ipurelease:0x%x!\n", ipurelease); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>ipurelease\n"); + return ret; +} + +static ssize_t ipurelease_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + int ret = 0; + + printk(KERN_DEBUG"[%s]: ipurelease_store begin\n", __FUNCTION__); + + call_ipu_clock_set_rate(&ics_adapter->clk, ics_adapter->clk.stop_rate); + printk(KERN_DEBUG"[%s]: call_ipu_clock_set_rate ok\n", __FUNCTION__); + + call_ipu_clock_stop(&ics_adapter->clk); + printk(KERN_DEBUG"[%s]: call_ipu_clock_stop ok\n", __FUNCTION__); + + ret = call_regulator_ip_vipu_disable(); + if (ret) { + printk(KERN_ERR"[%s]: No IPU device!\n", __FUNCTION__); + return -EBUSY; + } + printk(KERN_DEBUG"[%s]: call_regulator_ip_vipu_disable ok\n", __FUNCTION__); + ics_adapter->ipu_device_opened = 0; + + printk(KERN_DEBUG"[%s]: ipurelease_store end\n", __FUNCTION__); + return (ssize_t)size; +} + + +static ssize_t ipuversion_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret =0; + unsigned int regval; + + regval = ioread32((void *)((unsigned long)ics_adapter->config_reg_virt_addr + CONFIG_REG_VIRT_ADDR_OFFSET_ICS_VERSION)); + + ret = snprintf(buf, PAGE_SIZE, "ipuversion:0x%x, register value: 0x%x!\n", ipuversion, regval); + DEBUG("read version %x, cached %d", regval, ipuversion); + return ret; +} + +static ssize_t ipuversion_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + int ret = 0, ver = 0; + + if (sscanf(buf, "%x", &ver) != 1) { + printk(KERN_ERR "[%s]: version error, invalid number format!\n", __FUNCTION__); + return -EINVAL; + } + + ipuversion = ver; + iowrite32(ver, (void *)((unsigned long)ics_adapter->config_reg_virt_addr + CONFIG_REG_VIRT_ADDR_OFFSET_ICS_VERSION)); + DEBUG("write version %x", ver); + return (ssize_t)size; +} + + +static ssize_t resetproc_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret =0; + ret = snprintf(buf, PAGE_SIZE, "resetproc:0x%x!\n", resetproc); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>resetproc\n"); + return ret; +} + +static ssize_t resetproc_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + printk(KERN_DEBUG"[%s]: begin\n", __FUNCTION__); + call_ipu_reset_proc((unsigned int)ics_adapter->reset_va); + printk(KERN_DEBUG"[%s]: end\n", __FUNCTION__); + + return (ssize_t)size; +} + +static ssize_t ipurstcrtenv_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret =0; + ret = snprintf(buf, PAGE_SIZE, "ipu reset create environment:0x%x!\n", ipurstcrtenv); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>ipurstcrtenv (no need to pu ipu)\n"); + return ret; +} + +static ssize_t ipurstcrtenv_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + printk(KERN_DEBUG"[%s]: begin\n", __FUNCTION__); + smmu_ttbr0_bk = ics_adapter->smmu_ttbr0; + ics_adapter->smmu_ttbr0 = smmu_ttbr0_bk & 0xffffffff; + printk(KERN_DEBUG"[%s]: end\n", __FUNCTION__); + + return (ssize_t)size; +} + +static ssize_t ipurstdstenv_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret =0; + ret = snprintf(buf, PAGE_SIZE, "ipu reset destroy environment:0x%x!\n", ipurstdstenv); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>ipurstcrtenv (no need to pu ipu)\n"); + return ret; +} + +static ssize_t ipurstdstenv_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + printk(KERN_DEBUG"[%s]: begin\n", __FUNCTION__); + ics_adapter->smmu_ttbr0 = smmu_ttbr0_bk; + printk(KERN_DEBUG"[%s]: end\n", __FUNCTION__); + + return (ssize_t)size; +} + +static ssize_t pusetreg_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret = 0; + ret = snprintf(buf, PAGE_SIZE, "pusetreg:0x%x!\n", pusetreg); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>pusetreg\n"); + return ret; +} + +static ssize_t pusetreg_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + unsigned int read_value; + + printk(KERN_DEBUG"[%s]: peri_io_addr:%p, media2_io_addr:%p, pmctrl_io_addr=%p\n", __FUNCTION__, + ics_adapter->peri_io_addr, ics_adapter->media2_io_addr, ics_adapter->pmctrl_io_addr); + + //set_pu_media2_subsys + printk(KERN_DEBUG"[%s]:meidia module unrst\n",__FUNCTION__); + iowrite32(PERI_PERRSTDIS4_IP_RST_MEDIA2_CRG_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PERRSTDIS4)); + + printk(KERN_DEBUG"[%s]:meidia module clk enable\n",__FUNCTION__); + iowrite32(PERI_PEREN6_GT_CLK_CFGBUS_MEDIA_AND_MEDIA2_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PEREN6)); + udelay(1); + + printk(KERN_DEBUG"[%s]:meidia module clk disable\n",__FUNCTION__); + iowrite32(PERI_PERDIS6_GT_CLK_CFGBUS_MEDIA_AND_MEDIA2_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PERDIS6)); + udelay(1); + printk(KERN_DEBUG"[%s]:meidia module unrst\n",__FUNCTION__); + + iowrite32(PERI_PERRSTDIS4_IP_RST_MEDIA2, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PERRSTDIS4)); + printk(KERN_DEBUG"[%s]:meidia module clk enable\n",__FUNCTION__); + + iowrite32(PERI_PEREN6_GT_CLK_CFGBUS_MEDIA_AND_MEDIA2_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PEREN6)); + + //set_pu_vcodec + printk(KERN_DEBUG"[%s]:vcodec module clk enable\n",__FUNCTION__); + iowrite32(PERI_CLKDIV18_SC_GT_CLK_VCODEBUS_OPEN_AND_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV18)); + iowrite32(MEDIA2_PEREN0_GT_CLK_VCODEBUS_EN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PEREN0)); + iowrite32(PERI_PEREN0_GT_CLK_VCODECBUS2DDRC_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PEREN0)); + udelay(1); + + printk(KERN_DEBUG"[%s]:vcodec module clk disable\n",__FUNCTION__); + iowrite32(MEDIA2_PERDIS0_GT_CLK_VCODEBUS_UN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PERDIS0)); + iowrite32(PERI_PERDIS0_GT_CLK_VCODECBUS2DDRC_UN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PERDIS0)); + udelay(1); + + printk(KERN_DEBUG"[%s]:vcodec module clk enable\n",__FUNCTION__); + iowrite32(MEDIA2_PEREN0_GT_CLK_VCODEBUS_EN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PEREN0)); + iowrite32(PERI_PEREN0_GT_CLK_VCODECBUS2DDRC_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PEREN0)); + + printk(KERN_DEBUG"[%s]:vcodec bus idle clear\n",__FUNCTION__); + iowrite32(PMCTRL_NOC_NOC_POWER_IDLEREQ_0_NOC_VCODEC_BUS_POWER_IDLEREQ_EN, (void *)((unsigned long)ics_adapter->pmctrl_io_addr+ PMCTRL_OFFSET_NOC_POWER_IDLEREQ_0)); + udelay(1); + + read_value = ioread32((void *)((unsigned long)ics_adapter->pmctrl_io_addr+ PMCTRL_OFFSET_NOC_POWER_IDLEACK_0)); + if ((PMCTRL_NOC_POWER_IDLEACK_0_NOC_VCODEC_BUS_POWER_IDLEACK & PMCTRL_NOC_POWER_IDLEACK_0_NOC_VCODEC_BUS_POWER_IDLEACK_UN) != 0x0) { + printk(KERN_ERR"[%s]: pu_codec:no expect power idleack value:%d!\n", + __FUNCTION__ , read_value); + return -EINVAL; + } + udelay(1); + + read_value = ioread32((void *)((unsigned long)ics_adapter->pmctrl_io_addr + PMCTRL_OFFSET_NOC_POWER_IDLE_0)); + if ((read_value & PMCTRL_NOC_POWER_IDLE_0_NOC_VCODEC_BUS_POWER_IDLE_STATUS) != PMCTRL_NOC_POWER_IDLE_0_NOC_VCODEC_BUS_POWER_IDLE_STATUS_UN) { + printk(KERN_ERR"[%s]: pu_codec:no expect power idle value:%d!\n", + __FUNCTION__ , read_value); + return -EINVAL; + } + + //set_pu_ics + printk(KERN_DEBUG"[%s]:ipu module mtcmos on\n",__FUNCTION__); + iowrite32(PERI_PERPWREN_ICSPWREN_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PERPWREN)); + udelay(100); + + printk(KERN_DEBUG"[%s]:ipu module clk enable\n",__FUNCTION__); + iowrite32(PERI_CLKDIV18_SC_GT_CLK_ICS_OPEN_AND_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV18)); + iowrite32(MEDIA2_PEREN0_GT_CLK_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_EN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PEREN0)); + udelay(1); + + printk(KERN_DEBUG"[%s]:ipu module clk disable\n",__FUNCTION__); + iowrite32(MEDIA2_PERDIS0_GT_CLK_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_UN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PERDIS0)); + udelay(1); + + printk(KERN_DEBUG"[%s]:ipu module iso disable\n",__FUNCTION__); + iowrite32(PERI_ISODIS_ICS_ISO_UN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_ISODIS)); + printk(KERN_DEBUG"[%s]:ipu module unrst\n",__FUNCTION__); + iowrite32(MEDIA2_PERRSTDIS0_IP_RST_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_UN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PERRSTDIS0)); + udelay(1); + + printk(KERN_DEBUG"[%s]:ipu module clk enable\n",__FUNCTION__); + iowrite32(MEDIA2_PEREN0_GT_CLK_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_EN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PEREN0)); + printk(KERN_DEBUG"[%s]:ipu bus idle clear\n",__FUNCTION__); + iowrite32(PMCTRL_NOC_POWER_IDLEREQ_0_NOC_ICS_POWER_IDLEREQ_EN, (void *)((unsigned long)ics_adapter->pmctrl_io_addr+ PMCTRL_OFFSET_NOC_POWER_IDLEREQ_0)); + udelay(1); + + read_value = ioread32((void *)((unsigned long)ics_adapter->pmctrl_io_addr+ PMCTRL_OFFSET_NOC_POWER_IDLEACK_0)); + + if ((read_value & PMCTRL_NOC_POWER_IDLEACK_0_NOC_ICS_POWER_IDLEACK) != PMCTRL_NOC_POWER_IDLEACK_0_NOC_ICS_POWER_IDLEACK_UN) { + printk(KERN_ERR"[%s]: pu_ics:no expect power idleack value:%d!\n", + __FUNCTION__ , read_value); + return -EINVAL; + } + udelay(1); + + read_value = ioread32((void *)((unsigned long)ics_adapter->pmctrl_io_addr+ PMCTRL_OFFSET_NOC_POWER_IDLE_0)); + + if ((read_value & PMCTRL_NOC_POWER_IDLE_0_NOC_ICS_POWER_IDLE) != PMCTRL_NOC_POWER_IDLE_0_NOC_ICS_POWER_IDLE_UN) { + printk(KERN_ERR"[%s]: pu_ics:no expect power idle value:%d!\n", + __FUNCTION__ , read_value); + return -EINVAL; + } + + return (ssize_t)size; +} + +static ssize_t pdsetreg_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret = 0; + ret = snprintf(buf, PAGE_SIZE, "pdsetreg:0x%x!\n", pdsetreg); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>pdsetreg\n"); + return ret; +} + +static ssize_t pdsetreg_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + unsigned int read_value; + + //set_pd_ics + printk(KERN_DEBUG"[%s]:ipu bus idle set\n",__FUNCTION__); + iowrite32(PMCTRL_NOC_POWER_IDLEREQ_0_NOC_ICS_POWER_IDLEREQ_REQ_AND_EN, (void *)((unsigned long)ics_adapter->pmctrl_io_addr+ PMCTRL_OFFSET_NOC_POWER_IDLEREQ_0)); + udelay(1); + + read_value = ioread32((void *)((unsigned long)ics_adapter->pmctrl_io_addr+ PMCTRL_OFFSET_NOC_POWER_IDLEACK_0)); + if((read_value & PMCTRL_NOC_POWER_IDLEACK_0_NOC_ICS_POWER_IDLEACK) != PMCTRL_NOC_POWER_IDLEACK_0_NOC_ICS_POWER_IDLEACK_EN) { + printk(KERN_ERR"[%s]: pd_ics:no expect power idleack value:%d!\n", + __FUNCTION__ , read_value); + return -EINVAL; + } + udelay(1); + + read_value = ioread32((void *)((unsigned long)ics_adapter->pmctrl_io_addr+ PMCTRL_OFFSET_NOC_POWER_IDLE_0)); + if((read_value & PMCTRL_NOC_POWER_IDLE_0_NOC_ICS_POWER_IDLE) != PMCTRL_NOC_POWER_IDLE_0_NOC_ICS_POWER_IDLE_EN) { + printk(KERN_ERR"[%s]: pd_ics:no expect power idle value:%d!\n", + __FUNCTION__ , read_value); + return -EINVAL; + } + + printk(KERN_DEBUG"[%s]:ipu module clk disable\n",__FUNCTION__); + iowrite32(MEDIA2_PERDIS0_GT_CLK_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_UN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PERDIS0)); + udelay(1); + + printk(KERN_DEBUG"[%s]:ipu module rst\n",__FUNCTION__); + iowrite32(MEDIA2_PERRSTEN0_IP_RST_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_EN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PERRSTEN0)); + udelay(1); + + printk(KERN_DEBUG"[%s]:ipu module clk disable\n",__FUNCTION__); + iowrite32(MEDIA2_PEREN0_GT_CLK_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_EN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PEREN0)); + udelay(1); + + printk(KERN_DEBUG"[%s]:ipu module clk disable\n",__FUNCTION__); + iowrite32(MEDIA2_PERDIS0_GT_CLK_ICS_AND_NOC_ICS_AND_NOC_ICS_CFG_UN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PERDIS0)); + iowrite32(PERI_CLKDIV18_SC_GT_CLK_ICS__EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV18)); + + printk(KERN_DEBUG"[%s]:ipu module iso\n",__FUNCTION__); + iowrite32(PERI_ISOEN_ICS_ISO_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_ISOEN)); + + printk(KERN_DEBUG"[%s]:ipu module mtcmos off\n",__FUNCTION__); + iowrite32(PERI_PERPWRDIS_ICS_PWR_DIS, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PERPWRDIS)); + + //set_pd_vcodec + printk(KERN_DEBUG"[%s]:vcodec bus idle set\n",__FUNCTION__); + iowrite32(PMCTRL_NOC_NOC_POWER_IDLEREQ_0_NOC_VCODEC_BUS_POWER_IDLEREQ_REQ_AND_EN, (void *)((unsigned long)ics_adapter->pmctrl_io_addr+ PMCTRL_OFFSET_NOC_POWER_IDLEREQ_0)); + udelay(1); + + read_value = ioread32((void *)((unsigned long)ics_adapter->pmctrl_io_addr+ PMCTRL_OFFSET_NOC_POWER_IDLEACK_0)); + if((read_value & PMCTRL_NOC_POWER_IDLEACK_0_NOC_VCODEC_BUS_POWER_IDLEACK) != PMCTRL_NOC_POWER_IDLEACK_0_NOC_VCODEC_BUS_POWER_IDLEACK_EN) { + printk(KERN_ERR"[%s]: pd_codec:no expect power idleack value:%d!\n", + __FUNCTION__ , read_value); + return -EINVAL; + } + udelay(1); + + read_value = ioread32((void *)((unsigned long)ics_adapter->pmctrl_io_addr+ PMCTRL_OFFSET_NOC_POWER_IDLE_0)); + if((read_value & PMCTRL_NOC_POWER_IDLE_0_NOC_VCODEC_BUS_POWER_IDLE_STATUS) != PMCTRL_NOC_POWER_IDLE_0_NOC_VCODEC_BUS_POWER_IDLE_STATUS_EN) { + printk(KERN_ERR"[%s]: pd_codec:no expect power idle value:%d!\n", + __FUNCTION__ , read_value); + return -EINVAL; + } + + printk(KERN_DEBUG"[%s]:vcodec module clk disable\n",__FUNCTION__); + iowrite32(MEDIA2_PERDIS0_GT_CLK_VCODEBUS_UN, (void *)((unsigned long)ics_adapter->media2_io_addr + MEDIA2_OFFSET_PERDIS0)); + iowrite32(PERI_PERDIS0_GT_CLK_VCODECBUS2DDRC_UN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PERDIS0)); + iowrite32(PERI_CLKDIV18_SC_GT_CLK_VCODEBUS_EN, (void *)((unsigned long)ics_adapter->peri_io_addr+ PERI_OFFSET_CLKDIV18)); + + //set_pu_media2_subsys + printk(KERN_DEBUG"[%s]:media module rst\n",__FUNCTION__); + iowrite32(PERI_PERRSTEN4_IP_RST_MEDIA2_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PERRSTEN4)); + + printk(KERN_DEBUG"[%s]:media module clk disable\n",__FUNCTION__); + iowrite32(PERI_PERDIS6_GT_CLK_CFGBUS_MEDIA_AND_MEDIA2_EN, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PERDIS6)); + + printk(KERN_DEBUG"[%s]:media module unrst\n",__FUNCTION__); + iowrite32(PERI_PERRSTEN4_IP_RST_MEDIA_CRG, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_PERRSTEN4)); + return (ssize_t)size; +} + +static ssize_t rdcorereg_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret = 0; + ret = snprintf(buf, PAGE_SIZE, "rdcorereg:0x%x!\n", rdcorereg); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>rdcorereg\n"); + return ret; +} + +static ssize_t rdcorereg_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + unsigned int read_value; + read_value = ioread32((void *)((unsigned long)ics_adapter->config_reg_virt_addr + CONFIG_REG_VIRT_ADDR_OFFSET_ICS_VERSION)); + + if (read_value != 0x44400a7c && read_value != 0x4440031b) { + printk(KERN_ERR"[%s]: read_value error : 0x%x!\n", __FUNCTION__ , read_value); + return -EINVAL; + } + + return (ssize_t)size; +} + +static ssize_t wrcorereg_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret =0; + u32 value = 0; + ret = snprintf(buf, PAGE_SIZE, "wrcorereg:0x%x!\n", wrcorereg); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>wrcorereg\n"); + value = ioread32((void *)((unsigned long)ics_adapter->config_reg_virt_addr + CONFIG_REG_VIRT_ADDR_OFFSET_ICS_BASE_ADDR)); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "register value: %u\n", value); + return ret; +} + +static ssize_t wrcorereg_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + unsigned int read_value; + + if (sscanf(buf, "0x%x", &wrcorereg) != 1) + return -EINVAL; + + iowrite32(wrcorereg, (void *)((unsigned long)ics_adapter->config_reg_virt_addr + CONFIG_REG_VIRT_ADDR_OFFSET_ICS_BASE_ADDR)); + udelay(1); + + read_value = ioread32((void *)((unsigned long)ics_adapter->config_reg_virt_addr + CONFIG_REG_VIRT_ADDR_OFFSET_ICS_BASE_ADDR)); + + if (read_value != wrcorereg) { + printk(KERN_ERR"[%s]: read_value error : 0x%x!\n", __FUNCTION__ , read_value); + return -EINVAL; + } else { + printk(KERN_DEBUG "[%s]: write %lx success!\n", __FUNCTION__, (unsigned long)ics_adapter->config_reg_virt_addr + CONFIG_REG_VIRT_ADDR_OFFSET_ICS_BASE_ADDR); + } + + return (ssize_t)size; +} + +static ssize_t wrregvbusclk_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret = 0; + ret = snprintf(buf, PAGE_SIZE, "wrregvbusclk:0x%x!\n", wrregvbusclk); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>wrregvbusclk\n"); + return ret; +} + +static ssize_t wrregvbusclk_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + if (sscanf(buf, "%d", &wrregvbusclk) != 1) + return -EINVAL; + + if (wrregvbusclk != 480000000 && wrregvbusclk != 322000000 && wrregvbusclk != 207500000) { + return -EINVAL; + } + + if (wrregvbusclk == 480000000) { + iowrite32(PERI_CLKDIV8_SEL_VCODECBUS_PLL2, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV8)); + iowrite32(PERI_CLKDIV5_SET_FREQ_DIV4_VCODECBUS, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV5)); + } else if(wrregvbusclk == 322000000){ + iowrite32(PERI_CLKDIV8_SEL_VCODECBUS_PLL0, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV8)); + iowrite32(PERI_CLKDIV5_SET_FREQ_DIV5_VCODECBUS, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV5)); + } else if(wrregvbusclk == 207500000){ + iowrite32(PERI_CLKDIV8_SEL_VCODECBUS_PLL0, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV8)); + iowrite32(PERI_CLKDIV5_SET_FREQ_DIV8_VCODECBUS, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV5)); + } + + return (ssize_t)size; +} + +static ssize_t wrregcnnclk_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret = 0; + ret = snprintf(buf, PAGE_SIZE, "wrregcnnclk:0x%x!\n", wrregcnnclk); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>wrregcnnclk\n"); + return ret; +} + +static ssize_t wrregcnnclk_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + if (sscanf(buf, "%d", &wrregcnnclk) != 1) + return -EINVAL; + + if (wrregcnnclk != 960000000 && wrregvbusclk != 640000000 && wrregvbusclk != 415000000) { + return -EINVAL; + } + + if (wrregcnnclk == 960000000) { + iowrite32(PERI_CLKDIV8_SEL_ICS_PLL2, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV8)); + iowrite32(PERI_CLKDIV15_SEL_FREQ_DIV2_ICS, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV15)); + } else if (wrregcnnclk == 640000000) { + iowrite32(PERI_CLKDIV8_SEL_ICS_PLL2, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV8)); + iowrite32(PERI_CLKDIV15_SEL_FREQ_DIV3_ICS, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV15)); + } else if (wrregcnnclk == 415000000) { + iowrite32(PERI_CLKDIV8_SEL_ICS_PLL0, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV8)); + iowrite32(PERI_CLKDIV15_SEL_FREQ_DIV4_ICS, (void *)((unsigned long)ics_adapter->peri_io_addr + PERI_OFFSET_CLKDIV15)); + } + return (ssize_t)size; +} + +static ssize_t wrreglmt_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int ret = 0; + ret = snprintf(buf, PAGE_SIZE, "wrreglmt:0x%x!\n", wrreglmt); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "usage: echo param>wrreglmt_es\n"); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "wrreglmt = 0xf6b ,ics core:400M,vcodec bus:207.5M\n"); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "wrreglmt = 0xaaa,ics core:640M,vcodec bus:480M\n"); + ret += snprintf(buf+ret, (PAGE_SIZE-ret), "wrreglmt = 0xdd5 ,ics core:830M,vcodec bus:480M\n"); + return ret; +} + +static ssize_t wrreglmt_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t size) +{ + void __iomem *axi; + unsigned int read_value = 0; + if (sscanf(buf, "0x%x", &wrreglmt) != 1) + return -EINVAL; + if (wrreglmt != 0xf6b && + wrreglmt != 0xaaa && + wrreglmt != 0xdd5 ) { + printk(KERN_ERR"[%s]: limiter:input error value:%d!\n", + __FUNCTION__ , wrreglmt); + return -EINVAL; + } + axi = ioremap((unsigned long)0xe8950000,(unsigned long)0xfff); + iowrite32(0x1,(void *)((unsigned long)axi+0x0c)); + udelay(10); + iowrite32(wrreglmt,(void *)((unsigned long)axi+0x10)); + udelay(10); + iowrite32(0x40, (void *)((unsigned long)axi+0x14)); + read_value = ioread32((void *)((unsigned long)axi+ 0x10)); + printk(KERN_DEBUG"[%s]:limit value = 0x%x",__FUNCTION__, read_value); + iounmap(axi); + return (ssize_t)size; +} + +static const struct class_attribute ics_attrs[] = { + __ATTR(setclkrate, 0644, setclkrate_show, setclkrate_store), + __ATTR(setprofile, 0644, setprofile_show, setprofile_store), + __ATTR(ipuopen, 0644, ipuopen_show, ipuopen_store), + __ATTR(ipurelease, 0644, ipurelease_show, ipurelease_store), + __ATTR(ipuversion, 0644, ipuversion_show, ipuversion_store), + __ATTR(resetproc, 0644, resetproc_show, resetproc_store), + __ATTR(ipurstcrtenv, 0644, ipurstcrtenv_show, ipurstcrtenv_store), + __ATTR(ipurstdstenv, 0644, ipurstdstenv_show, ipurstdstenv_store), + __ATTR(pusetreg, 0644, pusetreg_show, pusetreg_store), + __ATTR(pdsetreg, 0644, pdsetreg_show, pdsetreg_store), + __ATTR(rdcorereg, 0644, rdcorereg_show, rdcorereg_store), + __ATTR(wrcorereg, 0644, wrcorereg_show, wrcorereg_store), + __ATTR(wrregvbusclk, 0644, wrregvbusclk_show, wrregvbusclk_store), + __ATTR(wrregcnnclk, 0644, wrregcnnclk_show, wrregcnnclk_store), + __ATTR(wrreglmt, 0644, wrreglmt_show, wrreglmt_store), +}; + +static int create_ics_attrs(struct class *class) +{ + unsigned int i = 0; + int ret = 0; + + for (i = 0; i < (sizeof(ics_attrs)/sizeof(struct class_attribute)); i++) { + ret = class_create_file(class, &ics_attrs[i]); + if (ret < 0) { + break; + } + } + + return ret; +} + +static void remove_ics_attrs(struct class *class) +{ + unsigned int i = 0; + for (i = 0; i < (sizeof(ics_attrs)/sizeof(struct class_attribute)); i++) { + class_remove_file(class, &ics_attrs[i]); + } +} + +static int __init ics_debug_init(void) { + int ret = 0; + printk(KERN_ERR"[%s:%d], test begin\n", __FUNCTION__, __LINE__); + + ics_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(ics_class)) { + printk(KERN_ERR"[%s:%d], class create error!\n", __FUNCTION__, __LINE__); + return -EFAULT; + } + + ret = create_ics_attrs(ics_class); + if(ret < 0) { + class_destroy(ics_class); + printk(KERN_ERR"[%s:%d], create_ics_attrs error!\n", __FUNCTION__, __LINE__); + return -EFAULT; + } + + ics_adapter = get_ipu_adapter(); + + return 0; + +} + +static void __exit ics_debug_exit(void) { + printk(KERN_ERR"[%s:%d], test end\n", __FUNCTION__, __LINE__); + remove_ics_attrs(ics_class); + class_destroy(ics_class); + + return; +} + +module_init(ics_debug_init); +module_exit(ics_debug_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hisilicon"); + diff --git a/drivers/hisi/ics/ics_debug.h b/drivers/hisi/ics/ics_debug.h new file mode 100644 index 000000000000..8ae64c964feb --- /dev/null +++ b/drivers/hisi/ics/ics_debug.h @@ -0,0 +1,6 @@ +#ifndef __ICS_DEBUG_H__ +#define __ICS_DEBUG_H__ + +#include "ics_debug_proxy.h" + +#endif diff --git a/drivers/hisi/ics/ics_debug_proxy.c b/drivers/hisi/ics/ics_debug_proxy.c new file mode 100644 index 000000000000..9973bfe3f348 --- /dev/null +++ b/drivers/hisi/ics/ics_debug_proxy.c @@ -0,0 +1,77 @@ +#include "ipu_clock.h" +#include "ipu_smmu_drv.h" +#include "ics_debug_proxy.h" + +struct ioctl_out_params { + bool ret_directly; + void *memory_node; + //TODO: add more out params here +}; + +extern struct cambricon_ipu_private *adapter; +extern int regulator_ip_vipu_enable(void); +extern int regulator_ip_vipu_disable(void); +extern void ipu_reset_proc(unsigned int addr); +extern void ipu_interrupt_init(void); +extern long ipu_set_profile(struct cambricon_ipu_private *, unsigned long, struct ioctl_out_params *); + +int call_regulator_ip_vipu_enable(void) +{ + return regulator_ip_vipu_enable(); +} +EXPORT_SYMBOL(call_regulator_ip_vipu_enable); + +int call_regulator_ip_vipu_disable(void) +{ + return regulator_ip_vipu_disable(); +} +EXPORT_SYMBOL(call_regulator_ip_vipu_disable); + +void call_ipu_reset_proc(unsigned int addr) +{ + ipu_reset_proc(addr); +} +EXPORT_SYMBOL(call_ipu_reset_proc); + +void call_ipu_interrupt_init(void) +{ + ipu_interrupt_init(); +} +EXPORT_SYMBOL(call_ipu_interrupt_init); + +void * get_ipu_adapter(void) +{ + return adapter; +} +EXPORT_SYMBOL(get_ipu_adapter); + +int call_ipu_clock_start(void *clock) +{ + return ipu_clock_start(clock); +} +EXPORT_SYMBOL(call_ipu_clock_start); + +int call_ipu_clock_set_rate(void *clock, unsigned int clock_rate) +{ + return ipu_clock_set_rate(clock, clock_rate); +} +EXPORT_SYMBOL(call_ipu_clock_set_rate); + +void call_ipu_clock_stop(void *clock) +{ + ipu_clock_stop(clock); +} +EXPORT_SYMBOL(call_ipu_clock_stop); + +void call_ipu_smmu_init(unsigned long ttbr0, unsigned long smmu_rw_err_phy_addr, bool port_sel, bool hardware_start) +{ + ipu_smmu_init(ttbr0, smmu_rw_err_phy_addr, port_sel, hardware_start); +} +EXPORT_SYMBOL(call_ipu_smmu_init); + +void call_ipu_set_profile(unsigned long profile) +{ + ipu_set_profile(0, profile, 0); +} +EXPORT_SYMBOL(call_ipu_set_profile); + diff --git a/drivers/hisi/ics/ics_debug_proxy.h b/drivers/hisi/ics/ics_debug_proxy.h new file mode 100644 index 000000000000..cd5dd7ca8f16 --- /dev/null +++ b/drivers/hisi/ics/ics_debug_proxy.h @@ -0,0 +1,17 @@ +#ifndef __ICS_DEBUG_PROXY_H__ +#define __ICS_DEBUG_PROXY_H__ + +#include "cambricon_ipu.h" + +int call_regulator_ip_vipu_enable(void); +int call_regulator_ip_vipu_disable(void); +void call_ipu_reset_proc(unsigned int addr); +void call_ipu_interrupt_init(void); +void * get_ipu_adapter(void); +int call_ipu_clock_start(void *clk); +int call_ipu_clock_set_rate(void *clock, unsigned int clock_rate); +void call_ipu_clock_stop(void *clock); +void call_ipu_smmu_init(unsigned long ttbr0, unsigned long smmu_rw_err_phy_addr, bool port_sel, bool hardware_start); +void call_ipu_set_profile(unsigned long profile); + +#endif diff --git a/drivers/hisi/ics/ipu_clock.c b/drivers/hisi/ics/ipu_clock.c new file mode 100644 index 000000000000..a724211e63d1 --- /dev/null +++ b/drivers/hisi/ics/ipu_clock.c @@ -0,0 +1,233 @@ +#include +#include +#include "ipu_clock.h" +// #include "ipu_mntn.h" +#include "cambricon_ipu.h" + +// #define CONFIG_IPU_CLOCK_CONTROL + +extern struct cambricon_ipu_private *adapter; + +/* this func use mutex, for interface only, and SHOULD NOT be called by other ipu_clock functions */ +int ipu_clock_init(struct device *dev, struct ics_clock *clk, bool lpm3_set_vcodecbus) +{ + int property_rd; + struct device_node *node; + + mutex_lock(&clk->clk_mutex); + clk->lpm3_set_vcodecbus = lpm3_set_vcodecbus; + + /* get clock of "clk-ics" from CLK API */ + clk->ipu_clk_ptr = devm_clk_get(dev, "clk-ics"); + + if (IS_ERR_OR_NULL(clk->ipu_clk_ptr)) { + printk(KERN_ERR"[%s]: IPU_ERROR:get clock failed, ipu_clk_ptr is %pK\n", __func__, clk->ipu_clk_ptr); + mutex_unlock(&clk->clk_mutex); + return -ENODEV; + } + + clk->vcodecbus_clk_ptr = devm_clk_get(dev, "clk_vcodecbus"); + printk(KERN_DEBUG "[%s]: IPU_DEBUG vcodecbus_clk_ptr is %pK\n", __func__, clk->vcodecbus_clk_ptr); +#ifdef CONFIG_HISI_IPU_SET_VCODECBUS + if (IS_ERR_OR_NULL(clk->vcodecbus_clk_ptr)) { + printk(KERN_ERR"[%s]: IPU_ERROR:get clock failed, vcodecbus_clk_ptr is %pK\n", __func__, clk->vcodecbus_clk_ptr); + mutex_unlock(&clk->clk_mutex); + return -ENODEV; + } +#endif + + node = of_find_node_by_name(dev->of_node, "ipu-and-vcodecbus-clock-rate"); + if(!node) { + printk(KERN_ERR"[%s]: IPU_ERROR:find clock node error\n", __func__); + mutex_unlock(&clk->clk_mutex); + return -ENODEV; + } + + property_rd = of_property_read_u32(node, "start-rate", &clk->start_rate); + property_rd |= of_property_read_u32(node, "stop-rate", &clk->stop_rate); + property_rd |= of_property_read_u32(node, "ipu-low", &clk->ipu_low); + property_rd |= of_property_read_u32(node, "ipu-middle", &clk->ipu_middle); + property_rd |= of_property_read_u32(node, "ipu-high", &clk->ipu_high); + property_rd |= of_property_read_u32(node, "ipu-low-temperature", &clk->ipu_low_temperature); + property_rd |= of_property_read_u32(node, "vcodecbus-low", &clk->vcodecbus_low); + property_rd |= of_property_read_u32(node, "vcodecbus-middle", &clk->vcodecbus_middle); + property_rd |= of_property_read_u32(node, "vcodecbus-high", &clk->vcodecbus_high); + property_rd |= of_property_read_u32(node, "vcodecbus-default", &clk->vcodecbus_default); + property_rd |= of_property_read_u32(node, "vcodecbus-high2default", &clk->vcodecbus_high2default); + if (property_rd) { + printk(KERN_ERR"[%s]: IPU_ERROR:read property of clock error\n", __func__); + mutex_unlock(&clk->clk_mutex); + return -ENODEV; + } + +#ifdef CONFIG_ES_VDEC_LOW_FREQ + clk->vcodecbus_high = clk->vcodecbus_middle; + clk->ipu_high = clk->ipu_middle; + clk->start_rate = clk->ipu_middle; +#endif + + printk(KERN_DEBUG"[%s]: get clk rate done, start clk rate:%u, stop clk rate:%u\n", + __func__, clk->start_rate, clk->stop_rate); + + clk->curr_rate = IPU_CLOCK_UNSET; + mutex_unlock(&clk->clk_mutex); + return 0; +} + +static int ipu_clock_set(struct ics_clock *clk, unsigned int new_rate) +{ + int ret; + unsigned int target_rate = new_rate; + + if (new_rate == clk->curr_rate) { + printk(KERN_ERR"[%s]: IPU_WARN:set some IPU clock rate %d, ignored\n", __func__, target_rate); + return 0; + } + +#ifdef CONFIG_IPU_CLOCK_CONTROL + if (clk->ipu_high == target_rate) { + /* for HIGH, set IPU clock to HIGH */ + ret = clk_set_rate(clk->ipu_clk_ptr, (unsigned long)target_rate); + if (ret) { + /* in low temperature, clk set rate to HIGH will fail, in this case try to set rate to another rate */ + printk(KERN_ERR"[%s]: IPU_ERROR:set ipu rate %d fail (possible in low temperature), ret:%d, try to set %d\n", + __func__, target_rate, ret, clk->ipu_low_temperature); + target_rate = clk->ipu_low_temperature; + ret = clk_set_rate(clk->ipu_clk_ptr, (unsigned long)target_rate); + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:set ipu rate %d fail, ret:%d\n", __func__, target_rate, ret); + rdr_system_error((unsigned int)MODID_NPU_EXC_SET_BACK_CLOCK_FAIL, 0, 0); + return ret; + } + } else { +#ifdef CONFIG_HISI_IPU_SET_VCODECBUS + if (!clk->lpm3_set_vcodecbus) { + /* for HIGH set IPU rate ok, set VCODECBUS to HIGH */ + ret = clk_set_rate(clk->vcodecbus_clk_ptr, clk->vcodecbus_high); + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:set vcodec rate %d fail, ret:%d, ignore\n", __func__, clk->vcodecbus_high, ret); + } + } +#endif + } + } else { +#ifdef CONFIG_HISI_IPU_SET_VCODECBUS + /* for MIDDLE or LOW, set VCODECBUS to default if necessary (when alter from HIGH) */ + if (!clk->lpm3_set_vcodecbus && clk->ipu_high == clk->curr_rate) { + /* set vcodec bus to "VCODECBUS_CLOCK_DEFAULT", which is used as the default rate for VENC/VDEC */ + ret = clk_set_rate(clk->vcodecbus_clk_ptr, clk->vcodecbus_high2default); + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:set vcodec rate %d fail, ret:%d, ignore\n", __func__, clk->vcodecbus_high2default, ret); + } + + ret = clk_set_rate(clk->vcodecbus_clk_ptr, clk->vcodecbus_default); + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:set vcodec rate %d fail, ret:%d, ignore\n", __func__, clk->vcodecbus_default, ret); + } + } +#endif + + ret = clk_set_rate(clk->ipu_clk_ptr, (unsigned long)target_rate); + if (ret) { + /* in low temperature, clk set rate to HIGH will fail, in this case try to set rate to MIDDLE */ + printk(KERN_ERR"[%s]: IPU_ERROR:set ipu rate %d fail, ret:%d\n", __func__, target_rate, ret); + rdr_system_error((unsigned int)MODID_NPU_EXC_SET_CLOCK_FAIL, 0, 0); + return ret; + } + } + +#ifdef CONFIG_HISI_IPU_SET_VCODECBUS + printk(KERN_ERR"[%s]: IPU_NOTE: set clock done, ipu clock(try/actually/clk_get)=%d/%d/%ld, vcodecbus clock=%ld\n", + __func__, new_rate, target_rate, clk_get_rate(clk->ipu_clk_ptr), clk_get_rate(clk->vcodecbus_clk_ptr)); +#else + printk(KERN_ERR"[%s]: IPU_NOTE: set clock done, ipu clock(try/actually/clk_get)=%d/%d/%ld\n", + __func__, new_rate, target_rate, clk_get_rate(clk->ipu_clk_ptr)); +#endif + +#endif // CONFIG_IPU_CLOCK_CONTROL + clk->curr_rate = target_rate; + + return 0; +} + +/* this func use mutex, for interface only, and SHOULD NOT be called by other ipu_clock functions */ +int ipu_clock_start(struct ics_clock *clk) +{ + int ret = 0; + + mutex_lock(&clk->clk_mutex); + +#ifdef CONFIG_IPU_CLOCK_CONTROL + /* WARNING: clk_prepare_enable should NOT be called in interrupt because it contains mutex. + If needed in furture, use API: clk_prepare and clk_enable instead of clk_prepare_enable + in interrupt functions. */ + ret = clk_prepare_enable(clk->ipu_clk_ptr); +#endif + + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:clk prepare enable failed,ret=%d\n", __func__, ret); + mutex_unlock(&clk->clk_mutex); + return ret; + } + + /* NOTE: here need not call "clk_prepare_enable(clk->vcodecbus_clk_ptr)" because because it is used by both IPU and VCODEC + process can guarentee it!! */ + + clk->curr_rate = IPU_CLOCK_UNSET; + ret = ipu_clock_set(clk, clk->start_rate); + if (ret) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu_clock_set_rate failed,ret=%d\n", __func__, ret); + mutex_unlock(&clk->clk_mutex); + return ret; + } + mutex_unlock(&clk->clk_mutex); + return 0; +} + +/* this func use mutex, for interface only, and SHOULD NOT be called by other ipu_clock functions */ +int ipu_clock_set_start_rate(struct ics_clock *clk, unsigned int new_rate) +{ + mutex_lock(&clk->clk_mutex); + + if (clk->ipu_high == new_rate || + clk->ipu_middle == new_rate || + clk->ipu_low == new_rate) { + + /* vote voltage hold lock if neccessary */ + clk->start_rate = new_rate; + + mutex_unlock(&clk->clk_mutex); + return 0; + } else { + printk(KERN_ERR"[%s]: IPU_ERROR:invalid start rate=%u\n", __func__, new_rate); + mutex_unlock(&clk->clk_mutex); + return -EINVAL; + } +} + +/* this func use mutex, for interface only, and SHOULD NOT be called by other ipu_clock functions */ +int ipu_clock_set_rate(struct ics_clock *clk, unsigned int new_rate) +{ + int ret; + + mutex_lock(&clk->clk_mutex); + ret = ipu_clock_set(clk, new_rate); + mutex_unlock(&clk->clk_mutex); + + return ret; +} + +/* this func use mutex, for interface only, and SHOULD NOT be called by other ipu_clock functions */ +void ipu_clock_stop(struct ics_clock *clk) +{ + mutex_lock(&clk->clk_mutex); + +#ifdef CONFIG_IPU_CLOCK_CONTROL + clk_disable_unprepare(clk->ipu_clk_ptr); +#endif // CONFIG_IPU_CLOCK_CONTROL + + /* NOTE: here need not call "clk_disable_unprepare(clk->vcodecbus_clk_ptr)" because it is used by both IPU and VCODEC + process can guarentee it!! */ + mutex_unlock(&clk->clk_mutex); +} + diff --git a/drivers/hisi/ics/ipu_clock.h b/drivers/hisi/ics/ipu_clock.h new file mode 100644 index 000000000000..173baff94920 --- /dev/null +++ b/drivers/hisi/ics/ipu_clock.h @@ -0,0 +1,48 @@ +/* Module internals + * + * Copyright (C) 2017 Hisilicon, Inc. All Rights Reserved. + * + * These coded instructions, statements, and computer programs are the + * copyrighted works and confidential proprietary information of + * Hisilicon Inc. and its licensors, and are licensed to the recipient + * under the terms of a separate license agreement. They may be + * adapted and modified by bona fide purchasers under the terms of the + * separate license agreement for internal use, but no adapted or + * modified version may be disclosed or distributed to third parties + * in any manner, medium, or form, in whole or in part, without the + * prior written consent of Hisilicon Inc. + */ + +#ifndef _IPU_CLOCK_H +#define _IPU_CLOCK_H + +#include +#include + +#define IPU_CLOCK_UNSET (0) + +struct ics_clock { + struct clk *ipu_clk_ptr; + struct clk *vcodecbus_clk_ptr; + unsigned int ipu_low; + unsigned int ipu_middle; + unsigned int ipu_high; + unsigned int ipu_low_temperature; + unsigned int vcodecbus_low; + unsigned int vcodecbus_middle; + unsigned int vcodecbus_high; + unsigned int vcodecbus_default; + unsigned int vcodecbus_high2default; + unsigned int start_rate; + unsigned int curr_rate; + unsigned int stop_rate; + struct mutex clk_mutex; + bool lpm3_set_vcodecbus; +}; + +extern int ipu_clock_init(struct device *dev, struct ics_clock *clk, bool lpm3_set_vcodecbus); +extern int ipu_clock_start(struct ics_clock *clk); +extern void ipu_clock_stop(struct ics_clock *clk); +extern int ipu_clock_set_rate(struct ics_clock *clk, unsigned int new_rate); +extern int ipu_clock_set_start_rate(struct ics_clock *clk, unsigned int new_rate); +#endif diff --git a/drivers/hisi/ics/ipu_mntn.c b/drivers/hisi/ics/ipu_mntn.c new file mode 100644 index 000000000000..54bcebd470f3 --- /dev/null +++ b/drivers/hisi/ics/ipu_mntn.c @@ -0,0 +1,433 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipu_mntn.h" +#include "ipu_smmu_drv.h" +#include "cambricon_ipu.h" + +struct rdr_exception_info_s ipu_excetption_info[] = { + { + .e_modid = (u32)MODID_NPU_EXC_DEAD, + .e_modid_end = (u32)MODID_NPU_EXC_DEAD, + .e_process_priority = RDR_ERR, + .e_reboot_priority = RDR_REBOOT_NO, + .e_notify_core_mask = RDR_NPU, + .e_reset_core_mask = RDR_NPU, + .e_from_core = RDR_NPU, + .e_reentrant = (u32)RDR_REENTRANT_DISALLOW, + .e_exce_type = NPU_S_EXCEPTION, + .e_exce_subtype = NPU_EXC_DEAD, + .e_upload_flag = (u32)RDR_UPLOAD_YES, + .e_from_module = "NPU", + .e_desc = "NPU_EXC_DEAD", + }, + { + .e_modid = (u32)MODID_NPU_EXC_SET_BACK_CLOCK_FAIL, + .e_modid_end = (u32)MODID_NPU_EXC_SET_BACK_CLOCK_FAIL, + .e_process_priority = RDR_ERR, + .e_reboot_priority = RDR_REBOOT_NO, + .e_notify_core_mask = RDR_NPU, + .e_reset_core_mask = RDR_NPU, + .e_from_core = RDR_NPU, + .e_reentrant = (u32)RDR_REENTRANT_DISALLOW, + .e_exce_type = NPU_S_EXCEPTION, + .e_exce_subtype = NPU_SET_BACK_CLOCK_FAIL, + .e_upload_flag = (u32)RDR_UPLOAD_YES, + .e_from_module = "NPU", + .e_desc = "NPU_SET_BACK_CLOCK_FAIL", + }, + { + .e_modid = (u32)MODID_NPU_EXC_SET_CLOCK_FAIL, + .e_modid_end = (u32)MODID_NPU_EXC_SET_CLOCK_FAIL, + .e_process_priority = RDR_ERR, + .e_reboot_priority = RDR_REBOOT_NO, + .e_notify_core_mask = RDR_NPU, + .e_reset_core_mask = RDR_NPU, + .e_from_core = RDR_NPU, + .e_reentrant = (u32)RDR_REENTRANT_DISALLOW, + .e_exce_type = NPU_S_EXCEPTION, + .e_exce_subtype = NPU_SET_CLOCK_FAIL, + .e_upload_flag = (u32)RDR_UPLOAD_YES, + .e_from_module = "NPU", + .e_desc = "NPU_SET_CLOCK_FAIL", + }, + { + .e_modid = (u32)MODID_NPU_EXC_SET_POWER_UP_FAIL, + .e_modid_end = (u32)MODID_NPU_EXC_SET_POWER_UP_FAIL, + .e_process_priority = RDR_ERR, + .e_reboot_priority = RDR_REBOOT_NO, + .e_notify_core_mask = RDR_NPU, + .e_reset_core_mask = RDR_NPU, + .e_from_core = RDR_NPU, + .e_reentrant = (u32)RDR_REENTRANT_DISALLOW, + .e_exce_type = NPU_S_EXCEPTION, + .e_exce_subtype = NPU_POWER_UP_FAIL, + .e_upload_flag = (u32)RDR_UPLOAD_YES, + .e_from_module = "NPU", + .e_desc = "NPU_POWER_UP_FAIL", + }, + { + .e_modid = (u32)MODID_NPU_EXC_SET_POWER_UP_STATUS_FAULT, + .e_modid_end = (u32)MODID_NPU_EXC_SET_POWER_UP_STATUS_FAULT, + .e_process_priority = RDR_ERR, + .e_reboot_priority = RDR_REBOOT_NO, + .e_notify_core_mask = RDR_NPU, + .e_reset_core_mask = RDR_NPU, + .e_from_core = RDR_NPU, + .e_reentrant = (u32)RDR_REENTRANT_DISALLOW, + .e_exce_type = NPU_S_EXCEPTION, + .e_exce_subtype = NPU_POWER_UP_STA_FAULT, + .e_upload_flag = (u32)RDR_UPLOAD_YES, + .e_from_module = "NPU", + .e_desc = "NPU_POWER_UP_STA_FAULT", + }, + { + .e_modid = (u32)MODID_NPU_EXC_SET_POWER_DOWN_FAIL, + .e_modid_end = (u32)MODID_NPU_EXC_SET_POWER_DOWN_FAIL, + .e_process_priority = RDR_ERR, + .e_reboot_priority = RDR_REBOOT_NO, + .e_notify_core_mask = RDR_NPU, + .e_reset_core_mask = RDR_NPU, + .e_from_core = RDR_NPU, + .e_reentrant = (u32)RDR_REENTRANT_DISALLOW, + .e_exce_type = NPU_S_EXCEPTION, + .e_exce_subtype = NPU_POWER_DOWN_FAIL, + .e_upload_flag = (u32)RDR_UPLOAD_YES, + .e_from_module = "NPU", + .e_desc = "NPU_POWER_DOWN_FAIL", + }, + { + .e_modid = (u32)MODID_NPU_EXC_INTERRUPT_ABNORMAL, + .e_modid_end = (u32)MODID_NPU_EXC_INTERRUPT_ABNORMAL, + .e_process_priority = RDR_ERR, + .e_reboot_priority = RDR_REBOOT_NO, + .e_notify_core_mask = RDR_NPU, + .e_reset_core_mask = RDR_NPU, + .e_from_core = RDR_NPU, + .e_reentrant = (u32)RDR_REENTRANT_DISALLOW, + .e_exce_type = NPU_S_EXCEPTION, + .e_exce_subtype = NPU_INTERRUPT_ABNORMAL, + .e_upload_flag = (u32)RDR_UPLOAD_YES, + .e_from_module = "NPU", + .e_desc = "NPU_INTERRUPT_ABNORMAL", + } +}; + +struct work_struct ipu_dump_work; +struct workqueue_struct *ipu_mntn_rdr_wq; + +struct ipu_mntn_info_s ipu_mntn_info; +struct ipu_reg_info_s ipu_reg_info; + +extern struct cambricon_ipu_private *adapter; + +/******************************************************************** +Description: ipu_mntn_copy_reg_to_bbox +input: char *src_addr, unsigned int* offset, unsigned int len +output: NA +return: void +********************************************************************/ +static int ipu_mntn_copy_reg_to_bbox(char *src_addr, unsigned int len) +{ + unsigned int temp_offset = 0; + + if ((NULL == src_addr) || (0 == len)) { + printk(KERN_ERR"[%s]:IPU_ERROR:Input parameter is error!\n", __func__); + return -EINVAL; + } + + temp_offset = ipu_mntn_info.bbox_addr_offset + len; + //ipu_bbox alloc size 64k + if (temp_offset > 0x10000) { + printk(KERN_ERR"[%s]:IPU_ERROR:Copy log to bbox size is error! temp_offset=%d\n", __func__, temp_offset); + temp_offset = 0; + ipu_mntn_info.bbox_addr_offset = 0; + return -ENOMEM ; + } + + memcpy(((char*)ipu_mntn_info.rdr_addr + ipu_mntn_info.bbox_addr_offset), src_addr, len); + ipu_mntn_info.bbox_addr_offset = temp_offset; + + return 0; + +} +/******************************************************************** +Description: ipu_mntn_write_adapter_info +input: char *file_path +output: NA +return: void +********************************************************************/ +static void ipu_mntn_write_adapter_info(void) +{ + char log_buf[IPU_LINE_MAX + 1] = {0}; + + snprintf(log_buf, IPU_LINE_MAX, "npu_status=%d, ttbr0=%lx, inst_set=%d, offchip{set=%x, base=%x}, last_computed_task=%ld.\r\n", + adapter->ipu_power_up, + adapter->smmu_ttbr0, + adapter->boot_inst_set.boot_inst_recorded_is_config, + adapter->boot_inst_set.access_ddr_addr_is_config, + adapter->boot_inst_set.ipu_access_ddr_addr, + adapter->computed_task_cnt); + + ipu_mntn_copy_reg_to_bbox(log_buf, strlen(log_buf)); + + return; +} +/******************************************************************** +Description: ipu_mntn_write_peri_reg_info +input: char *file_path +output: NA +return: void +********************************************************************/ +static void ipu_mntn_write_peri_reg_info(void) +{ + char log_buf[IPU_LINE_MAX + 1] = {0}; + + snprintf(log_buf, IPU_LINE_MAX, "peri_stat=%x, ppll_select=%x, power_stat=%x, power_ack=%x, reset_stat=%x, perclken=%x, perstat=%x.\r\n", + ipu_reg_info.peri_reg.peri_stat, + ipu_reg_info.peri_reg.ppll_select, + ipu_reg_info.peri_reg.power_stat, + ipu_reg_info.peri_reg.power_ack, + ipu_reg_info.peri_reg.reset_stat, + ipu_reg_info.peri_reg.perclken0, + ipu_reg_info.peri_reg.perstat0); + + ipu_mntn_copy_reg_to_bbox(log_buf, strlen(log_buf)); + return; +} +/******************************************************************** +Description: ipu_mntn_write_mstr_reg_info +input: char *file_path +output: NA +return: void +********************************************************************/ +static void ipu_mntn_write_mstr_reg_info(void) +{ + char log_buf[IPU_BUF_LEN_MAX + 1] = {0}; + + snprintf(log_buf, IPU_BUF_LEN_MAX, "RD_BITMAP=%x, WR_BITMAP=%x, rd_cmd_total_cnt[0-3]={%x, %x, %x}, wr_cmd_total_cnt=%x\n", + ipu_reg_info.mstr_reg.rd_bitmap, + ipu_reg_info.mstr_reg.wr_bitmap, + ipu_reg_info.mstr_reg.rd_cmd_total_cnt0, + ipu_reg_info.mstr_reg.rd_cmd_total_cnt0, + ipu_reg_info.mstr_reg.rd_cmd_total_cnt2, + ipu_reg_info.mstr_reg.wr_cmd_total_cnt); + + ipu_mntn_copy_reg_to_bbox(log_buf, strlen(log_buf)); + + return; +} +/******************************************************************** +Description: ipu_mntn_write_reg_log +input: void +output: NA +return: void +********************************************************************/ +static void ipu_mntn_write_reg_log(void) +{ + switch (ipu_mntn_info.dump_info.modid) { + case MODID_NPU_EXC_DEAD: //lint !e650 + ipu_mntn_write_adapter_info(); + ipu_mntn_write_peri_reg_info(); + ipu_mntn_write_mstr_reg_info(); +#ifdef CONFIG_HUAWEI_DSM + ipu_mntn_copy_reg_to_bbox(register_info, strlen(register_info)); +#endif + break; + + case MODID_NPU_EXC_SET_BACK_CLOCK_FAIL: //lint !e650 + case MODID_NPU_EXC_SET_CLOCK_FAIL: //lint !e650 + case MODID_NPU_EXC_SET_POWER_UP_FAIL: //lint !e650 + case MODID_NPU_EXC_SET_POWER_UP_STATUS_FAULT: //lint !e650 + case MODID_NPU_EXC_SET_POWER_DOWN_FAIL: //lint !e650 + case MODID_NPU_EXC_INTERRUPT_ABNORMAL: //lint !e650 + break; + + default: + break; + } + + return; +} +/******************************************************************** +Description: ipu_mntn_rdr_dump +input: modid: module id + etype:exception type + coreid: core id + pathname: log path + pfn_cb: callback function +output: NA +return: NA +********************************************************************/ +static void ipu_mntn_rdr_dump(u32 modid, u32 etype, u64 coreid, char *pathname, pfn_cb_dump_done pfn_cb) +{ + if (NULL == pathname) { + printk(KERN_ERR"[%s]:IPU_ERROR:pathname is empty\n", __func__); + return; + } + ipu_mntn_info.dump_info.modid = modid; + ipu_mntn_info.dump_info.coreid = coreid; + ipu_mntn_info.dump_info.pathname = pathname; + ipu_mntn_info.dump_info.cb = pfn_cb; + ipu_mntn_info.bbox_addr_offset = 0; + queue_work(ipu_mntn_rdr_wq, &ipu_dump_work); + return; +} +/******************************************************************** +Description: ipu_mntn_rdr_reset +input: modid:module id + etype:exception type + coreid:core id +output: NA +return: NA +********************************************************************/ +static void ipu_mntn_rdr_reset(u32 modid, u32 etype, u64 coreid) +{ + return; +} +/******************************************************************** +Description: ipu_mntn_dump_work +input: struct work_struct *work +output: NA +return: NA +********************************************************************/ +static void ipu_mntn_dump_work(struct work_struct *work) +{ + ipu_mntn_write_reg_log(); + + if (ipu_mntn_info.dump_info.cb) { + ipu_mntn_info.dump_info.cb(ipu_mntn_info.dump_info.modid, ipu_mntn_info.dump_info.coreid); + } + + return; +} +/******************************************************************** +Description: ipu_mntn_register_exception +input: NA +output: NA +return: int +********************************************************************/ +static int ipu_mntn_register_exception(void) +{ + int ret; + unsigned int size; + unsigned long index; + + size = sizeof(ipu_excetption_info)/sizeof(struct rdr_exception_info_s); + for (index = 0; index < size; index++) { + /* error return 0, ok return modid */ + ret = rdr_register_exception(&ipu_excetption_info[index]); + if (!ret) { + printk(KERN_ERR"[%s]:IPU_ERROR:rdr_register_exception is failed! index=%ld ret=%d\n", __func__, index, ret); + return -EINTR; + } + } + + return 0; +} +/******************************************************************** +Description: register ipu dump and reset function +input: NA +output: NA +return: int +********************************************************************/ +static int ipu_mntn_register_core(void) +{ + int ret; + struct rdr_module_ops_pub s_soc_ops; + + s_soc_ops.ops_dump = ipu_mntn_rdr_dump; + s_soc_ops.ops_reset = ipu_mntn_rdr_reset; + /* register ipu core dump and reset function */ + ret = rdr_register_module_ops((u64)RDR_NPU, &s_soc_ops, &ipu_mntn_info.ipu_ret_info); + if (ret != 0) { + printk(KERN_ERR"[%s]:IPU_ERROR:rdr_register_module_ops is failed! ret=0x%08x\n", __func__, ret); + } + + return ret; +} +/******************************************************************** +Description: init ipu addr function +input: NA +output: NA +return: int +********************************************************************/ +static int ipu_mntn_addr_map(void) +{ + ipu_mntn_info.rdr_addr = hisi_bbox_map((phys_addr_t)ipu_mntn_info.ipu_ret_info.log_addr, ipu_mntn_info.ipu_ret_info.log_len); + if (!ipu_mntn_info.rdr_addr) { + printk(KERN_ERR"[%s]:IPU_ERROR:hisi_bbox_map is failed!\n", __func__); + return -EFAULT; + } + + return 0; +} +/******************************************************************** +Description: ipu_mntn_rdr_resource_init +input: NA +output: NA +return: int +********************************************************************/ +static int ipu_mntn_rdr_resource_init(void) +{ + ipu_mntn_rdr_wq = create_singlethread_workqueue("ipu_mntn_rdr_wq"); + if (!ipu_mntn_rdr_wq) { + printk(KERN_ERR"[%s]:IPU_ERROR:Create_singlethread_workqueue is failed!\n", __func__); + return -EINTR; + } + + INIT_WORK(&ipu_dump_work, ipu_mntn_dump_work); + + return 0; +} +/******************************************************************** +Description: ipu_mntn_rdr_init +input: void +output: NA +return: int +********************************************************************/ +int ipu_mntn_rdr_init(void) +{ + int ret; + + ret = ipu_mntn_rdr_resource_init(); + if (0 != ret) { + printk(KERN_ERR"[%s]:IPU_ERROR:ipu_mntn_rdr_resource_init is faild!ret=%d\n", __func__, ret); + return ret; + } + + /* register ics exception */ + ret = ipu_mntn_register_exception(); + if (0 != ret) { + printk(KERN_ERR"[%s]:IPU_ERROR:ipu_mntn_register_exception is faild!ret=%d\n", __func__, ret); + return ret; + } + + /* register ics dump and reset function */ + ret = ipu_mntn_register_core(); + if (0 != ret) { + printk(KERN_ERR"[%s]:IPU_ERROR:ipu_register_core is failed!ret=%d\n", __func__, ret); + return ret; + } + + ret = ipu_mntn_addr_map(); + if (0 != ret) { + printk(KERN_ERR"[%s]:IPU_ERROR:ipu_mntn_addr_map is failed!ret=%d\n", __func__, ret); + return ret; + } + + return 0; + +} diff --git a/drivers/hisi/ics/ipu_mntn.h b/drivers/hisi/ics/ipu_mntn.h new file mode 100644 index 000000000000..ed2d146bde6d --- /dev/null +++ b/drivers/hisi/ics/ipu_mntn.h @@ -0,0 +1,69 @@ +#ifndef _IPU_MNTN_H_ +#define _IPU_MNTN_H_ + +#include +#include +#include +#include + +/* AI DRD */ +#define IPU_BUF_LEN_MAX (256) +#define IPU_LINE_MAX (128) + +enum rdr_ipu_system_error_type { + MODID_NPU_START = HISI_BB_MOD_NPU_START, + MODID_NPU_EXC_DEAD = MODID_NPU_START, + MODID_NPU_EXC_SET_BACK_CLOCK_FAIL, + MODID_NPU_EXC_SET_CLOCK_FAIL, + MODID_NPU_EXC_SET_POWER_UP_FAIL, + MODID_NPU_EXC_SET_POWER_UP_STATUS_FAULT, + MODID_NPU_EXC_SET_POWER_DOWN_FAIL, + MODID_NPU_EXC_INTERRUPT_ABNORMAL, + MODID_NPU_EXC_END = HISI_BB_MOD_NPU_END +}; + +struct rdr_dump_info_s { + u32 modid; + u64 coreid; + pfn_cb_dump_done cb; + char *pathname; +}; + +struct ipu_peri_reg_s { + unsigned int peri_stat; + unsigned int ppll_select; + unsigned int power_stat; + unsigned int power_ack; + unsigned int reset_stat; + unsigned int perclken0; + unsigned int perstat0; +}; + +struct ipu_mstr_reg_s { + unsigned int rd_bitmap; + unsigned int wr_bitmap; + unsigned int rd_cmd_total_cnt0; + unsigned int rd_cmd_total_cnt1; + unsigned int rd_cmd_total_cnt2; + unsigned int wr_cmd_total_cnt; +}; + +struct ipu_mntn_info_s { + unsigned int ipu_run_status; + unsigned int bbox_addr_offset; + struct rdr_register_module_result ipu_ret_info; + struct rdr_dump_info_s dump_info; + void *rdr_addr; +}; + +struct ipu_reg_info_s { + struct ipu_peri_reg_s peri_reg; + struct ipu_mstr_reg_s mstr_reg; +}; + +extern struct workqueue_struct *ipu_mntn_rdr_wq; +extern struct ipu_reg_info_s ipu_reg_info; + +extern int ipu_mntn_rdr_init(void); + +#endif diff --git a/drivers/hisi/ics/ipu_smmu_drv.c b/drivers/hisi/ics/ipu_smmu_drv.c new file mode 100644 index 000000000000..d32b898533f1 --- /dev/null +++ b/drivers/hisi/ics/ipu_smmu_drv.c @@ -0,0 +1,1052 @@ +#include +#include //for struct iommu_domain_data +#include //for struct iommu_domain +#include +#include +#include +#include "ipu_smmu_drv.h" +// #include "ipu_mntn.h" + +#define SMMU_MSTR_DEBUG_CONFIG_WR (16) +#define SMMU_MSTR_DEBUG_CONFIG_CS (17) +#define SMMU_MSTR_SET_DEBUG_PORT ((1 << SMMU_MSTR_DEBUG_CONFIG_WR) | (1 << SMMU_MSTR_DEBUG_CONFIG_CS)) +#define SMMU_MSTR_END_ACK_THRESHOLD (0x100) +#define SMMU_MSTR_INPUT_SEL_REGISTER (0x00000003) +#define SMMU_MSTR_ALL_STREAM_IS_END_ACK (0x0000000f) +#define SMMU_MSTR_GLB_BYPASS_NORMAL_MODE (0x00000000) +#define SMMU_MSTR_WDATA_BURST (0x00000010) +#define SMMU_MSTR_WR_VA_OUT_OF_128BYTE (0x00000008) +#define SMMU_MSTR_WR_VA_OUT_OF_BOUNDARY (0x00000004) +#define SMMU_MSTR_RD_VA_OUT_OF_128BYTE (0x00000002) +#define SMMU_MSTR_RD_VA_OUT_OF_BOUNDARY (0x00000001) +#define SMMU_MSTR_INTCLR_ALL (SMMU_MSTR_WDATA_BURST \ + | SMMU_MSTR_WR_VA_OUT_OF_128BYTE \ + | SMMU_MSTR_WR_VA_OUT_OF_BOUNDARY \ + | SMMU_MSTR_RD_VA_OUT_OF_128BYTE \ + | SMMU_MSTR_RD_VA_OUT_OF_BOUNDARY) + +#define SMMU_MSTR_INTCLR_ALL_UNMASK (0x00000000) +#define SMMU_MSTR_INTCLR_ALL_MASK (0x0000001f) +#define SMMU_MSTR_SMRX_0_LEN (0x00004000) +#define SMMU_MSTR_SMRX_START_ALL_STREAM (0x0000000f) +#define SMMU_INTCLR_NS_PTW_NS_STAT (0x00000020) +#define SMMU_INTCLR_NS_PTW_INVALID_STAT (0x00000010) +#define SMMU_INTCLR_NS_PTW_TRANS_STAT (0x00000008) +#define SMMU_INTCLR_NS_TLBMISS_STAT (0x00000004) +#define SMMU_INTCLR_NS_EXT_STAT (0x00000002) +#define SMMU_INTCLR_NS_PERMIS_STAT (0x00000001) +#define SMMU_COMMON_INTCLR_NS_ALL_MASK (0x0000003f) +#define SMMU_COMMON_INTCLR_NS_ALL (SMMU_INTCLR_NS_PTW_NS_STAT \ + | SMMU_INTCLR_NS_PTW_INVALID_STAT \ + | SMMU_INTCLR_NS_PTW_TRANS_STAT \ + | SMMU_INTCLR_NS_TLBMISS_STAT \ + | SMMU_INTCLR_NS_EXT_STAT \ + | SMMU_INTCLR_NS_PERMIS_STAT) +#define SMMU_CACHE_ALL_LEVEL_INVALID_LEVEL1 (0x00000003) +#define SMMU_CACHE_ALL_LEVEL_VALID_LEVEL1 (0x00000002) +#define SMMU_OPREF_CTRL_CONFIG_DUMMY (0x1) +#define SMMU_DEBUG_PORT_START1 (0x10000) +#define SMMU_DEBUG_PORT_START2 (0x20000) +struct smmu_master_reg_offset { + unsigned int smmu_mstr_base_addr; + unsigned int smmu_mstr_glb_bypass; + unsigned int smmu_mstr_end_ack; + unsigned int smmu_mstr_smrx_start; + unsigned int smmu_mstr_inpt_sel; + unsigned int smmu_mstr_intmask; + unsigned int smmu_mstr_intstat; + unsigned int smmu_mstr_intclr; + unsigned int smmu_mstr_dbg_port_in_0; + unsigned int smmu_mstr_dbg_port_out; + unsigned int smmu_mstr_smrx_0[IPU_SMMU_TOTAL_STREAM_ID_NUMBER]; + unsigned int read_cmd_total_cnt[IPU_SMMU_READ_STREAM_NUMBER]; + unsigned int read_cmd_miss_cnt[IPU_SMMU_READ_STREAM_NUMBER]; + unsigned int read_data_total_cnt[IPU_SMMU_READ_STREAM_NUMBER]; + unsigned int read_cmd_case_cnt[IPU_SMMU_TAG_COMPARE_CASE_NUMBER]; + unsigned int read_cmd_trans_latency; + unsigned int write_cmd_total_cnt; + unsigned int write_cmd_miss_cnt; + unsigned int write_data_total_cnt; + unsigned int write_cmd_case_cnt[IPU_SMMU_TAG_COMPARE_CASE_NUMBER]; + unsigned int write_cmd_trans_latency; +}; + +struct smmu_common_reg_offset { + unsigned int smmu_common_base_addr; + unsigned int smmu_scr; + unsigned int smmu_intmask_ns; + unsigned int smmu_intstat_ns; + unsigned int smmu_intclr_ns; + unsigned int smmu_cb_ttbr0; + unsigned int smmu_cb_ttbcr; + unsigned int smmu_scachei_all; + unsigned int smmu_addr_msb; + unsigned int smmu_err_rdaddr; + unsigned int smmu_err_wraddr; + unsigned int smmu_fama_ctrl1_ns; + unsigned int override_pref_addr; + unsigned int cfg_override_original_pref_addr; +}; + +struct smmu_master_reg_offset smmu_master_reg_offset; +struct smmu_common_reg_offset smmu_common_reg_offset; + +struct ion_client *ipu_ion_client = NULL; + +static struct iommu_domain *ipu_smmu_domain = NULL; +static struct gen_pool *ipu_iova_pool = NULL; + +struct smmu_manager { + void __iomem *master_io_addr; + void __iomem *common_io_addr; +}; + +struct smmu_manager smmu_manager; + +struct memory_manage_node memory_manager; + +#ifdef CONFIG_HUAWEI_DSM +char register_info[REGISTER_INFO_MAX_LEN] = {0}; +#endif /* CONFIG_HUAWEI_DSM */ + +DEFINE_MUTEX(ipu_pool_mutex);/*lint !e651 !e708 !e570 !e64 !e785 */ +DEFINE_MUTEX(ipu_mem_mngr_mutex);/*lint !e651 !e708 !e570 !e64 !e785 */ + +/* this func use mutex, for interface only, and SHOULD NOT be called by other mem_mngr functions */ +void ipu_mem_mngr_init(void) { + mutex_lock(&ipu_mem_mngr_mutex); + INIT_LIST_HEAD(&memory_manager.head); + mutex_unlock(&ipu_mem_mngr_mutex); +} + +/* this func use mutex, for interface only, and SHOULD NOT be called by other mem_mngr functions */ +void ipu_mem_mngr_deinit(unsigned long *reset_va) { + struct list_head *pos; + struct list_head *n; + struct memory_manage_node *memory_node; + + mutex_lock(&ipu_mem_mngr_mutex); + if (list_empty(&memory_manager.head)) { + printk(KERN_DEBUG"[%s]: no memory-leak\n", __func__); + mutex_unlock(&ipu_mem_mngr_mutex); + return; + } else { + printk(KERN_ERR"[%s]: IPU_ERROR:FATAL error, risk of memory-leak, try to unmap all memory in list\n", __func__); + } + + /* here should NOT use "list_for_each" but "list_for_each_safe" + - iterate over a list safe against removal of list entry */ + list_for_each_safe(pos, n, &memory_manager.head) { + memory_node = (struct memory_manage_node *)pos; + printk(KERN_ERR"[%s]: IPU_ERROR:Memory Leak: share_fd=%d,ipu_va=0x%pK,size=0x%lx\n", + __func__, memory_node->map.share_fd, (void *)memory_node->map.format.iova_start, memory_node->map.format.iova_size); + if (ipu_smmu_unmap(&memory_node->map)) { + printk(KERN_ERR"[%s]: IPU_ERROR:unmap failed\n", __func__); + } + + if (*reset_va == memory_node->map.format.iova_start) { + printk(KERN_ERR"[%s]: IPU_ERROR:virt addr is free\n", __func__); + *reset_va = 0; + } + + list_del(&memory_node->head); + kfree(&memory_node->head); + } + + mutex_unlock(&ipu_mem_mngr_mutex); + + return; +} + +void ipu_mem_mngr_dump(void) { + struct list_head *pos; + struct memory_manage_node *memory_node; + + list_for_each(pos, &memory_manager.head) { + memory_node = (struct memory_manage_node *)pos; + printk(KERN_DEBUG"[%s] share_fd=%d, VA_base=0x%pK, VA_length=0x%lx\n", __func__, + memory_node->map.share_fd, (void *)memory_node->map.format.iova_start, memory_node->map.format.iova_size); + } +} + +static void * ipu_mem_mngr_check_repeat(struct map_data *map, bool check_va) { + struct list_head *pos; + struct memory_manage_node *memory_node; + + list_for_each(pos, &memory_manager.head) { + memory_node = (struct memory_manage_node *)pos; + + if (memory_node->map.share_fd == map->share_fd && + memory_node->map.format.iova_start == map->format.iova_start && + memory_node->map.format.prot == map->format.prot && + (!check_va || memory_node->map.format.iova_size == map->format.iova_size)) { + return memory_node; + } + } + + return NULL; +} + +/* this func use mutex, for interface only, and SHOULD NOT be called by other mem_mngr functions */ +void * ipu_mem_mngr_add(struct map_data *map) { + struct memory_manage_node *node; + struct memory_manage_node *memory_node; + + mutex_lock(&ipu_mem_mngr_mutex); + memory_node = (struct memory_manage_node *)ipu_mem_mngr_check_repeat(map, false); + + if (memory_node) { + printk(KERN_ERR"[%s] IPU_ERROR:this memory has been remapped, use the old: share_fd=%d\n", + __func__, memory_node->map.share_fd); + mutex_unlock(&ipu_mem_mngr_mutex); + return 0; + } + + node = kmalloc(sizeof(struct memory_manage_node), GFP_KERNEL); + + if (!node) { + printk(KERN_ERR"[%s] IPU_ERROR:FATAL: kmalloc memory_manage_node fail!\n", __func__); + mutex_unlock(&ipu_mem_mngr_mutex); + return 0; + } + + memcpy(&node->map, map, sizeof(*map)); + list_add(&node->head, &memory_manager.head); + + mutex_unlock(&ipu_mem_mngr_mutex); + return node; +} + +/* this func use mutex, for interface only, and SHOULD NOT be called by other mem_mngr functions */ +int ipu_mem_mngr_del(struct map_data *map) { + struct memory_manage_node *memory_node; + + mutex_lock(&ipu_mem_mngr_mutex); + memory_node = (struct memory_manage_node *)ipu_mem_mngr_check_repeat(map, true); + + if (memory_node) { + list_del(&memory_node->head); + kfree(memory_node); + mutex_unlock(&ipu_mem_mngr_mutex); + return 0; + } else { + printk(KERN_ERR"[%s] IPU_ERROR:FATAL: Unknow memory,fd=%d,iova_start=0x%pK,iova_size=0x%lx,prot=0x%x,ignore\n", + __func__, map->share_fd, (void *)map->format.iova_start, map->format.iova_size, map->format.prot); + mutex_unlock(&ipu_mem_mngr_mutex); + return -EINVAL; + } +} + +/* this func use mutex, for interface only, and SHOULD NOT be called by other mem_mngr functions */ +int ipu_mem_mngr_check_valid(unsigned int inst_addr) +{ + struct list_head *pos; + struct memory_manage_node *memory_node; + + if (inst_addr > 0x7fffffff) { + printk(KERN_ERR"[%s] IPU_ERROR:FATAL: inst_addr=%d>0x7fffffff\n", __func__, inst_addr); + return -EINVAL; + } + + mutex_lock(&ipu_mem_mngr_mutex); + + list_for_each(pos, &memory_manager.head) { + memory_node = (struct memory_manage_node *)pos; + + if ((inst_addr >= memory_node->map.format.iova_start) && + (inst_addr < (memory_node->map.format.iova_start + memory_node->map.format.iova_size))) { + mutex_unlock(&ipu_mem_mngr_mutex); + return 0; + } + } + + mutex_unlock(&ipu_mem_mngr_mutex); + return -EINVAL; +} + +static void ipu_reg_bit_write_dword( + unsigned long reg_addr, + unsigned int start_bit, + unsigned int end_bit, + unsigned int content) +{ + unsigned int set_value; + unsigned int reg_content; + unsigned int tmp_mask; + unsigned int tmp_bit; + + if ((end_bit < start_bit) + || (start_bit > 31) + || (end_bit > 31)) { + printk(KERN_ERR"[%s]: IPU_ERROR:error input: reg_addr=%pK,start_bit=0x%x,end_bit=0x%x,content=0x%x\n", + __func__, (void *)reg_addr, start_bit, end_bit, content); + return; + } + set_value = content; + set_value = set_value << start_bit; + + tmp_bit = 31 - end_bit; + tmp_mask = 0xffffffff << tmp_bit; + tmp_mask = tmp_mask >> ( start_bit + tmp_bit); + tmp_mask = tmp_mask << start_bit; + + reg_content = (unsigned int) ioread32((void *)reg_addr); + reg_content &= (~tmp_mask); + set_value &= tmp_mask; + iowrite32((reg_content | set_value), (void *)reg_addr); + return; +} + +/* get ptr of iommu domain when probe */ +static int ipu_enable_iommu(struct device *dev) +{ + int ret; + + if (!dev) { + printk(KERN_ERR"[%s] dev is NULL\n", __func__); + return -EIO; + } + + if (!ipu_ion_client) { + ipu_ion_client = hisi_ion_client_create("ipu-client"); + if (!ipu_ion_client) { + printk(KERN_ERR"[%s] IPU_ERROR:hisi_ion_client_create fail\n", __func__); + return -ENODEV; + } + } + + if (ipu_smmu_domain) { + printk(KERN_ERR"[%s] ipu_smmu_domain is not NULL\n", __func__); + return 0; + } + + if (!iommu_present(dev->bus)) { + printk(KERN_ERR"[%s] iommu not found\n", __func__); + return 0; + } + + ipu_smmu_domain = iommu_domain_alloc(dev->bus); + + if (0 == ipu_smmu_domain) { + printk(KERN_ERR"[%s] IPU_ERROR:iommu_domain_alloc fail\n", __func__); + return -EIO; + } + + ret = iommu_attach_device(ipu_smmu_domain, dev); + + if (ret) { + printk(KERN_ERR"[%s] IPU_ERROR:iommu_attach_device fail, ret=%d\n", __func__, ret); + iommu_domain_free(ipu_smmu_domain); + ipu_smmu_domain = 0; + return -EIO; + } + + printk(KERN_DEBUG"[%s] success,ipu_smmu_domain=0x%pK, dev->bus is 0x%pK\n", + __func__, (void *)ipu_smmu_domain, (void *)dev->bus); + return 0; +} + +static struct gen_pool *iova_pool_setup(unsigned long start, + unsigned long size, unsigned long align) +{ + struct gen_pool *pool; + int ret; + + pool = gen_pool_create((int)order_base_2(align), -1);/*lint !e666 !e835 !e747 !e516 !e866 !e712 */ + if (!pool) { + printk(KERN_ERR"[%s] IPU_ERROR:Create gen pool failed!\n", __func__); + return NULL; + } + /* iova start should not be 0, because return + 0 when alloc iova is considered as error */ + if (!start) { + printk(KERN_ERR"[%s] IPU_ERROR:iova start should not be 0!\n", __func__); + return NULL; + } + + ret = gen_pool_add(pool, start, size, -1); + if (ret) { + printk(KERN_ERR"[%s] IPU_ERROR:Gen pool add failed, ret=%x\n", __func__, ret); + gen_pool_destroy(pool); + return NULL; + } + + return pool; +} + +unsigned long ipu_get_smmu_base_phy(struct device *dev) +{ + struct iommu_domain_data *domain_data = 0; + + if (ipu_enable_iommu(dev)) { + printk(KERN_ERR"[%s] IPU_ERROR:ipu_enable_iommu fail and cannot get TTBR\n", __func__); + return 0; + } + + if (0 == ipu_smmu_domain) { + printk(KERN_ERR"[%s]:IPU_ERROR:ipu_smmu_domain is NULL\n", __func__); + return 0; + } + + domain_data = (struct iommu_domain_data *)(ipu_smmu_domain->priv); /*lint !e838*/ + if (0 == domain_data) { + printk(KERN_ERR"[%s]:IPU_ERROR:domain_data is 0\n", __func__); + return 0; + } + + ipu_iova_pool = iova_pool_setup(domain_data->iova_start, + domain_data->iova_size, domain_data->iova_align); + if (0 == ipu_iova_pool) { + printk(KERN_ERR"[%s]:IPU_ERROR:iova_pool_setup ipu_iova_pool is 0\n", __func__); + return 0; + } + + return ((unsigned long)domain_data->phy_pgd_base); +} + +static void ipu_smmu_mstr_init(bool port_sel, bool hardware_start) +{ + unsigned long io_mstr_base = (unsigned long)smmu_manager.master_io_addr; + unsigned int stream_status; + int cnt = 0; + + /* set input signal as "register" by config SMMU_MSTR_INPT_SEL */ + if (port_sel) { + iowrite32(SMMU_MSTR_INPUT_SEL_REGISTER, (void *)(io_mstr_base + smmu_master_reg_offset.smmu_mstr_inpt_sel)); + } + + /* polling by loop read SMMU_MSTR_END_ACK */ + do { + cnt++; + stream_status = ioread32((void *)(io_mstr_base + smmu_master_reg_offset.smmu_mstr_end_ack)); + + if (cnt > SMMU_MSTR_END_ACK_THRESHOLD) { + printk(KERN_DEBUG"[%s] check SMMU MSTR END ACK loop overflow\n", __func__); + break; + } + } while((stream_status & 0xf) != SMMU_MSTR_ALL_STREAM_IS_END_ACK); + + /* set SMMU-normal mode */ + iowrite32(SMMU_MSTR_GLB_BYPASS_NORMAL_MODE, (void *)(io_mstr_base + smmu_master_reg_offset.smmu_mstr_glb_bypass)); + + /* here can config clk: + for core_clk_en, hardware open, for low-power ctrl + for apb_clk_en, software open, for debug (if want to read cache/ram status in RTL) + default value is OK, so NO need to config again */ + + /* clean interrupt, and NOT mask all interrupts by config SMMU_MSTR_INTCLR and SMMU_MSTR_INTMASK */ + iowrite32(SMMU_MSTR_INTCLR_ALL, (void *)(io_mstr_base + smmu_master_reg_offset.smmu_mstr_intclr)); + iowrite32(SMMU_MSTR_INTCLR_ALL_UNMASK, (void *)(io_mstr_base + smmu_master_reg_offset.smmu_mstr_intmask)); + + if (!hardware_start) { + /******************************************************** + config stream by SMMU_MSTR_SMRX_0 + for a sid, includes: + VA max and VA min for this stream-id r/w region; + len and upwin (in 32k, if VA continue increase, will not decrease in 32k, upwin is 0) + len should be iid/2, iid(index id) is 8, for iid, for example, if pingpong buffer, iid is 2 + + 00.b:weight + 01.b:input read + 10.b:output read + 11.b:output write + + .len = iid/2=4 + .upwin = 0(do not search in upwin) + .bypass = 0(no bypass) + + for malloc and free, VA is not in a designated area, so can not set VA max and VA min + *********************************************************/ + iowrite32(SMMU_MSTR_SMRX_0_LEN, (void *)(io_mstr_base + smmu_master_reg_offset.smmu_mstr_smrx_0[0])); + iowrite32(SMMU_MSTR_SMRX_0_LEN, (void *)(io_mstr_base + smmu_master_reg_offset.smmu_mstr_smrx_0[1])); + iowrite32(SMMU_MSTR_SMRX_0_LEN, (void *)(io_mstr_base + smmu_master_reg_offset.smmu_mstr_smrx_0[2])); + iowrite32(SMMU_MSTR_SMRX_0_LEN, (void *)(io_mstr_base + smmu_master_reg_offset.smmu_mstr_smrx_0[3])); + + /* stream startup by config SMMU_MSTR_SMRX_START */ + iowrite32(SMMU_MSTR_SMRX_START_ALL_STREAM, (void *)(io_mstr_base + smmu_master_reg_offset.smmu_mstr_smrx_start)); + } + + return; +} + +static void ipu_smmu_comm_init(unsigned long ttbr0, unsigned long smmu_rw_err_phy_addr) +{ + unsigned int low; + unsigned int high; + unsigned long io_comm_base = (unsigned long)smmu_manager.common_io_addr; + + /* set SMMU mode as normal */ + ipu_reg_bit_write_dword(io_comm_base + smmu_common_reg_offset.smmu_scr, 0, 0, 0); + + /* clear SMMU interrupt(SMMU_INTCLR_NS) */ + iowrite32(SMMU_COMMON_INTCLR_NS_ALL, (void *)(io_comm_base + smmu_common_reg_offset.smmu_intclr_ns)); + + /* clear MASK of interrupt(SMMU_INTMASK_NS) */ + ipu_reg_bit_write_dword(io_comm_base + smmu_common_reg_offset.smmu_intmask_ns, 0, 5, 0); + + /* set stream status: SMMU normal(SMMU_SMRX_NS). + default value is OK, NO need to config again */ + + /* set SMMU Translation Table Base Register for Non-Secure Context Bank0(SMMU_CB_TTBR0) */ + low = (unsigned int)(ttbr0 & 0xffffffff); + high = (unsigned int)((ttbr0 >> 32) & 0x7f); + iowrite32(low, (void *)(io_comm_base + smmu_common_reg_offset.smmu_cb_ttbr0)); + iowrite32(high, (void *)(io_comm_base + smmu_common_reg_offset.smmu_fama_ctrl1_ns)); + + /* set Descriptor select of the SMMU_CB_TTBR0 addressed region of Non-Secure Context Bank + for 64bit system, select Long Descriptor -> 1(SMMU_CB_TTBCR.cb_ttbcr_des) */ + ipu_reg_bit_write_dword(io_comm_base + smmu_common_reg_offset.smmu_cb_ttbcr, 0, 0, 0x1); + + /* set SMMU read/write phy addr in TLB miss case */ + low = (unsigned int)(smmu_rw_err_phy_addr & 0xffffffff); + high = (unsigned int)((smmu_rw_err_phy_addr >> 32) & 0x7f); + iowrite32(low, (void *)(io_comm_base + smmu_common_reg_offset.smmu_err_rdaddr)); + ipu_reg_bit_write_dword(io_comm_base + smmu_common_reg_offset.smmu_addr_msb, 0, 6, high); + + iowrite32(low, (void *)(io_comm_base + smmu_common_reg_offset.smmu_err_wraddr)); + ipu_reg_bit_write_dword(io_comm_base + smmu_common_reg_offset.smmu_addr_msb, 7, 13, high); + + return; +} + +void ipu_smmu_init(unsigned long ttbr0, unsigned long smmu_rw_err_phy_addr, bool port_sel, bool hardware_start) +{ + ipu_smmu_mstr_init(port_sel, hardware_start); + ipu_smmu_comm_init(ttbr0, smmu_rw_err_phy_addr); +} + +void ipu_smmu_deinit(void) +{ + iowrite32(SMMU_MSTR_INTCLR_ALL_MASK, + (void *)((unsigned long)smmu_manager.master_io_addr + smmu_master_reg_offset.smmu_mstr_intmask)); + iowrite32(SMMU_COMMON_INTCLR_NS_ALL_MASK, + (void *)((unsigned long)smmu_manager.common_io_addr + smmu_common_reg_offset.smmu_intmask_ns)); +} + +static unsigned long ipu_alloc_iova(struct gen_pool *pool, + unsigned long size) +{ + unsigned long iova; + + mutex_lock(&ipu_pool_mutex); + + iova = gen_pool_alloc(pool, size); + if (!iova) { + mutex_unlock(&ipu_pool_mutex); + printk(KERN_ERR"[%s]: IPU_ERROR:hisi iommu gen_pool_alloc failed! size = %lu\n", __func__, size); + return 0; + } + mutex_unlock(&ipu_pool_mutex); + return iova; +} + +static void ipu_free_iova(struct gen_pool *pool, + unsigned long iova, size_t size) +{ + mutex_lock(&ipu_pool_mutex); + gen_pool_free(pool, iova, size); + + mutex_unlock(&ipu_pool_mutex); +} + +long ipu_smmu_map(struct map_data *map) +{ + long ret; + size_t sg_size; + unsigned long phys_len; + unsigned long iova_size; + unsigned long iova_start; + struct scatterlist *sg; + struct sg_table *table; + struct ion_handle *hdl; + struct scatterlist *sgl; + struct map_format *format = &(map->format); + + if (0 == ipu_smmu_domain || 0 == ipu_ion_client || 0 == ipu_iova_pool) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu_smmu_domain or ipu_ion_client or ipu_iova_pool is NULL, ipu_smmu_domain=0x%pK, ipu_ion_client=0x%pK, ipu_iova_pool=0x%pK\n", + __func__, ipu_smmu_domain, ipu_ion_client, ipu_iova_pool); + return -EFAULT; + } + + hdl = ion_import_dma_buf(ipu_ion_client, map->share_fd);// coverity[UNINIT] + if (IS_ERR_OR_NULL(hdl)) { + printk(KERN_ERR"[%s]: IPU_ERROR:SETCONFIG_MAP hdl is error, which is: %pK\n", __func__, hdl); + return -EFAULT; + } + + table = ion_sg_table(ipu_ion_client, hdl); + if (IS_ERR_OR_NULL(table) || !table) { /* Coverity can not understand IS_ERR_OR_NULL, add "!table" */ + printk(KERN_ERR"[%s]: IPU_ERROR:SETCONFIG_MAP table is error, which is: %pK\n", __func__, table); + return -EFAULT; + } + + sgl = table->sgl; + + for (phys_len = 0, sg = sgl; sg; sg = sg_next(sg)) + phys_len += (unsigned long)ALIGN(sg->length, PAGE_SIZE);/*lint !e50 */ + + iova_size = phys_len; + iova_start = ipu_alloc_iova(ipu_iova_pool, iova_size); + + unsigned long smmu_scr_addr = (unsigned long)smmu_manager.common_io_addr + smmu_common_reg_offset.smmu_scr; + + unsigned long smmu_mint_addr = (unsigned long)smmu_manager.master_io_addr + smmu_master_reg_offset.smmu_mstr_intmask; + + sg_size = iommu_map_sg(ipu_smmu_domain, iova_start, sgl, + (unsigned int)sg_nents(sgl), format->prot); + + if (sg_size != iova_size) { + printk(KERN_ERR"[%s]: IPU_ERROR:map failed!iova_start = 0x%pK, iova_size = 0x%lx\n", + __func__, (void *)iova_start, iova_size); + + if (ipu_iova_pool) { + ipu_free_iova(ipu_iova_pool, iova_start, iova_size); + } + ret = -EIO; + } else { + format->iova_start = iova_start; + format->iova_size = iova_size; + ret = 0; + } + + ion_free(ipu_ion_client, hdl); + return ret; +} + +long ipu_smmu_unmap(struct map_data *map) +{ + int ret; + unsigned long unmapped_size; + struct map_format *format = &(map->format); + + if (0 == ipu_smmu_domain || 0 == ipu_iova_pool) { + printk(KERN_ERR"[%s]: IPU_ERROR:ipu_smmu_domain or ipu_ion_client or ipu_iova_pool is NULL, ipu_smmu_domain=0x%pK, ipu_iova_pool=0x%pK\n", + __func__, ipu_smmu_domain, ipu_iova_pool); + return -EFAULT; + } + + /* check if iova is in iova pool range */ + ret = addr_in_gen_pool(ipu_iova_pool, format->iova_start, + format->iova_size); + if(!ret) { + printk(KERN_ERR"[%s]IPU_ERROR:illegal para!!iova=0x%pK, size=%ld\n", + __func__, (void *)format->iova_start, format->iova_size); + return -EINVAL; + } + + unmapped_size = iommu_unmap(ipu_smmu_domain, format->iova_start, format->iova_size); + + if (unmapped_size != format->iova_size) { + printk(KERN_ERR"[%s]IPU_ERROR:unmap failed, unmapped_size=%ld, iova_size=%ld\n", __func__, unmapped_size, format->iova_size); + ret = -EINVAL; + } else { + ret = 0; + } + + ipu_free_iova(ipu_iova_pool, format->iova_start, format->iova_size); + + return ret; +} + +int ipu_smmu_mngr_init(void) +{ + smmu_manager.master_io_addr = ioremap((unsigned long)smmu_master_reg_offset.smmu_mstr_base_addr, (unsigned long)0xffff); + if (!smmu_manager.master_io_addr) { + printk(KERN_ERR"[%s]IPU_ERROR:smmu_manager.master_io_addr ioremap fail\n", __func__); + return -ENOMEM; + } + + smmu_manager.common_io_addr = ioremap((unsigned long)smmu_common_reg_offset.smmu_common_base_addr, (unsigned long)0xffff); + if (!smmu_manager.common_io_addr) { + printk(KERN_ERR"[%s]IPU_ERROR:smmu_manager.common_io_addr ioremap fail\n", __func__); + return -ENOMEM; + } + + return 0; +} + +void ipu_smmu_mngr_deinit(void) +{ + if (smmu_manager.master_io_addr) { + iounmap(smmu_manager.master_io_addr); + smmu_manager.master_io_addr = 0; + } + + if (smmu_manager.common_io_addr) { + iounmap(smmu_manager.common_io_addr); + smmu_manager.common_io_addr = 0; + } +} + +void ipu_smmu_override_prefetch_addr(unsigned long reset_va) +{ + iowrite32(reset_va, (void *)((unsigned long)smmu_manager.common_io_addr + + (unsigned long)smmu_common_reg_offset.override_pref_addr)); + iowrite32(SMMU_OPREF_CTRL_CONFIG_DUMMY, (void *)((unsigned long)smmu_manager.common_io_addr + + (unsigned long)smmu_common_reg_offset.cfg_override_original_pref_addr)); + + printk(KERN_DEBUG"[%s] done\n", __func__); +} + +bool ipu_smmu_interrupt_handler(struct smmu_irq_count *irq_count) +{ + unsigned int reg_smmu_mstr_status; + unsigned int reg_smmu_comm_status; + bool ret = false; + unsigned long mstr_io_addr = (unsigned long)smmu_manager.master_io_addr; + unsigned long comm_io_addr = (unsigned long)smmu_manager.common_io_addr; + + //fixme: if security/protect mode is needed, add code here + reg_smmu_comm_status = ioread32((void *)(comm_io_addr + smmu_common_reg_offset.smmu_intstat_ns)); + reg_smmu_mstr_status = ioread32((void *)(mstr_io_addr + smmu_master_reg_offset.smmu_mstr_intstat)); + + if (0 != reg_smmu_mstr_status) { + ret = true; + printk(KERN_ERR"[%s]: IPU_ERROR:error, smmu mstr interrupt received: %x\n", __func__, reg_smmu_mstr_status); + if (reg_smmu_mstr_status & SMMU_MSTR_WDATA_BURST) { + irq_count->mstr_wdata_burst++; + } + if (reg_smmu_mstr_status & SMMU_MSTR_WR_VA_OUT_OF_128BYTE) { + irq_count->mstr_wr_va_out_of_128byte++; + } + if (reg_smmu_mstr_status & SMMU_MSTR_WR_VA_OUT_OF_BOUNDARY) { + irq_count->mstr_wr_va_out_of_boundary++; + } + if (reg_smmu_mstr_status & SMMU_MSTR_RD_VA_OUT_OF_128BYTE) { + irq_count->mstr_rd_va_out_of_128byte++; + } + if (reg_smmu_mstr_status & SMMU_MSTR_RD_VA_OUT_OF_BOUNDARY) { + irq_count->mstr_rd_va_out_of_boundary++; + } + + printk(KERN_ERR"[%s]: IPU_ERROR:error, Rd_Inst_SID=0x%pK, RdAddr=0x%pK, Wr_Inst_SID=0x%pK, WrAddr=0x%pK\n", __func__, + (void *)(unsigned long)ioread32((void *)(mstr_io_addr + 0x50)), (void *)(unsigned long)ioread32((void *)(mstr_io_addr + 0x54)), + (void *)(unsigned long)ioread32((void *)(mstr_io_addr + 0x58)), (void *)(unsigned long)ioread32((void *)(mstr_io_addr + 0x5c))); + + printk(KERN_ERR"[%s]: IPU_ERROR:error, RW_Burst_len=0x%pK, Awaddr=0x%pK\n", __func__, + (void *)(unsigned long)ioread32((void *)(mstr_io_addr + 0x60)), (void *)(unsigned long)ioread32((void *)(mstr_io_addr + 0x64))); + + /* clear smmu mstr interrupt */ + iowrite32(SMMU_MSTR_INTCLR_ALL, (void *)(mstr_io_addr + (unsigned long)smmu_master_reg_offset.smmu_mstr_intclr)); + } + + if (0 != reg_smmu_comm_status) { + ret = true; + printk(KERN_ERR"[%s]: IPU_ERROR:error, smmu common interrupt received: 0x%x\n", __func__, reg_smmu_comm_status); + + if (reg_smmu_comm_status & SMMU_INTCLR_NS_PTW_NS_STAT) { + /* When PTW transaction receive an page table whose ns bit is not match to the prefetch + transaction, occur this fault. */ + irq_count->comm_ptw_ns_stat++; + } + if (reg_smmu_comm_status & SMMU_INTCLR_NS_PTW_INVALID_STAT) { + /* When PTW transaction receive an invalid page table descriptor or access the invalid + regoin between t0sz and t1sz in long descriptor mode, occur this fault.*/ + irq_count->comm_ptw_invalid_stat++; + } + if (reg_smmu_comm_status & SMMU_INTCLR_NS_PTW_TRANS_STAT) { + /* When PTW transaciont receive an error response, occur this fault. */ + irq_count->comm_ptw_trans_stat++; + } + if (reg_smmu_comm_status & SMMU_INTCLR_NS_TLBMISS_STAT) { + /* When there is a TLB miss generated during the translation process, the mmu will record this. */ + irq_count->comm_tlbmiss_stat++; + } + if (reg_smmu_comm_status & SMMU_INTCLR_NS_EXT_STAT) { + /* When mmu receive an en error response the mmu will record this as a fault. */ + irq_count->comm_ext_stat++; + } + if (reg_smmu_comm_status & SMMU_INTCLR_NS_PERMIS_STAT) { + /* When the input transaction¡¯s attributes doesn¡¯t match the attributes descripted in the page table, + the mmu will raise a fault for this. */ + irq_count->comm_permis_stat++; + } + + /* clear smmu interrupt */ + //fixme: if security/protect mode is needed, add code here + iowrite32(SMMU_COMMON_INTCLR_NS_ALL, (void *)(comm_io_addr + (unsigned long)smmu_common_reg_offset.smmu_intclr_ns)); + } + + return ret; +} + +void ipu_smmu_reset_statistic(void) +{ + int i; + struct smmu_master_reg_offset *offset = &smmu_master_reg_offset; + void *dbg_port_in = (void *)((unsigned long)smmu_manager.master_io_addr + + (unsigned long)offset->smmu_mstr_dbg_port_in_0); + + /* clean read channel cmd-total-count (by config SMMU_MSTR_DBG_PORT_IN_0) */ + for(i = 0; i < IPU_SMMU_READ_STREAM_NUMBER; i++) { + iowrite32((SMMU_MSTR_SET_DEBUG_PORT + offset->read_data_total_cnt[i]), dbg_port_in); + } + + /* clean read channel cmd-miss-count (by config SMMU_MSTR_DBG_PORT_IN_0) */ + for(i = 0; i < IPU_SMMU_READ_STREAM_NUMBER; i++) { + iowrite32((SMMU_MSTR_SET_DEBUG_PORT + offset->read_cmd_miss_cnt[i]), dbg_port_in); + } + + /* clean read channel data-length-count (by config SMMU_MSTR_DBG_PORT_IN_0) */ + for(i = 0; i < IPU_SMMU_READ_STREAM_NUMBER; i++) { + iowrite32((SMMU_MSTR_SET_DEBUG_PORT + offset->read_data_total_cnt[i]), dbg_port_in); + } + + /* clean read channel tag-stat (by config SMMU_MSTR_DBG_PORT_IN_0) */ + for(i = 0; i < IPU_SMMU_TAG_COMPARE_CASE_NUMBER; i++) { + iowrite32((SMMU_MSTR_SET_DEBUG_PORT + offset->read_cmd_case_cnt[i]), dbg_port_in); + } + + /* clean read channel latency (by config SMMU_MSTR_DBG_PORT_IN_0) */ + iowrite32((SMMU_MSTR_SET_DEBUG_PORT + offset->read_cmd_trans_latency), dbg_port_in); + + /* clean write channel cmd-total-count (by config SMMU_MSTR_DBG_PORT_IN_0) */ + iowrite32((SMMU_MSTR_SET_DEBUG_PORT + offset->write_cmd_total_cnt), dbg_port_in); + + /* clean write channel cmd-miss-count (by config SMMU_MSTR_DBG_PORT_IN_0) */ + iowrite32((SMMU_MSTR_SET_DEBUG_PORT + offset->write_cmd_miss_cnt), dbg_port_in); + + /* clean write channel data-length-count (by config SMMU_MSTR_DBG_PORT_IN_0) */ + iowrite32((SMMU_MSTR_SET_DEBUG_PORT + offset->write_data_total_cnt), dbg_port_in); + + /* clean write channel tag-stat (by config SMMU_MSTR_DBG_PORT_IN_0) */ + for(i = 0; i < IPU_SMMU_TAG_COMPARE_CASE_NUMBER; i++) { + iowrite32((SMMU_MSTR_SET_DEBUG_PORT + offset->write_cmd_case_cnt[i]), dbg_port_in); + } + + /* clean write channel latency (by config SMMU_MSTR_DBG_PORT_IN_0) */ + iowrite32((SMMU_MSTR_SET_DEBUG_PORT + offset->write_cmd_trans_latency), dbg_port_in); + iowrite32((SMMU_MSTR_SET_DEBUG_PORT + offset->write_cmd_trans_latency), dbg_port_in); +} + +void ipu_smmu_record_statistic(struct smmu_statistic *statistic) +{ + struct smmu_master_reg_offset *offset = &smmu_master_reg_offset; + unsigned long mstr_io_addr = (unsigned long)smmu_manager.master_io_addr; + + /* read channel cmd total count */ + statistic->read_stream_cmd_total[0] += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_total_cnt[0])); + statistic->read_stream_cmd_total[1] += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_total_cnt[1])); + statistic->read_stream_cmd_total[2] += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_total_cnt[2])); + + /* read channel cmd miss count */ + statistic->read_stream_cmd_miss[0] += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_miss_cnt[0])); + statistic->read_stream_cmd_miss[1] += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_miss_cnt[1])); + statistic->read_stream_cmd_miss[2] += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_miss_cnt[2])); + + /* read channel data total count */ + statistic->read_stream_data_total[0] += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_data_total_cnt[0])); + statistic->read_stream_data_total[1] += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_data_total_cnt[1])); + statistic->read_stream_data_total[2] += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_data_total_cnt[2])); + + /* read cmd miss/hit and latency */ + statistic->read_stream_cmd_miss_valid += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_case_cnt[0])); + statistic->read_stream_cmd_miss_pending += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_case_cnt[1])); + statistic->read_stream_cmd_hit_valid_not_slide_window += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_case_cnt[2])); + statistic->read_stream_cmd_hit_valid_slide_window += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_case_cnt[3])); + statistic->read_stream_cmd_hit_pending_not_slide_window += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_case_cnt[4])); + statistic->read_stream_cmd_hit_pending_slide_window += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_case_cnt[5])); + statistic->read_stream_cmd_latency += ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_trans_latency)); + + /* write channel cmd total count */ + statistic->write_stream_cmd_total += ioread32((void *)(mstr_io_addr + (unsigned long)offset->write_cmd_total_cnt)); + statistic->write_stream_cmd_miss += ioread32((void *)(mstr_io_addr + (unsigned long)offset->write_cmd_miss_cnt)); + statistic->write_stream_data_total += ioread32((void *)(mstr_io_addr + (unsigned long)offset->write_data_total_cnt)); + + /* write cmd miss/hit and latency */ + statistic->write_stream_cmd_miss_valid += ioread32((void *)(mstr_io_addr + (unsigned long)offset->write_cmd_case_cnt[0])); + statistic->write_stream_cmd_miss_pending += ioread32((void *)(mstr_io_addr + (unsigned long)offset->write_cmd_case_cnt[1])); + statistic->write_stream_cmd_hit_valid_not_slide_window += ioread32((void *)(mstr_io_addr + (unsigned long)offset->write_cmd_case_cnt[2])); + statistic->write_stream_cmd_hit_valid_slide_window += ioread32((void *)(mstr_io_addr + (unsigned long)offset->write_cmd_case_cnt[3])); + statistic->write_stream_cmd_hit_pending_not_slide_window += ioread32((void *)(mstr_io_addr + (unsigned long)offset->write_cmd_case_cnt[4])); + statistic->write_stream_cmd_hit_pending_slide_window += ioread32((void *)(mstr_io_addr + (unsigned long)offset->write_cmd_case_cnt[5])); + statistic->write_stream_cmd_latency += ioread32((void *)(mstr_io_addr + (unsigned long)offset->write_cmd_trans_latency)); +} + +/* for online layer-by-layer mode, run once each op(i.e. conv, pooling, ReLU...), while online merge and offline mode +this func will only call once before run */ +void ipu_smmu_pte_update(void) +{ + unsigned long mstr_io_addr = (unsigned long)smmu_manager.master_io_addr; + unsigned long comm_io_addr = (unsigned long)smmu_manager.common_io_addr; + + iowrite32(SMMU_MSTR_SMRX_START_ALL_STREAM, (void *)(mstr_io_addr + smmu_master_reg_offset.smmu_mstr_smrx_start)); + + /* update cache data to avoid this case: phy address across 8GB address-space */ + iowrite32(SMMU_CACHE_ALL_LEVEL_INVALID_LEVEL1, (void *)(comm_io_addr + smmu_common_reg_offset.smmu_scachei_all)); + iowrite32(SMMU_CACHE_ALL_LEVEL_VALID_LEVEL1, (void *)(comm_io_addr + smmu_common_reg_offset.smmu_scachei_all)); +} + +bool ipu_smmu_master_get_offset(struct device *dev) +{ + int property_rd; + struct smmu_master_reg_offset *offset = &smmu_master_reg_offset; + struct device_node *node = of_find_node_by_name(dev->of_node, "smmu_master"); + + if(!node) { + printk(KERN_ERR"[%s]: IPU_ERROR:find smmu_master node error\n", __func__); + return false; + } + + memset(offset, 0, sizeof(*offset));// coverity[secure_coding] + property_rd = of_property_read_u32(node, "smmu-mstr-base-addr", &offset->smmu_mstr_base_addr); + property_rd |= of_property_read_u32(node, "smmu-mstr-glb-bypass", &offset->smmu_mstr_glb_bypass); + property_rd |= of_property_read_u32(node, "smmu-mstr-end-ack", &offset->smmu_mstr_end_ack); + property_rd |= of_property_read_u32(node, "smmu-mstr-smrx-start", &offset->smmu_mstr_smrx_start); + property_rd |= of_property_read_u32(node, "smmu-mstr-inpt-sel", &offset->smmu_mstr_inpt_sel); + property_rd |= of_property_read_u32(node, "smmu-mstr-intmask", &offset->smmu_mstr_intmask); + property_rd |= of_property_read_u32(node, "smmu-mstr-intstat", &offset->smmu_mstr_intstat); + property_rd |= of_property_read_u32(node, "smmu-mstr-intclr", &offset->smmu_mstr_intclr); + property_rd |= of_property_read_u32(node, "smmu-mstr-dbg-port-in-0", &offset->smmu_mstr_dbg_port_in_0); + property_rd |= of_property_read_u32(node, "smmu-mstr-dbg-port-out", &offset->smmu_mstr_dbg_port_out); + property_rd |= of_property_read_u32(node, "smmu-mstr-smrx-0-stream-0", &offset->smmu_mstr_smrx_0[0]); + property_rd |= of_property_read_u32(node, "smmu-mstr-smrx-0-stream-1", &offset->smmu_mstr_smrx_0[1]); + property_rd |= of_property_read_u32(node, "smmu-mstr-smrx-0-stream-2", &offset->smmu_mstr_smrx_0[2]); + property_rd |= of_property_read_u32(node, "smmu-mstr-smrx-0-stream-3", &offset->smmu_mstr_smrx_0[3]); + property_rd |= of_property_read_u32(node, "read-cmd-total-cnt-stream-0", &offset->read_cmd_total_cnt[0]); + property_rd |= of_property_read_u32(node, "read-cmd-total-cnt-stream-1", &offset->read_cmd_total_cnt[1]); + property_rd |= of_property_read_u32(node, "read-cmd-total-cnt-stream-2", &offset->read_cmd_total_cnt[2]); + property_rd |= of_property_read_u32(node, "read-cmd-miss-cnt-stream-0", &offset->read_cmd_miss_cnt[0]); + property_rd |= of_property_read_u32(node, "read-cmd-miss-cnt-stream-1", &offset->read_cmd_miss_cnt[1]); + property_rd |= of_property_read_u32(node, "read-cmd-miss-cnt-stream-2", &offset->read_cmd_miss_cnt[2]); + property_rd |= of_property_read_u32(node, "read-data-total-cnt-stream-0", &offset->read_data_total_cnt[0]); + property_rd |= of_property_read_u32(node, "read-data-total-cnt-stream-1", &offset->read_data_total_cnt[1]); + property_rd |= of_property_read_u32(node, "read-data-total-cnt-stream-2", &offset->read_data_total_cnt[2]); + property_rd |= of_property_read_u32(node, "read-cmd-case-cnt-stream-0", &offset->read_cmd_case_cnt[0]); + property_rd |= of_property_read_u32(node, "read-cmd-case-cnt-stream-1", &offset->read_cmd_case_cnt[1]); + property_rd |= of_property_read_u32(node, "read-cmd-case-cnt-stream-2", &offset->read_cmd_case_cnt[2]); + property_rd |= of_property_read_u32(node, "read-cmd-case-cnt-stream-3", &offset->read_cmd_case_cnt[3]); + property_rd |= of_property_read_u32(node, "read-cmd-case-cnt-stream-4", &offset->read_cmd_case_cnt[4]); + property_rd |= of_property_read_u32(node, "read-cmd-case-cnt-stream-5", &offset->read_cmd_case_cnt[5]); + property_rd |= of_property_read_u32(node, "read-cmd-trans-latency", &offset->read_cmd_trans_latency); + property_rd |= of_property_read_u32(node, "write-cmd-total-cnt", &offset->write_cmd_total_cnt); + property_rd |= of_property_read_u32(node, "write-cmd-miss-cnt", &offset->write_cmd_miss_cnt); + property_rd |= of_property_read_u32(node, "write-data-total-cnt", &offset->write_data_total_cnt); + property_rd |= of_property_read_u32(node, "write-cmd-case-cnt-stream-0", &offset->write_cmd_case_cnt[0]); + property_rd |= of_property_read_u32(node, "write-cmd-case-cnt-stream-1", &offset->write_cmd_case_cnt[1]); + property_rd |= of_property_read_u32(node, "write-cmd-case-cnt-stream-2", &offset->write_cmd_case_cnt[2]); + property_rd |= of_property_read_u32(node, "write-cmd-case-cnt-stream-3", &offset->write_cmd_case_cnt[3]); + property_rd |= of_property_read_u32(node, "write-cmd-case-cnt-stream-4", &offset->write_cmd_case_cnt[4]); + property_rd |= of_property_read_u32(node, "write-cmd-case-cnt-stream-5", &offset->write_cmd_case_cnt[5]); + property_rd |= of_property_read_u32(node, "write-cmd-trans-latency", &offset->write_cmd_trans_latency); + + if (property_rd) { + printk(KERN_ERR"[%s]: IPU_ERROR:read property of smmu_master offset error\n", __func__); + return false; + } + + return true; +} + +bool ipu_smmu_common_get_offset (struct device *dev) +{ + int property_rd; + struct smmu_common_reg_offset *offset = &smmu_common_reg_offset; + struct device_node *node = of_find_node_by_name(dev->of_node, "smmu_common"); + + if(!node) { + printk(KERN_ERR"[%s]: IPU_ERROR:find smmu_common node error\n", __func__); + return false; + } + + memset(offset, 0, sizeof(smmu_common_reg_offset));// coverity[secure_coding] + property_rd = of_property_read_u32(node, "smmu-common-base-addr", &offset->smmu_common_base_addr); + property_rd |= of_property_read_u32(node, "smmu-scr", &offset->smmu_scr); + property_rd |= of_property_read_u32(node, "smmu-intmask-ns", &offset->smmu_intmask_ns); + property_rd |= of_property_read_u32(node, "smmu-intstat-ns", &offset->smmu_intstat_ns); + property_rd |= of_property_read_u32(node, "smmu-intclr-ns", &offset->smmu_intclr_ns); + property_rd |= of_property_read_u32(node, "smmu-cb-ttbr0", &offset->smmu_cb_ttbr0); + property_rd |= of_property_read_u32(node, "smmu-cb-ttbcr", &offset->smmu_cb_ttbcr); + property_rd |= of_property_read_u32(node, "smmu-scachei-all", &offset->smmu_scachei_all); + property_rd |= of_property_read_u32(node, "smmu-fama-ctrl1-ns", &offset->smmu_fama_ctrl1_ns); + property_rd |= of_property_read_u32(node, "smmu-opref-addr", &offset->override_pref_addr); + property_rd |= of_property_read_u32(node, "smmu-opref-ctrl", &offset->cfg_override_original_pref_addr); + property_rd |= of_property_read_u32(node, "smmu-addr-msb", &offset->smmu_addr_msb); + property_rd |= of_property_read_u32(node, "smmu-err-rdaddr", &offset->smmu_err_rdaddr); + property_rd |= of_property_read_u32(node, "smmu-err-wraddr", &offset->smmu_err_wraddr); + if (property_rd) { + printk(KERN_ERR"[%s]: IPU_ERROR:read property of smmu_common offset error\n", __func__); + return false; + } + return true; +} + +#define IPU_SMMU_MSTR_DEBUG_PORT_NUM (32) +#define IPU_SMMU_MSTR_DEBUG_BASE_NUM (4) +#define IPU_SMMU_MSTR_DEBUG_AXI_RD_CMD_ADDR (0x10000) +#define IPU_SMMU_MSTR_DEBUG_AXI_RD_CMD_INFO (0x10100) +#define IPU_SMMU_MSTR_DEBUG_AXI_WR_CMD_ADDR (0x18000) +#define IPU_SMMU_MSTR_DEBUG_AXI_WR_CMD_INFO (0x18100) +#define IPU_SMMU_RD_CMD_BUF_BITMAP (0x10300) +#define IPU_SMMU_WR_CMD_BUF_BITMAP (0x18300) + +void ipu_smmu_dump_strm(void) +{ + int i, j; + unsigned int base; + unsigned int port_out[IPU_SMMU_MSTR_DEBUG_PORT_NUM]; + unsigned int port_in[IPU_SMMU_MSTR_DEBUG_BASE_NUM] = { + IPU_SMMU_MSTR_DEBUG_AXI_RD_CMD_ADDR, IPU_SMMU_MSTR_DEBUG_AXI_RD_CMD_INFO, + IPU_SMMU_MSTR_DEBUG_AXI_WR_CMD_ADDR, IPU_SMMU_MSTR_DEBUG_AXI_WR_CMD_INFO}; + + #ifdef CONFIG_HUAWEI_DSM + int sz = REGISTER_INFO_MAX_LEN; + char *perr = register_info; + int dsm_offset = 0; + #endif /* CONFIG_HUAWEI_DSM */ + + struct smmu_master_reg_offset *offset = &smmu_master_reg_offset; + unsigned long mstr_io_addr = (unsigned long)smmu_manager.master_io_addr; + + for (i = 0; i < IPU_SMMU_MSTR_DEBUG_BASE_NUM; i++) { + + base = port_in[i]; + + for (j = 0; j < IPU_SMMU_MSTR_DEBUG_PORT_NUM; j ++) { + iowrite32(base + j * sizeof(unsigned int), (void *)(mstr_io_addr + (unsigned long)offset->smmu_mstr_dbg_port_in_0)); + port_out[j] = ioread32((void *)(mstr_io_addr + (unsigned long)offset->smmu_mstr_dbg_port_out)); + } + + /* dump strm status for analysis */ + printk(KERN_ERR"SMMU STRM %x:%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", base, + port_out[0], port_out[1], port_out[2], port_out[3], port_out[4], port_out[5], port_out[6], port_out[7], + port_out[8], port_out[9], port_out[10], port_out[11], port_out[12], port_out[13], port_out[14], port_out[15]); + + printk(KERN_ERR"(continue):%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", + port_out[16], port_out[17], port_out[18], port_out[19], port_out[20], port_out[21], port_out[22], port_out[23], + port_out[24], port_out[25], port_out[26], port_out[27], port_out[28], port_out[29], port_out[30], port_out[31]); + + #ifdef CONFIG_HUAWEI_DSM + if (0 < dsm_offset){ + perr += dsm_offset; + sz -= dsm_offset; + } + /* coverity[secure_coding] */ + dsm_offset = snprintf(perr, sz, "%d: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", base, + port_out[0], port_out[1], port_out[2], port_out[3], port_out[4], port_out[5], port_out[6], port_out[7], + port_out[8], port_out[9], port_out[10], port_out[11], port_out[12], port_out[13], port_out[14], port_out[15], + port_out[16], port_out[17], port_out[18], port_out[19], port_out[20], port_out[21], port_out[22], port_out[23], + port_out[24], port_out[25], port_out[26], port_out[27], port_out[28], port_out[29], port_out[30], port_out[31]); + #endif /* CONFIG_HUAWEI_DSM */ + + } + + iowrite32(IPU_SMMU_RD_CMD_BUF_BITMAP, (void *)(mstr_io_addr + (unsigned long)offset->smmu_mstr_dbg_port_in_0)); + iowrite32(IPU_SMMU_WR_CMD_BUF_BITMAP, (void *)(mstr_io_addr + (unsigned long)offset->smmu_mstr_dbg_port_in_0)); +#ifdef CONFIG_HISI_IPU_MNTN + ipu_reg_info.mstr_reg.rd_bitmap = ioread32((void *)(mstr_io_addr + (unsigned long)offset->smmu_mstr_dbg_port_out)); + ipu_reg_info.mstr_reg.wr_bitmap = ioread32((void *)(mstr_io_addr + (unsigned long)offset->smmu_mstr_dbg_port_out)); + ipu_reg_info.mstr_reg.rd_cmd_total_cnt0 = ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_total_cnt[0])); + ipu_reg_info.mstr_reg.rd_cmd_total_cnt1 = ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_total_cnt[1])); + ipu_reg_info.mstr_reg.rd_cmd_total_cnt2 = ioread32((void *)(mstr_io_addr + (unsigned long)offset->read_cmd_total_cnt[2])); + ipu_reg_info.mstr_reg.wr_cmd_total_cnt = ioread32((void *)(mstr_io_addr + (unsigned long)offset->write_cmd_total_cnt)); + printk(KERN_ERR"RD_BITMAP=%x, WR_BITMAP=%x, rd_cmd_total_cnt[0-3]={%x, %x, %x}, wr_cmd_total_cnt=%x\n", + ipu_reg_info.mstr_reg.rd_bitmap, + ipu_reg_info.mstr_reg.wr_bitmap, + ipu_reg_info.mstr_reg.rd_cmd_total_cnt0, + ipu_reg_info.mstr_reg.rd_cmd_total_cnt1, + ipu_reg_info.mstr_reg.rd_cmd_total_cnt2, + ipu_reg_info.mstr_reg.wr_cmd_total_cnt); +#endif +} + diff --git a/drivers/hisi/ics/ipu_smmu_drv.h b/drivers/hisi/ics/ipu_smmu_drv.h new file mode 100644 index 000000000000..e17b72a35bcb --- /dev/null +++ b/drivers/hisi/ics/ipu_smmu_drv.h @@ -0,0 +1,122 @@ +/* Module internals + * + * Copyright (C) 2016 Hisilicon, Inc. All Rights Reserved. + * + * These coded instructions, statements, and computer programs are the + * copyrighted works and confidential proprietary information of + * Hisilicon Inc. and its licensors, and are licensed to the recipient + * under the terms of a separate license agreement. They may be + * adapted and modified by bona fide purchasers under the terms of the + * separate license agreement for internal use, but no adapted or + * modified version may be disclosed or distributed to third parties + * in any manner, medium, or form, in whole or in part, without the + * prior written consent of Hisilicon Inc. + */ + +#ifndef _IPU_SMMU_DRV_H +#define _IPU_SMMU_DRV_H + +#include +#include +#include +#include +#include + +#define IPU_SMMU_ENABLE +#define CAMBRICON_IPU_IRQ + +#define IPU_BASE_ADDRESS 0xff400000 +#define IPU_SMMU_MSTR_BASE (IPU_BASE_ADDRESS + 0xA0000) +#define IPU_SMMU_COMM_BASE (IPU_BASE_ADDRESS + 0x80000) +#define IPU_SMMU_MSTR_BASE_ES (IPU_BASE_ADDRESS + 0x84000) +#define IPU_SMMU_COMM_BASE_ES (IPU_BASE_ADDRESS + 0x80000) +#define IPU_SMMU_READ_STREAM_NUMBER (3) +#define IPU_SMMU_TAG_COMPARE_CASE_NUMBER (6) +#define IPU_SMMU_TOTAL_STREAM_ID_NUMBER (4) + +#ifdef CONFIG_HUAWEI_DSM +#define REGISTER_INFO_MAX_LEN (1024) +extern char register_info[REGISTER_INFO_MAX_LEN]; +#endif + +#define DEBUG(fmt, ...) printk(KERN_DEBUG "[%s+%d]: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__) + +struct map_format { + unsigned long iova_start; + unsigned long iova_size; + int prot; +}; + +struct map_data { + int share_fd; + struct map_format format; +}; + +struct memory_manage_node { + struct list_head head; + struct map_data map; +}; + +struct smmu_irq_count { + unsigned int mstr_wdata_burst; + unsigned int mstr_wr_va_out_of_128byte; + unsigned int mstr_wr_va_out_of_boundary; + unsigned int mstr_rd_va_out_of_128byte; + unsigned int mstr_rd_va_out_of_boundary; + unsigned int comm_ptw_ns_stat; + unsigned int comm_ptw_invalid_stat; + unsigned int comm_ptw_trans_stat; + unsigned int comm_tlbmiss_stat; + unsigned int comm_ext_stat; + unsigned int comm_permis_stat; +}; + +struct smmu_statistic { + unsigned int read_stream_cmd_total[IPU_SMMU_READ_STREAM_NUMBER]; + unsigned int read_stream_cmd_miss[IPU_SMMU_READ_STREAM_NUMBER]; + unsigned int read_stream_data_total[IPU_SMMU_READ_STREAM_NUMBER]; + unsigned int read_stream_cmd_miss_valid; + unsigned int read_stream_cmd_miss_pending; + unsigned int read_stream_cmd_hit_valid_not_slide_window; + unsigned int read_stream_cmd_hit_valid_slide_window; + unsigned int read_stream_cmd_hit_pending_not_slide_window; + unsigned int read_stream_cmd_hit_pending_slide_window; + unsigned int read_stream_cmd_latency; + unsigned int write_stream_cmd_total; + unsigned int write_stream_cmd_miss; + unsigned int write_stream_data_total; + unsigned int write_stream_cmd_miss_valid; + unsigned int write_stream_cmd_miss_pending; + unsigned int write_stream_cmd_hit_valid_not_slide_window; + unsigned int write_stream_cmd_hit_valid_slide_window; + unsigned int write_stream_cmd_hit_pending_not_slide_window; + unsigned int write_stream_cmd_hit_pending_slide_window; + unsigned int write_stream_cmd_latency; + struct smmu_irq_count smmu_irq_count; +}; + +extern struct ion_client* ipu_ion_client; + +extern void ipu_smmu_init(unsigned long ttbr0, unsigned long smmu_rw_err_phy_addr, bool port_sel, bool hardware_start); +extern void ipu_smmu_deinit(void); +extern unsigned long ipu_get_smmu_base_phy(struct device *dev); +extern long ipu_smmu_map(struct map_data *map); +extern long ipu_smmu_unmap(struct map_data *map); +extern bool ipu_smmu_interrupt_handler(struct smmu_irq_count *irq_count); +extern void ipu_smmu_reset_statistic(void); +extern void ipu_smmu_record_statistic(struct smmu_statistic *statistic); +extern void ipu_smmu_pte_update(void); +extern bool ipu_smmu_master_get_offset(struct device *dev); +extern bool ipu_smmu_common_get_offset(struct device *dev); +extern void ipu_smmu_override_prefetch_addr(unsigned long reset_va); +extern int ipu_smmu_mngr_init(void); +extern void ipu_smmu_mngr_deinit(void); +extern void ipu_mem_mngr_init(void); +extern void ipu_mem_mngr_deinit(unsigned long *reset_va); +extern void * ipu_mem_mngr_add(struct map_data *map); +extern int ipu_mem_mngr_del(struct map_data *map); +extern int ipu_mem_mngr_check_valid(unsigned int inst_addr); +extern void ipu_mem_mngr_dump(void); +extern void ipu_smmu_dump_strm(void); + +#endif diff --git a/include/dt-bindings/clock/kirin970-clock.h b/include/dt-bindings/clock/kirin970-clock.h index 087707fcc78e..7606312221af 100644 --- a/include/dt-bindings/clock/kirin970-clock.h +++ b/include/dt-bindings/clock/kirin970-clock.h @@ -353,6 +353,6 @@ /*clk media2*/ #define KIRIN970_CLK_GATE_VDECFREQ 0 #define KIRIN970_CLK_GATE_VENCFREQ 1 - +#define KIRIN970_CLK_GATE_ICSFREQ 2 #endif /* __DT_BINDINGS_CLOCK_KIRIN970_H */