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