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