diff -drupN a/drivers/bus/sunxi_mbus.c b/drivers/bus/sunxi_mbus.c --- a/drivers/bus/sunxi_mbus.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/bus/sunxi_mbus.c 2022-06-12 05:28:14.000000000 +0300 @@ -0,0 +1,1369 @@ +/* + * SUNXI MBUS driver + * + * Copyright (C) 2015 AllWinnertech Ltd. + * Author: xiafeng + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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 + +#define DRIVER_NAME "MBUS" +#define DRIVER_NAME_PMU DRIVER_NAME"_PMU" + +#define MBUS_MAST_TMR_REG(n) (0x000c) /* Time Measurement Register */ + +/* Master N Configuration Register 0 */ +/* Master N Configuration Register 1 */ +#if (defined CONFIG_ARCH_SUN50IW3) || \ + (defined CONFIG_ARCH_SUN8IW12) || \ + (defined CONFIG_ARCH_SUN8IW15) || \ + (defined CONFIG_ARCH_SUN8IW16) || \ + (defined CONFIG_ARCH_SUN50IW8) || \ + (defined CONFIG_ARCH_SUN50IW9) || \ + (defined CONFIG_ARCH_SUN50IW6) || \ + (defined CONFIG_ARCH_SUN8IW17) || \ + (defined CONFIG_ARCH_SUN8IW18) || \ + (defined CONFIG_ARCH_SUN8IW19) +#define MBUS_MAST_CFG0_REG(n) (0x0210 + (0x10 * n)) +#define MBUS_MAST_CFG1_REG(n) (0x0214 + (0x10 * n)) +#define MBUS_MAST_ABS_BWL_REG(n) (0x0218 + (0x10 * n)) +#else +#define MBUS_MAST_CFG0_REG(n) (0x0010 + (0x8 * n)) +#define MBUS_MAST_CFG1_REG(n) (0x0014 + (0x8 * n)) +#define MBUS_MAST_ABS_BWL_REG(n) (0x0018 + (0x8 * n)) +#endif + +/* Bandwidth Window base on MCLK cycles */ +#if (defined CONFIG_ARCH_SUN50IW3) || \ + (defined CONFIG_ARCH_SUN8IW12) || \ + (defined CONFIG_ARCH_SUN8IW15) || \ + (defined CONFIG_ARCH_SUN8IW16) || \ + (defined CONFIG_ARCH_SUN50IW8) || \ + (defined CONFIG_ARCH_SUN50IW9) || \ + (defined CONFIG_ARCH_SUN50IW6) || \ + (defined CONFIG_ARCH_SUN8IW17) || \ + (defined CONFIG_ARCH_SUN8IW18) || \ + (defined CONFIG_ARCH_SUN8IW19) +#define MBUS_BW_CFG_REG (0x0200) +#else +#define MBUS_BW_CFG_REG (0x0090) +#endif + +/* Master Access Enable, 0:dis, 1:en */ +#if (defined CONFIG_ARCH_SUN50IW3) || \ + (defined CONFIG_ARCH_SUN8IW12) || \ + (defined CONFIG_ARCH_SUN8IW15) || \ + (defined CONFIG_ARCH_SUN8IW16) || \ + (defined CONFIG_ARCH_SUN50IW8) || \ + (defined CONFIG_ARCH_SUN50IW9) || \ + (defined CONFIG_ARCH_SUN50IW6) || \ + (defined CONFIG_ARCH_SUN8IW17) || \ + (defined CONFIG_ARCH_SUN8IW18) || \ + (defined CONFIG_ARCH_SUN8IW19) +#define MBUS_MAST_ACEN_CFG_REG(n) (0x0020 + (0x04 * n)) +#else +#define MBUS_MAST_ACEN_CFG_REG(n) (0x0094 + (0x04 * n)) +#endif + +/* Some platform implement master access priority + * in register MBUS_MAST_CFG0_REG(n) + * register: Master Access Priority, 0:low, 1:hg + */ +#if (!defined CONFIG_ARCH_SUN50IW3) && \ + (!defined CONFIG_ARCH_SUN8IW12) && \ + (!defined CONFIG_ARCH_SUN8IW15) && \ + (!defined CONFIG_ARCH_SUN8IW16) && \ + (!defined CONFIG_ARCH_SUN50IW8) && \ + (!defined CONFIG_ARCH_SUN50IW9) && \ + (!defined CONFIG_ARCH_SUN50IW6) && \ + (!defined CONFIG_ARCH_SUN8IW17) && \ + (!defined CONFIG_ARCH_SUN8IW18) +#define MBUS_MAST_ACPR_CFG_REG (0x0098) +#endif + +/* Counter Enable, 0x0001:enable all */ +#define MBUS_PMU_CNTEB_CFG_REG (0x009c) +/* Counter n = 0 ~ 7 */ +#define MBUS_PMU_CNT_REG(n) (0x00a0 + (0x4 * n)) + +/* Software Clock ON, 0:open by hws */ +/* Sofrware Clock OFF, 1:dis-access */ +#if (defined CONFIG_ARCH_SUN50IW3) || \ + (defined CONFIG_ARCH_SUN8IW12) || \ + (defined CONFIG_ARCH_SUN8IW15) || \ + (defined CONFIG_ARCH_SUN8IW16) || \ + (defined CONFIG_ARCH_SUN50IW8) || \ + (defined CONFIG_ARCH_SUN50IW9) || \ + (defined CONFIG_ARCH_SUN50IW6) || \ + (defined CONFIG_ARCH_SUN8IW17) || \ + (defined CONFIG_ARCH_SUN8IW18) || \ + (defined CONFIG_ARCH_SUN8IW19) +#define MBUS_SW_CLK_ON_REG (0x0030) +#define MBUS_SW_CLK_OFF_REG (0x0040) +#else +#define MBUS_SW_CLK_ON_REG (0x00c0) +#define MBUS_SW_CLK_OFF_REG (0x00c4) +#define MBUS_RESOURCE_SIZE (MBUS_SW_CLK_OFF_REG) +#endif + +/* for register MBUS_MAST_CFG0_REG(n) */ +#define MBUS_QOS_MAX 0x03 +#define MBUS_WT_MAX 0x0f /* wait time, based on MCLK */ +#define MBUS_ACS_MAX 0x0ff /* access commands sequence */ +#define MBUS_BWL_MAX 0x0ffff +#define MBUS_ABS_BWL_MAX 0x0fff +#define MBUS_BW_SATU_MAX 0x0fff + +/* for register: MBUS_MAST_CFG0_REG */ +/* shift, Bandwidth Limit function Enable, 0:dis */ +#define MBUS_BWLEN_SHIFT 0 +/* shift, Priority, 0:low */ +#define MBUS_PRI_SHIFT 1 +/* shift, QoS value, 0:lowest, 3:highest */ +#define MBUS_QOS_SHIFT 2 +/* shift, wait time, overflow, pr will be promoted */ +#define MBUS_WT_SHIFT 4 +/* shift, command number, overflow, CN will be 0 */ +#define MBUS_ACS_SHIFT 8 +/* shift, Bandwidth Limit in MB/S, 0: no limit */ +#define MBUS_BWL0_SHIFT 16 + +/* for register: MBUS_MAST_CFG1_REG */ +#define MBUS_BWL1_SHIFT 0 +#define MBUS_BWL2_SHIFT 16 + +/* for register: MBUS_MAST_ABS_BWL_REG */ +#define MBUS_ABS_BWLEN_SHIFT 31 +#define MBUS_ABS_BWL_SHIFT 16 + +/* for register MBUS_BW_CFG_REG */ +#define MBUS_BWSIZE_MAX 0x0f +#define MBUS_BWEN_SHIFT 16 + +/* MBUS PMU ids */ +enum mbus_pmu { + MBUS_PMU_CPU = 0, /* CPU bandwidth */ + +#if (defined CONFIG_ARCH_SUN8IW10) + MBUS_PMU_EINK0 = 1, + MBUS_PMU_EDMA = 2, + MBUS_PMU_MAHB = 3, +#elif (defined CONFIG_ARCH_SUN8IW12) + MBUS_PMU_ECI = 1, + MBUS_PMU_VE = 2, +#elif (defined CONFIG_ARCH_SUN8IW16) || \ + (defined CONFIG_ARCH_SUN50IW9) + MBUS_PMU_MAHB = 1, + MBUS_PMU_DMA = 2, + MBUS_PMU_VE = 3, /* VE */ + MBUS_PMU_CE = 4, /* CE */ + MBUS_PMU_NAND = 5, /* NAND */ +#elif (defined CONFIG_ARCH_SUN8IW19) + MBUS_PMU_MAHB = 1, + MBUS_PMU_DMA = 2, + MBUS_PMU_VE = 3, + MBUS_PMU_CE = 4, + MBUS_PMU_CSI = 5, +#else + MBUS_PMU_GPU = 1, /* GPU bandwidth */ + MBUS_PMU_VE = 2, /* VE */ + MBUS_PMU_DISP = 3, /* DISPLAY */ +#endif + +#if (!defined CONFIG_ARCH_SUN8IW16) && \ + (!defined CONFIG_ARCH_SUN50IW9) && \ + (!defined CONFIG_ARCH_SUN8IW19) + MBUS_PMU_OTH = 4, /* other masters */ + MBUS_PMU_TOTAL = 5, /* total masters */ +#endif + +#if (!defined CONFIG_ARCH_SUN8IW6) && \ + (!defined CONFIG_ARCH_SUN8IW19) + MBUS_PMU_CSI = 6, /* csi masters */ +#endif + +#if (defined CONFIG_ARCH_SUN8IW16) || \ + (defined CONFIG_ARCH_SUN50IW9) + MBUS_PMU_ISP = 7, /* ISP */ + MBUS_PMU_G2D_MIX = 8, /* g2d */ + MBUS_PMU_G2D_ROT = 9, /* g2d */ + MBUS_PMU_DE = 10, /* DE */ + MBUS_PMU_ISE = 11, + MBUS_PMU_EISE = 12, + MBUS_PMU_TOTAL = 13, +#elif (defined CONFIG_ARCH_SUN8IW19) + MBUS_PMU_G2D = 7, + MBUS_PMU_DE = 8, + MBUS_PMU_IOMMU = 9, + MBUS_PMU_EISE = 10, + MBUS_PMU_DSPO = 11, + MBUS_PMU_NNA = 12, + MBUS_PMU_TOTAL = 13, +#endif + +#if (!defined CONFIG_ARCH_SUN8IW16) && \ + (!defined CONFIG_ARCH_SUN50IW9) && \ + (!defined CONFIG_ARCH_SUN8IW19) + MBUS_PMU_MAX = 7, /* max masters */ +#else + MBUS_PMU_MAX = 14, /* max masters */ +#endif +}; + +#define MBUS_PORT_PRI (MBUS_PMU_MAX + 0) +#define MBUS_PORT_QOS (MBUS_PMU_MAX + 1) +#define MBUS_PORT_WT (MBUS_PMU_MAX + 2) +#define MBUS_PORT_ACS (MBUS_PMU_MAX + 3) +#define MBUS_PORT_BWL0 (MBUS_PMU_MAX + 4) +#define MBUS_PORT_BWL1 (MBUS_PMU_MAX + 5) +#define MBUS_PORT_BWL2 (MBUS_PMU_MAX + 6) +#define MBUS_PORT_BWLEN (MBUS_PMU_MAX + 7) +#define MBUS_PORT_ABS_BWLEN (MBUS_PMU_MAX + 8) +#define MBUS_PORT_ABS_BWL (MBUS_PMU_MAX + 9) +#define MBUS_PORT_BW_SATU (MBUS_PMU_MAX + 10) + +struct sunxi_mbus_port { + void __iomem *base; + unsigned long phys; + struct device_node *dn; +}; + +static struct sunxi_mbus_port *ports; +static void __iomem *mbus_ctrl_base; +static unsigned long mbus_ctrl_phys; + +static DEFINE_MUTEX(mbus_seting); +static DEFINE_MUTEX(mbus_pmureading); + +#define mbus_pmu_getstate() \ + (readl_relaxed(mbus_ctrl_base + MBUS_PMU_CNTEB_CFG_REG) & 1) +#define mbus_pmu_enable() \ + writel_relaxed( \ + ((readl_relaxed(mbus_ctrl_base + MBUS_PMU_CNTEB_CFG_REG)) | 1), \ + mbus_ctrl_base + MBUS_PMU_CNTEB_CFG_REG) + +/** + * mbus_port_set_abs_bwlen() - enable a master absolutely bandwidth limit + * function + * + * @port: index of the port to setup + * @en: 0-disable, 1-enable + */ +int notrace mbus_port_set_abs_bwlen(enum mbus_port port, bool en) +{ + unsigned int value; + + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + mutex_lock(&mbus_seting); + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_ABS_BWL_REG(port)); + value &= ~(1U << MBUS_ABS_BWLEN_SHIFT); + writel_relaxed(value | (en << MBUS_ABS_BWLEN_SHIFT), + mbus_ctrl_base + MBUS_MAST_ABS_BWL_REG(port)); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_set_abs_bwlen); + +/** + * mbus_port_set_abs_bwl() - set a master absolutely bandwidth limit + * + * @bwl: the number of bandwidth limit + */ +int notrace mbus_port_set_abs_bwl(enum mbus_port port, unsigned int bwl) +{ + unsigned int value; + + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + if (bwl > MBUS_ABS_BWL_MAX) + return -EPERM; + + mutex_lock(&mbus_seting); + /* absolutely bwl, used when en BWL */ + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_ABS_BWL_REG(port)); + value &= ~(MBUS_ABS_BWL_MAX << MBUS_ABS_BWL_SHIFT); + writel_relaxed(value | (bwl << MBUS_ABS_BWL_SHIFT), + mbus_ctrl_base + MBUS_MAST_ABS_BWL_REG(port)); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_set_abs_bwl); + +/** + * mbus_port_set_bw_saturation() - set a master bandwidth saturation + * + * @bw_satu: the number of bandwidth saturation + */ +int notrace mbus_port_set_bw_saturation(enum mbus_port port, + unsigned int bw_satu) +{ + unsigned int value; + + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + if (bw_satu > MBUS_BW_SATU_MAX) + return -EPERM; + + mutex_lock(&mbus_seting); + /* absolutely bw_satu, used when en BWL */ + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_ABS_BWL_REG(port)); + value &= ~MBUS_BW_SATU_MAX; + writel_relaxed(value | bw_satu, + mbus_ctrl_base + MBUS_MAST_ABS_BWL_REG(port)); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_set_bw_saturation); + +/** + * mbus_port_setreqn() - enable a master bandwidth limit function + * + * @port: index of the port to setup + * @en: 0-disable, 1-enable + */ +int notrace mbus_port_setbwlen(enum mbus_port port, bool en) +{ + unsigned int value; + + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + mutex_lock(&mbus_seting); + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); + value &= ~(1 << MBUS_BWLEN_SHIFT); + writel_relaxed(value | (en << MBUS_BWLEN_SHIFT), + mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_setbwlen); + +/** + * mbus_port_setthd() - set a master priority + * + * @pri, priority + */ +int notrace mbus_port_setpri(enum mbus_port port, bool pri) +{ + unsigned int value = 0; + + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + mutex_lock(&mbus_seting); +#if (defined MBUS_MAST_ACPR_CFG_REG) + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_ACPR_CFG_REG); + value &= ~(1 << port); + writel_relaxed(value | (pri << port), + mbus_ctrl_base + MBUS_MAST_ACPR_CFG_REG); +#else + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); + value &= ~(1 << MBUS_PRI_SHIFT); + writel_relaxed(value | (pri << MBUS_PRI_SHIFT), + mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); +#endif + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_setpri); + +/** + * mbus_port_setqos() - set a master QOS + * + * @qos: the qos value want to set + */ +int notrace mbus_port_setqos(enum mbus_port port, unsigned int qos) +{ + unsigned int value; + + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + if (qos > MBUS_QOS_MAX) + return -EPERM; + + mutex_lock(&mbus_seting); + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); + value &= ~(MBUS_QOS_MAX << MBUS_QOS_SHIFT); + writel_relaxed(value | (qos << MBUS_QOS_SHIFT), + mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_setqos); + +/** + * mbus_bw_setbw() - set a master wait time + * + * @wt: the wait time want to set, based on MCLK + */ +int notrace mbus_port_setwt(enum mbus_port port, unsigned int wt) +{ + unsigned int value; + + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + if (wt > MBUS_WT_MAX) + return -EPERM; + + mutex_lock(&mbus_seting); + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); + value &= ~(MBUS_WT_MAX << MBUS_WT_SHIFT); + writel_relaxed(value | (wt << MBUS_WT_SHIFT), + mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_setwt); + +/** + * mbus_bw_setams() - set a master access commands sequence + * + * @acs: the number of access commands sequency + */ +int notrace mbus_port_setacs(enum mbus_port port, unsigned int acs) +{ + unsigned int value; + + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + if (acs > MBUS_ACS_MAX) + return -EPERM; + + mutex_lock(&mbus_seting); + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); + value &= ~(MBUS_ACS_MAX << MBUS_ACS_SHIFT); + writel_relaxed(value | (acs << MBUS_ACS_SHIFT), + mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_setacs); + +/** + * mbus_bw_setbwl0() - function to set bandwidth limit0 + * + * @bwl: the number of bandwidth limit + */ +int notrace mbus_port_setbwl0(enum mbus_port port, unsigned int bwl0) +{ + unsigned int value; + + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + if (bwl0 > MBUS_BWL_MAX) + return -EPERM; + + mutex_lock(&mbus_seting); + /* bwl0, used when BWL function enable */ + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); + value &= ~(MBUS_BWL_MAX << MBUS_BWL0_SHIFT); + writel_relaxed(value | (bwl0 << MBUS_BWL0_SHIFT), + mbus_ctrl_base + MBUS_MAST_CFG0_REG(port)); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_setbwl0); + +/** + * mbus_bw_setbwl1() - set a master bandwidth limit1 + * + * @bwl: the number of bandwidth limit + */ +int notrace mbus_port_setbwl1(enum mbus_port port, unsigned int bwl1) +{ + unsigned int value; + + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + if (bwl1 > MBUS_BWL_MAX) + return -EPERM; + + mutex_lock(&mbus_seting); + /* bwl1, used when en BWL */ + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_CFG1_REG(port)); + value &= ~(MBUS_BWL_MAX << MBUS_BWL1_SHIFT); + writel_relaxed(value | (bwl1 << MBUS_BWL1_SHIFT), + mbus_ctrl_base + MBUS_MAST_CFG1_REG(port)); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_setbwl1); + +/** + * mbus_bw_setbwl2() - set a master bandwidth limit2 + * + * @bwl: the number of bandwidth limit + */ +int notrace mbus_port_setbwl2(enum mbus_port port, unsigned int bwl2) +{ + unsigned int value; + + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + if (bwl2 > MBUS_BWL_MAX) + return -EPERM; + + mutex_lock(&mbus_seting); + /* bwl2, used when en BWL */ + value = readl_relaxed(mbus_ctrl_base + MBUS_MAST_CFG1_REG(port)); + value &= ~(MBUS_BWL_MAX << MBUS_BWL2_SHIFT); + writel_relaxed(value | (bwl2 << MBUS_BWL2_SHIFT), + mbus_ctrl_base + MBUS_MAST_CFG1_REG(port)); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_setbwl2); + +/** + * mbus_bw_setbwl() - set a master bandwidth limit + * + * @bwl0/1/2: the number of bandwidth limit0/1/2 + */ +int notrace mbus_port_setbwl(enum mbus_port port, unsigned int bwl0, + unsigned int bwl1, unsigned int bwl2) +{ + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + + if ((bwl0 > MBUS_BWL_MAX) || (bwl1 > MBUS_BWL_MAX) + || (bwl2 > MBUS_BWL_MAX)) + return -EPERM; + + mbus_port_setbwl0(port, bwl0); + mbus_port_setbwl1(port, bwl1); + mbus_port_setbwl2(port, bwl2); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_setbwl); + +/** + * mbus_bw_control() - set BandWidth limit window enable or disable + * + * @enable: if true enables the bwlw, if false disables it + */ +int notrace mbus_set_bwlwen(bool enable) +{ + unsigned int value; + + mutex_lock(&mbus_seting); + value = readl_relaxed(mbus_ctrl_base + MBUS_BW_CFG_REG); + + writel_relaxed(enable ? (value | (1 << MBUS_BWEN_SHIFT)) + : (value & ~(1 << MBUS_BWEN_SHIFT)), + mbus_ctrl_base + MBUS_BW_CFG_REG); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_set_bwlwen); + +/** + * mbus_bw_control() - set BandWidth limit window size + * + * @size: the size of bwl window, base on MCLK + */ +int notrace mbus_set_bwlwsiz(unsigned int size) +{ + unsigned int value; + + mutex_lock(&mbus_seting); + value = readl_relaxed(mbus_ctrl_base + MBUS_BW_CFG_REG); + value &= ~MBUS_BWSIZE_MAX; + writel_relaxed(value | size, mbus_ctrl_base + MBUS_BW_CFG_REG); + mutex_unlock(&mbus_seting); + + return 0; +} +EXPORT_SYMBOL_GPL(mbus_set_bwlwsiz); + +/** + * They are called by low-level power management code to disable slave + * interfaces snoops and DVM broadcast. + */ + +/** + * mbus_port_control() - control a master port access DRAM + * + * @enable: if true enables the port, if false disables it + */ +static void notrace mbus_port_control(enum mbus_port port, bool enable) +{ + unsigned int value, reg, pos; + + reg = MBUS_MAST_ACEN_CFG_REG(port/32); + pos = port%32; + + /* + * This function is called from power down procedures + * and must not execute any instruction that might + * cause the processor to be put in a quiescent state + * (eg wfi). Hence, cpu_relax() can not be added to this + * read loop to optimize power, since it might hide possibly + * disruptive operations. + */ + mutex_lock(&mbus_seting); + value = readl_relaxed(mbus_ctrl_base + reg); + if (enable) + value |= (1 << pos); + else + value &= ~(1 << pos); + writel_relaxed(value, mbus_ctrl_base + reg); + mutex_unlock(&mbus_seting); +} + +/** + * mbus_control_port_by_index() - control a master port by port index + * + * @port: port index previously retrieved with mbus_ace_get_port() + * @enable: if true enables the port, if false disables it + * + * Return: + * 0 on success + * -ENODEV on port index out of range + * -EPERM if operation carried out on an ACE PORT + */ +int notrace mbus_port_control_by_index(enum mbus_port port, bool enable) +{ + if (port >= MBUS_PORTS_MAX) + return -ENODEV; + /* + * MBUS control for ports connected to CPUS is extremely fragile + * and must be made to go through a specific. + */ + + mbus_port_control(port, enable); + return 0; +} +EXPORT_SYMBOL_GPL(mbus_port_control_by_index); + +static const struct of_device_id sunxi_mbus_matches[] = { +#ifdef CONFIG_ARCH_SUN8I + {.compatible = "allwinner,sun8i-mbus", }, +#endif +#ifdef CONFIG_ARCH_SUN50I + {.compatible = "allwinner,sun50i-mbus", }, +#endif + {}, +}; + +static int mbus_probe(void) +{ + int ret; + struct device_node *np; + struct resource res; + + np = of_find_matching_node(NULL, sunxi_mbus_matches); + if (!np) + return -ENODEV; + + ports = kcalloc(1, sizeof(*ports), GFP_KERNEL); + if (!ports) + return -ENOMEM; + + ret = of_address_to_resource(np, 0, &res); + if (!ret) { + mbus_ctrl_base = ioremap(res.start, resource_size(&res)); + mbus_ctrl_phys = res.start; + + } + if (ret || !mbus_ctrl_base) { + WARN(1, "unable to ioremap mbus ctrl\n"); + ret = -ENXIO; + goto memalloc_err; + } + + /* the purpose freq of MBUS is 400M, has been configied by boot */ + + /* all the port is default opened */ + + /* set default bandwidth */ + + /* set default QOS */ + + /* set masters' request number sequency */ + + /* set masters' bandwidth limit0/1/2 */ + + /* sync_cache_w(&mbus_ctrl_base); */ + /* sync_cache_w(&mbus_ctrl_phys); */ + /* sync_cache_w(&ports); */ + /* __sync_cache_range_w(ports, sizeof(*ports)); */ + +memalloc_err: + kfree(ports); + + return 0; +} + +static int mbus_init_status = -EAGAIN; +static DEFINE_MUTEX(mbus_proing); + +static int mbus_init(void) +{ + if (mbus_init_status != -EAGAIN) + return mbus_init_status; + + mutex_lock(&mbus_proing); + if (mbus_init_status == -EAGAIN) + mbus_init_status = mbus_probe(); + mutex_unlock(&mbus_proing); + + return mbus_init_status; +} + +/** + * To sort out early init calls ordering a helper function is provided to + * check if the mbus driver has beed initialized. Function check if the driver + * has been initialized, if not it calls the init function that probes + * the driver and updates the return value. + */ +bool mbus_probed(void) +{ + return mbus_init() == 0; +} +EXPORT_SYMBOL_GPL(mbus_probed); + +struct mbus_data { + struct device *hwmon_dev; + struct mutex update_lock; + bool valid; + unsigned long last_updated; + int kind; +}; + +static struct mbus_data hw_mbus_pmu; + +static unsigned int mbus_update_device(struct mbus_data *data, + enum mbus_pmu port) +{ + unsigned int value = 0; + + mutex_lock(&data->update_lock); + + /* confirm the pmu is enabled */ + if (!mbus_pmu_getstate()) + mbus_pmu_enable(); + + /* read pmu conter */ + value = readl_relaxed(mbus_ctrl_base + MBUS_PMU_CNT_REG(port)); + + mutex_unlock(&data->update_lock); + + return value; +} + +#define for_each_ports(port) for (port = 0; port < MBUS_PORTS_MAX; port++) + +static unsigned int mbus_get_value(struct mbus_data *data, + unsigned int index, char *buf) +{ + unsigned int i, size = 0; + unsigned int value; + + mutex_lock(&data->update_lock); + switch (index) { + case MBUS_PORT_PRI: + for_each_ports(i) { +#if (defined MBUS_MAST_ACPR_CFG_REG) + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_ACPR_CFG_REG); + value >>= i; +#else + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_CFG0_REG(i)); + value >>= MBUS_PRI_SHIFT; +#endif + size += sprintf(buf + size, "master%2u priority:%1u\n", + i, (value & 1)); + } + break; + case MBUS_PORT_QOS: + for_each_ports(i) { + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_CFG0_REG(i)); + value >>= MBUS_QOS_SHIFT; + value &= MBUS_QOS_MAX; + size += sprintf(buf + size, "master%2u qos:%1u\n", + i, value); + } + break; + case MBUS_PORT_WT: + for_each_ports(i) { + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_CFG0_REG(i)); + value >>= MBUS_WT_SHIFT; + value &= MBUS_WT_MAX; + size += sprintf(buf + size, + "master%2u threshold0:%2u\n", i, value); + } + break; + case MBUS_PORT_ACS: + for_each_ports(i) { + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_CFG0_REG(i)); + value >>= MBUS_ACS_SHIFT; + value &= MBUS_ACS_MAX; + size += sprintf(buf + size, + "master%2u accsess commands:%4u\n", + i, value); + } + break; + case MBUS_PORT_BWL0: + for_each_ports(i) { + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_CFG0_REG(i)); + value >>= MBUS_BWL0_SHIFT; + value &= MBUS_BWL_MAX; + size += sprintf(buf + size, + "master%2u bandwidth limit0:%5u\n", + i, value); + } + break; + case MBUS_PORT_BWL1: + for_each_ports(i) { + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_CFG1_REG(i)); + value >>= MBUS_BWL1_SHIFT; + value &= MBUS_BWL_MAX; + size += sprintf(buf + size, + "master%2u bandwidth limit1:%5u\n", + i, value); + } + break; + case MBUS_PORT_BWL2: + for_each_ports(i) { + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_CFG1_REG(i)); + value >>= MBUS_BWL2_SHIFT; + value &= MBUS_BWL_MAX; + size += sprintf(buf + size, + "master%2u bandwidth limit2:%5u\n", + i, value); + } + break; + case MBUS_PORT_BWLEN: + for_each_ports(i) { + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_CFG0_REG(i)); + value &= 1; + size += sprintf(buf + size, + "master%2u BWLimit_en:%1u\n", + i, value); + } + break; + case MBUS_PORT_ABS_BWLEN: + for_each_ports(i) { + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_ABS_BWL_REG(i)); + value >>= MBUS_ABS_BWLEN_SHIFT; + value &= 1; + size += sprintf(buf + size, + "master%2u absolutely BWLimit_en:%1u\n", i, value); + } + break; + case MBUS_PORT_ABS_BWL: + for_each_ports(i) { + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_ABS_BWL_REG(i)); + value >>= MBUS_ABS_BWL_SHIFT; + value &= MBUS_ABS_BWL_MAX; + size += sprintf(buf + size, + "master%2u absolutely bandwidth limit:%5u\n", i, value); + } + break; + case MBUS_PORT_BW_SATU: + for_each_ports(i) { + value = readl_relaxed(mbus_ctrl_base + + MBUS_MAST_ABS_BWL_REG(i)); + value &= MBUS_BW_SATU_MAX; + size += sprintf(buf + size, + "master%2u bandwidth saturation:%5u\n", i, value); + } + break; + default: + /* programmer goofed */ + WARN_ON_ONCE(1); + value = 0; + break; + } + mutex_unlock(&data->update_lock); + + return size; +} + +static ssize_t mbus_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned int len; + + if (attr->index >= MBUS_PMU_MAX) { + len = mbus_get_value(&hw_mbus_pmu, attr->index, buf); + len = (len < PAGE_SIZE) ? len : PAGE_SIZE; + return len; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + mbus_update_device(&hw_mbus_pmu, attr->index)); +} + +static unsigned int mbus_set_value(struct mbus_data *data, unsigned int index, + enum mbus_port port, unsigned int val) +{ + unsigned int value; + + mutex_lock(&data->update_lock); + switch (index) { + case MBUS_PORT_PRI: + mbus_port_setpri(port, val); + break; + case MBUS_PORT_QOS: + mbus_port_setqos(port, val); + break; + case MBUS_PORT_WT: + mbus_port_setwt(port, val); + break; + case MBUS_PORT_ACS: + mbus_port_setacs(port, val); + break; + case MBUS_PORT_BWL0: + mbus_port_setbwl0(port, val); + break; + case MBUS_PORT_BWL1: + mbus_port_setbwl1(port, val); + break; + case MBUS_PORT_BWL2: + mbus_port_setbwl2(port, val); + break; + case MBUS_PORT_BWLEN: + mbus_port_setbwlen(port, val); + break; + case MBUS_PORT_ABS_BWLEN: + mbus_port_set_abs_bwlen(port, val); + break; + case MBUS_PORT_ABS_BWL: + mbus_port_set_abs_bwl(port, val); + break; + case MBUS_PORT_BW_SATU: + mbus_port_set_bw_saturation(port, val); + break; + default: + /* programmer goofed */ + WARN_ON_ONCE(1); + value = 0; + break; + } + mutex_unlock(&data->update_lock); + + return 0; +} + +static ssize_t mbus_store_value(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(attr)->index; + unsigned long port, val; + unsigned char buffer[64]; + unsigned char *pbuf, *pbufi; + int err; + + if (strlen(buf) >= 64) { + dev_err(dev, "arguments out of range!\n"); + return -EINVAL; + } + + while (*buf == ' ') /* find the first unblank character */ + buf++; + strncpy(buffer, buf, strlen(buf)); + + pbufi = buffer; + while (*pbufi != ' ') /* find the first argument */ + pbufi++; + *pbufi = 0x0; + pbuf = (unsigned char *)buffer; + err = kstrtoul(pbuf, 10, &port); + if (err < 0) + return err; + if (port >= MBUS_PORTS_MAX) { + dev_err(dev, "master is illegal\n"); + return -EINVAL; + } + + pbuf = ++pbufi; + while (*pbuf == ' ') /* remove extra space character */ + pbuf++; + pbufi = pbuf; + while ((*pbufi != ' ') && (*pbufi != '\n')) + pbufi++; + *pbufi = 0x0; + + err = kstrtoul(pbuf, 10, &val); + if (err < 0) + return err; + + mbus_set_value(&hw_mbus_pmu, nr, + (enum mbus_port)port, (unsigned int)val); + + return count; +} + +/* CPU bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_cpuddr, 0400, + mbus_show_value, NULL, MBUS_PMU_CPU); + +#if (defined CONFIG_ARCH_SUN8IW10) +static SENSOR_DEVICE_ATTR(pmu_eink0_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_EINK0); +static SENSOR_DEVICE_ATTR(pmu_edma_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_EDMA); +static SENSOR_DEVICE_ATTR(pmu_mahb_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_MAHB); +#elif (defined CONFIG_ARCH_SUN8IW12) +/* ECI(EVE, CVE, ISE) bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_eci_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_ECI); +/* VE bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_ve_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_VE); +#elif (defined CONFIG_ARCH_SUN8IW16) || \ + (defined CONFIG_ARCH_SUN50IW9) +/* ECI(EVE, CVE, ISE) bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_mahb_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_MAHB); +/* VE bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_ve_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_VE); +static SENSOR_DEVICE_ATTR(pmu_ce_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_CE); +static SENSOR_DEVICE_ATTR(pmu_nand_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_NAND); +#elif (defined CONFIG_ARCH_SUN8IW19) +/* ECI(EVE, CVE, ISE) bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_mahb_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_MAHB); +static SENSOR_DEVICE_ATTR(pmu_dma_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_DMA); +/* VE bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_ve_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_VE); +static SENSOR_DEVICE_ATTR(pmu_ce_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_CE); +static SENSOR_DEVICE_ATTR(pmu_csi_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_CSI); +#else +/* GPU bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_gpuddr, 0400, + mbus_show_value, NULL, MBUS_PMU_GPU); +/* VE & CSI & FD bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_ve_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_VE); +/* DE bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_de_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_DISP); +#endif + +#if (!defined CONFIG_ARCH_SUN8IW16) && \ + (!defined CONFIG_ARCH_SUN50IW9) && \ + (!defined CONFIG_ARCH_SUN8IW19) +/* other master bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_othddr, 0400, + mbus_show_value, NULL, MBUS_PMU_OTH); +/* total bandwidth of DDR channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_totddr, 0400, + mbus_show_value, NULL, MBUS_PMU_TOTAL); +#endif +#if (!defined CONFIG_ARCH_SUN8IW6) && \ + (!defined CONFIG_ARCH_SUN8IW19) +/* csi bandwidth of CSI channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_csiddr, 0400, + mbus_show_value, NULL, MBUS_PMU_CSI); +#endif + +#if (defined CONFIG_ARCH_SUN8IW16) || \ + (defined CONFIG_ARCH_SUN50IW9) +/* isp bandwidth of CSI channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_isp_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_ISP); +static SENSOR_DEVICE_ATTR(pmu_g2d_mix_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_G2D_MIX); +static SENSOR_DEVICE_ATTR(pmu_g2d_rot_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_G2D_ROT); +static SENSOR_DEVICE_ATTR(pmu_de_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_DE); +static SENSOR_DEVICE_ATTR(pmu_ise_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_ISE); +static SENSOR_DEVICE_ATTR(pmu_eise_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_EISE); +static SENSOR_DEVICE_ATTR(pmu_totddr, 0400, + mbus_show_value, NULL, MBUS_PMU_TOTAL); +#elif (defined CONFIG_ARCH_SUN8IW19) +/* isp bandwidth of CSI channel 0 */ +static SENSOR_DEVICE_ATTR(pmu_g2d_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_G2D); +static SENSOR_DEVICE_ATTR(pmu_de_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_DE); +static SENSOR_DEVICE_ATTR(pmu_iommu_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_IOMMU); +static SENSOR_DEVICE_ATTR(pmu_eise_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_EISE); +static SENSOR_DEVICE_ATTR(pmu_dspo_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_DSPO); +static SENSOR_DEVICE_ATTR(pmu_nna_ddr, 0400, + mbus_show_value, NULL, MBUS_PMU_NNA); +static SENSOR_DEVICE_ATTR(pmu_totddr, 0400, + mbus_show_value, NULL, MBUS_PMU_TOTAL); +#endif +/* get all masters' priority or set a master's priority */ +static SENSOR_DEVICE_ATTR(port_prio, 0644, + mbus_show_value, mbus_store_value, MBUS_PORT_PRI); +/* get all masterss' qos or set a master's qos */ +static SENSOR_DEVICE_ATTR(port_qos, 0644, + mbus_show_value, mbus_store_value, MBUS_PORT_QOS); +/* get all masterss' threshold or set a master's threshold */ +static SENSOR_DEVICE_ATTR(port_watt, 0644, + mbus_show_value, mbus_store_value, MBUS_PORT_WT); +/* get all masterss' threshold or set a master's threshold */ +static SENSOR_DEVICE_ATTR(port_acs, 0644, + mbus_show_value, mbus_store_value, MBUS_PORT_ACS); +/* get all masters' requeset number or set a master's number */ +static SENSOR_DEVICE_ATTR(port_bwl0, 0644, + mbus_show_value, mbus_store_value, MBUS_PORT_BWL0); +/* get all masters' requeset number or set a master's number */ +static SENSOR_DEVICE_ATTR(port_bwl1, 0644, + mbus_show_value, mbus_store_value, MBUS_PORT_BWL1); +/* get all masters' requeset number or set a master's number */ +static SENSOR_DEVICE_ATTR(port_bwl2, 0644, + mbus_show_value, mbus_store_value, MBUS_PORT_BWL2); +/* get all masters' requeset number or set a master's number */ +static SENSOR_DEVICE_ATTR(port_bwlen, 0644, + mbus_show_value, mbus_store_value, MBUS_PORT_BWLEN); +/* get all masters' requeset number or set a master's number */ +static SENSOR_DEVICE_ATTR(port_abs_bwlen, 0644, + mbus_show_value, mbus_store_value, MBUS_PORT_ABS_BWLEN); +/* get all masters' requeset number or set a master's number */ +static SENSOR_DEVICE_ATTR(port_abs_bwl, 0644, + mbus_show_value, mbus_store_value, MBUS_PORT_ABS_BWL); +/* get all masters' requeset number or set a master's number */ +static SENSOR_DEVICE_ATTR(port_bw_satu, 0644, + mbus_show_value, mbus_store_value, MBUS_PORT_BW_SATU); + +/* pointers to created device attributes */ +static struct attribute *mbus_attributes[] = { + &sensor_dev_attr_pmu_cpuddr.dev_attr.attr, + +#if (defined CONFIG_ARCH_SUN8IW10) + &sensor_dev_attr_pmu_eink0_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_edma_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_mahb_ddr.dev_attr.attr, +#elif defined(CONFIG_ARCH_SUN8IW12) + &sensor_dev_attr_pmu_eci_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_ve_ddr.dev_attr.attr, +#elif defined(CONFIG_ARCH_SUN8IW16) || \ + (defined CONFIG_ARCH_SUN50IW9) + &sensor_dev_attr_pmu_mahb_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_ve_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_ce_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_nand_ddr.dev_attr.attr, +#elif defined(CONFIG_ARCH_SUN8IW19) + &sensor_dev_attr_pmu_mahb_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_dma_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_ve_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_ce_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_csi_ddr.dev_attr.attr, +#else + &sensor_dev_attr_pmu_gpuddr.dev_attr.attr, + &sensor_dev_attr_pmu_ve_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_de_ddr.dev_attr.attr, +#endif + +#if (!defined CONFIG_ARCH_SUN8IW16) && \ + (!defined CONFIG_ARCH_SUN50IW9) && \ + (!defined CONFIG_ARCH_SUN8IW19) + &sensor_dev_attr_pmu_othddr.dev_attr.attr, + &sensor_dev_attr_pmu_totddr.dev_attr.attr, +#endif +#if (!defined CONFIG_ARCH_SUN8IW6) && \ + (!defined CONFIG_ARCH_SUN8IW19) + &sensor_dev_attr_pmu_csiddr.dev_attr.attr, +#endif + +#if (defined CONFIG_ARCH_SUN8IW16) || \ + (defined CONFIG_ARCH_SUN50IW9) + &sensor_dev_attr_pmu_isp_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_g2d_mix_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_g2d_rot_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_de_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_ise_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_eise_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_totddr.dev_attr.attr, +#elif (defined CONFIG_ARCH_SUN8IW19) + &sensor_dev_attr_pmu_g2d_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_de_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_iommu_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_eise_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_dspo_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_nna_ddr.dev_attr.attr, + &sensor_dev_attr_pmu_totddr.dev_attr.attr, + +#endif + + &sensor_dev_attr_port_prio.dev_attr.attr, + &sensor_dev_attr_port_qos.dev_attr.attr, + &sensor_dev_attr_port_watt.dev_attr.attr, + &sensor_dev_attr_port_acs.dev_attr.attr, + &sensor_dev_attr_port_bwl0.dev_attr.attr, + &sensor_dev_attr_port_bwl1.dev_attr.attr, + &sensor_dev_attr_port_bwl2.dev_attr.attr, + &sensor_dev_attr_port_bwlen.dev_attr.attr, + &sensor_dev_attr_port_abs_bwlen.dev_attr.attr, + &sensor_dev_attr_port_abs_bwl.dev_attr.attr, + &sensor_dev_attr_port_bw_satu.dev_attr.attr, + NULL, +}; + +static struct attribute_group mbus_group = { + .attrs = mbus_attributes, +}; + +static const struct attribute_group *mbus_groups[] = { + &mbus_group, + NULL, +}; + +static int mbus_pmu_probe(struct platform_device *pdev) +{ + int ret; + + hw_mbus_pmu.hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, + "mbus_pmu", + NULL, + mbus_groups); + + if (IS_ERR(hw_mbus_pmu.hwmon_dev)) { + ret = PTR_ERR(hw_mbus_pmu.hwmon_dev); + goto out_err; + } + + hw_mbus_pmu.last_updated = 0; + hw_mbus_pmu.valid = 0; + mutex_init(&hw_mbus_pmu.update_lock); + + return 0; + +out_err: + dev_err(&(pdev->dev), "probed failed\n"); + sysfs_remove_group(&pdev->dev.kobj, &mbus_group); + + return ret; +} + +static int mbus_pmu_remove(struct platform_device *pdev) +{ + hwmon_device_unregister(hw_mbus_pmu.hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &mbus_group); + + return 0; +} + +#ifdef CONFIG_PM +static int sunxi_mbus_suspend(struct device *dev) +{ + dev_info(dev, "suspend okay\n"); + + return 0; +} + +static int sunxi_mbus_resume(struct device *dev) +{ + dev_info(dev, "resume okay\n"); + + return 0; +} + +static const struct dev_pm_ops sunxi_mbus_pm_ops = { + .suspend = sunxi_mbus_suspend, + .resume = sunxi_mbus_resume, +}; + +#define SUNXI_MBUS_PM_OPS (&sunxi_mbus_pm_ops) +#else +#define SUNXI_MBUS_PM_OPS NULL +#endif + +static struct platform_driver mbus_pmu_driver = { + .driver = { + .name = DRIVER_NAME_PMU, + .owner = THIS_MODULE, + .pm = SUNXI_MBUS_PM_OPS, + .of_match_table = sunxi_mbus_matches, + }, + .probe = mbus_pmu_probe, + .remove = mbus_pmu_remove, +}; + +static int __init mbus_pmu_init(void) +{ + int ret; + + ret = platform_driver_register(&mbus_pmu_driver); + if (ret) { + pr_err("register sunxi mbus platform driver failed\n"); + goto drv_err; + } + + return ret; + +drv_err: + platform_driver_unregister(&mbus_pmu_driver); + + return -EINVAL; +} + +early_initcall(mbus_init); +device_initcall(mbus_pmu_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SUNXI MBUS support"); +MODULE_AUTHOR("xiafeng");