1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2019 Rockchip Electronics Co., Ltd 4 */ 5 #include <common.h> 6 #include <asm/io.h> 7 #include <clk.h> 8 #include <dm.h> 9 #include <linux/bitops.h> 10 #include <misc.h> 11 #include <irq-generic.h> 12 #include <reset.h> 13 14 DECLARE_GLOBAL_DATA_PTR; 15 16 #define DECOM_CTRL 0x0 17 #define DECOM_ENR 0x4 18 #define DECOM_RADDR 0x8 19 #define DECOM_WADDR 0xc 20 #define DECOM_UDDSL 0x10 21 #define DECOM_UDDSH 0x14 22 #define DECOM_TXTHR 0x18 23 #define DECOM_RXTHR 0x1c 24 #define DECOM_SLEN 0x20 25 #define DECOM_STAT 0x24 26 #define DECOM_ISR 0x28 27 #define DECOM_IEN 0x2c 28 #define DECOM_AXI_STAT 0x30 29 #define DECOM_TSIZEL 0x34 30 #define DECOM_TSIZEH 0x38 31 #define DECOM_MGNUM 0x3c 32 #define DECOM_FRAME 0x40 33 #define DECOM_DICTID 0x44 34 #define DECOM_CSL 0x48 35 #define DECOM_CSH 0x4c 36 #define DECOM_LMTSL 0x50 37 #define DECOM_LMTSH 0x54 38 39 #define LZ4_HEAD_CSUM_CHECK_EN BIT(1) 40 #define LZ4_BLOCK_CSUM_CHECK_EN BIT(2) 41 #define LZ4_CONT_CSUM_CHECK_EN BIT(3) 42 43 #define DSOLIEN BIT(19) 44 #define ZDICTEIEN BIT(18) 45 #define GCMEIEN BIT(17) 46 #define GIDEIEN BIT(16) 47 #define CCCEIEN BIT(15) 48 #define BCCEIEN BIT(14) 49 #define HCCEIEN BIT(13) 50 #define CSEIEN BIT(12) 51 #define DICTEIEN BIT(11) 52 #define VNEIEN BIT(10) 53 #define WNEIEN BIT(9) 54 #define RDCEIEN BIT(8) 55 #define WRCEIEN BIT(7) 56 #define DISEIEN BIT(6) 57 #define LENEIEN BIT(5) 58 #define LITEIEN BIT(4) 59 #define SQMEIEN BIT(3) 60 #define SLCIEN BIT(2) 61 #define HDEIEN BIT(1) 62 #define DSIEN BIT(0) 63 64 #define DECOM_STOP BIT(0) 65 #define DECOM_COMPLETE BIT(0) 66 #define DECOM_GZIP_MODE BIT(4) 67 #define DECOM_ZLIB_MODE BIT(5) 68 #define DECOM_DEFLATE_MODE BIT(0) 69 #define DECOM_AXI_IDLE BIT(4) 70 #define DECOM_LZ4_MODE 0 71 72 #define DECOM_ENABLE 0x1 73 #define DECOM_DISABLE 0x0 74 75 #define DECOM_IRQ 0xffff /* fixme */ 76 77 #define DECOM_INT_MASK \ 78 (DSOLIEN | ZDICTEIEN | GCMEIEN | GIDEIEN | \ 79 CCCEIEN | BCCEIEN | HCCEIEN | CSEIEN | \ 80 DICTEIEN | VNEIEN | WNEIEN | RDCEIEN | WRCEIEN | \ 81 DISEIEN | LENEIEN | LITEIEN | SQMEIEN | SLCIEN | \ 82 HDEIEN | DSIEN) 83 84 #define DCLK_DECOM 400 * 1000 * 1000 85 86 struct rockchip_decom_priv { 87 struct reset_ctl rst; 88 void __iomem *base; 89 bool idle_check_once; 90 bool done; 91 struct clk dclk; 92 int cached; /* 1: access the data through dcache; 0: no dcache */ 93 }; 94 95 static int rockchip_decom_start(struct udevice *dev, void *buf) 96 { 97 struct rockchip_decom_priv *priv = dev_get_priv(dev); 98 struct decom_param *param = (struct decom_param *)buf; 99 unsigned int limit_lo = param->size_dst & 0xffffffff; 100 unsigned int limit_hi = param->size_dst >> 32; 101 u32 irq_status; 102 103 /* Increase limit_size by 16 to avoid premature decompression termination by decom */ 104 limit_lo += 16; 105 106 #if CONFIG_IS_ENABLED(DM_RESET) 107 reset_assert(&priv->rst); 108 udelay(10); 109 reset_deassert(&priv->rst); 110 #endif 111 /* 112 * Purpose: 113 * src: clean dcache to get the real compressed data from ddr. 114 * dst: invalidate dcache. 115 * 116 * flush_dcache_all() operating on set/way is faster than 117 * flush_cache() and invalidate_dcache_range() operating 118 * on virtual address. 119 */ 120 if (!priv->cached) 121 flush_dcache_all(); 122 123 priv->done = false; 124 125 irq_status = readl(priv->base + DECOM_ISR); 126 /* clear interrupts */ 127 if (irq_status) 128 writel(irq_status, priv->base + DECOM_ISR); 129 130 if (param->mode == DECOM_LZ4) 131 writel(LZ4_CONT_CSUM_CHECK_EN | 132 LZ4_HEAD_CSUM_CHECK_EN | 133 LZ4_BLOCK_CSUM_CHECK_EN | 134 DECOM_LZ4_MODE, 135 priv->base + DECOM_CTRL); 136 else if (param->mode == DECOM_GZIP) 137 writel(DECOM_DEFLATE_MODE | DECOM_GZIP_MODE, 138 priv->base + DECOM_CTRL); 139 else if (param->mode == DECOM_ZLIB) 140 writel(DECOM_DEFLATE_MODE | DECOM_ZLIB_MODE, 141 priv->base + DECOM_CTRL); 142 143 writel(param->addr_src, priv->base + DECOM_RADDR); 144 writel(param->addr_dst, priv->base + DECOM_WADDR); 145 146 writel(limit_lo, priv->base + DECOM_LMTSL); 147 writel(limit_hi, priv->base + DECOM_LMTSH); 148 149 if (param->flags && DCOMP_FLG_IRQ_ONESHOT) 150 writel(DECOM_INT_MASK, priv->base + DECOM_IEN); 151 writel(DECOM_ENABLE, priv->base + DECOM_ENR); 152 153 priv->idle_check_once = true; 154 155 return 0; 156 } 157 158 static int rockchip_decom_stop(struct udevice *dev) 159 { 160 struct rockchip_decom_priv *priv = dev_get_priv(dev); 161 162 writel(DECOM_DISABLE, priv->base + DECOM_ENR); 163 164 return 0; 165 } 166 167 /* Caller must call this function to check if decompress done */ 168 static int rockchip_decom_done_poll(struct udevice *dev) 169 { 170 struct rockchip_decom_priv *priv = dev_get_priv(dev); 171 172 /* 173 * Test the decom is idle first time. 174 */ 175 if (!priv->idle_check_once) 176 return !(readl(priv->base + DECOM_AXI_STAT) & DECOM_AXI_IDLE); 177 178 return !(readl(priv->base + DECOM_STAT) & DECOM_COMPLETE); 179 } 180 181 static int rockchip_decom_capability(u32 *buf) 182 { 183 *buf = DECOM_GZIP; 184 #if !defined(CONFIG_ROCKCHIP_RK3576) && !defined(CONFIG_ROCKCHIP_RV1103B) && \ 185 !defined(CONFIG_ROCKCHIP_RV1126B) 186 *buf |= DECOM_LZ4; 187 #endif 188 return 0; 189 } 190 191 static int rockchip_decom_data_size(struct udevice *dev, u64 *buf) 192 { 193 struct rockchip_decom_priv *priv = dev_get_priv(dev); 194 struct decom_param *param = (struct decom_param *)buf; 195 u32 sizel, sizeh; 196 197 sizel = readl(priv->base + DECOM_TSIZEL); 198 sizeh = readl(priv->base + DECOM_TSIZEH); 199 param->size_dst = sizel | ((u64)sizeh << 32); 200 201 return 0; 202 } 203 204 /* Caller must fill in param @buf which represent struct decom_param */ 205 static int rockchip_decom_ioctl(struct udevice *dev, unsigned long request, 206 void *buf) 207 { 208 int ret = -EINVAL; 209 210 switch (request) { 211 case IOCTL_REQ_START: 212 ret = rockchip_decom_start(dev, buf); 213 break; 214 case IOCTL_REQ_POLL: 215 ret = rockchip_decom_done_poll(dev); 216 break; 217 case IOCTL_REQ_STOP: 218 ret = rockchip_decom_stop(dev); 219 break; 220 case IOCTL_REQ_CAPABILITY: 221 ret = rockchip_decom_capability(buf); 222 break; 223 case IOCTL_REQ_DATA_SIZE: 224 ret = rockchip_decom_data_size(dev, buf); 225 break; 226 default: 227 printf("Unsupported ioctl: %ld\n", (ulong)request); 228 break; 229 } 230 231 return ret; 232 } 233 234 static const struct misc_ops rockchip_decom_ops = { 235 .ioctl = rockchip_decom_ioctl, 236 }; 237 238 static int rockchip_decom_ofdata_to_platdata(struct udevice *dev) 239 { 240 struct rockchip_decom_priv *priv = dev_get_priv(dev); 241 242 priv->base = dev_read_addr_ptr(dev); 243 if (!priv->base) 244 return -ENOENT; 245 246 priv->cached = dev_read_u32_default(dev, "data-cached", 0); 247 248 return 0; 249 } 250 251 static int rockchip_decom_probe(struct udevice *dev) 252 { 253 struct rockchip_decom_priv *priv = dev_get_priv(dev); 254 int ret; 255 256 #if CONFIG_IS_ENABLED(DM_RESET) 257 ret = reset_get_by_name(dev, "dresetn", &priv->rst); 258 if (ret) { 259 debug("reset_get_by_name() failed: %d\n", ret); 260 return ret; 261 } 262 #endif 263 264 ret = clk_get_by_index(dev, 1, &priv->dclk); 265 if (ret < 0) 266 return ret; 267 268 ret = clk_set_rate(&priv->dclk, DCLK_DECOM); 269 if (ret < 0) 270 return ret; 271 272 return 0; 273 } 274 275 static const struct udevice_id rockchip_decom_ids[] = { 276 { .compatible = "rockchip,hw-decompress" }, 277 {} 278 }; 279 280 U_BOOT_DRIVER(rockchip_hw_decompress) = { 281 .name = "rockchip_hw_decompress", 282 .id = UCLASS_MISC, 283 .of_match = rockchip_decom_ids, 284 .probe = rockchip_decom_probe, 285 .ofdata_to_platdata = rockchip_decom_ofdata_to_platdata, 286 .priv_auto_alloc_size = sizeof(struct rockchip_decom_priv), 287 .ops = &rockchip_decom_ops, 288 }; 289