1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
* Copyright (C) 2006 Andrey Volkov, Varma Electronics
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
* Copyright (c) 2025 Vincent Mailhol <mailhol@kernel.org>
*/
#include <linux/can/dev.h>
void can_sjw_set_default(struct can_bittiming *bt)
{
if (bt->sjw)
return;
/* If user space provides no sjw, use sane default of phase_seg2 / 2 */
bt->sjw = max(1U, min(bt->phase_seg1, bt->phase_seg2 / 2));
}
int can_sjw_check(const struct net_device *dev, const struct can_bittiming *bt,
const struct can_bittiming_const *btc, struct netlink_ext_ack *extack)
{
if (bt->sjw > btc->sjw_max) {
NL_SET_ERR_MSG_FMT(extack, "sjw: %u greater than max sjw: %u",
bt->sjw, btc->sjw_max);
return -EINVAL;
}
if (bt->sjw > bt->phase_seg1) {
NL_SET_ERR_MSG_FMT(extack,
"sjw: %u greater than phase-seg1: %u",
bt->sjw, bt->phase_seg1);
return -EINVAL;
}
if (bt->sjw > bt->phase_seg2) {
NL_SET_ERR_MSG_FMT(extack,
"sjw: %u greater than phase-seg2: %u",
bt->sjw, bt->phase_seg2);
return -EINVAL;
}
return 0;
}
/* Checks the validity of the specified bit-timing parameters prop_seg,
* phase_seg1, phase_seg2 and sjw and tries to determine the bitrate
* prescaler value brp. You can find more information in the header
* file linux/can/netlink.h.
*/
static int can_fixup_bittiming(const struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc,
struct netlink_ext_ack *extack)
{
const unsigned int tseg1 = bt->prop_seg + bt->phase_seg1;
const struct can_priv *priv = netdev_priv(dev);
u64 brp64;
int err;
if (tseg1 < btc->tseg1_min) {
NL_SET_ERR_MSG_FMT(extack, "prop-seg + phase-seg1: %u less than tseg1-min: %u",
tseg1, btc->tseg1_min);
return -EINVAL;
}
if (tseg1 > btc->tseg1_max) {
NL_SET_ERR_MSG_FMT(extack, "prop-seg + phase-seg1: %u greater than tseg1-max: %u",
tseg1, btc->tseg1_max);
return -EINVAL;
}
if (bt->phase_seg2 < btc->tseg2_min) {
NL_SET_ERR_MSG_FMT(extack, "phase-seg2: %u less than tseg2-min: %u",
bt->phase_seg2, btc->tseg2_min);
return -EINVAL;
}
if (bt->phase_seg2 > btc->tseg2_max) {
NL_SET_ERR_MSG_FMT(extack, "phase-seg2: %u greater than tseg2-max: %u",
bt->phase_seg2, btc->tseg2_max);
return -EINVAL;
}
can_sjw_set_default(bt);
err = can_sjw_check(dev, bt, btc, extack);
if (err)
return err;
brp64 = (u64)priv->clock.freq * (u64)bt->tq;
if (btc->brp_inc > 1)
do_div(brp64, btc->brp_inc);
brp64 += 500000000UL - 1;
do_div(brp64, 1000000000UL); /* the practicable BRP */
if (btc->brp_inc > 1)
brp64 *= btc->brp_inc;
bt->brp = (u32)brp64;
if (bt->brp < btc->brp_min) {
NL_SET_ERR_MSG_FMT(extack, "resulting brp: %u less than brp-min: %u",
bt->brp, btc->brp_min);
return -EINVAL;
}
if (bt->brp > btc->brp_max) {
NL_SET_ERR_MSG_FMT(extack, "resulting brp: %u greater than brp-max: %u",
bt->brp, btc->brp_max);
return -EINVAL;
}
bt->bitrate = priv->clock.freq / (bt->brp * can_bit_time(bt));
bt->sample_point = ((CAN_SYNC_SEG + tseg1) * 1000) / can_bit_time(bt);
bt->tq = DIV_U64_ROUND_CLOSEST(mul_u32_u32(bt->brp, NSEC_PER_SEC),
priv->clock.freq);
return 0;
}
/* Checks the validity of predefined bitrate settings */
static int
can_validate_bitrate(const struct net_device *dev, const struct can_bittiming *bt,
const u32 *bitrate_const,
const unsigned int bitrate_const_cnt,
struct netlink_ext_ack *extack)
{
unsigned int i;
for (i = 0; i < bitrate_const_cnt; i++) {
if (bt->bitrate == bitrate_const[i])
return 0;
}
NL_SET_ERR_MSG_FMT(extack, "bitrate %u bps not supported",
bt->brp);
return -EINVAL;
}
int can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc,
const u32 *bitrate_const,
const unsigned int bitrate_const_cnt,
struct netlink_ext_ack *extack)
{
/* Depending on the given can_bittiming parameter structure the CAN
* timing parameters are calculated based on the provided bitrate OR
* alternatively the CAN timing parameters (tq, prop_seg, etc.) are
* provided directly which are then checked and fixed up.
*/
if (!bt->tq && bt->bitrate && btc)
return can_calc_bittiming(dev, bt, btc, extack);
if (bt->tq && !bt->bitrate && btc)
return can_fixup_bittiming(dev, bt, btc, extack);
if (!bt->tq && bt->bitrate && bitrate_const)
return can_validate_bitrate(dev, bt, bitrate_const,
bitrate_const_cnt, extack);
return -EINVAL;
}
int can_validate_pwm_bittiming(const struct net_device *dev,
const struct can_pwm *pwm,
struct netlink_ext_ack *extack)
{
const struct can_priv *priv = netdev_priv(dev);
u32 xl_bit_time_tqmin = can_bit_time_tqmin(&priv->xl.data_bittiming);
u32 nom_bit_time_tqmin = can_bit_time_tqmin(&priv->bittiming);
u32 pwms_ns = can_tqmin_to_ns(pwm->pwms, priv->clock.freq);
u32 pwml_ns = can_tqmin_to_ns(pwm->pwml, priv->clock.freq);
if (pwms_ns + pwml_ns > CAN_PWM_NS_MAX) {
NL_SET_ERR_MSG_FMT(extack,
"The PWM symbol duration: %u ns may not exceed %u ns",
pwms_ns + pwml_ns, CAN_PWM_NS_MAX);
return -EINVAL;
}
if (pwms_ns < CAN_PWM_DECODE_NS) {
NL_SET_ERR_MSG_FMT(extack,
"PWMS: %u ns shall be at least %u ns",
pwms_ns, CAN_PWM_DECODE_NS);
return -EINVAL;
}
if (pwm->pwms >= pwm->pwml) {
NL_SET_ERR_MSG_FMT(extack,
"PWMS: %u tqmin shall be smaller than PWML: %u tqmin",
pwm->pwms, pwm->pwml);
return -EINVAL;
}
if (pwml_ns - pwms_ns < 2 * CAN_PWM_DECODE_NS) {
NL_SET_ERR_MSG_FMT(extack,
"At least %u ns shall separate PWMS: %u ns from PMWL: %u ns",
2 * CAN_PWM_DECODE_NS, pwms_ns, pwml_ns);
return -EINVAL;
}
if (xl_bit_time_tqmin % (pwm->pwms + pwm->pwml) != 0) {
NL_SET_ERR_MSG_FMT(extack,
"PWM duration: %u tqmin does not divide XL's bit time: %u tqmin",
pwm->pwms + pwm->pwml, xl_bit_time_tqmin);
return -EINVAL;
}
if (pwm->pwmo >= pwm->pwms + pwm->pwml) {
NL_SET_ERR_MSG_FMT(extack,
"PWMO: %u tqmin can not be greater than PWMS + PWML: %u tqmin",
pwm->pwmo, pwm->pwms + pwm->pwml);
return -EINVAL;
}
if (nom_bit_time_tqmin % (pwm->pwms + pwm->pwml) != pwm->pwmo) {
NL_SET_ERR_MSG_FMT(extack,
"Can not assemble nominal bit time: %u tqmin out of PWMS + PMWL and PWMO",
nom_bit_time_tqmin);
return -EINVAL;
}
return 0;
}
|