diff --git a/MAINTAINERS b/MAINTAINERS index 20e34583cf82..e920c86fe95a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13409,3 +13409,25 @@ F: */ AMLOGIC Pinmux M: Jianxin Pan F: drivers/amlogic/pinctrl/* + +AMLOGIC CLOCKSOURCE DRIVER +M: Yun Cai +F: drivers/amlogic/clocksource/ + +AMLOGIC CPU INFORMATION DRIVER +M: Yun Cai +F: drivers/amlogic/cpu_info/ +F: drivers/amlogic/cpu_version/ + +AMLOGIC MAILBOX DRIVER +M: Yun Cai +F: drivers/amlogic/mailbox/ + +AMLOGIC REGISTER DEBUG DRIVER +M: Yun Cai +F: drivers/amlogic/reg_access/ + +AMLOGIC SECURE MONITOR DRIVER +M: Yun Cai +F: drivers/amlogic/secmon/ + diff --git a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi index 88ed7392cb0c..a144bf13d908 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi @@ -130,7 +130,6 @@ interrupts = ; }; - psci { compatible = "arm,psci"; method = "smc"; @@ -139,12 +138,14 @@ cpu_on = <0xC4000003>; migrate = <0xC4000005>; }; + secmon { compatible = "amlogic, secmon"; memory-region = <&secmon_reserved>; in_base_func = <0x82000020>; out_base_func = <0x82000021>; }; + cpu_iomap{ compatible = "amlogic, iomap"; #address-cells=<2>; @@ -163,7 +164,26 @@ cpu_version{ reg=<0x0 0xc8100220 0x0 0x4>; + status = "okay"; }; + + cpu_info{ + compatible = "amlogic, cpuinfo"; + cpuinfo_cmd = <0x82000044>; + status = "okay"; + }; + + mailbox: mhu@c883c400 { + compatible = "amlogic, meson_mhu"; + reg = <0x0 0xc883c400 0x0 0x4c>, /* MHU registers */ + <0x0 0xc8013000 0x0 0x800>; /* Payload area */ + interrupts = <0 209 8>, /* low priority interrupt */ + <0 210 8>; /* high priority interrupt */ + #mbox-cells = <1>; + mbox-names = "cpu_to_scp_low", "cpu_to_scp_high"; + mboxes = <&mailbox 0 &mailbox 1>; + }; + aobus: aobus@c8100000 { compatible = "simple-bus"; reg = <0x0 0xc8100000 0x0 0x100000>; diff --git a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi index 19fd81c46047..eca8e5d5d748 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi @@ -175,7 +175,6 @@ interrupts = ; }; - psci { compatible = "arm,psci"; method = "smc"; @@ -184,12 +183,14 @@ cpu_on = <0xC4000003>; migrate = <0xC4000005>; }; + secmon { compatible = "amlogic, secmon"; memory-region = <&secmon_reserved>; in_base_func = <0x82000020>; out_base_func = <0x82000021>; }; + cpu_iomap{ compatible = "amlogic, iomap"; #address-cells=<2>; @@ -208,7 +209,26 @@ cpu_version{ reg=<0x0 0xc8100220 0x0 0x4>; + status = "okay"; }; + + cpu_info{ + compatible = "amlogic, cpuinfo"; + cpuinfo_cmd = <0x82000044>; + status = "okay"; + }; + + mailbox: mhu@c883c400 { + compatible = "amlogic, meson_mhu"; + reg = <0x0 0xc883c400 0x0 0x4c>, /* MHU registers */ + <0x0 0xc8013000 0x0 0x800>; /* Payload area */ + interrupts = <0 209 8>, /* low priority interrupt */ + <0 210 8>; /* high priority interrupt */ + #mbox-cells = <1>; + mbox-names = "cpu_to_scp_low", "cpu_to_scp_high"; + mboxes = <&mailbox 0 &mailbox 1>; + }; + aobus: aobus@c8100000 { compatible = "simple-bus"; reg = <0x0 0xc8100000 0x0 0x100000>; diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index 3b4c3e942541..4eee8c1081a9 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -158,6 +158,14 @@ CONFIG_AMLOGIC_UART=y CONFIG_AMLOGIC_SERIAL_MESON_CONSOLE=y CONFIG_AMLOGIC_IOMAP=y CONFIG_AMLOGIC_PINCTRL=y +CONFIG_AMLOGIC_SEC=y +CONFIG_AMLOGIC_CPU_VERSION=y +CONFIG_AMLOGIC_MESON64_VERSION=y +CONFIG_AMLOGIC_CPU_INFO=y +CONFIG_AMLOGIC_MHU_MBOX=y +CONFIG_AMLOGIC_REG_ACCESS=y +CONFIG_AMLOGIC_TIMER=y +CONFIG_AMLOGIC_BC_TIMER=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y @@ -306,4 +314,3 @@ CONFIG_CRYPTO_USER_API_HASH=y CONFIG_CRYPTO_USER_API_SKCIPHER=y CONFIG_CRC_T10DIF=y CONFIG_CRC7=y - diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig index 5264dd8a6047..3bd7aba5685b 100644 --- a/drivers/amlogic/Kconfig +++ b/drivers/amlogic/Kconfig @@ -16,5 +16,18 @@ source "drivers/amlogic/uart/Kconfig" source "drivers/amlogic/iomap/Kconfig" source "drivers/amlogic/pinctrl/Kconfig" + +source "drivers/amlogic/secmon/Kconfig" + +source "drivers/amlogic/cpu_version/Kconfig" + +source "drivers/amlogic/cpu_info/Kconfig" + +source "drivers/amlogic/mailbox/Kconfig" + +source "drivers/amlogic/reg_access/Kconfig" + +source "drivers/amlogic/clocksource/Kconfig" + endmenu endif diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index ba5d7d40ae2e..fb994eb1a1f5 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -11,4 +11,18 @@ obj-$(CONFIG_AMLOGIC_UART) += uart/ obj-$(CONFIG_AMLOGIC_PINCTRL) += pinctrl/ + obj-$(CONFIG_AMLOGIC_IOMAP) += iomap/ + +obj-$(CONFIG_AMLOGIC_SEC) += secmon/ + +obj-$(CONFIG_AMLOGIC_CPU_VERSION) += cpu_version/ + +obj-$(CONFIG_AMLOGIC_CPU_INFO) += cpu_info/ + +obj-$(CONFIG_AMLOGIC_MHU_MBOX) += mailbox/ + +obj-$(CONFIG_AMLOGIC_REG_ACCESS) += reg_access/ + +obj-$(CONFIG_AMLOGIC_TIMER) += clocksource/ + diff --git a/drivers/amlogic/clocksource/Kconfig b/drivers/amlogic/clocksource/Kconfig new file mode 100644 index 000000000000..136e889d4c02 --- /dev/null +++ b/drivers/amlogic/clocksource/Kconfig @@ -0,0 +1,14 @@ +menuconfig AMLOGIC_TIMER + bool "Amlogic Meson timer driver" + default n + help + This is the Amlogic Meson driver interface driver +if AMLOGIC_TIMER +config AMLOGIC_BC_TIMER + bool "meson broadcast timer support" + def_bool n + depends on AMLOGIC_TIMER + select CLKSRC_OF if OF + help + This is a new clocksource driver for amlogic timer +endif diff --git a/drivers/amlogic/clocksource/Makefile b/drivers/amlogic/clocksource/Makefile new file mode 100644 index 000000000000..83a0ca2bf140 --- /dev/null +++ b/drivers/amlogic/clocksource/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AMLOGIC_BC_TIMER) += meson_bc_timer.o diff --git a/drivers/amlogic/clocksource/meson_bc_timer.c b/drivers/amlogic/clocksource/meson_bc_timer.c new file mode 100644 index 000000000000..0a24b4aa1eea --- /dev/null +++ b/drivers/amlogic/clocksource/meson_bc_timer.c @@ -0,0 +1,282 @@ +/* + * drivers/amlogic/clocksource/meson_bc_timer.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) "meson_bc_timer: " fmt + +/*********************************************************************** + * System timer + **********************************************************************/ +#define TIMER_E_RESOLUTION_BIT 8 +#define TIMER_E_ENABLE_BIT 20 +#define TIMER_E_RESOLUTION_MASK (7UL << TIMER_E_RESOLUTION_BIT) +#define TIMER_E_RESOLUTION_SYS 0 +#define TIMER_E_RESOLUTION_1us 1 +#define TIMER_E_RESOLUTION_10us 2 +#define TIMER_E_RESOLUTION_100us 3 +#define TIMER_E_RESOLUTION_1ms 4 + +#define TIMER_DI_RESOLUTION_BIT 6 +#define TIMER_CH_RESOLUTION_BIT 4 +#define TIMER_BG_RESOLUTION_BIT 2 +#define TIMER_AF_RESOLUTION_BIT 0 + +#define TIMER_DI_ENABLE_BIT 19 +#define TIMER_CH_ENABLE_BIT 18 +#define TIMER_BG_ENABLE_BIT 17 +#define TIMER_AF_ENABLE_BIT 16 + +#define TIMER_DI_MODE_BIT 15 +#define TIMER_CH_MODE_BIT 14 +#define TIMER_BG_MODE_BIT 13 +#define TIMER_AF_MODE_BIT 12 + +#define TIMER_RESOLUTION_1us 0 +#define TIMER_RESOLUTION_10us 1 +#define TIMER_RESOLUTION_100us 2 +#define TIMER_RESOLUTION_1ms 3 + +void __iomem *timer_ctrl_base; + +static inline void aml_set_reg32_mask(void __iomem *_reg, const uint32_t _mask) +{ + uint32_t _val; + + _val = readl_relaxed(_reg) | _mask; + + writel_relaxed(_val, _reg); +} + +static inline void aml_write_reg32(void __iomem *_reg, const uint32_t _value) +{ + writel_relaxed(_value, _reg); +} + +static inline void aml_clr_reg32_mask(void __iomem *_reg, const uint32_t _mask) +{ + writel_relaxed((readl_relaxed(_reg) & (~(_mask))), _reg); +} + +static inline void +aml_set_reg32_bits(void __iomem *_reg, const uint32_t _value, + const uint32_t _start, const uint32_t _len) +{ + writel_relaxed(((readl_relaxed(_reg) & + ~(((1L << (_len))-1) << (_start))) | + (((_value)&((1L<<(_len))-1)) << (_start))), + _reg); +} + +/********** Clock Event Device, Timer-ABCD/FGHI *********/ + +struct meson_clock { + struct irqaction irq; + const char *name; /*A,B,C,D,F,G,H,I*/ + int bit_enable; + int bit_mode; + int bit_resolution; + void __iomem *mux_reg; + void __iomem *reg; + unsigned int init_flag; +}; +static struct meson_clock bc_clock; +static irqreturn_t meson_timer_interrupt(int irq, void *dev_id); +static int meson_set_next_event(unsigned long evt, + struct clock_event_device *dev); + +static DEFINE_SPINLOCK(time_lock); + +static int meson_set_next_event(unsigned long evt, + struct clock_event_device *dev) +{ + struct meson_clock *clk = &bc_clock; + /* use a big number to clear previous trigger cleanly */ + aml_set_reg32_mask(clk->reg, evt & 0xffff); + /* then set next event */ + aml_set_reg32_bits(clk->reg, evt, 0, 16); + return 0; +} + +static int meson_clkevt_shutdown(struct clock_event_device *dev) +{ + struct meson_clock *clk = &bc_clock; + + spin_lock(&time_lock); + /* pr_info("Disable timer %p %s\n",dev,dev->name); */ + aml_set_reg32_bits(clk->mux_reg, 0, clk->bit_enable, 1); + spin_unlock(&time_lock); + return 0; +} + +static int meson_clkevt_set_periodic(struct clock_event_device *dev) +{ + struct meson_clock *clk = &bc_clock; + + spin_lock(&time_lock); + aml_set_reg32_bits(clk->mux_reg, 1, clk->bit_mode, 1); + aml_set_reg32_bits(clk->mux_reg, 1, clk->bit_enable, 1); + /* pr_info("Periodic timer %s!,mux_reg=%x\n",*/ + /* dev->name,readl_relaxed(clk->mux_reg)); */ + spin_unlock(&time_lock); + return 0; +} + +static int meson_clkevt_set_oneshot(struct clock_event_device *dev) +{ + struct meson_clock *clk = &bc_clock; + + spin_lock(&time_lock); + aml_set_reg32_bits(clk->mux_reg, 0, clk->bit_mode, 1); + aml_set_reg32_bits(clk->mux_reg, 1, clk->bit_enable, 1); + /* pr_info("One shot timer %s!mux_reg=%x\n",*/ + /* dev->name,readl_relaxed(clk->mux_reg)); */ + spin_unlock(&time_lock); + return 0; +} + +static int meson_clkevt_resume(struct clock_event_device *dev) +{ + struct meson_clock *clk = &bc_clock; + + spin_lock(&time_lock); + /* pr_info("Resume timer%s\n", dev->name); */ + aml_set_reg32_bits(clk->mux_reg, 1, clk->bit_enable, 1); + spin_unlock(&time_lock); + return 0; +} + +/* Clock event timer interrupt handler */ +static irqreturn_t meson_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + if (evt == NULL || evt->event_handler == NULL) { + WARN_ONCE(evt == NULL || evt->event_handler == NULL, + "%p %s %p %d", + evt, evt?evt->name:NULL, + evt?evt->event_handler:NULL, irq); + return IRQ_HANDLED; + } + evt->event_handler(evt); + return IRQ_HANDLED; + +} + +static struct clock_event_device meson_clockevent = { + .cpumask = cpu_all_mask, + .set_next_event = meson_set_next_event, + .set_state_shutdown = meson_clkevt_shutdown, + .set_state_periodic = meson_clkevt_set_periodic, + .set_state_oneshot = meson_clkevt_set_oneshot, + .tick_resume = meson_clkevt_resume, +}; + +/* + * This sets up the system timers, clock source and clock event. + */ +int clockevent_init_and_register(struct device_node *np) +{ + struct device_node *timer; + struct meson_clock *mclk = &bc_clock; + + timer = np; + if (!timer) { + pr_info(" * %s missing timer phandle\n", + timer->full_name); + return -1; + } + if (of_property_read_string(timer, "timer_name", + &meson_clockevent.name)) + return -1; + + if (of_property_read_u32(timer, "clockevent-rating", + &meson_clockevent.rating)) + return -1; + + if (of_property_read_u32(timer, "clockevent-shift", + &meson_clockevent.shift)) + return -1; + + if (of_property_read_u32(timer, "clockevent-features", + &meson_clockevent.features)) + return -1; + + if (of_property_read_u32(timer, "bit_enable", &mclk->bit_enable)) + return -1; + + if (of_property_read_u32(timer, "bit_mode", &mclk->bit_mode)) + return -1; + + if (of_property_read_u32(timer, "bit_resolution", + &mclk->bit_resolution)) + return -1; + + mclk->mux_reg = timer_ctrl_base; + mclk->reg = of_iomap(timer, 1); + pr_info("mclk->mux_reg =%p,mclk->reg =%p\n", mclk->mux_reg, mclk->reg); + mclk->irq.irq = irq_of_parse_and_map(timer, 0); + + aml_set_reg32_mask(mclk->mux_reg, + ((1 << mclk->bit_mode) + |(TIMER_RESOLUTION_1us << mclk->bit_resolution))); + + meson_clockevent.mult = div_sc(1000000, NSEC_PER_SEC, 20); + meson_clockevent.max_delta_ns = + clockevent_delta2ns(0xfffe, &meson_clockevent); + meson_clockevent.min_delta_ns = + clockevent_delta2ns(1, &meson_clockevent); + mclk->irq.dev_id = &meson_clockevent; + mclk->irq.handler = meson_timer_interrupt; + mclk->irq.name = meson_clockevent.name; + mclk->irq.flags = + IRQF_TIMER | IRQF_IRQPOLL|IRQF_TRIGGER_RISING; + /* Set up the IRQ handler */ + meson_clockevent.irq = mclk->irq.irq; + clockevents_register_device(&meson_clockevent); + setup_irq(mclk->irq.irq, &mclk->irq); + return 0; +} + +/*meson broadcast timer*/ +int __init meson_timer_init(struct device_node *np) +{ + timer_ctrl_base = of_iomap(np, 0); + if (clockevent_init_and_register(np) < 0) + pr_err("%s err\n", __func__); + + return 0; +} +CLOCKSOURCE_OF_DECLARE(meson_timer, "arm, meson-bc-timer", meson_timer_init); diff --git a/drivers/amlogic/cpu_info/Kconfig b/drivers/amlogic/cpu_info/Kconfig new file mode 100644 index 000000000000..d62b483bab8a --- /dev/null +++ b/drivers/amlogic/cpu_info/Kconfig @@ -0,0 +1,6 @@ +config AMLOGIC_CPU_INFO + bool "Amlogic chipid" + default n + help + say y to enable Amlogic chipid driver. + diff --git a/drivers/amlogic/cpu_info/Makefile b/drivers/amlogic/cpu_info/Makefile new file mode 100644 index 000000000000..ff0c31691b70 --- /dev/null +++ b/drivers/amlogic/cpu_info/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AMLOGIC_CPU_INFO) += cpu_info.o diff --git a/drivers/amlogic/cpu_info/cpu_info.c b/drivers/amlogic/cpu_info/cpu_info.c new file mode 100644 index 000000000000..fd51341f7bb0 --- /dev/null +++ b/drivers/amlogic/cpu_info/cpu_info.c @@ -0,0 +1,136 @@ +/* + * drivers/amlogic/cpu_info/cpu_info.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include + #include +#include +#include +#include +#include +#include +#ifndef CONFIG_ARM64 +#include +#endif +#include +#include +#include +#include +static int cpuinfo_func_id; + +unsigned int system_serial_low0; +unsigned int system_serial_low1; +unsigned int system_serial_high0; +unsigned int system_serial_high1; + +#undef pr_fmt +#define pr_fmt(fmt) "cpuinfo: " fmt +struct aml_cpu_info { + unsigned int version; + u8 chipid[12]; + unsigned int reserved[103]; +}; +static void __iomem *sharemem_output; +static struct aml_cpu_info *cpu_info_buf; +static noinline int fn_smc(u64 function_id, u64 arg0, u64 arg1, + u64 arg2) +{ + register long x0 asm("x0") = function_id; + register long x1 asm("x1") = arg0; + register long x2 asm("x2") = arg1; + register long x3 asm("x3") = arg2; + asm volatile( + __asmeq("%0", "x0") + __asmeq("%1", "x1") + __asmeq("%2", "x2") + __asmeq("%3", "x3") + "smc #0\n" + : "+r" (x0) + : "r" (x1), "r" (x2), "r" (x3)); + + return function_id; +} + +static int cpuinfo_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + unsigned int id; + unsigned int *p = NULL; + unsigned int version = + (get_meson_cpu_version(MESON_CPU_VERSION_LVL_MAJOR) << 24) | + (get_meson_cpu_version(MESON_CPU_VERSION_LVL_MINOR) << 16) | + (get_meson_cpu_version(MESON_CPU_VERSION_LVL_PACK) << 8); + + if (!of_property_read_u32(np, "cpuinfo_cmd", &id)) + cpuinfo_func_id = id; + cpu_info_buf = kzalloc(sizeof(struct aml_cpu_info), GFP_KERNEL); + if (!cpu_info_buf) { + pr_info("No memory to alloc\n"); + return -ENOMEM; + } + sharemem_output = get_secmon_sharemem_output_base(); + if (!sharemem_output) { + pr_info("secmon share mem prepare not okay\n"); + return -ENOMEM; + } + sharemem_mutex_lock(); + fn_smc(cpuinfo_func_id, 0, 0, 0); + memcpy((void *)cpu_info_buf, + (const void *)sharemem_output, sizeof(struct aml_cpu_info)); + sharemem_mutex_unlock(); +#if 0 + int i; + unsigned int *p = (unsigned int *)(cpu_info_buf->chipid); + + for (i = 0; i < 12; i++) + pr_info("cpu_info_buf->chipid[%d]=%x\n", + i, cpu_info_buf->chipid[i]); +#endif + p = (unsigned int *)(cpu_info_buf->chipid); + system_serial_low0 = *p; + system_serial_low1 = *(p+1); + system_serial_high0 = *(p+2); + system_serial_high1 = version; + + pr_info("probe done\n"); + return 0; +} + +static const struct of_device_id cpuinfo_dt_match[] = { + { .compatible = "amlogic, cpuinfo" }, + { /* sentinel */ }, +}; + +static struct platform_driver cpuinfo_platform_driver = { + .probe = cpuinfo_probe, + .driver = { + .owner = THIS_MODULE, + .name = "cpuinfo", + .of_match_table = cpuinfo_dt_match, + }, +}; + +static int __init meson_cpuinfo_init(void) +{ + return platform_driver_register(&cpuinfo_platform_driver); +} +module_init(meson_cpuinfo_init); + + diff --git a/drivers/amlogic/cpu_version/Kconfig b/drivers/amlogic/cpu_version/Kconfig new file mode 100644 index 000000000000..153474db3cff --- /dev/null +++ b/drivers/amlogic/cpu_version/Kconfig @@ -0,0 +1,18 @@ +menuconfig AMLOGIC_CPU_VERSION + bool "Amlogic cpu version support" + default n + help + This is the Amlogic CPU version interface driver + +if AMLOGIC_CPU_VERSION + +config AMLOGIC_MESON64_VERSION + bool "Amlogic Meson64 cpu version" + default n + help + say y to enable + Amlogic gx-X + cpu version driver + . + +endif diff --git a/drivers/amlogic/cpu_version/Makefile b/drivers/amlogic/cpu_version/Makefile new file mode 100644 index 000000000000..a33637582d00 --- /dev/null +++ b/drivers/amlogic/cpu_version/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AMLOGIC_MESON64_VERSION) += meson64_cpu.o diff --git a/drivers/amlogic/cpu_version/meson64_cpu.c b/drivers/amlogic/cpu_version/meson64_cpu.c new file mode 100644 index 000000000000..6772d6cdf0d5 --- /dev/null +++ b/drivers/amlogic/cpu_version/meson64_cpu.c @@ -0,0 +1,92 @@ +/* + * drivers/amlogic/cpu_version/meson64_cpu.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +static int meson_cpu_version[MESON_CPU_VERSION_LVL_MAX+1]; +void __iomem *assist_hw_rev; + +int get_meson_cpu_version(int level) +{ + if (level >= 0 && level <= MESON_CPU_VERSION_LVL_MAX) + return meson_cpu_version[level]; + return 0; +} +EXPORT_SYMBOL(get_meson_cpu_version); + +/* + * detect if a cpu id is big cpu + */ +int arch_big_cpu(int cpu) +{ + int type; + struct device_node *cpu_version; + + cpu_version = of_find_node_by_name(NULL, "cpu_version"); + if (cpu_version) + assist_hw_rev = of_iomap(cpu_version, 0); + else + return 0; + + type = readl(assist_hw_rev) >> 24; + switch (type) { + case MESON_CPU_MAJOR_ID_GXM: /* 0 ~ 3 is faster cpu for GXM */ + if (cpu < 4) + return 1; + + default: + return 0; + } + return 0; +} +EXPORT_SYMBOL(arch_big_cpu); + +int __init meson_cpu_version_init(void) +{ + unsigned int ver; + struct device_node *cpu_version; + + cpu_version = of_find_node_by_name(NULL, "cpu_version"); + if (cpu_version) + assist_hw_rev = of_iomap(cpu_version, 0); + else + return 0; + + meson_cpu_version[MESON_CPU_VERSION_LVL_MAJOR] = + readl(assist_hw_rev) >> 24; + + ver = (readl(assist_hw_rev) >> 8) & 0xff; + + meson_cpu_version[MESON_CPU_VERSION_LVL_MINOR] = ver; + ver = (readl(assist_hw_rev) >> 16) & 0xff; + meson_cpu_version[MESON_CPU_VERSION_LVL_PACK] = ver; + pr_info("Meson chip version = Rev%X (%X:%X - %X:%X)\n", + meson_cpu_version[MESON_CPU_VERSION_LVL_MINOR], + meson_cpu_version[MESON_CPU_VERSION_LVL_MAJOR], + meson_cpu_version[MESON_CPU_VERSION_LVL_MINOR], + meson_cpu_version[MESON_CPU_VERSION_LVL_PACK], + meson_cpu_version[MESON_CPU_VERSION_LVL_MISC] + ); + return 0; +} +early_initcall(meson_cpu_version_init); diff --git a/drivers/amlogic/mailbox/Kconfig b/drivers/amlogic/mailbox/Kconfig new file mode 100644 index 000000000000..7e2a868179f9 --- /dev/null +++ b/drivers/amlogic/mailbox/Kconfig @@ -0,0 +1,31 @@ +# +# Amlogic mhu mailbox device configuration +# + +menu "MESON MHU mailbox Support" + +config AMLOGIC_MHU_MBOX + tristate "MESON MHU Mailbox" + default n + select MAILBOX + select ARM_SCPI_PROTOCOL + help + MESON MHU Mailbox driver. + . + . + . + +config ARM_SCPI_PROTOCOL + tristate "ARM System Control and Power Interface (SCPI) Message Protocol" + default n + help + System Control and Power Interface (SCPI) Message Protocol is + defined for the purpose of communication between the Application + Cores(AP) and the System Control Processor(SCP). The MHU peripheral + provides a mechanism for inter-processor communication between SCP + and AP. + + This protocol library provides interface for all the client drivers + making use of the features offered by the SCP. + +endmenu diff --git a/drivers/amlogic/mailbox/Makefile b/drivers/amlogic/mailbox/Makefile new file mode 100644 index 000000000000..9f66916f90ea --- /dev/null +++ b/drivers/amlogic/mailbox/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_AMLOGIC_MHU_MBOX) += meson_mhu.o +obj-$(CONFIG_ARM_SCPI_PROTOCOL) += scpi_protocol.o diff --git a/drivers/amlogic/mailbox/meson_mhu.c b/drivers/amlogic/mailbox/meson_mhu.c new file mode 100644 index 000000000000..8a7f72c816b2 --- /dev/null +++ b/drivers/amlogic/mailbox/meson_mhu.c @@ -0,0 +1,304 @@ +/* + * drivers/amlogic/mailbox/meson_mhu.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "meson_mhu.h" + +struct device *the_scpi_device; + +#define DRIVER_NAME "meson_mhu" + +/* + * +--------------------+-------+---------------+ + * | Hardware Register | Offset| Driver View | + * +--------------------+-------+---------------+ + * | SCP_INTR_L_STAT | 0x014 | RX_STATUS(L) | + * | SCP_INTR_L_SET | 0x010 | RX_SET(L) | + * | SCP_INTR_L_CLEAR | 0x018 | RX_CLEAR(L) | + * +--------------------+-------+---------------+ + * | SCP_INTR_H_STAT | 0x020 | RX_STATUS(H) | + * | SCP_INTR_H_SET | 0x01c | RX_SET(H) | + * | SCP_INTR_H_CLEAR | 0x024 | RX_CLEAR(H) | + * +--------------------+-------+---------------+ + * | CPU_INTR_L_STAT | 0x038 | TX_STATUS(L) | + * | CPU_INTR_L_SET | 0x034 | TX_SET(L) | + * | CPU_INTR_L_CLEAR | 0x03c | TX_CLEAR(L) | + * +--------------------+-------+---------------+ + * | CPU_INTR_H_STAT | 0x044 | TX_STATUS(H) | + * | CPU_INTR_H_SET | 0x040 | TX_SET(H) | + * | CPU_INTR_H_CLEAR | 0x048 | TX_CLEAR(H) | + * +--------------------+-------+---------------+ + */ +#define RX_OFFSET(chan) (0x10 + (idx) * 0xc) +#define RX_STATUS(chan) (RX_OFFSET(chan) + 0x4) +#define RX_SET(chan) RX_OFFSET(chan) +#define RX_CLEAR(chan) (RX_OFFSET(chan) + 0x8) + +#define TX_OFFSET(chan) (0x34 + (idx) * 0xc) +#define TX_STATUS(chan) (TX_OFFSET(chan) + 0x4) +#define TX_SET(chan) TX_OFFSET(chan) +#define TX_CLEAR(chan) (TX_OFFSET(chan) + 0x8) + +/* + * +---------------+-------+----------------+ + * | Payload | Offset| Driver View | + * +---------------+-------+----------------+ + * | SCP->AP Low | 0x000 | RX_PAYLOAD(L) | + * | SCP->AP High | 0x400 | RX_PAYLOAD(H) | + * +---------------+-------+----------------+ + * | AP->SCP Low | 0x200 | TX_PAYLOAD(H) | + * | AP->SCP High | 0x600 | TX_PAYLOAD(H) | + * +---------------+-------+----------------+ + */ +#define PAYLOAD_MAX_SIZE 0x200 +#define PAYLOAD_OFFSET 0x400 +#define RX_PAYLOAD(chan) ((chan) * PAYLOAD_OFFSET) +#define TX_PAYLOAD(chan) ((chan) * PAYLOAD_OFFSET + PAYLOAD_MAX_SIZE) + +struct mhu_chan { + int index; + int rx_irq; + struct mhu_ctlr *ctlr; + struct mhu_data_buf *data; +}; + +struct mhu_ctlr { + struct device *dev; + void __iomem *mbox_base; + void __iomem *payload_base; + struct mbox_controller mbox_con; + struct mhu_chan channels[CHANNEL_MAX]; +}; + +static irqreturn_t mbox_handler(int irq, void *p) +{ + struct mbox_chan *link = (struct mbox_chan *)p; + struct mhu_chan *chan = link->con_priv; + struct mhu_ctlr *ctlr = chan->ctlr; + void __iomem *mbox_base = ctlr->mbox_base; + void __iomem *payload = ctlr->payload_base; + int idx = chan->index; + struct mhu_data_buf *data; + unsigned int *pp; + u32 status = readl(mbox_base + RX_STATUS(idx)); + + if (status && irq == chan->rx_irq) { + data = chan->data; + pp = (unsigned int *)data->rx_buf; + if (!data) + return IRQ_NONE; /* spurious */ + if (data->rx_buf) + memcpy(data->rx_buf, payload + RX_PAYLOAD(idx), + data->rx_size); + chan->data = NULL; + mbox_chan_received_data(link, data); + writel(~0, mbox_base + RX_CLEAR(idx)); + } + + return IRQ_HANDLED; +} + +static int mhu_send_data(struct mbox_chan *link, void *msg) +{ + struct mhu_chan *chan = link->con_priv; + struct mhu_ctlr *ctlr = chan->ctlr; + void __iomem *mbox_base = ctlr->mbox_base; + void __iomem *payload = ctlr->payload_base; + struct mhu_data_buf *data = (struct mhu_data_buf *)msg; + int idx = chan->index; + + if (!data) + return -EINVAL; + + chan->data = data; + if (data->tx_buf) + memcpy(payload + TX_PAYLOAD(idx), data->tx_buf, data->tx_size); + writel(data->cmd, mbox_base + TX_SET(idx)); + + return 0; +} + +static int mhu_startup(struct mbox_chan *link) +{ + struct mhu_chan *chan = link->con_priv; + int err, mbox_irq = chan->rx_irq; + + err = request_threaded_irq(mbox_irq, mbox_handler, NULL, IRQF_ONESHOT, + DRIVER_NAME, link); + return err; +} + +static void mhu_shutdown(struct mbox_chan *link) +{ + struct mhu_chan *chan = link->con_priv; + + chan->data = NULL; + free_irq(chan->rx_irq, link); +} + +static bool mhu_last_tx_done(struct mbox_chan *link) +{ + struct mhu_chan *chan = link->con_priv; + struct mhu_ctlr *ctlr = chan->ctlr; + void __iomem *mbox_base = ctlr->mbox_base; + int idx = chan->index; + + return !readl(mbox_base + TX_STATUS(idx)); +} + +static struct mbox_chan_ops mhu_ops = { + .send_data = mhu_send_data, + .startup = mhu_startup, + .shutdown = mhu_shutdown, + .last_tx_done = mhu_last_tx_done, +}; + +static int mhu_probe(struct platform_device *pdev) +{ + struct mhu_ctlr *ctlr; + struct mhu_chan *chan; + struct device *dev = &pdev->dev; + struct mbox_chan *l; + struct resource *res; + int idx; + static const char * const channel_names[] = { + CHANNEL_LOW_PRIORITY, + CHANNEL_HIGH_PRIORITY + }; + + ctlr = devm_kzalloc(dev, sizeof(*ctlr), GFP_KERNEL); + if (!ctlr) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to get mailbox memory resource\n"); + return -ENXIO; + } + + ctlr->mbox_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctlr->mbox_base)) + return PTR_ERR(ctlr->mbox_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "failed to get payload memory resource\n"); + return -ENXIO; + } + + ctlr->payload_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctlr->payload_base)) + return PTR_ERR(ctlr->payload_base); + + ctlr->dev = dev; + platform_set_drvdata(pdev, ctlr); + + l = devm_kzalloc(dev, sizeof(*l) * CHANNEL_MAX, GFP_KERNEL); + if (!l) + return -ENOMEM; + + ctlr->mbox_con.chans = l; + ctlr->mbox_con.num_chans = CHANNEL_MAX; + ctlr->mbox_con.txdone_irq = true; + ctlr->mbox_con.ops = &mhu_ops; + ctlr->mbox_con.dev = dev; + + for (idx = 0; idx < CHANNEL_MAX; idx++) { + chan = &ctlr->channels[idx]; + chan->index = idx; + chan->ctlr = ctlr; + chan->rx_irq = platform_get_irq(pdev, idx); + + if (chan->rx_irq < 0) { + dev_err(dev, "failed to get interrupt for %s\n", + channel_names[idx]); + return -ENXIO; + } + l[idx].con_priv = chan; + } + + if (mbox_controller_register(&ctlr->mbox_con)) { + dev_err(dev, "failed to register mailbox controller\n"); + return -ENOMEM; + } + + the_scpi_device = dev; + return 0; +} + +static int mhu_remove(struct platform_device *pdev) +{ + struct mhu_ctlr *ctlr = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + mbox_controller_unregister(&ctlr->mbox_con); + devm_kfree(dev, ctlr->mbox_con.chans); + + devm_iounmap(dev, ctlr->payload_base); + devm_iounmap(dev, ctlr->mbox_base); + + platform_set_drvdata(pdev, NULL); + devm_kfree(dev, ctlr); + return 0; +} + +static const struct of_device_id mhu_of_match[] = { + { .compatible = "amlogic, meson_mhu" }, + {}, +}; + +static struct platform_driver mhu_driver = { + .probe = mhu_probe, + .remove = mhu_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = mhu_of_match, + }, +}; + +static int __init mhu_init(void) +{ + return platform_driver_register(&mhu_driver); +} +core_initcall(mhu_init); + +static void __exit mhu_exit(void) +{ + platform_driver_unregister(&mhu_driver); +} +module_exit(mhu_exit); + +MODULE_AUTHOR("yan wang "); +MODULE_DESCRIPTION("MESON MHU mailbox driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/amlogic/mailbox/meson_mhu.h b/drivers/amlogic/mailbox/meson_mhu.h new file mode 100644 index 000000000000..5303b7c0a53e --- /dev/null +++ b/drivers/amlogic/mailbox/meson_mhu.h @@ -0,0 +1,33 @@ +/* + * drivers/amlogic/mailbox/meson_mhu.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#define CONTROLLER_NAME "mhu_ctlr" + +#define CHANNEL_MAX 2 +#define CHANNEL_LOW_PRIORITY "cpu_to_scp_low" +#define CHANNEL_HIGH_PRIORITY "cpu_to_scp_high" + +struct mhu_data_buf { + u32 cmd; + int tx_size; + void *tx_buf; + int rx_size; + void *rx_buf; + void *cl_data; +}; + +extern struct device *the_scpi_device; diff --git a/drivers/amlogic/mailbox/scpi_protocol.c b/drivers/amlogic/mailbox/scpi_protocol.c new file mode 100644 index 000000000000..8f00249691eb --- /dev/null +++ b/drivers/amlogic/mailbox/scpi_protocol.c @@ -0,0 +1,428 @@ +/* + * drivers/amlogic/mailbox/scpi_protocol.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "meson_mhu.h" + + +#define CMD_ID_SHIFT 0 +#define CMD_ID_MASK 0xff +#define CMD_SENDER_ID_SHIFT 8 +#define CMD_SENDER_ID_MASK 0xff +#define CMD_DATA_SIZE_SHIFT 20 +#define CMD_DATA_SIZE_MASK 0x1ff +#define PACK_SCPI_CMD(cmd, sender, txsz) \ + ((((cmd) & CMD_ID_MASK) << CMD_ID_SHIFT) | \ + (((sender) & CMD_SENDER_ID_MASK) << CMD_SENDER_ID_SHIFT) | \ + (((txsz) & CMD_DATA_SIZE_MASK) << CMD_DATA_SIZE_SHIFT)) + +#define MAX_DVFS_DOMAINS 3 +#define MAX_DVFS_OPPS 16 +#define DVFS_LATENCY(hdr) ((hdr) >> 16) +#define DVFS_OPP_COUNT(hdr) (((hdr) >> 8) & 0xff) + +enum scpi_error_codes { + SCPI_SUCCESS = 0, /* Success */ + SCPI_ERR_PARAM = 1, /* Invalid parameter(s) */ + SCPI_ERR_ALIGN = 2, /* Invalid alignment */ + SCPI_ERR_SIZE = 3, /* Invalid size */ + SCPI_ERR_HANDLER = 4, /* Invalid handler/callback */ + SCPI_ERR_ACCESS = 5, /* Invalid access/permission denied */ + SCPI_ERR_RANGE = 6, /* Value out of range */ + SCPI_ERR_TIMEOUT = 7, /* Timeout has occurred */ + SCPI_ERR_NOMEM = 8, /* Invalid memory area or pointer */ + SCPI_ERR_PWRSTATE = 9, /* Invalid power state */ + SCPI_ERR_SUPPORT = 10, /* Not supported or disabled */ + SCPI_ERR_DEVICE = 11, /* Device error */ + SCPI_ERR_MAX +}; + + +struct scpi_data_buf { + int client_id; + struct mhu_data_buf *data; + struct completion complete; +}; + +static int high_priority_cmds[] = { + SCPI_CMD_GET_CSS_PWR_STATE, + SCPI_CMD_CFG_PWR_STATE_STAT, + SCPI_CMD_GET_PWR_STATE_STAT, + SCPI_CMD_SET_DVFS, + SCPI_CMD_GET_DVFS, + SCPI_CMD_SET_RTC, + SCPI_CMD_GET_RTC, + SCPI_CMD_SET_CLOCK_INDEX, + SCPI_CMD_SET_CLOCK_VALUE, + SCPI_CMD_GET_CLOCK_VALUE, + SCPI_CMD_SET_PSU, + SCPI_CMD_GET_PSU, + SCPI_CMD_SENSOR_CFG_PERIODIC, + SCPI_CMD_SENSOR_CFG_BOUNDS, +}; + +static struct scpi_dvfs_info *scpi_opps[MAX_DVFS_DOMAINS]; + +static int scpi_linux_errmap[SCPI_ERR_MAX] = { + 0, -EINVAL, -ENOEXEC, -EMSGSIZE, + -EINVAL, -EACCES, -ERANGE, -ETIMEDOUT, + -ENOMEM, -EINVAL, -EOPNOTSUPP, -EIO, +}; + +static inline int scpi_to_linux_errno(int errno) +{ + if (errno >= SCPI_SUCCESS && errno < SCPI_ERR_MAX) + return scpi_linux_errmap[errno]; + return -EIO; +} + +static bool high_priority_chan_supported(int cmd) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(high_priority_cmds); idx++) + if (cmd == high_priority_cmds[idx]) + return true; + return false; +} + +static void scpi_rx_callback(struct mbox_client *cl, void *msg) +{ + struct mhu_data_buf *data = (struct mhu_data_buf *)msg; + struct scpi_data_buf *scpi_buf = data->cl_data; + + complete(&scpi_buf->complete); +} + +static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority) +{ + struct mbox_chan *chan; + struct mbox_client cl = {0}; + struct mhu_data_buf *data = scpi_buf->data; + u32 status; + + cl.dev = the_scpi_device; + cl.rx_callback = scpi_rx_callback; + + chan = mbox_request_channel(&cl, high_priority); + if (IS_ERR(chan)) + return PTR_ERR(chan); + + init_completion(&scpi_buf->complete); + if (mbox_send_message(chan, (void *)data) < 0) { + status = SCPI_ERR_TIMEOUT; + goto free_channel; + } + + wait_for_completion(&scpi_buf->complete); + status = *(u32 *)(data->rx_buf); /* read first word */ + +free_channel: + mbox_free_channel(chan); + + return scpi_to_linux_errno(status); +} + +#define SCPI_SETUP_DBUF(scpi_buf, mhu_buf, _client_id,\ + _cmd, _tx_buf, _rx_buf) \ +do { \ + struct mhu_data_buf *pdata = &mhu_buf; \ + pdata->cmd = _cmd; \ + pdata->tx_buf = &_tx_buf; \ + pdata->tx_size = sizeof(_tx_buf); \ + pdata->rx_buf = &_rx_buf; \ + pdata->rx_size = sizeof(_rx_buf); \ + scpi_buf.client_id = _client_id; \ + scpi_buf.data = pdata; \ +} while (0) + +#define SCPI_SETUP_DBUF_SIZE(scpi_buf, mhu_buf, _client_id,\ + _cmd, _tx_buf, _tx_size, _rx_buf, _rx_size) \ +do { \ + struct mhu_data_buf *pdata = &mhu_buf; \ + pdata->cmd = _cmd; \ + pdata->tx_buf = _tx_buf; \ + pdata->tx_size = _tx_size; \ + pdata->rx_buf = _rx_buf; \ + pdata->rx_size = _rx_size; \ + scpi_buf.client_id = _client_id; \ + scpi_buf.data = pdata; \ +} while (0) + +static int scpi_execute_cmd(struct scpi_data_buf *scpi_buf) +{ + struct mhu_data_buf *data; + bool high_priority; + + if (!scpi_buf || !scpi_buf->data) + return -EINVAL; + data = scpi_buf->data; + high_priority = high_priority_chan_supported(data->cmd); + data->cmd = PACK_SCPI_CMD(data->cmd, scpi_buf->client_id, + data->tx_size); + data->cl_data = scpi_buf; + + return send_scpi_cmd(scpi_buf, high_priority); +} + +unsigned long scpi_clk_get_val(u16 clk_id) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + struct __packed { + u32 status; + u32 clk_rate; + } buf; + + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_CLOCKS, + SCPI_CMD_GET_CLOCK_VALUE, clk_id, buf); + if (scpi_execute_cmd(&sdata)) + return 0; + + return buf.clk_rate; +} +EXPORT_SYMBOL_GPL(scpi_clk_get_val); + +int scpi_clk_set_val(u16 clk_id, unsigned long rate) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + int stat; + struct __packed { + u32 clk_rate; + u16 clk_id; + } buf; + + buf.clk_rate = (u32)rate; + buf.clk_id = clk_id; + + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_CLOCKS, + SCPI_CMD_SET_CLOCK_VALUE, buf, stat); + return scpi_execute_cmd(&sdata); +} +EXPORT_SYMBOL_GPL(scpi_clk_set_val); + +struct scpi_dvfs_info *scpi_dvfs_get_opps(u8 domain) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + struct __packed { + u32 status; + u32 header; + struct scpi_opp_entry opp[MAX_DVFS_OPPS]; + } buf; + struct scpi_dvfs_info *opps; + size_t opps_sz; + int count, ret; + + if (domain >= MAX_DVFS_DOMAINS) + return ERR_PTR(-EINVAL); + + if (scpi_opps[domain]) /* data already populated */ + return scpi_opps[domain]; + + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS, + SCPI_CMD_GET_DVFS_INFO, domain, buf); + ret = scpi_execute_cmd(&sdata); + if (ret) + return ERR_PTR(ret); + + opps = kmalloc(sizeof(*opps), GFP_KERNEL); + if (!opps) + return ERR_PTR(-ENOMEM); + + count = DVFS_OPP_COUNT(buf.header); + opps_sz = count * sizeof(*(opps->opp)); + + opps->count = count; + opps->latency = DVFS_LATENCY(buf.header); + opps->opp = kmalloc(opps_sz, GFP_KERNEL); + if (!opps->opp) { + kfree(opps); + return ERR_PTR(-ENOMEM); + } + + memcpy(opps->opp, &buf.opp[0], opps_sz); + scpi_opps[domain] = opps; + + return opps; +} +EXPORT_SYMBOL_GPL(scpi_dvfs_get_opps); + +int scpi_dvfs_get_idx(u8 domain) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + struct __packed { + u32 status; + u8 dvfs_idx; + } buf; + int ret; + + if (domain >= MAX_DVFS_DOMAINS) + return -EINVAL; + + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS, + SCPI_CMD_GET_DVFS, domain, buf); + ret = scpi_execute_cmd(&sdata); + + if (!ret) + ret = buf.dvfs_idx; + return ret; +} +EXPORT_SYMBOL_GPL(scpi_dvfs_get_idx); + +int scpi_dvfs_set_idx(u8 domain, u8 idx) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + struct __packed { + u8 dvfs_domain; + u8 dvfs_idx; + } buf; + int stat; + + buf.dvfs_idx = idx; + buf.dvfs_domain = domain; + + if (domain >= MAX_DVFS_DOMAINS) + return -EINVAL; + + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS, + SCPI_CMD_SET_DVFS, buf, stat); + return scpi_execute_cmd(&sdata); +} +EXPORT_SYMBOL_GPL(scpi_dvfs_set_idx); + +int scpi_get_sensor(char *name) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + struct __packed { + u32 status; + u16 sensors; + } cap_buf; + struct __packed { + u32 status; + u16 sensor; + u8 class; + u8 trigger; + char name[20]; + } info_buf; + int ret; + u16 sensor_id; + + /* This should be handled by a generic macro */ + do { + struct mhu_data_buf *pdata = &mdata; + + pdata->cmd = SCPI_CMD_SENSOR_CAPABILITIES; + pdata->tx_size = 0; + pdata->rx_buf = &cap_buf; + pdata->rx_size = sizeof(cap_buf); + sdata.client_id = SCPI_CL_THERMAL; + sdata.data = pdata; + } while (0); + ret = scpi_execute_cmd(&sdata); + if (ret) + goto out; + + ret = -ENODEV; + for (sensor_id = 0; sensor_id < cap_buf.sensors; sensor_id++) { + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_THERMAL, + SCPI_CMD_SENSOR_INFO, sensor_id, info_buf); + ret = scpi_execute_cmd(&sdata); + if (ret) + break; + + if (!strcmp(name, info_buf.name)) { + ret = sensor_id; + break; + } + } +out: + return ret; +} +EXPORT_SYMBOL_GPL(scpi_get_sensor); + +int scpi_get_sensor_value(u16 sensor, u32 *val) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + struct __packed { + u32 status; + u32 val; + } buf; + int ret; + + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_THERMAL, SCPI_CMD_SENSOR_VALUE, + sensor, buf); + ret = scpi_execute_cmd(&sdata); + if (ret == 0) + *val = buf.val; + + return ret; +} +EXPORT_SYMBOL_GPL(scpi_get_sensor_value); + +/****Send fail when data size > 0x1fd. *** + * Because of USER_LOW_TASK_SHARE_MEM_BASE *** + * size limitation. + * You can call scpi_send_usr_data() + * multi-times when your data is bigger + * than 0x1fe + */ +int scpi_send_usr_data(u32 client_id, u32 *val, u32 size) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + struct __packed { + u32 status; + u32 val; + } buf; + int ret; + + /*client_id bit map should locates @ 0xff. + * bl30 will send client_id via half-Word + */ + if (client_id & ~0xff) + return -E2BIG; + + /*Check size here because of USER_LOW_TASK_SHARE_MEM_BASE + * size limitation, and first Word is used as command, + * second word is used as tx_size. + */ + if (size > 0x1fd) + return -EPERM; + + SCPI_SETUP_DBUF_SIZE(sdata, mdata, client_id, SCPI_CMD_SET_USR_DATA, + val, size, &buf, sizeof(buf)); + ret = scpi_execute_cmd(&sdata); + + return ret; +} +EXPORT_SYMBOL_GPL(scpi_send_usr_data); + diff --git a/drivers/amlogic/reg_access/Kconfig b/drivers/amlogic/reg_access/Kconfig new file mode 100644 index 000000000000..7ec8a33f7f50 --- /dev/null +++ b/drivers/amlogic/reg_access/Kconfig @@ -0,0 +1,7 @@ +# Amlogic DEBUG +# +config AMLOGIC_REG_ACCESS + bool "Amlogic register access from userspace support" + default n + help + This is the Amlogic debug sysfs interface driver using debugfs interface diff --git a/drivers/amlogic/reg_access/Makefile b/drivers/amlogic/reg_access/Makefile new file mode 100644 index 000000000000..a98cb2566d73 --- /dev/null +++ b/drivers/amlogic/reg_access/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AMLOGIC_REG_ACCESS) =reg_access.o diff --git a/drivers/amlogic/reg_access/reg_access.c b/drivers/amlogic/reg_access/reg_access.c new file mode 100644 index 000000000000..1a07c061da75 --- /dev/null +++ b/drivers/amlogic/reg_access/reg_access.c @@ -0,0 +1,210 @@ +/* + * drivers/amlogic/reg_access/reg_access.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +/* Standard Linux headers */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +static struct dentry *debugfs_root; +/* amlogic debug device*/ +struct aml_ddev { + unsigned int cached_reg_addr; + unsigned int size; + int (*debugfs_reg_access)(struct aml_ddev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval); +}; +int aml_reg_access(struct aml_ddev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + void __iomem *vaddr; + + reg = round_down(reg, 0x3); + + vaddr = ioremap(reg, 0x4); + + if (readval) + *readval = readl(vaddr); + else + writel(writeval, vaddr); + iounmap(vaddr); + return 0; +} +static struct aml_ddev aml_dev; +static ssize_t paddr_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct aml_ddev *indio_dev = file->private_data; + char buf[80]; + unsigned int val = 0; + ssize_t len; + int ret; + + ret = indio_dev->debugfs_reg_access(indio_dev, + indio_dev->cached_reg_addr, + 0, &val); + if (ret) + pr_err("%s: read failed\n", __func__); + + len = snprintf(buf, sizeof(buf), "[0x%x] = 0x%X\n", + indio_dev->cached_reg_addr, val); + + return simple_read_from_buffer(userbuf, count, ppos, buf, len); + +} + +static ssize_t paddr_write_file(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct aml_ddev *indio_dev = file->private_data; + unsigned int reg, val; + char buf[80]; + int ret; + + count = min_t(size_t, count, (sizeof(buf)-1)); + if (copy_from_user(buf, userbuf, count)) + return -EFAULT; + + buf[count] = 0; + + ret = sscanf(buf, "%x %x", ®, &val); + + switch (ret) { + case 1: + indio_dev->cached_reg_addr = reg; + break; + case 2: + indio_dev->cached_reg_addr = reg; + ret = indio_dev->debugfs_reg_access(indio_dev, reg, + val, NULL); + if (ret) { + pr_err("%s: write failed\n", __func__); + return ret; + } + break; + default: + return -EINVAL; + } + + return count; +} + +static const struct file_operations paddr_file_ops = { + .open = simple_open, + .read = paddr_read_file, + .write = paddr_write_file, +}; + + +static ssize_t dump_write_file(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct aml_ddev *indio_dev = s->private; + unsigned int reg, val; + char buf[80]; + int ret; + + count = min_t(size_t, count, (sizeof(buf)-1)); + if (copy_from_user(buf, userbuf, count)) + return -EFAULT; + + buf[count] = 0; + + ret = sscanf(buf, "%x %i", ®, &val); + switch (ret) { + case 2: + indio_dev->cached_reg_addr = reg; + indio_dev->size = val; + break; + default: + return -EINVAL; + } + + return count; +} + +static int dump_show(struct seq_file *s, void *what) +{ + unsigned int i = 0, val; + int ret; + struct aml_ddev *indio_dev = s->private; + + for (i = 0; i < indio_dev->size ; i++) { + ret = indio_dev->debugfs_reg_access(indio_dev, + indio_dev->cached_reg_addr, + 0, &val); + if (ret) + pr_err("%s: read failed\n", __func__); + + seq_printf(s, "[0x%x] = 0x%X\n", + indio_dev->cached_reg_addr, val); + indio_dev->cached_reg_addr += 4; + } + return 0; +} + +static int dump_open(struct inode *inode, struct file *file) +{ + return single_open(file, dump_show, inode->i_private); +} + + +static const struct file_operations dump_file_ops = { + .open = dump_open, + .read = seq_read, + .write = dump_write_file, + .llseek = seq_lseek, +}; +static int __init aml_debug_init(void) +{ + debugfs_root = debugfs_create_dir("aml_reg", NULL); + if (IS_ERR(debugfs_root) || !debugfs_root) { + pr_warn("failed to create debugfs directory\n"); + debugfs_root = NULL; + return -1; + } + aml_dev.debugfs_reg_access = aml_reg_access; + + debugfs_create_file("paddr", S_IFREG | 0444, + debugfs_root, &aml_dev, &paddr_file_ops); + debugfs_create_file("dump", S_IFREG | 0444, + debugfs_root, &aml_dev, &dump_file_ops); + return 0; +} + +static void __exit aml_debug_exit(void) +{ +} + + +module_init(aml_debug_init); +module_exit(aml_debug_exit); + +MODULE_DESCRIPTION("Amlogic debug module"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Xing Xu "); + diff --git a/drivers/amlogic/secmon/Kconfig b/drivers/amlogic/secmon/Kconfig new file mode 100644 index 000000000000..ea561f15af98 --- /dev/null +++ b/drivers/amlogic/secmon/Kconfig @@ -0,0 +1,12 @@ +# Amlogic secure monitor driver + + +config AMLOGIC_SEC + bool "Amlogic secure monitor driver support" + default n + help + This is the Amlogic secure monitor driver + this config only ctrl in makefile + prepare share memory + etc + diff --git a/drivers/amlogic/secmon/Makefile b/drivers/amlogic/secmon/Makefile new file mode 100644 index 000000000000..6670c2dcde65 --- /dev/null +++ b/drivers/amlogic/secmon/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AMLOGIC_SEC) += secmon.o diff --git a/drivers/amlogic/secmon/secmon.c b/drivers/amlogic/secmon/secmon.c new file mode 100644 index 000000000000..aa4f2f414844 --- /dev/null +++ b/drivers/amlogic/secmon/secmon.c @@ -0,0 +1,154 @@ +/* + * drivers/amlogic/secmon/secmon.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef CONFIG_ARM64 +#include +#endif +#undef pr_fmt +#define pr_fmt(fmt) "secmon: " fmt + +static void __iomem *sharemem_in_base; +static void __iomem *sharemem_out_base; +static long phy_in_base; +static long phy_out_base; +#ifdef CONFIG_ARM64 +#define IN_SIZE 0x1000 +#else + #define IN_SIZE 0x8000 +#endif + #define OUT_SIZE 0x1000 +static DEFINE_MUTEX(sharemem_mutex); +#ifdef CONFIG_ARM64 +static long get_sharemem_info(unsigned int function_id) +{ + asm volatile( + __asmeq("%0", "x0") + "smc #0\n" + : "+r" (function_id)); + + return function_id; +} +#else +static long get_sharemem_info(unsigned int function_id) +{ + register long r0 asm("r0") = function_id; + asm volatile( + __asmeq("%0", "r0") + __asmeq("%1", "r0") + __SMC(0) + : "=r" (r0) + : "r"(r0)); + + return r0; +} +#endif +static int secmon_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + unsigned int id; + int ret; + + if (!of_property_read_u32(np, "in_base_func", &id)) + phy_in_base = get_sharemem_info(id); + + if (!of_property_read_u32(np, "out_base_func", &id)) + phy_out_base = get_sharemem_info(id); + + ret = of_reserved_mem_device_init(&pdev->dev); + if (ret == 0) + pr_info("probe done\n"); + + return ret; +} + +static const struct of_device_id secmon_dt_match[] = { + { .compatible = "amlogic, secmon" }, + { /* sentinel */ }, +}; + +static struct platform_driver secmon_platform_driver = { + .probe = secmon_probe, + .driver = { + .owner = THIS_MODULE, + .name = "secmon", + .of_match_table = secmon_dt_match, + }, +}; + +int __init meson_secmon_init(void) +{ + return platform_driver_register(&secmon_platform_driver); +} +module_init(meson_secmon_init); + +void sharemem_mutex_lock(void) +{ + mutex_lock(&sharemem_mutex); +} +void sharemem_mutex_unlock(void) +{ + mutex_unlock(&sharemem_mutex); +} + +void __iomem *get_secmon_sharemem_input_base(void) +{ + return sharemem_in_base; +} +void __iomem *get_secmon_sharemem_output_base(void) +{ + return sharemem_out_base; +} + +static int secmon_mem_device_init(struct reserved_mem *rmem, struct device *dev) +{ + sharemem_in_base = ioremap_cache(phy_in_base, IN_SIZE); + if (!sharemem_in_base) { + pr_info("secmon share mem in buffer remap fail!\n"); + return -ENOMEM; + } + sharemem_out_base = ioremap_cache(phy_out_base, OUT_SIZE); + if (!sharemem_out_base) { + pr_info("secmon share mem out buffer remap fail!\n"); + return -ENOMEM; + } + pr_info("share in base: 0x%lx, share out base: 0x%lx\n", + (long)sharemem_in_base, (long)sharemem_out_base); + return 0; +} +static const struct reserved_mem_ops rmem_secmon_ops = { + .device_init = secmon_mem_device_init, +}; +static int __init secmon_mem_setup(struct reserved_mem *rmem) +{ + rmem->ops = &rmem_secmon_ops; + pr_debug("share mem setup\n"); + + return 0; +} + +RESERVEDMEM_OF_DECLARE(secmonmem, "amlogic, aml_secmon_memory", + secmon_mem_setup); diff --git a/include/linux/amlogic/cpu_version.h b/include/linux/amlogic/cpu_version.h new file mode 100644 index 000000000000..6bf8288aa6fa --- /dev/null +++ b/include/linux/amlogic/cpu_version.h @@ -0,0 +1,132 @@ +/* + * include/linux/amlogic/cpu_version.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __PLAT_MESON_CPU_H +#define __PLAT_MESON_CPU_H + +#define MESON_CPU_MAJOR_ID_GXBB 0x1F +#define MESON_CPU_MAJOR_ID_GXTVBB 0x20 +#define MESON_CPU_MAJOR_ID_GXL 0x21 +#define MESON_CPU_MAJOR_ID_GXM 0x22 +#define MESON_CPU_MAJOR_ID_TXL 0x23 + +#define MESON_CPU_VERSION_LVL_MAJOR 0 +#define MESON_CPU_VERSION_LVL_MINOR 1 +#define MESON_CPU_VERSION_LVL_PACK 2 +#define MESON_CPU_VERSION_LVL_MISC 3 +#define MESON_CPU_VERSION_LVL_MAX MESON_CPU_VERSION_LVL_MISC +extern unsigned int system_serial_low0; +extern unsigned int system_serial_low1; +extern unsigned int system_serial_high0; +extern unsigned int system_serial_high1; + +int meson_cpu_version_init(void); +#ifdef CONFIG_AMLOGIC_CPU_VERSION +int get_meson_cpu_version(int level); +int arch_big_cpu(int cpu); +#else +static inline int get_meson_cpu_version(int level) +{ + return -1; +} + +static inline int arch_big_cpu(int cpu) +{ + return 0; +} +#endif + +static inline int get_cpu_type(void) +{ + return get_meson_cpu_version(MESON_CPU_VERSION_LVL_MAJOR); +} + +static inline u32 get_cpu_package(void) +{ + unsigned int pk; + + pk = get_meson_cpu_version(MESON_CPU_VERSION_LVL_PACK) & 0xF0; + return pk; +} + +static inline bool package_id_is(unsigned int id) +{ + return get_cpu_package() == id; +} + +static inline bool is_meson_gxbb_cpu(void) +{ + return get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB; +} + +static inline bool is_meson_gxtvbb_cpu(void) +{ + return get_cpu_type() == MESON_CPU_MAJOR_ID_GXTVBB; +} + +static inline bool is_meson_gxbb_package_905(void) +{ + return (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) && + (get_cpu_package() != 0x20); +} + +static inline bool is_meson_gxbb_package_905m(void) +{ + return (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) && + (get_cpu_package() == 0x20); +} + +static inline bool is_meson_gxl_cpu(void) +{ + return get_cpu_type() == MESON_CPU_MAJOR_ID_GXL; +} + +static inline bool is_meson_gxl_package_905D(void) +{ + return is_meson_gxl_cpu() && package_id_is(0x0); +} +static inline bool is_meson_gxl_package_905X(void) +{ + return is_meson_gxl_cpu() && package_id_is(0x80); +} + +static inline bool is_meson_gxl_package_905L(void) +{ + return is_meson_gxl_cpu() && package_id_is(0xc0); +} + +static inline bool is_meson_gxl_package_905M2(void) +{ + return is_meson_gxl_cpu() && package_id_is(0xe0); +} + +static inline bool is_meson_gxm_cpu(void) +{ + return get_cpu_type() == MESON_CPU_MAJOR_ID_GXM; +} + +static inline bool is_meson_txl_cpu(void) +{ + return get_cpu_type() == MESON_CPU_MAJOR_ID_TXL; +} + +static inline bool cpu_after_eq(unsigned int id) +{ + return get_cpu_type() >= id; +} + +#endif diff --git a/include/linux/amlogic/scpi_protocol.h b/include/linux/amlogic/scpi_protocol.h new file mode 100644 index 000000000000..8b8f2d759a05 --- /dev/null +++ b/include/linux/amlogic/scpi_protocol.h @@ -0,0 +1,90 @@ +/* + * include/linux/amlogic/scpi_protocol.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef _SCPI_PROTOCOL_H_ +#define _SCPI_PROTOCOL_H_ +#include + +enum scpi_client_id { + SCPI_CL_NONE, + SCPI_CL_CLOCKS, + SCPI_CL_DVFS, + SCPI_CL_POWER, + SCPI_CL_THERMAL, + SCPI_CL_REMOTE, + SCPI_CL_LED_TIMER, + SCPI_MAX, +}; + +enum scpi_std_cmd { + SCPI_CMD_INVALID = 0x00, + SCPI_CMD_SCPI_READY = 0x01, + SCPI_CMD_SCPI_CAPABILITIES = 0x02, + SCPI_CMD_EVENT = 0x03, + SCPI_CMD_SET_CSS_PWR_STATE = 0x04, + SCPI_CMD_GET_CSS_PWR_STATE = 0x05, + SCPI_CMD_CFG_PWR_STATE_STAT = 0x06, + SCPI_CMD_GET_PWR_STATE_STAT = 0x07, + SCPI_CMD_SYS_PWR_STATE = 0x08, + SCPI_CMD_L2_READY = 0x09, + SCPI_CMD_SET_AP_TIMER = 0x0a, + SCPI_CMD_CANCEL_AP_TIME = 0x0b, + SCPI_CMD_DVFS_CAPABILITIES = 0x0c, + SCPI_CMD_GET_DVFS_INFO = 0x0d, + SCPI_CMD_SET_DVFS = 0x0e, + SCPI_CMD_GET_DVFS = 0x0f, + SCPI_CMD_GET_DVFS_STAT = 0x10, + SCPI_CMD_SET_RTC = 0x11, + SCPI_CMD_GET_RTC = 0x12, + SCPI_CMD_CLOCK_CAPABILITIES = 0x13, + SCPI_CMD_SET_CLOCK_INDEX = 0x14, + SCPI_CMD_SET_CLOCK_VALUE = 0x15, + SCPI_CMD_GET_CLOCK_VALUE = 0x16, + SCPI_CMD_PSU_CAPABILITIES = 0x17, + SCPI_CMD_SET_PSU = 0x18, + SCPI_CMD_GET_PSU = 0x19, + SCPI_CMD_SENSOR_CAPABILITIES = 0x1a, + SCPI_CMD_SENSOR_INFO = 0x1b, + SCPI_CMD_SENSOR_VALUE = 0x1c, + SCPI_CMD_SENSOR_CFG_PERIODIC = 0x1d, + SCPI_CMD_SENSOR_CFG_BOUNDS = 0x1e, + SCPI_CMD_SENSOR_ASYNC_VALUE = 0x1f, + SCPI_CMD_SET_USR_DATA = 0x20, + SCPI_CMD_COUNT +}; + +struct scpi_opp_entry { + u32 freq_hz; + u32 volt_mv; +} __packed; + +struct scpi_dvfs_info { + unsigned int count; + unsigned int latency; /* in usecs */ + struct scpi_opp_entry *opp; +} __packed; + + +unsigned long scpi_clk_get_val(u16 clk_id); +int scpi_clk_set_val(u16 clk_id, unsigned long rate); +int scpi_dvfs_get_idx(u8 domain); +int scpi_dvfs_set_idx(u8 domain, u8 idx); +struct scpi_dvfs_info *scpi_dvfs_get_opps(u8 domain); +int scpi_get_sensor(char *name); +int scpi_get_sensor_value(u16 sensor, u32 *val); +int scpi_send_usr_data(u32 client_id, u32 *val, u32 size); +#endif /*_SCPI_PROTOCOL_H_*/ diff --git a/include/linux/amlogic/secmon.h b/include/linux/amlogic/secmon.h new file mode 100644 index 000000000000..21db071213e8 --- /dev/null +++ b/include/linux/amlogic/secmon.h @@ -0,0 +1,26 @@ +/* + * include/linux/amlogic/secmon.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __SEC_MON_H__ +#define __SEC_MON_H__ + +void __iomem *get_secmon_sharemem_input_base(void); +void __iomem *get_secmon_sharemem_output_base(void); +void sharemem_mutex_lock(void); +void sharemem_mutex_unlock(void); + +#endif