From: Janghyuck Kim Date: Tue, 8 Nov 2016 11:34:41 +0000 (+0900) Subject: [COMMON] iommu/exynos: add TLB port setting X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=e22fcbd654af2ea672674957416e3ac12defb63b;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git [COMMON] iommu/exynos: add TLB port setting To support TLB port setting, parsing device tree and setting code are added. Change-Id: I63f5854d4d23f2aabb4fe9a4a68a35b8902ab6fd Signed-off-by: Janghyuck Kim --- diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 11508ba82c21..bc5a3add2601 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -653,40 +653,19 @@ static int __init __sysmmu_secure_irq_init(struct device *sysmmu, return ret; } -static int __init sysmmu_parse_dt(struct device *sysmmu, +static int __init sysmmu_parse_tlb_way_dt(struct device *sysmmu, struct sysmmu_drvdata *drvdata) { const char *props_name = "sysmmu,tlb_property"; struct tlb_props *tlb_props = &drvdata->tlb_props; struct tlb_priv_id *priv_id_cfg = NULL; struct tlb_priv_addr *priv_addr_cfg = NULL; - unsigned int qos = DEFAULT_QOS_VALUE; - unsigned int prop; - int ret; int i, cnt, priv_id_cnt = 0, priv_addr_cnt = 0; unsigned int priv_id_idx = 0, priv_addr_idx = 0; + unsigned int prop; + int ret; - /* Parsing QoS */ - ret = of_property_read_u32_index(sysmmu->of_node, "qos", 0, &qos); - if (!ret && (qos > 15)) { - dev_err(sysmmu, "Invalid QoS value %d, use default.\n", qos); - qos = DEFAULT_QOS_VALUE; - } - drvdata->qos = qos; - - /* Secure IRQ */ - if (of_find_property(sysmmu->of_node, "sysmmu,secure-irq", NULL)) { - ret = __sysmmu_secure_irq_init(sysmmu, drvdata); - if (ret) { - dev_err(sysmmu, "Failed to init secure irq\n"); - return ret; - } - } - - if (of_property_read_bool(sysmmu->of_node, "sysmmu,no-suspend")) - dev_pm_syscore_device(sysmmu, true); - - /* Parsing TLB properties */ + /* Parsing TLB way properties */ cnt = of_property_count_u32_elems(sysmmu->of_node, props_name); for (i = 0; i < cnt; i+=2) { ret = of_property_read_u32_index(sysmmu->of_node, @@ -708,7 +687,7 @@ static int __init sysmmu_parse_dt(struct device *sysmmu, break; case _PUBLIC_WAY: tlb_props->flags |= TLB_WAY_PUBLIC; - tlb_props->public_cfg = (prop & ~WAY_TYPE_MASK); + tlb_props->way_props.public_cfg = (prop & ~WAY_TYPE_MASK); break; default: dev_err(sysmmu, "Undefined properties!: %#x\n", prop); @@ -767,11 +746,11 @@ static int __init sysmmu_parse_dt(struct device *sysmmu, } } - tlb_props->priv_id_cfg = priv_id_cfg; - tlb_props->priv_id_cnt = priv_id_cnt; + tlb_props->way_props.priv_id_cfg = priv_id_cfg; + tlb_props->way_props.priv_id_cnt = priv_id_cnt; - tlb_props->priv_addr_cfg = priv_addr_cfg; - tlb_props->priv_addr_cnt = priv_addr_cnt; + tlb_props->way_props.priv_addr_cfg = priv_addr_cfg; + tlb_props->way_props.priv_addr_cnt = priv_addr_cnt; return 0; @@ -785,6 +764,96 @@ err_priv_id: return ret; } +static int __init sysmmu_parse_tlb_port_dt(struct device *sysmmu, + struct sysmmu_drvdata *drvdata) +{ + const char *props_name = "sysmmu,tlb_property"; + struct tlb_props *tlb_props = &drvdata->tlb_props; + struct tlb_port_cfg *port_cfg = NULL; + int i, cnt, ret; + int port_id_cnt = 0; + + cnt = of_property_count_u32_elems(sysmmu->of_node, props_name); + if (!cnt || cnt < 0) { + dev_info(sysmmu, "No TLB port propeties found.\n"); + return 0; + } + + port_cfg = kzalloc(sizeof(*port_cfg) * (cnt/2), GFP_KERNEL); + if (!port_cfg) + return -ENOMEM; + + for (i = 0; i < cnt; i+=2) { + ret = of_property_read_u32_index(sysmmu->of_node, + props_name, i, &port_cfg[port_id_cnt].cfg); + if (ret) { + dev_err(sysmmu, "failed to get cfg property." + "cnt = %d, ret = %d\n", i, ret); + ret = -EINVAL; + goto err_port_prop; + } + + ret = of_property_read_u32_index(sysmmu->of_node, + props_name, i+1, &port_cfg[port_id_cnt].id); + if (ret) { + dev_err(sysmmu, "failed to get id property." + "cnt = %d, ret = %d\n", i, ret); + ret = -EINVAL; + goto err_port_prop; + } + port_id_cnt++; + } + + tlb_props->port_props.port_id_cnt = port_id_cnt; + tlb_props->port_props.port_cfg = port_cfg; + + return 0; + +err_port_prop: + kfree(port_cfg); + + return ret; +} + +static int __init sysmmu_parse_dt(struct device *sysmmu, + struct sysmmu_drvdata *drvdata) +{ + unsigned int qos = DEFAULT_QOS_VALUE; + int ret; + + /* Parsing QoS */ + ret = of_property_read_u32_index(sysmmu->of_node, "qos", 0, &qos); + if (!ret && (qos > 15)) { + dev_err(sysmmu, "Invalid QoS value %d, use default.\n", qos); + qos = DEFAULT_QOS_VALUE; + } + drvdata->qos = qos; + + /* Secure IRQ */ + if (of_find_property(sysmmu->of_node, "sysmmu,secure-irq", NULL)) { + ret = __sysmmu_secure_irq_init(sysmmu, drvdata); + if (ret) { + dev_err(sysmmu, "Failed to init secure irq\n"); + return ret; + } + } + + if (of_property_read_bool(sysmmu->of_node, "sysmmu,no-suspend")) + dev_pm_syscore_device(sysmmu, true); + + if (IS_TLB_WAY_TYPE(drvdata)) { + ret = sysmmu_parse_tlb_way_dt(sysmmu, drvdata); + if (ret) + dev_err(sysmmu, "Failed to parse TLB way property\n"); + } else if (IS_TLB_PORT_TYPE(drvdata)) { + ret = sysmmu_parse_tlb_port_dt(sysmmu, drvdata); + if (ret) + dev_err(sysmmu, "Failed to parse TLB port property\n"); + }; + + return ret; +} + static struct iommu_ops exynos_iommu_ops; static int __init exynos_sysmmu_probe(struct platform_device *pdev) { @@ -943,7 +1012,7 @@ static void __sysmmu_set_public_way(struct sysmmu_drvdata *drvdata, static void __sysmmu_set_private_way_id(struct sysmmu_drvdata *drvdata, unsigned int way_idx) { - struct tlb_priv_id *priv_cfg = drvdata->tlb_props.priv_id_cfg; + struct tlb_priv_id *priv_cfg = drvdata->tlb_props.way_props.priv_id_cfg; u32 cfg = __raw_readl(drvdata->sfrbase + REG_PRIVATE_WAY_CFG(way_idx)); cfg &= ~MMU_PRIVATE_WAY_MASK; @@ -961,8 +1030,8 @@ static void __sysmmu_set_private_way_id(struct sysmmu_drvdata *drvdata, static void __sysmmu_set_private_way_addr(struct sysmmu_drvdata *drvdata, unsigned int priv_addr_idx) { - struct tlb_priv_addr *priv_cfg = drvdata->tlb_props.priv_addr_cfg; - unsigned int way_idx = drvdata->tlb_props.priv_id_cnt + priv_addr_idx; + struct tlb_priv_addr *priv_cfg = drvdata->tlb_props.way_props.priv_addr_cfg; + unsigned int way_idx = drvdata->tlb_props.way_props.priv_id_cnt + priv_addr_idx; u32 cfg = __raw_readl(drvdata->sfrbase + REG_PRIVATE_WAY_CFG(way_idx)); cfg &= ~MMU_PRIVATE_WAY_MASK; @@ -974,48 +1043,99 @@ static void __sysmmu_set_private_way_addr(struct sysmmu_drvdata *drvdata, dev_dbg(drvdata->sysmmu, "priv ADDR way[%d] cfg : %#x\n", way_idx, cfg); } -static void __sysmmu_init_config(struct sysmmu_drvdata *drvdata) +static void __sysmmu_set_tlb_way_dedication(struct sysmmu_drvdata *drvdata) { - unsigned long cfg = 0; - - writel_relaxed(CTRL_BLOCK, drvdata->sfrbase + REG_MMU_CTRL); + u32 cfg = __raw_readl(drvdata->sfrbase + REG_MMU_CAPA0_V7); + u32 tlb_way_num = MMU_CAPA_NUM_TLB_WAY(cfg); + u32 set_cnt = 0; + struct tlb_props *tlb_props = &drvdata->tlb_props; + unsigned int i; + int priv_id_cnt = tlb_props->way_props.priv_id_cnt; + int priv_addr_cnt = tlb_props->way_props.priv_addr_cnt; - if (drvdata->qos != DEFAULT_QOS_VALUE) - cfg |= CFG_QOS_OVRRIDE | CFG_QOS(drvdata->qos); + if (tlb_props->flags & TLB_WAY_PUBLIC) + __sysmmu_set_public_way(drvdata, + tlb_props->way_props.public_cfg); - if (MMU_MAJ_VER(drvdata->version) >= 7) { - u32 cfg = __raw_readl(drvdata->sfrbase + REG_MMU_CAPA0_V7); - u32 tlb_way_num = MMU_CAPA_NUM_TLB_WAY(cfg); - u32 set_cnt = 0; - struct tlb_props *tlb_props = &drvdata->tlb_props; - unsigned int i; - int priv_id_cnt = tlb_props->priv_id_cnt; - int priv_addr_cnt = tlb_props->priv_addr_cnt; - - if (tlb_props->flags & TLB_WAY_PUBLIC) - __sysmmu_set_public_way(drvdata, - tlb_props->public_cfg); - - if (tlb_props->flags & TLB_WAY_PRIVATE_ID) { - for (i = 0; i < priv_id_cnt && - set_cnt < tlb_way_num; i++, set_cnt++) - __sysmmu_set_private_way_id(drvdata, i); - } + if (tlb_props->flags & TLB_WAY_PRIVATE_ID) { + for (i = 0; i < priv_id_cnt && + set_cnt < tlb_way_num; i++, set_cnt++) + __sysmmu_set_private_way_id(drvdata, i); + } - if (tlb_props->flags & TLB_WAY_PRIVATE_ADDR) { - for (i = 0; i < priv_addr_cnt && - set_cnt < tlb_way_num; i++, set_cnt++) - __sysmmu_set_private_way_addr(drvdata, i); - } + if (tlb_props->flags & TLB_WAY_PRIVATE_ADDR) { + for (i = 0; i < priv_addr_cnt && + set_cnt < tlb_way_num; i++, set_cnt++) + __sysmmu_set_private_way_addr(drvdata, i); + } - if (priv_id_cnt + priv_addr_cnt > tlb_way_num) { - dev_warn(drvdata->sysmmu, + if (priv_id_cnt + priv_addr_cnt > tlb_way_num) { + dev_warn(drvdata->sysmmu, "Too many values than TLB way count %d," " so ignored!\n", tlb_way_num); - dev_warn(drvdata->sysmmu, + dev_warn(drvdata->sysmmu, "Number of private way id/addr = %d/%d\n", - priv_id_cnt, priv_addr_cnt); - } + priv_id_cnt, priv_addr_cnt); + } +} + +static void __sysmmu_set_tlb_port(struct sysmmu_drvdata *drvdata, + unsigned int port_idx) +{ + struct tlb_port_cfg *port_cfg = drvdata->tlb_props.port_props.port_cfg; + + writel_relaxed(MMU_TLB_CFG_MASK(port_cfg[port_idx].cfg), + drvdata->sfrbase + REG_MMU_TLB_CFG(port_idx)); + + /* port_idx 0 is default port. */ + if (port_idx == 0) { + dev_dbg(drvdata->sysmmu, "port[%d] cfg : %#x for common\n", + port_idx, + MMU_TLB_CFG_MASK(port_cfg[port_idx].cfg)); + return; + } + + writel_relaxed(MMU_TLB_MATCH_CFG_MASK(port_cfg[port_idx].cfg), + drvdata->sfrbase + REG_MMU_TLB_MATCH_CFG(port_idx)); + writel_relaxed(port_cfg[port_idx].id, + drvdata->sfrbase + REG_MMU_TLB_MATCH_ID(port_idx)); + + dev_dbg(drvdata->sysmmu, "port[%d] cfg : %#x, match : %#x, id : %#x\n", + port_idx, + MMU_TLB_CFG_MASK(port_cfg[port_idx].cfg), + MMU_TLB_MATCH_CFG_MASK(port_cfg[port_idx].cfg), + port_cfg[port_idx].id); +} + +static void __sysmmu_set_tlb_port_dedication(struct sysmmu_drvdata *drvdata) +{ + u32 cfg = __raw_readl(drvdata->sfrbase + REG_MMU_CAPA1_V7); + u32 tlb_num = MMU_CAPA1_NUM_TLB(cfg); + struct tlb_props *tlb_props = &drvdata->tlb_props; + unsigned int i; + int port_id_cnt = tlb_props->port_props.port_id_cnt; + + if (port_id_cnt > tlb_num) { + dev_warn(drvdata->sysmmu, + "Too many values %d than TLB count %d," + " so ignored!\n", port_id_cnt, tlb_num); + port_id_cnt = tlb_num; + } + + for (i = 0; i < port_id_cnt; i++) + __sysmmu_set_tlb_port(drvdata, i); +} + +static void __sysmmu_init_config(struct sysmmu_drvdata *drvdata) +{ + unsigned long cfg = 0; + + writel_relaxed(CTRL_BLOCK, drvdata->sfrbase + REG_MMU_CTRL); + + if (IS_TLB_WAY_TYPE(drvdata)) { + __sysmmu_set_tlb_way_dedication(drvdata); + } else if (IS_TLB_PORT_TYPE(drvdata)) { + __sysmmu_set_tlb_port_dedication(drvdata); } else { __exynos_sysmmu_set_prefbuf_axi_id(drvdata); if (has_sysmmu_set_associative_tlb(drvdata->sfrbase)) @@ -1023,6 +1143,9 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *drvdata) cfg |= CFG_FLPDCACHE | CFG_ACGEN; } + if (drvdata->qos != DEFAULT_QOS_VALUE) + cfg |= CFG_QOS_OVRRIDE | CFG_QOS(drvdata->qos); + cfg |= __raw_readl(drvdata->sfrbase + REG_MMU_CFG) & ~CFG_MASK; writel_relaxed(cfg, drvdata->sfrbase + REG_MMU_CFG); } diff --git a/drivers/iommu/exynos-iommu.h b/drivers/iommu/exynos-iommu.h index b215ddbe8f34..9526b883bdec 100644 --- a/drivers/iommu/exynos-iommu.h +++ b/drivers/iommu/exynos-iommu.h @@ -169,6 +169,11 @@ typedef u32 sysmmu_pte_t; #define MMU_TLB_INFO(n) (0x2000 + ((n) * 0x20)) #define MMU_CAPA1_NUM_TLB_SET(reg) ((reg >> 16) & 0xFF) #define MMU_CAPA1_NUM_TLB_WAY(reg) ((reg) & 0xFF) +#define REG_MMU_TLB_CFG(n) (0x2000 + ((n) * 0x20) + 0x4) +#define REG_MMU_TLB_MATCH_CFG(n) (0x2000 + ((n) * 0x20) + 0x8) +#define REG_MMU_TLB_MATCH_SVA(n) (0x2000 + ((n) * 0x20) + 0xC) +#define REG_MMU_TLB_MATCH_EVA(n) (0x2000 + ((n) * 0x20) + 0x10) +#define REG_MMU_TLB_MATCH_ID(n) (0x2000 + ((n) * 0x20) + 0x14) #define REG_CAPA1_TLB_READ 0x8000 #define REG_CAPA1_TLB_VPN 0x8004 #define REG_CAPA1_TLB_PPN 0x8008 @@ -179,6 +184,8 @@ typedef u32 sysmmu_pte_t; #define REG_CAPA1_SBB_ATTR 0x802C #define MMU_CAPA1_SET_TLB_READ_ENTRY(tid, set, way, line) \ ((set) | ((way) << 8) | ((line) << 16) | ((tid) << 20)) +#define MMU_TLB_CFG_MASK(reg) ((reg) & ((0x7 << 5) | (0x3 << 2) | (0x1 << 1))) +#define MMU_TLB_MATCH_CFG_MASK(reg) ((reg) & ((0xFFFF << 16) | (0x3 << 8))) #define MMU_CAPA_NUM_SBB_ENTRY(reg) ((reg >> 12) & 0xF) #define MMU_CAPA_NUM_TLB_SET(reg) ((reg >> 8) & 0xF) @@ -266,6 +273,11 @@ struct tlb_priv_id { unsigned int id; }; +struct tlb_port_cfg { + unsigned int cfg; + unsigned int id; +}; + /* * flags[7:4] specifies TLB matching types. * 0x1 : TLB way dedication @@ -282,8 +294,8 @@ struct tlb_priv_id { #define TLB_WAY_PRIVATE_ID (1 << 0) #define TLB_WAY_PRIVATE_ADDR (1 << 1) #define TLB_WAY_PUBLIC (1 << 2) -struct tlb_props { - int flags; + +struct tlb_way_props { int priv_id_cnt; int priv_addr_cnt; unsigned int public_cfg; @@ -291,6 +303,19 @@ struct tlb_props { struct tlb_priv_addr *priv_addr_cfg; }; +struct tlb_port_props { + int port_id_cnt; + struct tlb_port_cfg *port_cfg; +}; + +struct tlb_props { + int flags; + union { + struct tlb_way_props way_props; + struct tlb_port_props port_props; + }; +}; + /* * This structure hold all data of a single SYSMMU controller, this includes * hw resources like registers and clocks, pointers and list nodes to connect