bcm27xx: update 6.1 patches to latest version
[openwrt/openwrt.git] / target / linux / bcm27xx / patches-6.1 / 950-1005-media-rp1-csi2-Track-CSI-2-errors.patch
1 From b19c2b5f88f141e58044e5d1012f867d46f74bf3 Mon Sep 17 00:00:00 2001
2 From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
3 Date: Thu, 21 Sep 2023 18:18:53 +0300
4 Subject: [PATCH] media: rp1: csi2: Track CSI-2 errors
5
6 Track the errors from the CSI-2 receiver: overflows and discards. These
7 are recorded in a table which can be read by the userspace via debugfs.
8
9 As tracking the errors may cause much more interrupt load, the tracking
10 needs to be enabled with a module parameter.
11
12 Note that the recording is not perfect: we only record the last
13 discarded DT for each discard type, instead of recording all of them.
14 This means that e.g. if the device is discarding two unmatched DTs, the
15 debugfs file only shows the last one recorded. Recording all of them
16 would need a more sophisticated recording system to avoid the need of a
17 very large table, or dynamic allocation.
18
19 Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
20 ---
21 .../media/platform/raspberrypi/rp1_cfe/csi2.c | 123 ++++++++++++++++++
22 .../media/platform/raspberrypi/rp1_cfe/csi2.h | 16 +++
23 2 files changed, 139 insertions(+)
24
25 --- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
26 +++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
27 @@ -16,6 +16,10 @@
28 #include "csi2.h"
29 #include "cfe.h"
30
31 +static bool csi2_track_errors;
32 +module_param_named(track_csi2_errors, csi2_track_errors, bool, 0);
33 +MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors");
34 +
35 #define csi2_dbg_verbose(fmt, arg...) \
36 do { \
37 if (cfe_debug_verbose) \
38 @@ -32,9 +36,28 @@
39 #define CSI2_DISCARDS_INACTIVE 0x00c
40 #define CSI2_DISCARDS_UNMATCHED 0x010
41 #define CSI2_DISCARDS_LEN_LIMIT 0x014
42 +
43 +#define CSI2_DISCARDS_AMOUNT_SHIFT 0
44 +#define CSI2_DISCARDS_AMOUNT_MASK GENMASK(23, 0)
45 +#define CSI2_DISCARDS_DT_SHIFT 24
46 +#define CSI2_DISCARDS_DT_MASK GENMASK(29, 24)
47 +#define CSI2_DISCARDS_VC_SHIFT 30
48 +#define CSI2_DISCARDS_VC_MASK GENMASK(31, 30)
49 +
50 #define CSI2_LLEV_PANICS 0x018
51 #define CSI2_ULEV_PANICS 0x01c
52 #define CSI2_IRQ_MASK 0x020
53 +#define CSI2_IRQ_MASK_IRQ_OVERFLOW BIT(0)
54 +#define CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW BIT(1)
55 +#define CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT BIT(2)
56 +#define CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED BIT(3)
57 +#define CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE BIT(4)
58 +#define CSI2_IRQ_MASK_IRQ_ALL \
59 + (CSI2_IRQ_MASK_IRQ_OVERFLOW | CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW | \
60 + CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT | \
61 + CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED | \
62 + CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE)
63 +
64 #define CSI2_CTRL 0x024
65 #define CSI2_CH_CTRL(x) ((x) * 0x40 + 0x28)
66 #define CSI2_CH_ADDR0(x) ((x) * 0x40 + 0x2c)
67 @@ -149,6 +172,92 @@ static int csi2_regs_show(struct seq_fil
68
69 DEFINE_SHOW_ATTRIBUTE(csi2_regs);
70
71 +static int csi2_errors_show(struct seq_file *s, void *data)
72 +{
73 + struct csi2_device *csi2 = s->private;
74 + unsigned long flags;
75 + u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES];
76 + u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
77 + u32 overflows;
78 +
79 + spin_lock_irqsave(&csi2->errors_lock, flags);
80 +
81 + memcpy(discards_table, csi2->discards_table, sizeof(discards_table));
82 + memcpy(discards_dt_table, csi2->discards_dt_table,
83 + sizeof(discards_dt_table));
84 + overflows = csi2->overflows;
85 +
86 + csi2->overflows = 0;
87 + memset(csi2->discards_table, 0, sizeof(discards_table));
88 + memset(csi2->discards_dt_table, 0, sizeof(discards_dt_table));
89 +
90 + spin_unlock_irqrestore(&csi2->errors_lock, flags);
91 +
92 + seq_printf(s, "Overflows %u\n", overflows);
93 + seq_puts(s, "Discards:\n");
94 + seq_puts(s, "VC OVLF LEN UNMATCHED INACTIVE\n");
95 +
96 + for (unsigned int vc = 0; vc < DISCARDS_TABLE_NUM_VCS; ++vc) {
97 + seq_printf(s, "%u %10u %10u %10u %10u\n", vc,
98 + discards_table[vc][DISCARDS_TABLE_OVERFLOW],
99 + discards_table[vc][DISCARDS_TABLE_LENGTH_LIMIT],
100 + discards_table[vc][DISCARDS_TABLE_UNMATCHED],
101 + discards_table[vc][DISCARDS_TABLE_INACTIVE]);
102 + }
103 +
104 + seq_printf(s, "Last DT %10u %10u %10u %10u\n",
105 + discards_dt_table[DISCARDS_TABLE_OVERFLOW],
106 + discards_dt_table[DISCARDS_TABLE_LENGTH_LIMIT],
107 + discards_dt_table[DISCARDS_TABLE_UNMATCHED],
108 + discards_dt_table[DISCARDS_TABLE_INACTIVE]);
109 +
110 + return 0;
111 +}
112 +
113 +DEFINE_SHOW_ATTRIBUTE(csi2_errors);
114 +
115 +static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status)
116 +{
117 + spin_lock(&csi2->errors_lock);
118 +
119 + if (status & IRQ_OVERFLOW)
120 + csi2->overflows++;
121 +
122 + for (unsigned int i = 0; i < DISCARDS_TABLE_NUM_ENTRIES; ++i) {
123 + static const u32 discard_bits[] = {
124 + IRQ_DISCARD_OVERFLOW,
125 + IRQ_DISCARD_LEN_LIMIT,
126 + IRQ_DISCARD_UNMATCHED,
127 + IRQ_DISCARD_INACTIVE,
128 + };
129 + static const u8 discard_regs[] = {
130 + CSI2_DISCARDS_OVERFLOW,
131 + CSI2_DISCARDS_LEN_LIMIT,
132 + CSI2_DISCARDS_UNMATCHED,
133 + CSI2_DISCARDS_INACTIVE,
134 + };
135 + u32 amount;
136 + u8 dt, vc;
137 + u32 v;
138 +
139 + if (!(status & discard_bits[i]))
140 + continue;
141 +
142 + v = csi2_reg_read(csi2, discard_regs[i]);
143 + csi2_reg_write(csi2, discard_regs[i], 0);
144 +
145 + amount = (v & CSI2_DISCARDS_AMOUNT_MASK) >>
146 + CSI2_DISCARDS_AMOUNT_SHIFT;
147 + dt = (v & CSI2_DISCARDS_DT_MASK) >> CSI2_DISCARDS_DT_SHIFT;
148 + vc = (v & CSI2_DISCARDS_VC_MASK) >> CSI2_DISCARDS_VC_SHIFT;
149 +
150 + csi2->discards_table[vc][i] += amount;
151 + csi2->discards_dt_table[i] = dt;
152 + }
153 +
154 + spin_unlock(&csi2->errors_lock);
155 +}
156 +
157 void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci)
158 {
159 unsigned int i;
160 @@ -183,6 +292,9 @@ void csi2_isr(struct csi2_device *csi2,
161 eof[i] = !!(status & IRQ_FE_ACK(i));
162 lci[i] = !!(status & IRQ_LE_ACK(i));
163 }
164 +
165 + if (csi2_track_errors)
166 + csi2_isr_handle_errors(csi2, status);
167 }
168
169 void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
170 @@ -277,6 +389,9 @@ void csi2_stop_channel(struct csi2_devic
171
172 void csi2_open_rx(struct csi2_device *csi2)
173 {
174 + csi2_reg_write(csi2, CSI2_IRQ_MASK,
175 + csi2_track_errors ? CSI2_IRQ_MASK_IRQ_ALL : 0);
176 +
177 dphy_start(&csi2->dphy);
178
179 csi2_reg_write(csi2, CSI2_CTRL,
180 @@ -286,6 +401,8 @@ void csi2_open_rx(struct csi2_device *cs
181 void csi2_close_rx(struct csi2_device *csi2)
182 {
183 dphy_stop(&csi2->dphy);
184 +
185 + csi2_reg_write(csi2, CSI2_IRQ_MASK, 0);
186 }
187
188 static struct csi2_device *to_csi2_device(struct v4l2_subdev *subdev)
189 @@ -398,11 +515,17 @@ int csi2_init(struct csi2_device *csi2,
190 {
191 unsigned int i, ret;
192
193 + spin_lock_init(&csi2->errors_lock);
194 +
195 csi2->dphy.dev = csi2->v4l2_dev->dev;
196 dphy_probe(&csi2->dphy);
197
198 debugfs_create_file("csi2_regs", 0444, debugfs, csi2, &csi2_regs_fops);
199
200 + if (csi2_track_errors)
201 + debugfs_create_file("csi2_errors", 0444, debugfs, csi2,
202 + &csi2_errors_fops);
203 +
204 for (i = 0; i < CSI2_NUM_CHANNELS * 2; i++)
205 csi2->pad[i].flags = i < CSI2_NUM_CHANNELS ?
206 MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
207 --- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
208 +++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
209 @@ -17,6 +17,8 @@
210
211 #define CSI2_NUM_CHANNELS 4
212
213 +#define DISCARDS_TABLE_NUM_VCS 4
214 +
215 enum csi2_mode {
216 CSI2_MODE_NORMAL,
217 CSI2_MODE_REMAP,
218 @@ -37,6 +39,14 @@ struct csi2_cfg {
219 u32 buffer_size;
220 };
221
222 +enum discards_table_index {
223 + DISCARDS_TABLE_OVERFLOW = 0,
224 + DISCARDS_TABLE_LENGTH_LIMIT,
225 + DISCARDS_TABLE_UNMATCHED,
226 + DISCARDS_TABLE_INACTIVE,
227 + DISCARDS_TABLE_NUM_ENTRIES,
228 +};
229 +
230 struct csi2_device {
231 /* Parent V4l2 device */
232 struct v4l2_device *v4l2_dev;
233 @@ -53,6 +63,12 @@ struct csi2_device {
234
235 struct media_pad pad[CSI2_NUM_CHANNELS * 2];
236 struct v4l2_subdev sd;
237 +
238 + /* lock for csi2 errors counters */
239 + spinlock_t errors_lock;
240 + u32 overflows;
241 + u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES];
242 + u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
243 };
244
245 void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci);