1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * rcar-fcp.c -- R-Car Frame Compression Processor Driver 4 * 5 * Copyright (C) 2016 Renesas Electronics Corporation 6 * 7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 8 */ 9 10 #include <linux/device.h> 11 #include <linux/dma-mapping.h> 12 #include <linux/io.h> 13 #include <linux/iopoll.h> 14 #include <linux/list.h> 15 #include <linux/module.h> 16 #include <linux/mod_devicetable.h> 17 #include <linux/mutex.h> 18 #include <linux/platform_device.h> 19 #include <linux/pm_runtime.h> 20 #include <linux/slab.h> 21 22 #include <media/rcar-fcp.h> 23 24 #define RCAR_FCP_REG_RST 0x0010 25 #define RCAR_FCP_REG_RST_SOFTRST BIT(0) 26 #define RCAR_FCP_REG_STA 0x0018 27 #define RCAR_FCP_REG_STA_ACT BIT(0) 28 29 struct rcar_fcp_device { 30 struct list_head list; 31 struct device *dev; 32 void __iomem *base; 33 }; 34 35 static LIST_HEAD(fcp_devices); 36 static DEFINE_MUTEX(fcp_lock); 37 38 static inline void rcar_fcp_write(struct rcar_fcp_device *fcp, u32 reg, u32 val) 39 { 40 iowrite32(val, fcp->base + reg); 41 } 42 43 /* ----------------------------------------------------------------------------- 44 * Public API 45 */ 46 47 /** 48 * rcar_fcp_get - Find and acquire a reference to an FCP instance 49 * @np: Device node of the FCP instance 50 * 51 * Search the list of registered FCP instances for the instance corresponding to 52 * the given device node. 53 * 54 * Return a pointer to the FCP instance, or an ERR_PTR if the instance can't be 55 * found. 56 */ 57 struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np) 58 { 59 struct rcar_fcp_device *fcp; 60 61 mutex_lock(&fcp_lock); 62 63 list_for_each_entry(fcp, &fcp_devices, list) { 64 if (fcp->dev->of_node != np) 65 continue; 66 67 get_device(fcp->dev); 68 goto done; 69 } 70 71 fcp = ERR_PTR(-EPROBE_DEFER); 72 73 done: 74 mutex_unlock(&fcp_lock); 75 return fcp; 76 } 77 EXPORT_SYMBOL_GPL(rcar_fcp_get); 78 79 /** 80 * rcar_fcp_put - Release a reference to an FCP instance 81 * @fcp: The FCP instance 82 * 83 * Release the FCP instance acquired by a call to rcar_fcp_get(). 84 */ 85 void rcar_fcp_put(struct rcar_fcp_device *fcp) 86 { 87 if (fcp) 88 put_device(fcp->dev); 89 } 90 EXPORT_SYMBOL_GPL(rcar_fcp_put); 91 92 struct device *rcar_fcp_get_device(struct rcar_fcp_device *fcp) 93 { 94 return fcp->dev; 95 } 96 EXPORT_SYMBOL_GPL(rcar_fcp_get_device); 97 98 /** 99 * rcar_fcp_enable - Enable an FCP 100 * @fcp: The FCP instance 101 * 102 * Before any memory access through an FCP is performed by a module, the FCP 103 * must be enabled by a call to this function. The enable calls are reference 104 * counted, each successful call must be followed by one rcar_fcp_disable() 105 * call when no more memory transfer can occur through the FCP. 106 * 107 * Return 0 on success or a negative error code if an error occurs. The enable 108 * reference count isn't increased when this function returns an error. 109 */ 110 int rcar_fcp_enable(struct rcar_fcp_device *fcp) 111 { 112 if (!fcp) 113 return 0; 114 115 return pm_runtime_resume_and_get(fcp->dev); 116 } 117 EXPORT_SYMBOL_GPL(rcar_fcp_enable); 118 119 /** 120 * rcar_fcp_disable - Disable an FCP 121 * @fcp: The FCP instance 122 * 123 * This function is the counterpart of rcar_fcp_enable(). As enable calls are 124 * reference counted a disable call may not disable the FCP synchronously. 125 */ 126 void rcar_fcp_disable(struct rcar_fcp_device *fcp) 127 { 128 if (fcp) 129 pm_runtime_put(fcp->dev); 130 } 131 EXPORT_SYMBOL_GPL(rcar_fcp_disable); 132 133 int rcar_fcp_soft_reset(struct rcar_fcp_device *fcp) 134 { 135 u32 value; 136 int ret; 137 138 if (!fcp) 139 return 0; 140 141 rcar_fcp_write(fcp, RCAR_FCP_REG_RST, RCAR_FCP_REG_RST_SOFTRST); 142 ret = readl_poll_timeout(fcp->base + RCAR_FCP_REG_STA, 143 value, !(value & RCAR_FCP_REG_STA_ACT), 144 1, 100); 145 if (ret) 146 dev_err(fcp->dev, "Failed to soft-reset\n"); 147 148 return ret; 149 } 150 EXPORT_SYMBOL_GPL(rcar_fcp_soft_reset); 151 152 /* ----------------------------------------------------------------------------- 153 * Platform Driver 154 */ 155 156 static int rcar_fcp_probe(struct platform_device *pdev) 157 { 158 struct rcar_fcp_device *fcp; 159 160 fcp = devm_kzalloc(&pdev->dev, sizeof(*fcp), GFP_KERNEL); 161 if (fcp == NULL) 162 return -ENOMEM; 163 164 fcp->dev = &pdev->dev; 165 166 fcp->base = devm_platform_ioremap_resource(pdev, 0); 167 if (IS_ERR(fcp->base)) 168 return PTR_ERR(fcp->base); 169 170 dma_set_max_seg_size(fcp->dev, UINT_MAX); 171 172 pm_runtime_enable(&pdev->dev); 173 174 mutex_lock(&fcp_lock); 175 list_add_tail(&fcp->list, &fcp_devices); 176 mutex_unlock(&fcp_lock); 177 178 platform_set_drvdata(pdev, fcp); 179 180 return 0; 181 } 182 183 static void rcar_fcp_remove(struct platform_device *pdev) 184 { 185 struct rcar_fcp_device *fcp = platform_get_drvdata(pdev); 186 187 mutex_lock(&fcp_lock); 188 list_del(&fcp->list); 189 mutex_unlock(&fcp_lock); 190 191 pm_runtime_disable(&pdev->dev); 192 } 193 194 static const struct of_device_id rcar_fcp_of_match[] = { 195 { .compatible = "renesas,fcpf" }, 196 { .compatible = "renesas,fcpv" }, 197 { }, 198 }; 199 MODULE_DEVICE_TABLE(of, rcar_fcp_of_match); 200 201 static struct platform_driver rcar_fcp_platform_driver = { 202 .probe = rcar_fcp_probe, 203 .remove = rcar_fcp_remove, 204 .driver = { 205 .name = "rcar-fcp", 206 .of_match_table = rcar_fcp_of_match, 207 .suppress_bind_attrs = true, 208 }, 209 }; 210 211 module_platform_driver(rcar_fcp_platform_driver); 212 213 MODULE_ALIAS("rcar-fcp"); 214 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 215 MODULE_DESCRIPTION("Renesas FCP Driver"); 216 MODULE_LICENSE("GPL"); 217