/* * Copyright (C) 2006 Wolfgang Grandegger, * Copyright (C) 2005 Marc Kleine-Budde, Pengutronix * Copyright (C) 2006 Andrey Volkov, Varma Electronics * * This program is free software; you can redistribute it and/or modify * it under the terms of the version 2 of the GNU General Public License * as published by the Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "rtcan_dev.h" #include "rtcan_raw.h" #include "rtcan_internal.h" #ifdef CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD #define RTCAN_MAX_TSEG1 15 #define RTCAN_MAX_TSEG2 7 /* * Calculate standard bit-time values for odd bitrates. * Most parts of this code is from Arnaud Westenberg */ static int rtcan_calc_bit_time(struct rtcan_device *dev, can_baudrate_t rate, struct can_bittime_std *bit_time) { int best_error = 1000000000; int error; int best_tseg=0, best_brp=0, best_rate=0, brp=0; int tseg=0, tseg1=0, tseg2=0; int clock = dev->can_sys_clock; int sjw = 0; int sampl_pt = 90; /* some heuristic specials */ if (rate > ((1000000 + 500000) / 2)) sampl_pt = 75; if (rate < ((12500 + 10000) / 2)) sampl_pt = 75; if (rate < ((100000 + 125000) / 2)) sjw = 1; /* tseg even = round down, odd = round up */ for (tseg = (0 + 0 + 2) * 2; tseg <= (RTCAN_MAX_TSEG2 + RTCAN_MAX_TSEG1 + 2) * 2 + 1; tseg++) { brp = clock / ((1 + tseg / 2) * rate) + tseg % 2; if ((brp == 0) || (brp > 64)) continue; error = rate - clock / (brp * (1 + tseg / 2)); if (error < 0) error = -error; if (error <= best_error) { best_error = error; best_tseg = tseg/2; best_brp = brp - 1; best_rate = clock / (brp * (1 + tseg / 2)); } } if (best_error && (rate / best_error < 10)) { RTCAN_RTDM_DBG("%s: bitrate %d is not possible with %d Hz clock\n", dev->name, rate, clock); return -EDOM; } tseg2 = best_tseg - (sampl_pt * (best_tseg + 1)) / 100; if (tseg2 < 0) tseg2 = 0; if (tseg2 > RTCAN_MAX_TSEG2) tseg2 = RTCAN_MAX_TSEG2; tseg1 = best_tseg - tseg2 - 2; if (tseg1 > RTCAN_MAX_TSEG1) { tseg1 = RTCAN_MAX_TSEG1; tseg2 = best_tseg-tseg1-2; } bit_time->brp = best_brp + 1; bit_time->prop_seg = 0; bit_time->phase_seg1 = tseg1 + 1; bit_time->phase_seg2 = tseg2 + 1; bit_time->sjw = sjw + 1; bit_time->sam = 0; return 0; } #else /* !CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD */ /* This is the bit-time calculation method from the Linux kernel */ #define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ static int can_update_spt(const struct can_bittiming_const *btc, unsigned int sampl_pt, unsigned int tseg, unsigned int *tseg1, unsigned int *tseg2) { *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000; *tseg2 = clamp(*tseg2, btc->tseg2_min, btc->tseg2_max); *tseg1 = tseg - *tseg2; if (*tseg1 > btc->tseg1_max) { *tseg1 = btc->tseg1_max; *tseg2 = tseg - *tseg1; } return 1000 * (tseg + 1 - *tseg2) / (tseg + 1); } static int rtcan_calc_bit_time(struct rtcan_device *dev, can_baudrate_t bitrate, struct can_bittime_std *bt) { const struct can_bittiming_const *btc = dev->bittiming_const; long rate; /* current bitrate */ long rate_error;/* difference between current and target value */ long best_rate_error = 1000000000; int spt; /* current sample point in thousandth */ int spt_error; /* difference between current and target value */ int best_spt_error = 1000; int sampl_pt; /* target sample point */ int best_tseg = 0, best_brp = 0; /* current best values for tseg and brp */ unsigned int brp, tsegall, tseg, tseg1, tseg2; u64 v64; if (!dev->bittiming_const) return -ENOTSUPP; /* Use CIA recommended sample points */ if (bitrate > 800000) sampl_pt = 750; else if (bitrate > 500000) sampl_pt = 800; else sampl_pt = 875; /* tseg even = round down, odd = round up */ for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1; tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) { tsegall = 1 + tseg / 2; /* Compute all possible tseg choices (tseg=tseg1+tseg2) */ brp = dev->can_sys_clock / (tsegall * bitrate) + tseg % 2; /* chose brp step which is possible in system */ brp = (brp / btc->brp_inc) * btc->brp_inc; if ((brp < btc->brp_min) || (brp > btc->brp_max)) continue; rate = dev->can_sys_clock / (brp * tsegall); rate_error = abs((long)(bitrate - rate)); /* tseg brp biterror */ if (rate_error > best_rate_error) continue; /* reset sample point error if we have a better bitrate */ if (rate_error < best_rate_error) best_spt_error = 1000; spt = can_update_spt(btc, sampl_pt, tseg / 2, &tseg1, &tseg2); spt_error = abs((long)(sampl_pt - spt)); if (spt_error > best_spt_error) continue; best_spt_error = spt_error; best_rate_error = rate_error; best_tseg = tseg / 2; best_brp = brp; if (rate_error == 0 && spt_error == 0) break; } if (best_rate_error) { /* Error in one-tenth of a percent */ rate_error = (best_rate_error * 1000) / bitrate; if (rate_error > CAN_CALC_MAX_ERROR) { rtcandev_err(dev, "bitrate error %ld.%ld%% too high\n", rate_error / 10, rate_error % 10); return -EDOM; } else { rtcandev_warn(dev, "bitrate error %ld.%ld%%\n", rate_error / 10, rate_error % 10); } } /* real sample point */ sampl_pt = can_update_spt(btc, sampl_pt, best_tseg, &tseg1, &tseg2); v64 = (u64)best_brp * 1000000000UL; do_div(v64, dev->can_sys_clock); bt->prop_seg = tseg1 / 2; bt->phase_seg1 = tseg1 - bt->prop_seg; bt->phase_seg2 = tseg2; bt->sjw = 1; bt->sam = 0; bt->brp = best_brp; /* real bit-rate */ rate = dev->can_sys_clock / (bt->brp * (tseg1 + tseg2 + 1)); rtcandev_dbg(dev, "real bitrate %ld, sampling point %d.%d%%\n", rate, sampl_pt/10, sampl_pt%10); return 0; } #endif /* CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD */ static inline int rtcan_raw_ioctl_dev_get(struct rtcan_device *dev, int request, struct can_ifreq *ifr) { rtdm_lockctx_t lock_ctx; switch (request) { case SIOCGIFINDEX: ifr->ifr_ifindex = dev->ifindex; break; case SIOCGCANSTATE: rtdm_lock_get_irqsave(&dev->device_lock, lock_ctx); if (dev->do_get_state) dev->state = dev->do_get_state(dev); ifr->ifr_ifru.state = dev->state; rtdm_lock_put_irqrestore(&dev->device_lock, lock_ctx); break; case SIOCGCANCTRLMODE: ifr->ifr_ifru.ctrlmode = dev->ctrl_mode; break; case SIOCGCANBAUDRATE: ifr->ifr_ifru.baudrate = dev->baudrate; break; case SIOCGCANCUSTOMBITTIME: ifr->ifr_ifru.bittime = dev->bit_time; break; } return 0; } static inline int rtcan_raw_ioctl_dev_set(struct rtcan_device *dev, int request, struct can_ifreq *ifr) { rtdm_lockctx_t lock_ctx; int ret = 0, started = 0; struct can_bittime bit_time, *bt; switch (request) { case SIOCSCANBAUDRATE: if (!dev->do_set_bit_time) return 0; ret = rtcan_calc_bit_time(dev, ifr->ifr_ifru.baudrate, &bit_time.std); if (ret) break; bit_time.type = CAN_BITTIME_STD; break; } rtdm_lock_get_irqsave(&dev->device_lock, lock_ctx); if (dev->do_get_state) dev->state = dev->do_get_state(dev); switch (request) { case SIOCSCANCTRLMODE: case SIOCSCANBAUDRATE: case SIOCSCANCUSTOMBITTIME: if ((started = CAN_STATE_OPERATING(dev->state))) { if ((ret = dev->do_set_mode(dev, CAN_MODE_STOP, &lock_ctx))) goto out; } break; } switch (request) { case SIOCSCANMODE: if (dev->do_set_mode && !(ifr->ifr_ifru.mode == CAN_MODE_START && CAN_STATE_OPERATING(dev->state))) ret = dev->do_set_mode(dev, ifr->ifr_ifru.mode, &lock_ctx); break; case SIOCSCANCTRLMODE: dev->ctrl_mode = ifr->ifr_ifru.ctrlmode; break; case SIOCSCANBAUDRATE: ret = dev->do_set_bit_time(dev, &bit_time, &lock_ctx); if (!ret) { dev->baudrate = ifr->ifr_ifru.baudrate; dev->bit_time = bit_time; } break; case SIOCSCANCUSTOMBITTIME: bt = &ifr->ifr_ifru.bittime; ret = dev->do_set_bit_time(dev, bt, &lock_ctx); if (!ret) { dev->bit_time = *bt; if (bt->type == CAN_BITTIME_STD && bt->std.brp) dev->baudrate = (dev->can_sys_clock / (bt->std.brp * (1 + bt->std.prop_seg + bt->std.phase_seg1 + bt->std.phase_seg2))); else dev->baudrate = CAN_BAUDRATE_UNKNOWN; } break; default: ret = -EOPNOTSUPP; break; } out: if (started) dev->do_set_mode(dev, CAN_MODE_START, &lock_ctx); rtdm_lock_put_irqrestore(&dev->device_lock, lock_ctx); return ret; } int rtcan_raw_ioctl_dev(struct rtdm_fd *fd, int request, void *arg) { struct can_ifreq *ifr; int ret = 0, get = 0; union { /* * We need to deal with callers still passing struct ifreq * instead of can_ifreq, which might have a larger memory * footprint (but can't be smaller though). Field offsets * will be the same regardless. */ struct ifreq ifr_legacy; struct can_ifreq ifr_can; } ifr_buf; struct rtcan_device *dev; switch (request) { case SIOCGIFINDEX: case SIOCGCANSTATE: case SIOCGCANBAUDRATE: case SIOCGCANCUSTOMBITTIME: get = 1; fallthrough; case SIOCSCANMODE: case SIOCSCANCTRLMODE: case SIOCSCANBAUDRATE: case SIOCSCANCUSTOMBITTIME: if (rtdm_fd_is_user(fd)) { /* Copy struct can_ifreq from userspace */ if (!rtdm_read_user_ok(fd, arg, sizeof(struct can_ifreq)) || rtdm_copy_from_user(fd, &ifr_buf, arg, sizeof(struct can_ifreq))) return -EFAULT; ifr = &ifr_buf.ifr_can; } else ifr = (struct can_ifreq *)arg; /* Get interface index and data */ dev = rtcan_dev_get_by_name(ifr->ifr_name); if (dev == NULL) return -ENODEV; if (get) { ret = rtcan_raw_ioctl_dev_get(dev, request, ifr); rtcan_dev_dereference(dev); if (ret == 0 && rtdm_fd_is_user(fd)) { /* * Since we yet tested if user memory is rw safe, * we can copy to user space directly. */ if (rtdm_copy_to_user(fd, arg, ifr, sizeof(struct can_ifreq))) return -EFAULT; } } else { ret = rtcan_raw_ioctl_dev_set(dev, request, ifr); rtcan_dev_dereference(dev); } break; default: ret = -EOPNOTSUPP; break; } return ret; } #ifdef CONFIG_XENO_DRIVERS_CAN_BUS_ERR void __rtcan_raw_enable_bus_err(struct rtcan_socket *sock) { int i, begin, end; struct rtcan_device *dev; rtdm_lockctx_t lock_ctx; int ifindex = atomic_read(&sock->ifindex); if (ifindex) { begin = ifindex; end = ifindex; } else { begin = 1; end = RTCAN_MAX_DEVICES; } for (i = begin; i <= end; i++) { if ((dev = rtcan_dev_get_by_index(i)) == NULL) continue; if (dev->do_enable_bus_err) { rtdm_lock_get_irqsave(&dev->device_lock, lock_ctx); dev->do_enable_bus_err(dev); rtdm_lock_put_irqrestore(&dev->device_lock, lock_ctx); } rtcan_dev_dereference(dev); } } #endif /* CONFIG_XENO_DRIVERS_CAN_BUS_ERR*/