| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2019 Andes Technology Corporation |
| * Rick Chen, Andes Technology Corporation <rick@andestech.com> |
| */ |
| |
| #include <command.h> |
| #include <cache.h> |
| #include <dm.h> |
| #include <hang.h> |
| #include <asm/global_data.h> |
| #include <asm/io.h> |
| #include <dm/ofnode.h> |
| #include <linux/bitops.h> |
| |
| struct l2cache { |
| volatile u64 configure; |
| volatile u64 control; |
| volatile u64 hpm0; |
| volatile u64 hpm1; |
| volatile u64 hpm2; |
| volatile u64 hpm3; |
| volatile u64 error_status; |
| volatile u64 ecc_error; |
| volatile u64 cctl_command0; |
| volatile u64 cctl_access_line0; |
| volatile u64 cctl_command1; |
| volatile u64 cctl_access_line1; |
| volatile u64 cctl_command2; |
| volatile u64 cctl_access_line2; |
| volatile u64 cctl_command3; |
| volatile u64 cctl_access_line3; |
| volatile u64 cctl_status; |
| }; |
| |
| /* Configuration register */ |
| #define MEM_MAP_OFF 20 |
| #define MEM_MAP_MSK BIT(MEM_MAP_OFF) |
| /* offset of v0 memory map (Gen1) */ |
| static u32 cmd_stride = 0x10; |
| static u32 status_stride = 0x0; |
| static u32 status_bit_offset = 0x4; |
| |
| /* Control Register */ |
| #define L2_ENABLE 0x1 |
| /* prefetch */ |
| #define IPREPETCH_OFF 3 |
| #define DPREPETCH_OFF 5 |
| #define IPREPETCH_MSK (3 << IPREPETCH_OFF) |
| #define DPREPETCH_MSK (3 << DPREPETCH_OFF) |
| /* tag ram */ |
| #define TRAMOCTL_OFF 8 |
| #define TRAMICTL_OFF 10 |
| #define TRAMOCTL_MSK (3 << TRAMOCTL_OFF) |
| #define TRAMICTL_MSK BIT(TRAMICTL_OFF) |
| /* data ram */ |
| #define DRAMOCTL_OFF 11 |
| #define DRAMICTL_OFF 13 |
| #define DRAMOCTL_MSK (3 << DRAMOCTL_OFF) |
| #define DRAMICTL_MSK BIT(DRAMICTL_OFF) |
| |
| /* CCTL Command Register */ |
| #define CCTL_CMD_REG(base, hart) ((ulong)(base) + 0x40 + (hart) * (cmd_stride)) |
| #define L2_WBINVAL_ALL 0x12 |
| |
| /* CCTL Status Register */ |
| #define CCTL_STATUS_REG(base, hart) ((ulong)(base) + 0x80 + (hart) * (status_stride)) |
| #define CCTL_STATUS_MSK(hart) (0xf << ((hart) * (status_bit_offset))) |
| #define CCTL_STATUS_IDLE(hart) (0 << ((hart) * (status_bit_offset))) |
| #define CCTL_STATUS_PROCESS(hart) (1 << ((hart) * (status_bit_offset))) |
| #define CCTL_STATUS_ILLEGAL(hart) (2 << ((hart) * (status_bit_offset))) |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| struct andes_l2_plat { |
| struct l2cache *regs; |
| u32 iprefetch; |
| u32 dprefetch; |
| u32 tram_ctl[2]; |
| u32 dram_ctl[2]; |
| }; |
| |
| static int andes_l2_enable(struct udevice *dev) |
| { |
| struct andes_l2_plat *plat = dev_get_plat(dev); |
| volatile struct l2cache *regs = plat->regs; |
| |
| if (regs) |
| setbits_le32(®s->control, L2_ENABLE); |
| |
| return 0; |
| } |
| |
| static int andes_l2_disable(struct udevice *dev) |
| { |
| struct andes_l2_plat *plat = dev_get_plat(dev); |
| volatile struct l2cache *regs = plat->regs; |
| u8 hart = gd->arch.boot_hart; |
| |
| void __iomem *cctlcmd = (void __iomem *)CCTL_CMD_REG(regs, hart); |
| void __iomem *cctlstatus = (void __iomem *)CCTL_STATUS_REG(regs, hart); |
| |
| if ((regs) && (readl(®s->control) & L2_ENABLE)) { |
| writel(L2_WBINVAL_ALL, cctlcmd); |
| |
| while ((readl(cctlstatus) & CCTL_STATUS_MSK(hart))) { |
| if ((readl(cctlstatus) & CCTL_STATUS_ILLEGAL(hart))) { |
| printf("L2 flush illegal! hanging..."); |
| hang(); |
| } |
| } |
| clrbits_le32(®s->control, L2_ENABLE); |
| } |
| |
| return 0; |
| } |
| |
| static int andes_l2_of_to_plat(struct udevice *dev) |
| { |
| struct andes_l2_plat *plat = dev_get_plat(dev); |
| struct l2cache *regs; |
| |
| regs = dev_read_addr_ptr(dev); |
| plat->regs = regs; |
| |
| plat->iprefetch = -EINVAL; |
| plat->dprefetch = -EINVAL; |
| plat->tram_ctl[0] = -EINVAL; |
| plat->dram_ctl[0] = -EINVAL; |
| |
| /* Instruction and data fetch prefetch depth */ |
| dev_read_u32(dev, "andes,inst-prefetch", &plat->iprefetch); |
| dev_read_u32(dev, "andes,data-prefetch", &plat->dprefetch); |
| |
| /* Set tag RAM and data RAM setup and output cycle */ |
| dev_read_u32_array(dev, "andes,tag-ram-ctl", plat->tram_ctl, 2); |
| dev_read_u32_array(dev, "andes,data-ram-ctl", plat->dram_ctl, 2); |
| |
| return 0; |
| } |
| |
| static int andes_l2_probe(struct udevice *dev) |
| { |
| struct andes_l2_plat *plat = dev_get_plat(dev); |
| struct l2cache *regs = plat->regs; |
| u32 cfg_val, ctl_val; |
| |
| cfg_val = readl(®s->configure); |
| ctl_val = readl(®s->control); |
| |
| /* If true, v1 memory map (Gen2) */ |
| if (cfg_val & MEM_MAP_MSK) { |
| cmd_stride = 0x1000; |
| status_stride = 0x1000; |
| status_bit_offset = 0x0; |
| } |
| |
| ctl_val |= L2_ENABLE; |
| |
| if (plat->iprefetch != -EINVAL) { |
| ctl_val &= ~(IPREPETCH_MSK); |
| ctl_val |= (plat->iprefetch << IPREPETCH_OFF); |
| } |
| |
| if (plat->dprefetch != -EINVAL) { |
| ctl_val &= ~(DPREPETCH_MSK); |
| ctl_val |= (plat->dprefetch << DPREPETCH_OFF); |
| } |
| |
| if (plat->tram_ctl[0] != -EINVAL) { |
| ctl_val &= ~(TRAMOCTL_MSK | TRAMICTL_MSK); |
| ctl_val |= plat->tram_ctl[0] << TRAMOCTL_OFF; |
| ctl_val |= plat->tram_ctl[1] << TRAMICTL_OFF; |
| } |
| |
| if (plat->dram_ctl[0] != -EINVAL) { |
| ctl_val &= ~(DRAMOCTL_MSK | DRAMICTL_MSK); |
| ctl_val |= plat->dram_ctl[0] << DRAMOCTL_OFF; |
| ctl_val |= plat->dram_ctl[1] << DRAMICTL_OFF; |
| } |
| |
| writel(ctl_val, ®s->control); |
| |
| return 0; |
| } |
| |
| static const struct udevice_id andes_l2_cache_ids[] = { |
| { .compatible = "cache" }, |
| {} |
| }; |
| |
| static const struct cache_ops andes_l2_cache_ops = { |
| .enable = andes_l2_enable, |
| .disable = andes_l2_disable, |
| }; |
| |
| U_BOOT_DRIVER(andes_l2_cache) = { |
| .name = "andes_l2_cache", |
| .id = UCLASS_CACHE, |
| .of_match = andes_l2_cache_ids, |
| .of_to_plat = andes_l2_of_to_plat, |
| .probe = andes_l2_probe, |
| .plat_auto = sizeof(struct andes_l2_plat), |
| .ops = &andes_l2_cache_ops, |
| .flags = DM_FLAG_PRE_RELOC, |
| }; |