Commit 05bbebf8 authored by Federico Vaga's avatar Federico Vaga

Merge branch 'release/v6.0.0.rc1'

parents 2f6f18d7 8ce74dac
......@@ -26,6 +26,13 @@ cppcheck:
script:
- make -C software cppcheck
flawfinder:
stage: static-analysis
image:
name: gitlab-registry.cern.ch/coht/common-containers/static-analysis:latest
script:
- make -C software/tools flawfinder
documentation:
stage: build
image:
......
......@@ -6,6 +6,26 @@
Changelog
=========
6.0.0 - 2021-09-07
==================
Added
-----
- hdl: configurable auto byte swap in hardware, useful for SVEC to reduce software complexity
- hdl,sw: DMA data is always little-endian
- sw: software version validation against FPGA version
- bld: flawfinder check on software tools
Changed
-------
- sw: offsets are not anymore in uV but they are just raw values
Fixed
-----
- sw: security fixes detected by flawfinder
- sw: fixes detected by checkpatch.pl
- sw: style fixes detected by checkpatch.pl
- sw: improve compatibility with newer ( > 3.10) Linux kernel versions
5.0.4 - 2021-07-09
==================
Fixed
......
......@@ -377,7 +377,7 @@ Mezzanine 1-wire Master
~~~~~~~~~~~~~~~~~~~~~~~
.. note::
FIXME talk about the themometer core in general-cores
FIXME talk about the thermometer core in general-cores
Mezzanine I2C Master
~~~~~~~~~~~~~~~~~~~~
......@@ -470,8 +470,8 @@ register enables the sampling clock (Si570 chip), and the other
internal components. Also, in order to use the input offset DACs, the
``OFFSET_DAC_CLR_N`` field must be set to one.
The field ``MAN_BITSLIP`` allows to ’manually’ control the ADC data
alignment in the de-serialiser. The fields ``TRIG_LED`` and
The field ``SERDES_CALIB`` allows to ’manually’ restart the timing
calibration and alignment in the de-serialiser. The fields ``TRIG_LED`` and
``ACQ_LED`` allows to control the FMC front panel LEDs. Those four
fields are for test purpose only and must stay zero in normal
operation.
......@@ -1047,6 +1047,9 @@ state.
The start of an acquisition is prohibited if either the number of
shots or the number of post-trigger samples is equal to zero.
.. note::
Acquired data are always stored to be read in little endian.
Single-shot Mode
----------------
......
......@@ -195,16 +195,16 @@ chN-50ohm-term
turn on the termination resistor. Default is 0.
chN-offset
The user offset is an integer value in the range [-5000000,5000000], and
it represents microvolts. The offset represents the center-scale
of conversion for the input channel. Internally, a DAC is used to
The user offset is an integer value in the range [-5.000V, +4.999V]. It
follows the DAC data format, so the range is describe by 16bits from 0x0000
(-5.000V) to 0xFFFF (+4.999V). Internally, a DAC is used to
generate the requested voltage, which is then subtracted from the
input signal. DAC values are corrected according to the
calibration values retrieved from the FMC EEPROM. For this reason,
the offset may saturate at values less than +/- 5V.
chN-offset-zero
The necessary offset to to bring the signal to 0 in microvolts (it must be
The necessary offset to to bring the signal to 0 in Volts (it must be
withing the range of chN-offset).
chN-vref
......
......@@ -41,11 +41,9 @@ memory-map:
range: 3
description: Offset DACs clear (active low)
- field:
name: man_bitslip
name: serdes_calib
range: 4
description: Manual serdes bitslip (ignore on read)
x-hdl:
type: wire
description: Initial serdes calibration
- field:
name: trig_led
range: 6
......
......@@ -15,7 +15,7 @@ package fmc_adc_100ms_csr_pkg is
ctl_fsm_cmd : std_logic_vector(1 downto 0);
ctl_fmc_clk_oe : std_logic;
ctl_offset_dac_clr_n : std_logic;
ctl_man_bitslip : std_logic;
ctl_serdes_calib : std_logic;
ctl_trig_led : std_logic;
ctl_acq_led : std_logic;
ctl_clear_trig_stat : std_logic;
......@@ -47,7 +47,6 @@ package fmc_adc_100ms_csr_pkg is
type t_fmc_adc_100ms_csr_slave_out is record
ctl_fsm_cmd : std_logic_vector(1 downto 0);
ctl_man_bitslip : std_logic;
ctl_clear_trig_stat : std_logic;
ctl_calib_apply : std_logic;
sta_fsm : std_logic_vector(2 downto 0);
......@@ -120,6 +119,7 @@ architecture syn of fmc_adc_100ms_csr is
signal wb_wip : std_logic;
signal ctl_fmc_clk_oe_reg : std_logic;
signal ctl_offset_dac_clr_n_reg : std_logic;
signal ctl_serdes_calib_reg : std_logic;
signal ctl_trig_led_reg : std_logic;
signal ctl_acq_led_reg : std_logic;
signal ctl_wreq : std_logic;
......@@ -250,7 +250,7 @@ begin
fmc_adc_100ms_csr_o.ctl_fsm_cmd <= wr_dat_d0(1 downto 0);
fmc_adc_100ms_csr_o.ctl_fmc_clk_oe <= ctl_fmc_clk_oe_reg;
fmc_adc_100ms_csr_o.ctl_offset_dac_clr_n <= ctl_offset_dac_clr_n_reg;
fmc_adc_100ms_csr_o.ctl_man_bitslip <= wr_dat_d0(4);
fmc_adc_100ms_csr_o.ctl_serdes_calib <= ctl_serdes_calib_reg;
fmc_adc_100ms_csr_o.ctl_trig_led <= ctl_trig_led_reg;
fmc_adc_100ms_csr_o.ctl_acq_led <= ctl_acq_led_reg;
fmc_adc_100ms_csr_o.ctl_clear_trig_stat <= wr_dat_d0(8);
......@@ -260,6 +260,7 @@ begin
if rst_n_i = '0' then
ctl_fmc_clk_oe_reg <= '0';
ctl_offset_dac_clr_n_reg <= '0';
ctl_serdes_calib_reg <= '0';
ctl_trig_led_reg <= '0';
ctl_acq_led_reg <= '0';
ctl_wack <= '0';
......@@ -267,6 +268,7 @@ begin
if ctl_wreq = '1' then
ctl_fmc_clk_oe_reg <= wr_dat_d0(2);
ctl_offset_dac_clr_n_reg <= wr_dat_d0(3);
ctl_serdes_calib_reg <= wr_dat_d0(4);
ctl_trig_led_reg <= wr_dat_d0(6);
ctl_acq_led_reg <= wr_dat_d0(7);
end if;
......@@ -624,7 +626,7 @@ begin
end process;
-- Process for read requests.
process (rd_adr_d0, rd_req_d0, fmc_adc_100ms_csr_i.ctl_fsm_cmd, ctl_fmc_clk_oe_reg, ctl_offset_dac_clr_n_reg, fmc_adc_100ms_csr_i.ctl_man_bitslip, ctl_trig_led_reg, ctl_acq_led_reg, fmc_adc_100ms_csr_i.ctl_clear_trig_stat, fmc_adc_100ms_csr_i.ctl_calib_apply, fmc_adc_100ms_csr_i.sta_fsm, fmc_adc_100ms_csr_i.sta_serdes_pll, fmc_adc_100ms_csr_i.sta_serdes_synced, fmc_adc_100ms_csr_i.sta_acq_cfg, fmc_adc_100ms_csr_i.sta_fmc_nr, fmc_adc_100ms_csr_i.sta_calib_busy, fmc_adc_100ms_csr_i.trig_stat_ext, fmc_adc_100ms_csr_i.trig_stat_sw, fmc_adc_100ms_csr_i.trig_stat_time, fmc_adc_100ms_csr_i.trig_stat_ch1, fmc_adc_100ms_csr_i.trig_stat_ch2, fmc_adc_100ms_csr_i.trig_stat_ch3, fmc_adc_100ms_csr_i.trig_stat_ch4, trig_en_ext_reg, fmc_adc_100ms_csr_i.trig_en_sw, trig_en_time_reg, fmc_adc_100ms_csr_i.trig_en_aux_time, trig_en_ch1_reg, trig_en_ch2_reg, trig_en_ch3_reg, trig_en_ch4_reg, trig_pol_ext_reg, trig_pol_ch1_reg, trig_pol_ch2_reg, trig_pol_ch3_reg, trig_pol_ch4_reg, ext_trig_dly_reg, shots_nbr_reg, fmc_adc_100ms_csr_i.shots_remain, fmc_adc_100ms_csr_i.multi_depth, fmc_adc_100ms_csr_i.trig_pos, fmc_adc_100ms_csr_i.fs_freq, downsample_reg, pre_samples_reg, post_samples_reg, fmc_adc_100ms_csr_i.samples_cnt, fmc_adc_ch1_i.dat, fmc_adc_ch1_rack, fmc_adc_ch2_i.dat, fmc_adc_ch2_rack, fmc_adc_ch3_i.dat, fmc_adc_ch3_rack, fmc_adc_ch4_i.dat, fmc_adc_ch4_rack) begin
process (rd_adr_d0, rd_req_d0, fmc_adc_100ms_csr_i.ctl_fsm_cmd, ctl_fmc_clk_oe_reg, ctl_offset_dac_clr_n_reg, ctl_serdes_calib_reg, ctl_trig_led_reg, ctl_acq_led_reg, fmc_adc_100ms_csr_i.ctl_clear_trig_stat, fmc_adc_100ms_csr_i.ctl_calib_apply, fmc_adc_100ms_csr_i.sta_fsm, fmc_adc_100ms_csr_i.sta_serdes_pll, fmc_adc_100ms_csr_i.sta_serdes_synced, fmc_adc_100ms_csr_i.sta_acq_cfg, fmc_adc_100ms_csr_i.sta_fmc_nr, fmc_adc_100ms_csr_i.sta_calib_busy, fmc_adc_100ms_csr_i.trig_stat_ext, fmc_adc_100ms_csr_i.trig_stat_sw, fmc_adc_100ms_csr_i.trig_stat_time, fmc_adc_100ms_csr_i.trig_stat_ch1, fmc_adc_100ms_csr_i.trig_stat_ch2, fmc_adc_100ms_csr_i.trig_stat_ch3, fmc_adc_100ms_csr_i.trig_stat_ch4, trig_en_ext_reg, fmc_adc_100ms_csr_i.trig_en_sw, trig_en_time_reg, fmc_adc_100ms_csr_i.trig_en_aux_time, trig_en_ch1_reg, trig_en_ch2_reg, trig_en_ch3_reg, trig_en_ch4_reg, trig_pol_ext_reg, trig_pol_ch1_reg, trig_pol_ch2_reg, trig_pol_ch3_reg, trig_pol_ch4_reg, ext_trig_dly_reg, shots_nbr_reg, fmc_adc_100ms_csr_i.shots_remain, fmc_adc_100ms_csr_i.multi_depth, fmc_adc_100ms_csr_i.trig_pos, fmc_adc_100ms_csr_i.fs_freq, downsample_reg, pre_samples_reg, post_samples_reg, fmc_adc_100ms_csr_i.samples_cnt, fmc_adc_ch1_i.dat, fmc_adc_ch1_rack, fmc_adc_ch2_i.dat, fmc_adc_ch2_rack, fmc_adc_ch3_i.dat, fmc_adc_ch3_rack, fmc_adc_ch4_i.dat, fmc_adc_ch4_rack) begin
-- By default ack read requests
rd_dat_d0 <= (others => 'X');
fmc_adc_ch1_re <= '0';
......@@ -640,7 +642,7 @@ begin
rd_dat_d0(1 downto 0) <= fmc_adc_100ms_csr_i.ctl_fsm_cmd;
rd_dat_d0(2) <= ctl_fmc_clk_oe_reg;
rd_dat_d0(3) <= ctl_offset_dac_clr_n_reg;
rd_dat_d0(4) <= fmc_adc_100ms_csr_i.ctl_man_bitslip;
rd_dat_d0(4) <= ctl_serdes_calib_reg;
rd_dat_d0(5) <= '0';
rd_dat_d0(6) <= ctl_trig_led_reg;
rd_dat_d0(7) <= ctl_acq_led_reg;
......
......@@ -50,8 +50,8 @@ entity ltc2174_2l16b_receiver is
adc_outb_n_i : in std_logic_vector(3 downto 0);
-- Async reset input (active high) for iserdes
serdes_arst_i : in std_logic := '0';
-- Manual bitslip command (optional)
serdes_bslip_i : in std_logic := '0';
-- Enable serdes calibration (start with an initial calibration)
serdes_calib_i : in std_logic := '0';
-- SERDES BUFPLL lock status flag
-- (used when g_USE_PLL=TRUE, otherwise it is tied to '1')
serdes_locked_o : out std_logic;
......@@ -70,26 +70,29 @@ end entity ltc2174_2l16b_receiver;
architecture arch of ltc2174_2l16b_receiver is
signal adc_dco : std_logic;
signal adc_fr : std_logic;
signal adc_outa : std_logic_vector(3 downto 0);
signal adc_outb : std_logic_vector(3 downto 0);
signal adc_out, adc_out_dly_m, adc_out_dly_s : std_logic_vector(8 downto 0);
signal serdes_incdec, serdes_valid : std_logic_vector(8 downto 0);
signal clk_serdes_p : std_logic;
signal clk_serdes_n : std_logic;
signal clk_div_buf : std_logic;
signal serdes_strobe : std_logic := '0';
signal serdes_auto_bslip : std_logic := '0';
signal serdes_bitslip : std_logic := '0';
signal serdes_synced : std_logic := '0';
signal serdes_m2s_shift : std_logic_vector(8 downto 0) := (others => '0');
signal serdes_s2m_shift : std_logic_vector(8 downto 0) := (others => '0');
signal serdes_serial_in : std_logic_vector(8 downto 0) := (others => '0');
signal serdes_out_fr : std_logic_vector(7 downto 0) := (others => '0');
signal serdes_out_fr : std_logic_vector(6 downto 0) := (others => '0');
signal bitslip_sreg : unsigned(7 downto 0) := to_unsigned(1, 8);
type serdes_array is array (0 to 8) of std_logic_vector(7 downto 0);
type serdes_array is array (0 to 8) of std_logic_vector(6 downto 0);
signal serdes_parallel_out : serdes_array := (others => (others => '0'));
signal iodelay_cal_s, iodelay_cal_m : std_logic;
signal iodelay_rst : std_logic;
signal iodelay_busy : std_logic;
signal iodelay_recal : std_logic;
signal iodelay_busy_m, iodelay_busy_s : std_logic_vector(8 downto 0);
-- used to select the data rate of the ISERDES blocks
function f_data_rate_sel (
constant SDR : boolean)
......@@ -128,10 +131,10 @@ begin -- architecture arch
port map (
I => adc_fr_p_i,
IB => adc_fr_n_i,
O => adc_fr);
O => adc_out(8));
-- ADC data
gen_adc_data_buf : for I in 0 to 3 generate
cmp_adc_outa_buf : IBUFDS
generic map (
DIFF_TERM => TRUE,
......@@ -140,7 +143,7 @@ begin -- architecture arch
port map (
I => adc_outa_p_i(i),
IB => adc_outa_n_i(i),
O => adc_outa(i));
O => adc_out(2 * i + 1));
cmp_adc_outb_buf : IBUFDS
generic map (
......@@ -150,10 +153,161 @@ begin -- architecture arch
port map (
I => adc_outb_p_i(i),
IB => adc_outb_n_i(i),
O => adc_outb(i));
O => adc_out(2 * i));
end generate gen_adc_data_buf;
-- IDELAY (master and slave) on data and frame inputs.
gen_adc_idelay: for I in adc_out'range generate
signal inc, ce : std_logic;
signal phasediff : unsigned(4 downto 0);
begin
cmp_idelay_master: IODELAY2
generic map (
DATA_RATE => f_data_rate_sel(g_USE_PLL), -- <SDR>, DDR
IDELAY_VALUE => 0, -- {0 ... 255}
IDELAY2_VALUE => 0, -- {0 ... 255}
IDELAY_MODE => "NORMAL",-- NORMAL, PCI
ODELAY_VALUE => 0, -- {0 ... 255}
IDELAY_TYPE => "DIFF_PHASE_DETECTOR",-- "DEFAULT", "DIFF_PHASE_DETECTOR", "FIXED", "VARIABLE_FROM_HALF_MAX", "VARIABLE_FROM_ZERO"
COUNTER_WRAPAROUND => "WRAPAROUND", -- <STAY_AT_LIMIT>, WRAPAROUND
DELAY_SRC => "IDATAIN", -- "IO", "IDATAIN", "ODATAIN"
SERDES_MODE => "MASTER", -- <NONE>, MASTER, SLAVE
SIM_TAPDELAY_VALUE => 49) --
port map (
IDATAIN => adc_out(i), -- data from primary IOB
TOUT => open, -- tri-state signal to IOB
DOUT => open, -- output data to IOB
T => '1', -- tri-state control from OLOGIC/OSERDES2
ODATAIN => '0', -- data from OLOGIC/OSERDES2
DATAOUT => adc_out_dly_m(i), -- Output data 1 to ILOGIC/ISERDES2
DATAOUT2 => open, -- Output data 2 to ILOGIC/ISERDES2
IOCLK0 => clk_serdes_p, -- High speed clock for calibration
IOCLK1 => clk_serdes_n, -- High speed clock for calibration
CLK => clk_div_buf, -- Fabric clock (GCLK) for control signals
CAL => iodelay_cal_m, -- Calibrate control signal
INC => inc, -- Increment counter
CE => ce, -- Clock Enable
RST => iodelay_rst, -- Reset delay line
BUSY => iodelay_busy_m(i)); -- output signal indicating sync circuit has finished / calibration has finished
cmp_idelay_slave: IODELAY2
generic map (
DATA_RATE => f_data_rate_sel(g_USE_PLL), -- <SDR>, DDR
IDELAY_VALUE => 0, -- {0 ... 255}
IDELAY2_VALUE => 0, -- {0 ... 255}
IDELAY_MODE => "NORMAL",-- NORMAL, PCI
ODELAY_VALUE => 0, -- {0 ... 255}
IDELAY_TYPE => "DIFF_PHASE_DETECTOR",-- "DEFAULT", "DIFF_PHASE_DETECTOR", "FIXED", "VARIABLE_FROM_HALF_MAX", "VARIABLE_FROM_ZERO"
COUNTER_WRAPAROUND => "WRAPAROUND", -- <STAY_AT_LIMIT>, WRAPAROUND
DELAY_SRC => "IDATAIN", -- "IO", "IDATAIN", "ODATAIN"
SERDES_MODE => "SLAVE", -- <NONE>, MASTER, SLAVE
SIM_TAPDELAY_VALUE => 49) --
port map (
IDATAIN => adc_out(i), -- data from primary IOB
TOUT => open, -- tri-state signal to IOB
DOUT => open, -- output data to IOB
T => '1', -- tri-state control from OLOGIC/OSERDES2
ODATAIN => '0', -- data from OLOGIC/OSERDES2
DATAOUT => adc_out_dly_s(i), -- Output data 1 to ILOGIC/ISERDES2
DATAOUT2 => open, -- Output data 2 to ILOGIC/ISERDES2
IOCLK0 => clk_serdes_p, -- High speed clock for calibration
IOCLK1 => clk_serdes_n, -- High speed clock for calibration
CLK => clk_div_buf, -- Fabric clock (GCLK) for control signals
CAL => iodelay_cal_s, -- Calibrate control signal
INC => inc, -- Increment counter
CE => ce, -- Clock Enable
RST => iodelay_rst, -- Reset delay line
BUSY => iodelay_busy_s(i)); -- output signal indicating sync circuit has finished / calibration has finished
-- Adjust delay
process (clk_div_buf)
begin
if rising_edge(clk_div_buf) then
ce <= '0';
inc <= '0';
if serdes_calib_i = '0' or iodelay_recal = '1' then
phasediff <= "10000";
elsif serdes_valid (i) = '1' then
if serdes_incdec (i) = '1' then
if phasediff = "11111" then
ce <= '1';
inc <= '1';
phasediff <= "10000";
else
phasediff <= phasediff + 1;
end if;
else
if phasediff = "00000" then
ce <= '1';
inc <= '0';
phasediff <= "10000";
else
phasediff <= phasediff - 1;
end if;
end if;
end if;
end if;
end process;
end generate;
iodelay_busy <= '0' when iodelay_busy_s = (iodelay_busy_s'range => '0') and iodelay_busy_m = (iodelay_busy_m'range => '0') else '1';
-- IODELAY calibration
process(clk_div_buf, serdes_arst_i)
type t_state is (S_RESET, S_RSTCAL, S_RSTBUSY, S_WAIT, S_BUSY);
variable state : t_state;
variable counter : natural;
begin
if serdes_arst_i = '1' then
iodelay_cal_m <= '0';
iodelay_cal_s <= '0';
iodelay_rst <= '0';
iodelay_recal <= '1';
state := S_RESET;
elsif rising_edge(clk_div_buf) then
iodelay_cal_m <= '0';
iodelay_cal_s <= '0';
iodelay_rst <= '0';
iodelay_recal <= '1';
case state is
when S_RESET =>
if serdes_calib_i = '1' and iodelay_busy = '0' then
state := S_RSTCAL;
end if;
when S_RSTCAL =>
-- Issue CAL on reset
iodelay_cal_m <= '1';
iodelay_cal_s <= '1';
state := S_BUSY;
when S_RSTBUSY =>
-- And then RESET
if iodelay_busy = '0' then
counter := 0;
iodelay_rst <= '1';
state := S_WAIT;
end if;
when S_WAIT =>
-- Periodically calibrate
if serdes_calib_i = '0' then
state := S_RESET;
elsif counter = 2**20 then
if iodelay_busy = '0' then
iodelay_cal_s <= '1';
state := S_BUSY;
end if;
else
counter := counter + 1;
iodelay_recal <= '0';
end if;
when S_BUSY =>
if iodelay_busy = '0' then
counter := 0;
state := S_WAIT;
end if;
end case;
end if;
end process;
------------------------------------------------------------------------------
-- Clock generation for deserializer
--
......@@ -183,7 +337,7 @@ begin -- architecture arch
I_INVERT => FALSE,
USE_DOUBLER => FALSE)
port map (
I => adc_dco,
I => adc_dco, -- 350Mhz
IOCLK => open,
DIVCLK => l_pll_clkin,
SERDESSTROBE => open);
......@@ -194,42 +348,42 @@ begin -- architecture arch
DIVIDE_BYPASS => TRUE)
port map (
O => l_pll_clkfbin,
I => clk_serdes_p);
I => clk_serdes_p); -- 100Mhz
cmp_dco_pll : PLL_BASE
generic map (
BANDWIDTH => "OPTIMIZED",
CLKFBOUT_MULT => 2,
CLKIN_PERIOD => 2.5,
CLKFBOUT_MULT => 2, -- M=2, Fvco=700Mhz
CLKIN_PERIOD => 2.86,
CLKOUT0_DIVIDE => 1,
CLKOUT1_DIVIDE => 8,
CLKOUT1_DIVIDE => 7,
CLK_FEEDBACK => "CLKOUT0",
COMPENSATION => "SOURCE_SYNCHRONOUS",
DIVCLK_DIVIDE => 1,
REF_JITTER => 0.01)
port map (
CLKOUT0 => l_pll_clkout0,
CLKOUT1 => l_pll_clkout1,
CLKOUT0 => l_pll_clkout0, -- 700Mhz (to BUFPLL)
CLKOUT1 => l_pll_clkout1, -- 100Mhz
LOCKED => l_pll_locked,
CLKFBIN => l_pll_clkfbin,
CLKIN => l_pll_clkin,
CLKIN => l_pll_clkin, -- 350Mhz
RST => '0');
cmp_clk_div_buf : BUFG
port map (
I => l_pll_clkout1,
I => l_pll_clkout1, -- 100Mhz
O => clk_div_buf);
cmp_dco_bufpll : BUFPLL
generic map (
DIVIDE => 8)
DIVIDE => 7)
port map (
IOCLK => clk_serdes_p,
IOCLK => clk_serdes_p, -- 700Mhz (to BUFIO2FB and serdes)
LOCK => serdes_locked_o,
SERDESSTROBE => serdes_strobe,
SERDESSTROBE => serdes_strobe, -- 100Mhz
GCLK => clk_div_buf,
LOCKED => l_pll_locked,
PLLIN => l_pll_clkout0);
PLLIN => l_pll_clkout0); -- 700Mhz
-- not used in this case
clk_serdes_n <= '0';
......@@ -247,25 +401,25 @@ begin -- architecture arch
cmp_dco_bufio_p : BUFIO2
generic map (
DIVIDE => 8,
DIVIDE => 7,
DIVIDE_BYPASS => FALSE,
I_INVERT => FALSE,
USE_DOUBLER => TRUE)
port map (
I => adc_dco,
IOCLK => clk_serdes_p,
DIVCLK => l_clk_div,
I => adc_dco, -- 350Mhz DDR
IOCLK => clk_serdes_p, -- 350Mhz
DIVCLK => l_clk_div, -- 100Mhz (2*350/7)
SERDESSTROBE => serdes_strobe);
cmp_dco_bufio_n : BUFIO2
generic map (
DIVIDE => 8,
DIVIDE => 7,
DIVIDE_BYPASS => FALSE,
I_INVERT => TRUE,
USE_DOUBLER => FALSE)
port map (
I => adc_dco,
IOCLK => clk_serdes_n,
IOCLK => clk_serdes_n, -- 350Mhz, 180 phase shift.
DIVCLK => open,
SERDESSTROBE => open);
......@@ -290,25 +444,23 @@ begin -- architecture arch
begin
if rising_edge(clk_div_buf) then
-- Shift register to generate bitslip enable once every 8 clock ticks
bitslip_sreg <= bitslip_sreg(0) & bitslip_sreg(bitslip_sreg'length-1 downto 1);
bitslip_sreg <= bitslip_sreg(0) & bitslip_sreg(bitslip_sreg'left downto 1);
-- Generate bitslip and synced signal
if(bitslip_sreg(bitslip_sreg'LEFT) = '1') then
-- use fr_n pattern (fr_p and fr_n are swapped on the adc mezzanine)
if(serdes_out_fr /= "00001111") then
serdes_auto_bslip <= '1';
if bitslip_sreg(bitslip_sreg'LEFT) = '1' then
if serdes_out_fr /= "0000000" and serdes_out_fr /= "1111111" then
serdes_bitslip <= '1';
serdes_synced <= '0';
else
serdes_auto_bslip <= '0';
serdes_bitslip <= '0';
serdes_synced <= '1';
end if;
else
serdes_auto_bslip <= '0';
serdes_bitslip <= '0';
end if;
end if;
end process p_auto_bitslip;
serdes_bitslip <= serdes_auto_bslip or serdes_bslip_i;
serdes_synced_o <= serdes_synced;
------------------------------------------------------------------------------
......@@ -320,19 +472,13 @@ begin -- architecture arch
------------------------------------------------------------------------------
-- serdes inputs forming
serdes_serial_in <= adc_fr
& adc_outa(3) & adc_outb(3)
& adc_outa(2) & adc_outb(2)
& adc_outa(1) & adc_outb(1)
& adc_outa(0) & adc_outb(0);
gen_adc_data_iserdes : for I in 0 to 8 generate
cmp_adc_iserdes_master : ISERDES2
generic map (
BITSLIP_ENABLE => TRUE,
DATA_RATE => f_data_rate_sel(g_USE_PLL),
DATA_WIDTH => 8,
DATA_WIDTH => 7,
INTERFACE_TYPE => "RETIMED",
SERDES_MODE => "MASTER")
port map (
......@@ -340,19 +486,19 @@ begin -- architecture arch
CFB1 => open,
DFB => open,
FABRICOUT => open,
INCDEC => open,
INCDEC => serdes_incdec(i),
Q1 => serdes_parallel_out(I)(3),
Q2 => serdes_parallel_out(I)(2),
Q3 => serdes_parallel_out(I)(1),
Q4 => serdes_parallel_out(I)(0),
SHIFTOUT => serdes_m2s_shift(I),
VALID => open,
VALID => serdes_valid(i),
BITSLIP => serdes_bitslip,
CE0 => '1',
CLK0 => clk_serdes_p,
CLK1 => clk_serdes_n,
CLKDIV => clk_div_buf,
D => serdes_serial_in(I),
D => adc_out_dly_m(i),
IOCE => serdes_strobe,
RST => serdes_arst_i,
SHIFTIN => serdes_s2m_shift(I));
......@@ -361,7 +507,7 @@ begin -- architecture arch
generic map (
BITSLIP_ENABLE => TRUE,
DATA_RATE => f_data_rate_sel(g_USE_PLL),
DATA_WIDTH => 8,
DATA_WIDTH => 7,
INTERFACE_TYPE => "RETIMED",
SERDES_MODE => "SLAVE")
port map (
......@@ -370,7 +516,7 @@ begin -- architecture arch
DFB => open,
FABRICOUT => open,
INCDEC => open,
Q1 => serdes_parallel_out(I)(7),
Q1 => open,
Q2 => serdes_parallel_out(I)(6),
Q3 => serdes_parallel_out(I)(5),
Q4 => serdes_parallel_out(I)(4),
......@@ -381,7 +527,7 @@ begin -- architecture arch
CLK0 => clk_serdes_p,
CLK1 => clk_serdes_n,
CLKDIV => clk_div_buf,
D => '0',
D => adc_out_dly_s(i),
IOCE => serdes_strobe,
RST => serdes_arst_i,
SHIFTIN => serdes_m2s_shift(I));
......@@ -393,12 +539,14 @@ begin -- architecture arch
-- Data re-ordering for serdes outputs
gen_serdes_dout_reorder : for I in 0 to 3 generate
gen_serdes_dout_reorder_bits : for J in 0 to 7 generate
gen_serdes_dout_reorder_bits : for J in 0 to 6 generate
-- OUT#B: even bits
adc_data_o(I*16 + 2*J) <= serdes_parallel_out(2*I)(J);
adc_data_o(I*16 + 2*J + 2) <= serdes_parallel_out(2*I)(J);
-- OUT#A: odd bits
adc_data_o(I*16 + 2*J + 1) <= serdes_parallel_out(2*I + 1)(J);
adc_data_o(I*16 + 2*J + 3) <= serdes_parallel_out(2*I + 1)(J);
end generate gen_serdes_dout_reorder_bits;
adc_data_o(I*16 + 0) <= '0';
adc_data_o(I*16 + 1) <= '0';
end generate gen_serdes_dout_reorder;
end architecture arch;
......@@ -40,6 +40,8 @@ entity fmc_adc_100Ms_core is
g_TRIG_DELAY_SW : natural := 9;
-- FMC-ADC identification number
g_FMC_ADC_NR : natural := 0;
-- Data endianness. If set, swap memory data byte
g_BYTE_SWAP : boolean := false;
-- WB interface configuration
g_WB_CSR_MODE : t_wishbone_interface_mode := PIPELINED;
g_WB_CSR_GRANULARITY : t_wishbone_address_granularity := BYTE);
......@@ -71,7 +73,7 @@ entity fmc_adc_100Ms_core is
acq_stop_p_o : out std_logic;
acq_end_p_o : out std_logic;
-- Trigger time-tag inputs
-- Trigger time-tag inputs (sys_clk_i)
trigger_tag_i : in t_timetag;
time_trig_i : in std_logic;
aux_time_trig_i : in std_logic;
......@@ -151,8 +153,7 @@ architecture rtl of fmc_adc_100Ms_core is
-- SerDes
signal serdes_out_data : std_logic_vector(63 downto 0);
signal serdes_out_data_synced : std_logic_vector(63 downto 0);
signal serdes_man_bitslip : std_logic;
signal serdes_man_bitslip_sync : std_logic;
signal serdes_calib_sync : std_logic;
signal serdes_locked : std_logic;
signal serdes_locked_sync : std_logic;
signal serdes_synced : std_logic;
......@@ -293,6 +294,7 @@ architecture rtl of fmc_adc_100Ms_core is
-- Wishbone to DDR flowcontrol FIFO
signal wb_ddr_fifo_din : std_logic_vector(64 downto 0);
signal wb_ddr_fifo_dout : std_logic_vector(64 downto 0);
signal wb_ddr_fifo_dout2 : std_logic_vector(63 downto 0);
signal wb_ddr_fifo_empty : std_logic;
signal wb_ddr_fifo_full : std_logic;
signal wb_ddr_fifo_wr : std_logic;
......@@ -427,8 +429,8 @@ begin
port map (
clk_i => fs_clk,
rst_n_a_i => '1',
d_i => serdes_man_bitslip,
q_o => serdes_man_bitslip_sync);
d_i => csr_regout.ctl_serdes_calib,
q_o => serdes_calib_sync);
cmp_adc_serdes : entity work.ltc2174_2l16b_receiver
generic map (
......@@ -443,7 +445,7 @@ begin
adc_outb_p_i => adc_outb_p_i,
adc_outb_n_i => adc_outb_n_i,
serdes_arst_i => serdes_arst,
serdes_bslip_i => serdes_man_bitslip_sync,
serdes_calib_i => serdes_calib_sync,
serdes_locked_o => serdes_locked,
serdes_synced_o => serdes_synced,
adc_data_o => serdes_out_data,
......@@ -484,7 +486,6 @@ begin
fmc_adc_ch4_o => wb_channel_in(4));
csr_regin.ctl_fsm_cmd <= fsm_cmd;
csr_regin.ctl_man_bitslip <= serdes_man_bitslip;
csr_regin.ctl_clear_trig_stat <= trig_storage_clear;
csr_regin.ctl_calib_apply <= sync_calib_apply;
......@@ -530,12 +531,10 @@ begin
if rising_edge(sys_clk_i) then
if ctl_reg_wr = '1' then
fsm_cmd <= csr_regout.ctl_fsm_cmd;
serdes_man_bitslip <= csr_regout.ctl_man_bitslip;
trig_storage_clear <= csr_regout.ctl_clear_trig_stat;
sync_calib_apply <= csr_regout.ctl_calib_apply;
else
fsm_cmd <= (others => '0');
serdes_man_bitslip <= '0';
trig_storage_clear <= '0';
sync_calib_apply <= '0';
end if;
......@@ -1633,6 +1632,20 @@ begin
-- Convert to 32-bit word addressing for Wishbone
wb_ddr_skidpad_adr_in <= std_logic_vector(ram_addr_cnt);
gen_no_byte_swap: if not g_BYTE_SWAP generate
wb_ddr_fifo_dout2 <= wb_ddr_fifo_dout(63 downto 0);
end generate;
gen_byte_swap: if g_BYTE_SWAP generate
wb_ddr_fifo_dout2 (63 downto 32) <= ( wb_ddr_fifo_dout(39 downto 32)
& wb_ddr_fifo_dout(47 downto 40)
& wb_ddr_fifo_dout(55 downto 48)
& wb_ddr_fifo_dout(63 downto 56));
wb_ddr_fifo_dout2 (31 downto 0) <= ( wb_ddr_fifo_dout(7 downto 0)
& wb_ddr_fifo_dout(15 downto 8)
& wb_ddr_fifo_dout(23 downto 16)
& wb_ddr_fifo_dout(31 downto 24));
end generate;
inst_skidpad: entity work.wb_skidpad2
generic map (
g_adrbits => ram_addr_cnt'length,
......@@ -1644,7 +1657,7 @@ begin
stb_i => wb_ddr_skidpad_stb_in,
adr_i => wb_ddr_skidpad_adr_in,
dat_i => wb_ddr_fifo_dout(63 downto 0),
dat_i => wb_ddr_fifo_dout2,
sel_i => (others => '1'),
we_i => '1',
stall_o => wb_ddr_skidpad_stall,
......
......@@ -41,6 +41,8 @@ entity fmc_adc_mezzanine is
g_TAG_ADJUST : natural := 27;
-- FMC-ADC identification number
g_FMC_ADC_NR : natural := 0;
-- Data endianness. If set, swap memory data byte
g_BYTE_SWAP : boolean := false;
-- WB interface configuration
g_WB_MODE : t_wishbone_interface_mode := PIPELINED;
g_WB_GRANULARITY : t_wishbone_address_granularity := BYTE);
......@@ -67,11 +69,11 @@ entity fmc_adc_mezzanine is
acq_cfg_ok_o : out std_logic;
-- Auxiliary trigger input wishbone interface
wb_trigin_slave_i : in t_wishbone_slave_in;
wb_trigin_slave_i : in t_wishbone_slave_in := c_DUMMY_WB_SLAVE_IN;
wb_trigin_slave_o : out t_wishbone_slave_out;
-- Trigout wishbone interface
wb_trigout_slave_i : in t_wishbone_slave_in;
wb_trigout_slave_i : in t_wishbone_slave_in := c_DUMMY_WB_SLAVE_IN;
wb_trigout_slave_o : out t_wishbone_slave_out;
-- FMC interface
......@@ -309,6 +311,7 @@ begin
g_TRIG_DELAY_EXT => g_TRIG_DELAY_EXT,
g_TRIG_DELAY_SW => g_TRIG_DELAY_SW,
g_FMC_ADC_NR => g_FMC_ADC_NR,
g_BYTE_SWAP => g_BYTE_SWAP,
g_WB_CSR_MODE => PIPELINED,
g_WB_CSR_GRANULARITY => BYTE)
port map (
......
......@@ -10,8 +10,8 @@
`define FMC_ADC_100MS_CSR_CTL_FMC_CLK_OE 'h4
`define FMC_ADC_100MS_CSR_CTL_OFFSET_DAC_CLR_N_OFFSET 3
`define FMC_ADC_100MS_CSR_CTL_OFFSET_DAC_CLR_N 'h8
`define FMC_ADC_100MS_CSR_CTL_MAN_BITSLIP_OFFSET 4
`define FMC_ADC_100MS_CSR_CTL_MAN_BITSLIP 'h10
`define FMC_ADC_100MS_CSR_CTL_SERDES_CALIB_OFFSET 4
`define FMC_ADC_100MS_CSR_CTL_SERDES_CALIB 'h10
`define FMC_ADC_100MS_CSR_CTL_TRIG_LED_OFFSET 6
`define FMC_ADC_100MS_CSR_CTL_TRIG_LED 'h40
`define FMC_ADC_100MS_CSR_CTL_ACQ_LED_OFFSET 7
......
......@@ -531,10 +531,11 @@ begin -- architecture arch
d_i => fmc_irq(I),
q_o => irq_vector(I));
cmp_fmc_adc_mezzanine : fmc_adc_mezzanine
cmp_fmc_adc_mezzanine : entity work.fmc_adc_mezzanine
generic map (
g_MULTISHOT_RAM_SIZE => g_MULTISHOT_RAM_SIZE,
g_SPARTAN6_USE_PLL => TRUE,
g_BYTE_SWAP => TRUE,
g_FMC_ADC_NR => I,
g_WB_MODE => PIPELINED,
g_WB_GRANULARITY => BYTE)
......
......@@ -8,12 +8,16 @@ VMEBUS_EXTRA_SYMBOLS-$(CONFIG_FMC_ADC_SVEC) := $(VMEBUS_ABS)/driver/Module.symve
ZIO_VERSION = $(shell cd $(ZIO_ABS); git describe --always --dirty --long --tags)
VERSION = $(shell cd $(src); git describe --always --dirty --long --tags)
VER_MAJ := $(shell echo $(subst v,,$(VERSION)) | cut -d '.' -f 1)
VER_MIN := $(shell echo $(subst v,,$(VERSION)) | cut -d '.' -f 2)
FA_VERSION_BLD := $(shell printf "0x%02x%02x0000" $(VER_MAJ) $(VER_MIN))
KBUILD_EXTRA_SYMBOLS += $(ZIO_EXTRA_SYMBOLS-y)
KBUILD_EXTRA_SYMBOLS += $(FMC_EXTRA_SYMBOLS-y)
KBUILD_EXTRA_SYMBOLS += $(VMEBUS_EXTRA_SYMBOLS-y)
ccflags-y = -DVERSION=\"$(VERSION)\"
ccflags-y += -DFA_VERSION_BLD=$(FA_VERSION_BLD)
ccflags-y += -DCONFIG_FMC_ADC_SVEC
ccflags-y += -I$(src)
ccflags-y += -I$(ZIO_ABS)/include
......
// SPDX-FileCopyrightText: 2020 CERN (home.cern)
//
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2020 CERN (home.cern)
/*
* EEPROM calibration block retreival code for fa-dev
......@@ -16,10 +15,10 @@
#include <linux/jiffies.h>
#include "fmc-adc-100m14b4cha-private.h"
static int fa_calib_temp_period = 0;
static int fa_calib_temp_period;
module_param_named(temp_calib_period, fa_calib_temp_period, int, 0444);
static int fa_calib_temp = 0;
static int fa_calib_temp;
module_param_named(temp_calib, fa_calib_temp, int, 0444);
/* This identity calibration is used as default */
......@@ -48,7 +47,7 @@ static int fa_calib_apply(struct fa_dev *fa)
return -EBUSY;
}
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_CALIB_APPLY], 1);
ndelay(100);
ndelay(100);
if (fa_calib_is_busy(fa)) {
dev_err(&fa->pdev->dev,
"%s Calibration value applied but still 'busy'\n",
......@@ -134,7 +133,7 @@ static const int64_t gain_dac_error_slope_fix[] = {
static int fa_calib_dac_gain_fix(int range, uint32_t gain_c,
int32_t delta_temp)
{
int64_t error;
int64_t error;
error = gain_dac_error_slope_fix[range] * delta_temp;
error /= 0x2000; /* see comment above for gain_dac_error_slope_fix */
......@@ -148,7 +147,7 @@ static bool fa_calib_is_compensation_on(struct fa_dev *fa)
if (unlikely((fa->flags & FA_DEV_F_PATTERN_DATA)))
return false;
if (unlikely(fa_calib_temp))
if (unlikely(fa_calib_temp))
return true;
return false;
......@@ -208,47 +207,52 @@ static int fa_dac_offset_set(struct fa_dev *fa, unsigned int chan,
return fa_spi_xfer(fa, FA_SPI_SS_DAC(chan), 16, val, NULL);
}
static int64_t fa_dac_offset_raw_get(int32_t offset)
static uint16_t fa_dac_offset_raw_calibrate(struct fa_dev *fa,
uint16_t raw_offset,
int gain, int offset)
{
int32_t signed_offset = raw_offset - 0x8000;
int64_t hwval;
hwval = offset * 0x8000LL / 5000000;
if (hwval == 0x8000)
hwval = 0x7fff; /* -32768 .. 32767 */
return hwval;
}
static int64_t fa_dac_offset_raw_calibrate(int32_t raw_offset,
int gain, int offset)
{
int64_t hwval;
hwval = ((signed_offset + offset) * gain) >> 15; /* signed */
hwval += 0x8000; /* offset binary */
dev_dbg(&fa->pdev->dev,
"Final DAC calibrated value: (0x%08x + 0x%08x) * 0x%08x = 0x%08llx\n",
signed_offset, offset, gain, hwval);
hwval = ((raw_offset + offset) * gain) >> 15; /* signed */
hwval += 0x8000; /* offset binary */
if (hwval < 0)
/* Saturate */
if (hwval < 0) {
hwval = 0;
if (hwval > 0xffff)
dev_warn(&fa->pdev->dev,
"Final DAC calibrated value: lower saturation, set 0x%04llx",
hwval);
}
if (hwval > 0xffff) {
hwval = 0xffff;
dev_warn(&fa->pdev->dev,
"Final DAC calibrated value: lower saturation, set 0x%04llx",
hwval);
}
return hwval;
}
static int fa_dac_offset_get(struct fa_dev *fa, unsigned int chan)
static int fa_dac_offset_get(struct fa_dev *fa, unsigned int chan, uint16_t *offset)
{
int32_t off_uv = fa->user_offset[chan] + fa->zero_offset[chan];
int32_t user = fa->user_offset[chan];
int32_t zero = fa->zero_offset[chan];
int32_t __offset = (user + zero) - 0x8000; /* Bring back to DAC format */
if (WARN(off_uv < DAC_SAT_LOW,
"DAC lower saturation %d < %d\n",
off_uv, DAC_SAT_LOW)) {
off_uv = DAC_SAT_LOW;
}
if (WARN(off_uv > DAC_SAT_UP,
"DAC upper saturation %d > %d\n",
off_uv, DAC_SAT_UP)) {
off_uv = DAC_SAT_UP;
if (__offset & ~DAC_VAL_MASK) {
dev_err(&fa->pdev->dev,
"DAC offset value overflows 16bits. {user: 0x%04x, zero: 0x%04x, sum: 0x%08x}\n",
user, zero, __offset);
return -EINVAL;
}
return off_uv;
*offset = __offset;
return 0;
}
/**
......@@ -262,12 +266,16 @@ static int fa_dac_offset_get(struct fa_dev *fa, unsigned int chan)
int fa_calib_dac_config_chan(struct fa_dev *fa, unsigned int chan,
int32_t temperature, unsigned int flags)
{
int32_t off_uv = fa_dac_offset_get(fa, chan);
int32_t off_uv_raw = fa_dac_offset_raw_get(off_uv);
int range = fa->range[chan];
struct fa_calib_stanza *cal = &fa->calib.dac[range];
uint16_t value;
int range = fa->range[chan];
struct fa_calib_stanza *cal = &fa->calib.dac[range];
int gain;
int hwval;
int err;
err = fa_dac_offset_get(fa, chan, &value);
if (err)
return err;
if (fa_calib_is_compensation_on(fa)) {
int32_t delta_temp;
......@@ -287,10 +295,10 @@ int fa_calib_dac_config_chan(struct fa_dev *fa, unsigned int chan,
__func__, chan, range, gain, cal->offset[chan]);
}
hwval = fa_dac_offset_raw_calibrate(off_uv_raw, gain,
hwval = fa_dac_offset_raw_calibrate(fa, value, gain,
cal->offset[chan]);
return fa_dac_offset_set(fa, chan, hwval);
return fa_dac_offset_set(fa, chan, hwval);
}
void fa_calib_config_chan(struct fa_dev *fa, unsigned int chan,
......@@ -311,6 +319,14 @@ void fa_calib_config(struct fa_dev *fa)
fa_calib_config_chan(fa, i, temperature, 0);
spin_unlock(&fa->zdev->cset->lock);
}
static void __fa_calib_gain_update(struct fa_dev *fa)
{
fa_calib_config(fa);
mod_timer(&fa->calib_timer, jiffies + HZ * fa_calib_temp_period);
}
/**
* Periodically update gain calibration values
* @fa: FMC ADC device
......@@ -320,14 +336,17 @@ void fa_calib_config(struct fa_dev *fa)
* linear behavior with respect to the temperature.
*
*/
#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
static void fa_calib_gain_update(struct timer_list *timer)
{
__fa_calib_gain_update(from_timer(fa, timer, calib_timer));
}
#else
static void fa_calib_gain_update(unsigned long arg)
{
struct fa_dev *fa = (void *)arg;
fa_calib_config(fa);
mod_timer(&fa->calib_timer, jiffies + HZ * fa_calib_temp_period);
__fa_calib_gain_update((void *)arg);
}
#endif
/* Actual verification code */
static int fa_verify_calib_stanza(struct device *msgdev, char *name, int r,
struct fa_calib_stanza *cal)
......@@ -493,14 +512,19 @@ int fa_calib_init(struct fa_dev *fa)
fa_calib_write(fa, &calib);
/* First calibration.
The board has just been reset by the carrier before calling this
driver and reading the temperature read needs at least 350ms */
* The board has just been reset by the carrier before calling this
* driver and reading the temperature read needs at least 350ms
*/
msleep(400);
fa_calib_config(fa);
/* Prepare the timely recalibration */
if (fa_calib_is_compensation_on(fa) && fa_calib_temp_period) {
#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
timer_setup(&fa->calib_timer, fa_calib_gain_update, 0);
#else
setup_timer(&fa->calib_timer, fa_calib_gain_update, (unsigned long)fa);
#endif
mod_timer(&fa->calib_timer,
jiffies + HZ * fa_calib_temp_period);
}
......
......@@ -12,6 +12,7 @@
#include <linux/mod_devicetable.h>
#include <uapi/linux/ipmi/fru.h>
#include <linux/fmc.h>
#include <linux/io.h>
#include "fmc-adc-100m14b4cha-private.h"
#include <platform_data/fmc-adc-100m14b4cha.h>
......@@ -19,6 +20,12 @@
static int fa_enable_test_data_fpga;
module_param_named(enable_test_data_fpga, fa_enable_test_data_fpga, int, 0444);
static int version_ignore;
module_param(version_ignore, int, 0644);
MODULE_PARM_DESC(version_ignore,
"Ignore the version declared in the FPGA and force the driver to load all components (default 0)");
#define FA_EEPROM_TYPE "at24c64"
......@@ -36,6 +43,29 @@ static const int zfad_hw_range[] = {
struct workqueue_struct *fa_workqueue;
static int fa_sg_alloc_table_from_pages(struct sg_table *sgt,
struct page **pages,
unsigned int n_pages,
unsigned int offset,
unsigned long size,
unsigned int max_segment,
gfp_t gfp_mask)
{
#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE
struct scatterlist *sg;
sg = __sg_alloc_table_from_pages(sgt, pages, n_pages, offset, size,
max_segment, NULL, 0, gfp_mask);
if (IS_ERR(sg))
return PTR_ERR(sg);
else
return 0;
#else
return __sg_alloc_table_from_pages(sgt, pages, n_pages, offset, size,
max_segment, gfp_mask);
#endif
}
/**
* Enable/Disable Data Output Randomizer
* @fa: the adc descriptor
......@@ -46,17 +76,20 @@ int fa_adc_output_randomizer_set(struct fa_dev *fa, bool enable)
uint32_t tx, rx;
int err;
tx = 0x8000;
/* Read register A1 */
tx = 0x8000;
tx |= (1 << 8);
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, tx, &rx);
if (err)
return err;
/* Set or clear RAND bit */
if (enable)
rx |= BIT(6);
else
rx &= ~BIT(6);
/* Write back A1 */
tx = 0x0000;
tx |= (1 << 8);
tx |= (rx & 0xFF);
......@@ -76,7 +109,8 @@ bool fa_adc_is_output_randomizer(struct fa_dev *fa)
uint32_t tx, rx;
int err;
tx = 0x8000;
/* Read register A1 */
tx = 0x8000;
tx |= (1 << 8);
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, tx, &rx);
if (err)
......@@ -127,13 +161,13 @@ int fa_trigger_software(struct fa_dev *fa)
return -EPERM;
}
/* Fire if nsamples!=0 */
/* Fire if nsamples!=0 */
if (!ti->nsamples) {
dev_info(&fa->pdev->dev, "pre + post = 0: cannot acquire\n");
return -EINVAL;
}
/*
/*
* We can do a software trigger if the FSM is not in
* the WAIT trigger status. Wait for it.
* Remember that: timeout is in us, a sample takes 10ns
......@@ -347,7 +381,7 @@ err:
* @enable 0 to disable, 1 to enable
*/
int fa_adc_data_pattern_get(struct fa_dev *fa, uint16_t *pattern,
unsigned int *enable)
unsigned int *enable)
{
uint32_t tx, rx;
int err;
......@@ -443,8 +477,7 @@ int zfad_fsm_command(struct fa_dev *fa, uint32_t command)
if (command == FA100M14B4C_CMD_START) {
if (!fa_adc_is_serdes_ready(fa)) {
dev_err(fa->msgdev,
"Cannot start acquisition: "
"SerDes PLL not locked or synchronized (0x%08x)\n",
"Cannot start acquisition: SerDes PLL not locked or synchronized (0x%08x)\n",
fa_ioread(fa, fa->fa_adc_csr_base + ADC_CSR_STA_REG_OFFSET));
return -EBUSY;
}
......@@ -459,8 +492,8 @@ int zfad_fsm_command(struct fa_dev *fa, uint32_t command)
* from zfat_arm_trigger() or zfad_input_cset()
*/
if (!(cset->ti->flags & ZIO_TI_ARMED)) {
dev_info(fa->msgdev, "Cannot start acquisition: "
"Trigger refuses to arm\n");
dev_info(fa->msgdev,
"Cannot start acquisition: Trigger refuses to arm\n");
return -EIO;
}
......@@ -484,7 +517,11 @@ static void fa_init_timetag(struct fa_dev *fa)
{
unsigned long seconds;
#if KERNEL_VERSION(5, 11, 0) <= LINUX_VERSION_CODE
seconds = ktime_get_real_seconds();
#else
seconds = get_seconds();
#endif
fa_writel(fa, fa->fa_utc_base, &zfad_regs[ZFA_UTC_SECONDS_U],
(seconds >> 32) & 0xFFFFFFFF);
fa_writel(fa, fa->fa_utc_base, &zfad_regs[ZFA_UTC_SECONDS_L],
......@@ -517,12 +554,8 @@ static int __fa_init(struct fa_dev *fa)
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_FMS_CMD],
FA100M14B4C_CMD_STOP);
/* Initialize channels to use 1V range */
for (i = 0; i < 4; ++i) {
for (i = 0; i < FA100M14B4C_NCHAN; ++i)
fa_adc_range_set(fa, &zdev->cset->chan[i], FA100M14B4C_RANGE_1V);
/* reset channel offset */
fa->user_offset[i] = 0;
fa->zero_offset[i] = 0;
}
/* Set decimation to minimum */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SR_UNDER], 1);
......@@ -554,12 +587,11 @@ static int __fa_init(struct fa_dev *fa)
/* This structure lists the various subsystems */
struct fa_modlist {
char *name;
int (*init)(struct fa_dev *);
void (*exit)(struct fa_dev *);
int (*init)(struct fa_dev *fa);
void (*exit)(struct fa_dev *fa);
};
static struct fa_modlist mods[] = {
{"spi", fa_spi_init, fa_spi_exit},
{"zio", fa_zio_init, fa_zio_exit},
{"debug", fa_debug_init, fa_debug_exit},
{"calibration", fa_calib_init, fa_calib_exit},
......@@ -645,19 +677,85 @@ static void fa_sg_alloc_table_init(struct fa_dev *fa)
if (fa_is_flag_set(fa, FMC_ADC_NOSQUASH_SCATTERLIST))
fa->sg_alloc_table_from_pages = sg_alloc_table_from_pages_no_squash;
else
fa->sg_alloc_table_from_pages = __sg_alloc_table_from_pages;
fa->sg_alloc_table_from_pages = fa_sg_alloc_table_from_pages;
}
static struct fmc_adc_platform_data fmc_adc_pdata_default = {
.flags = 0,
.vme_reg_offset = 0,
.vme_dma_offset = 0,
.vme_dma_offset = 0,
.calib_trig_time = 0,
.calib_trig_threshold = 0,
.calib_trig_internal = 0,
};
static int fa_metadata_get(struct fa_dev *fa)
{
struct resource *r;
void *mem;
int i;
r = platform_get_resource(fa->pdev, IORESOURCE_MEM, ADC_MEM_META);
if (r == NULL) {
dev_err(&fa->pdev->dev, "Can't inspect ADC device metadata: missing resource\n");
return -ENODEV;
}
mem = ioremap(r->start, resource_size(r));
if (!mem) {
dev_err(&fa->pdev->dev, "Can't inspect ADC device metadata: failed to map\n");
return -ENODEV;
}
/* Dump meta*/
for (i = 0; i < sizeof(fa->meta) / 4; ++i)
((uint32_t *)&fa->meta)[i] = fa_ioread(fa, mem + (i * 4));
iounmap(mem);
return 0;
}
static bool fa_is_fpga_version_valid(uint32_t expected, uint32_t found)
{
if (version_ignore)
return true;
if (FA_VERSION_MAJ(found) != FA_VERSION_MAJ(expected))
return false;
if (FA_VERSION_MIN(found) < FA_VERSION_MIN(expected))
return false;
return true;
}
static bool fa_is_fpga_valid(struct fa_dev *fa)
{
if (fa->meta.vendor != FA_META_VENDOR_ID) {
dev_err(&fa->pdev->dev,
"Unknow vendor ID: %08x\n", fa->meta.vendor);
return false;
}
switch (fa->meta.device) {
case FA_META_DEVICE_ID_SVEC_DBL_ADC:
break;
case FA_META_DEVICE_ID_SPEC:
break;
default:
dev_err(&fa->pdev->dev, "Unknow device ID: %08x\n",
fa->meta.device);
return false;
}
if (!fa_is_fpga_version_valid(FA_VERSION_DRV, fa->meta.version)) {
dev_err(&fa->pdev->dev,
"Invalid version: %08x, expected: %08x\n",
fa->meta.version, FA_VERSION_DRV);
return false;
}
return true;
}
/* probe and remove are called by fa-spec.c */
int fa_probe(struct platform_device *pdev)
{
......@@ -720,7 +818,7 @@ int fa_probe(struct platform_device *pdev)
}
}
if(!fa_fmc_slot_is_valid(fa))
if (!fa_fmc_slot_is_valid(fa))
goto out_fmc_err;
err = sysfs_create_link(&fa->pdev->dev.kobj, &fa->slot->dev.kobj,
......@@ -731,18 +829,34 @@ int fa_probe(struct platform_device *pdev)
goto err_fmc_link;
}
err = fa_metadata_get(fa);
if (err)
goto out_meta;
if (!fa_is_fpga_valid(fa))
goto out_valid;
err = fa_dma_request_channel(fa);
if (err)
goto out_dma;
fa_clock_enable(fa);
err = fa_spi_init(fa);
if (err)
goto out_spi;
err = fa_adc_wait_serdes_ready(fa, msecs_to_jiffies(10));
if (err) {
dev_err(&fa->pdev->dev, "The SERDES did not syncronize\n");
goto out_serdes;
}
/* reset channel offset before calibration */
for (i = 0; i < FA100M14B4C_NCHAN; ++i) {
fa->user_offset[i] = 0x8000;
fa->zero_offset[i] = 0x8000;
}
/* init all subsystems */
for (i = 0, m = mods; i < ARRAY_SIZE(mods); i++, m++) {
dev_dbg(fa->msgdev, "Calling init for \"%s\"\n", m->name);
......@@ -771,9 +885,13 @@ out:
m->exit(fa);
iounmap(fa->fa_top_level);
out_serdes:
fa_spi_exit(fa);
out_spi:
fa_clock_disable(fa);
fa_dma_release_channel(fa);
out_dma:
out_valid:
out_meta:
sysfs_remove_link(&fa->pdev->dev.kobj, dev_name(&fa->slot->dev));
err_fmc_link:
out_fmc_err:
......@@ -799,9 +917,12 @@ int fa_remove(struct platform_device *pdev)
while (--i >= 0) {
struct fa_modlist *m = mods + i;
if (m->exit)
m->exit(fa);
}
fa_spi_exit(fa);
fa_clock_disable(fa);
fa_dma_release_channel(fa);
......@@ -836,14 +957,14 @@ static int fa_init(void)
{
int ret;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)
#if KERNEL_VERSION(3, 15, 0) > LINUX_VERSION_CODE
fa_workqueue = alloc_workqueue(fa_dev_drv.driver.name,
WQ_NON_REENTRANT | WQ_UNBOUND |
WQ_MEM_RECLAIM, 1);
#else
#else
fa_workqueue = alloc_workqueue(fa_dev_drv.driver.name,
WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
#endif
#endif
if (fa_workqueue == NULL)
return -ENOMEM;
......
......@@ -9,22 +9,22 @@
#include "fmc-adc-100m14b4cha-private.h"
#define FA_DBG_REG32_CH(_n) \
{.name = "ADC-CSR:ch"#_n"_ctl", .offset = ADC_CSR_OFF + 0x080 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_sta", .offset = ADC_CSR_OFF + 0x084 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_cal_nb", .offset = ADC_CSR_OFF + 0x088 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_sat", .offset = ADC_CSR_OFF + 0x08C + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_trig_thres", .offset = ADC_CSR_OFF + 0x090 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_ctl", .offset = ADC_CSR_OFF + 0x080 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_sta", .offset = ADC_CSR_OFF + 0x084 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_cal_nb", .offset = ADC_CSR_OFF + 0x088 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_sat", .offset = ADC_CSR_OFF + 0x08C + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_trig_thres", .offset = ADC_CSR_OFF + 0x090 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_trig_dly", .offset = ADC_CSR_OFF + 0x094 + ((_n - 1) * 0x40)}
#define FA_DBG_REG32_TIM(_name, _off) \
{ \
.name = "TIME-TAG:"#_name"_seconds_upper", \
#define FA_DBG_REG32_TIM(_name, _off) \
{ \
.name = "TIME-TAG:"#_name"_seconds_upper", \
.offset = ADC_UTC_OFF + _off \
}, { \
.name = "TIME-TAG:"#_name"_seconds_lower", \
}, { \
.name = "TIME-TAG:"#_name"_seconds_lower", \
.offset = ADC_UTC_OFF + _off + 0x4, \
}, { \
.name = "TIME-TAG:"#_name"_coarse", \
}, { \
.name = "TIME-TAG:"#_name"_coarse", \
.offset = ADC_UTC_OFF + _off + 0x8, \
}
......@@ -139,8 +139,8 @@ static void fa_regdump_seq_read_spi(struct fa_dev *fa, struct seq_file *s)
{
int i;
seq_printf(s, "ADC SPI registers\n");
seq_printf(s, "Address Data\n");
seq_puts(s, "ADC SPI registers\n");
seq_puts(s, "Address Data\n");
for (i = 0; i < 5; ++i) {
uint32_t tx, rx;
int err;
......@@ -149,11 +149,9 @@ static void fa_regdump_seq_read_spi(struct fa_dev *fa, struct seq_file *s)
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, tx, &rx);
rx &= 0xFF; /* the value is 8bit */
if (err)
seq_printf(s, "A%d %02xh read failure!\n",
i, i);
seq_printf(s, "A%d %02xh read failure!\n", i, i);
else
seq_printf(s, "A%d %02xh 0x%02x\n",
i, i, rx);
seq_printf(s, "A%d %02xh 0x%02x\n", i, i, rx);
}
}
......@@ -183,7 +181,7 @@ static const struct file_operations fa_regdump_ops = {
static ssize_t fa_trg_sw_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
size_t count, loff_t *ppos)
{
struct fa_dev *fa = file->private_data;
int err;
......@@ -212,7 +210,7 @@ static int fa_data_pattern_adc_write(struct fa_dev *fa, const char __user *buf,
if (err)
return -EFAULT;
if ((count == 1 || count == 2)&& buf_l[0] == '0') {
if ((count == 1 || count == 2) && buf_l[0] == '0') {
err = fa_adc_data_pattern_set(fa, 0, 0);
fa_calib_init(fa);
return err;
......@@ -249,10 +247,10 @@ static ssize_t fa_data_pattern_write(struct file *file, const char __user *buf,
err = fa_data_pattern_adc_write(fa, buf + 4, count - 4);
return err ? err : count;
} else {
dev_err(&fa->pdev->dev, "Unknown command \"%s\"\n", buf_l);
return -EINVAL;
}
dev_err(&fa->pdev->dev, "Unknown command \"%s\"\n", buf_l);
return -EINVAL;
}
static ssize_t fa_data_pattern_read(struct file *file, char __user *buf,
......@@ -264,10 +262,10 @@ static ssize_t fa_data_pattern_read(struct file *file, char __user *buf,
unsigned int enable;
int err;
if (*ppos > 0)
if (*ppos > 0)
return 0;
err = fa_adc_data_pattern_get(fa, &pattern, &enable);
err = fa_adc_data_pattern_get(fa, &pattern, &enable);
if (err)
return err;
snprintf(buf_l, FA_ADC_DATA_PATTERN_CMD_SIZE, "adc %u 0x%02x\n",
......@@ -301,14 +299,7 @@ int fa_debug_init(struct fa_dev *fa)
fa->dbg_reg32.regs = fa_debugfs_reg32;
fa->dbg_reg32.nregs = ARRAY_SIZE(fa_debugfs_reg32);
fa->dbg_reg32.base = fa->fa_top_level;
fa->dbg_reg = debugfs_create_regset32("regs", 0444, fa->dbg_dir,
&fa->dbg_reg32);
if (IS_ERR_OR_NULL(fa->dbg_reg)) {
err = PTR_ERR(fa->dbg_reg);
dev_warn(&fa->pdev->dev,
"Cannot create debugfs file \"regs\" (%d)\n",
err);
}
debugfs_create_regset32("regs", 0444, fa->dbg_dir, &fa->dbg_reg32);
fa->dbg_reg_spi = debugfs_create_file("spi-regs", 0444,
fa->dbg_dir, fa,
......
......@@ -4,53 +4,43 @@
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/types.h>
#ifdef CONFIG_FMC_ADC_SVEC
#include "vmebus.h"
#endif
#include "fmc-adc-100m14b4cha-private.h"
/* Endianess */
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 0
#endif
#ifndef BIG_ENDIAN
#define BIG_ENDIAN 1
#endif
static void zfad_dma_done(struct zio_cset *cset);
static int __get_endian(void)
{
int i = 1;
char *p = (char *)&i;
if (p[0] == 1)
return LITTLE_ENDIAN;
else
return BIG_ENDIAN;
}
/**
* Fix endianess from big to host endianess (32bit)
* Fix endianness from little to host endianess (32bit)
* @byte_lenght: number of bytes to fix (32bit aligned)
* @buffer: buffer to fix
*
* Data coming from the ADC IP-CORE are little-endian, so on big endian CPUs
* we have to swap the byte order.
*/
static void __endianness(unsigned int byte_length, void *buffer)
#ifdef __BIG_ENDIAN
static void fix_endianness(unsigned int byte_length, void *buffer)
{
/* CPU may be little endian, VME is big endian */
if (__get_endian() == LITTLE_ENDIAN) {
/* swap samples and trig timetag all seen as 32bits words */
int i;
int size = byte_length / 4;
uint32_t *ptr = buffer;
/* swap samples and trig timetag all seen as 32bits words */
int i;
int size = byte_length / 4;
uint32_t *ptr = buffer;
for (i = 0; i < size; ++i, ++ptr)
*ptr = __be32_to_cpu(*ptr);
}
for (i = 0; i < size; ++i, ++ptr)
le32_to_cpus(ptr);
}
#else
static void fix_endianness(unsigned int byte_length, void *buffer)
{
}
#endif
struct zfad_timetag {
uint32_t sec_low;
......@@ -76,7 +66,7 @@ static bool fa_dmaengine_filter_svec(struct dma_chan *dchan, void *arg)
struct fa_dev *fa = arg;
struct device *device_ref;
device_ref = fa->pdev->dev.parent->parent->parent->parent->parent->parent;
device_ref = fa->pdev->dev.parent->parent->parent->parent->parent->parent;
return (dchan->device->dev == device_ref);
}
......@@ -87,7 +77,7 @@ int fa_dma_request_channel(struct fa_dev *fa)
struct resource *r;
int dma_dev_id;
if (fa_is_flag_set(fa, FMC_ADC_SVEC))
if (fa_is_flag_set(fa, FMC_ADC_SVEC))
return 0;
r = platform_get_resource(fa->pdev, IORESOURCE_DMA, ADC_DMA);
......@@ -189,7 +179,7 @@ static uint32_t fa_ddr_offset_multi(struct fa_dev *fa, uint32_t shot_n)
struct zio_cset *cset = fa->zdev->cset;
uint32_t off;
off = cset->interleave->current_ctrl->ssize * cset->ti->nsamples;
off = cset->interleave->current_ctrl->ssize * cset->ti->nsamples;
off += FA_TRIG_TIMETAG_BYTES;
off *= shot_n;
......@@ -200,11 +190,10 @@ static uint32_t fa_ddr_offset(struct fa_dev *fa, uint32_t shot_n)
{
WARN(fa->n_shots == 1 && shot_n != 0,
"Inconsistent shot number %d\n", shot_n);
if (fa->n_shots == 1) {
if (fa->n_shots == 1)
return fa_ddr_offset_single(fa);
} else {
else
return fa_ddr_offset_multi(fa, shot_n);
}
}
static unsigned int zfad_block_n_pages(struct zio_block *block)
......@@ -320,12 +309,7 @@ static int zfad_dma_block_to_pages(struct page **pages, unsigned int nr_pages,
static void zfad_dma_context_exit_svec(struct zio_cset *cset,
struct zfad_block *zfad_block)
{
struct fa_dev *fa = cset->zdev->priv_d;
kfree(zfad_block->dma_ctx);
if (fa_is_flag_set(fa, FMC_ADC_DATA_NO_SWAP))
return;
__endianness(zfad_block->block->datalen, zfad_block->block->data);
}
static void zfad_dma_context_exit(struct zio_cset *cset,
......@@ -333,6 +317,7 @@ static void zfad_dma_context_exit(struct zio_cset *cset,
{
struct fa_dev *fa = cset->zdev->priv_d;
fix_endianness(zfad_block->block->datalen, zfad_block->block->data);
if (fa_is_flag_set(fa, FMC_ADC_SVEC))
zfad_dma_context_exit_svec(cset, zfad_block);
}
......@@ -546,7 +531,7 @@ static int fa_svec_ddr_window_set(struct fa_dev *fa, unsigned int offset)
return -ENODEV;
fa_iowrite(fa, offset, addr);
return 0;
return 0;
}
static int fa_dmaengine_slave_config(struct fa_dev *fa,
......@@ -582,7 +567,7 @@ static int fa_dma_shot_wait_svec(struct fa_dev *fa,
{
int err;
if (fa->n_shots == 1)
if (fa->n_shots == 1)
return 0;
err = wait_for_completion_interruptible_timeout(&zfad_block->shot_done,
......@@ -596,7 +581,7 @@ static int fa_dma_shot_wait_svec(struct fa_dev *fa,
return -EINVAL;
}
return 0;
return 0;
}
static int fa_dma_start_svec(struct zio_cset *cset)
......@@ -698,12 +683,11 @@ static int zfad_dma_start(struct zio_cset *cset)
err = fa_fsm_wait_state(fa, FA100M14B4C_STATE_IDLE, 10);
if (err) {
dev_warn(&fa->pdev->dev,
"Can't start DMA on the last acquisition, "
"State Machine is not IDLE\n");
"Can't start DMA on the last acquisition, State Machine is not IDLE\n");
return err;
}
dev_dbg(&fa->pdev->dev,
dev_dbg(&fa->pdev->dev,
"Start DMA transfer for %i shots of %i samples\n",
fa->n_shots, cset->ti->nsamples);
......@@ -712,14 +696,14 @@ static int zfad_dma_start(struct zio_cset *cset)
* different DMA transfers required for multi-shots
*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC], 0);
if (fa_is_flag_set(fa, FMC_ADC_SVEC))
if (fa_is_flag_set(fa, FMC_ADC_SVEC))
err = fa_dma_start_svec(cset);
else
err = fa_dma_start_spec(cset);
if (err)
goto err_start;
return 0;
return 0;
err_start:
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC],
......@@ -774,7 +758,7 @@ static void zfad_block_ctrl_attr_update(struct zio_block *block,
struct zio_control *ctrl = zio_get_ctrl(block);
uint32_t *ext_val = ctrl->attr_channel.ext_val;
ext_val[FA100M14B4C_TATTR_STA]= timetag->status;
ext_val[FA100M14B4C_TATTR_STA] = timetag->status;
ctrl->seq_num = seq_num;
}
......
......@@ -44,15 +44,15 @@ static struct zio_attribute zfad_cset_ext_zattr[] = {
*/
ZIO_ATTR_EXT("undersample", ZIO_RW_PERM, ZFAT_SR_UNDER, 1),
ZIO_ATTR_EXT("ch0-offset", ZIO_RW_PERM, ZFA_CH1_OFFSET, 0),
ZIO_ATTR_EXT("ch1-offset", ZIO_RW_PERM, ZFA_CH2_OFFSET, 0),
ZIO_ATTR_EXT("ch2-offset", ZIO_RW_PERM, ZFA_CH3_OFFSET, 0),
ZIO_ATTR_EXT("ch3-offset", ZIO_RW_PERM, ZFA_CH4_OFFSET, 0),
ZIO_ATTR_EXT("ch0-offset", ZIO_RW_PERM, ZFA_CH1_OFFSET, 0x8000),
ZIO_ATTR_EXT("ch1-offset", ZIO_RW_PERM, ZFA_CH2_OFFSET, 0x8000),
ZIO_ATTR_EXT("ch2-offset", ZIO_RW_PERM, ZFA_CH3_OFFSET, 0x8000),
ZIO_ATTR_EXT("ch3-offset", ZIO_RW_PERM, ZFA_CH4_OFFSET, 0x8000),
ZIO_ATTR_EXT("ch0-offset-zero", ZIO_RW_PERM, ZFA_SW_CH1_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch1-offset-zero", ZIO_RW_PERM, ZFA_SW_CH2_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch2-offset-zero", ZIO_RW_PERM, ZFA_SW_CH3_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch3-offset-zero", ZIO_RW_PERM, ZFA_SW_CH4_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch0-offset-zero", ZIO_RW_PERM, ZFA_SW_CH1_OFFSET_ZERO, 0x8000),
ZIO_ATTR_EXT("ch1-offset-zero", ZIO_RW_PERM, ZFA_SW_CH2_OFFSET_ZERO, 0x8000),
ZIO_ATTR_EXT("ch2-offset-zero", ZIO_RW_PERM, ZFA_SW_CH3_OFFSET_ZERO, 0x8000),
ZIO_ATTR_EXT("ch3-offset-zero", ZIO_RW_PERM, ZFA_SW_CH4_OFFSET_ZERO, 0x8000),
ZIO_ATTR_EXT("ch0-vref", ZIO_RW_PERM, ZFA_CH1_CTL_RANGE, 0),
ZIO_ATTR_EXT("ch1-vref", ZIO_RW_PERM, ZFA_CH2_CTL_RANGE, 0),
......@@ -104,7 +104,7 @@ static struct zio_attribute zfad_cset_ext_zattr[] = {
* 4: POST_TRIG
* 5: DECR_SHOT
* 7: Illegal
* */
*/
ZIO_PARAM_EXT("fsm-state", ZIO_RO_PERM, ZFA_STA_FSM, 0),
/* last acquisition end time stamp */
ZIO_PARAM_EXT("tstamp-acq-end-su", ZIO_RO_PERM,
......@@ -160,12 +160,6 @@ int zfad_convert_user_range(uint32_t user_val)
return zfad_convert_hw_range(user_val);
}
static bool fa_is_dac_offset_valid(int32_t user, int32_t zero)
{
int32_t offset = user + zero;
return (offset >= DAC_SAT_LOW && offset <= DAC_SAT_UP);
}
/*
* zfad_conf_set
*
......@@ -209,15 +203,17 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
/*fallthrough*/
case ZFA_SW_CH4_OFFSET_ZERO:
i--;
chan = to_zio_cset(dev)->chan + i;
if (!fa_is_dac_offset_valid(fa->user_offset[chan->index],
usr_val))
if (usr_val & 0xFFFF0000) {
dev_err(dev,
"Offset must be a 16bit unsigned value (0x%08x)\n",
usr_val);
return -EINVAL;
}
spin_lock(&fa->zdev->cset->lock);
fa->zero_offset[i] = usr_val;
fa_calib_dac_config_chan(fa, i, 0, FA_CALIB_FLAG_READ_TEMP);
err = fa_calib_dac_config_chan(fa, i, 0, FA_CALIB_FLAG_READ_TEMP);
spin_unlock(&fa->zdev->cset->lock);
return 0;
return err;
case ZFA_CHx_SAT:
/* TODO when TLV */
break;
......@@ -244,18 +240,27 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
/*fallthrough*/
case ZFA_CH4_OFFSET:
i--;
chan = to_zio_cset(dev)->chan + i;
if (!fa_is_dac_offset_valid(usr_val,
fa->zero_offset[chan->index]))
if (usr_val & 0xFFFF0000) {
dev_err(dev,
"Offset must be a 16bit unsigned value (0x%08x)\n",
usr_val);
return -EINVAL;
}
spin_lock(&fa->zdev->cset->lock);
fa->user_offset[chan->index] = usr_val;
fa->user_offset[i] = usr_val;
err = fa_calib_dac_config_chan(fa, i, 0,
FA_CALIB_FLAG_READ_TEMP);
spin_unlock(&fa->zdev->cset->lock);
return err;
case ZFA_CHx_OFFSET:
chan = to_zio_chan(dev);
if (usr_val & 0xFFFF0000) {
dev_err(dev,
"Offset must be a 16bit unsigned value (0x%08x)\n",
usr_val);
return -EINVAL;
}
spin_lock(&fa->zdev->cset->lock);
fa->user_offset[chan->index] = usr_val;
err = fa_calib_dac_config_chan(fa, chan->index, 0,
......@@ -425,18 +430,16 @@ static inline int zfat_overflow_detection(struct zio_ti *ti)
nsamples = ti_zattr[ZIO_ATTR_TRIG_PRE_SAMP].value +
ti_zattr[ZIO_ATTR_TRIG_POST_SAMP].value;
shot_size = ((nsamples + 2) * ti->cset->ssize) * FA100M14B4C_NCHAN;
if ( (shot_size * nshot_t) > FA100M14B4C_MAX_ACQ_BYTE ) {
if ((shot_size * nshot_t) > FA100M14B4C_MAX_ACQ_BYTE) {
dev_err(fa->msgdev, "Cannot acquire, dev memory overflow\n");
return -ENOMEM;
}
/* in case of multi shot, each shot cannot exceed the dpram size */
if ( (nshot_t > 1) &&
(nsamples > fa->mshot_max_samples) ) {
dev_err(fa->msgdev, "Cannot acquire such amount of samples "
"(req: %d , max: %d) in multi shot mode."
"dev memory overflow\n",
nsamples, fa->mshot_max_samples);
if ((nshot_t > 1) && (nsamples > fa->mshot_max_samples)) {
dev_err(fa->msgdev,
"Cannot acquire such amount of samples (req: %d , max: %d) in multi shot mode. dev memory overflow\n",
nsamples, fa->mshot_max_samples);
return -ENOMEM;
}
return 0;
......@@ -634,9 +637,8 @@ static struct zio_driver fa_zdrv = {
.remove = zfad_zio_remove,
/* Take the version from ZIO git sub-module */
.min_version = ZIO_VERSION(__ZIO_MIN_MAJOR_VERSION,
__ZIO_MIN_MINOR_VERSION,
0), /* Change it if you use new features from
a specific patch */
__ZIO_MIN_MINOR_VERSION,
0),
};
......
......@@ -297,8 +297,7 @@ static int zfat_data_done(struct zio_cset *cset)
i + 1, fa->n_shots);
zio_buffer_store_block(bi, zfad_block[i].block);
} else { /* Free un-filled blocks */
dev_dbg(fa->msgdev, "Free un-acquired block %d/%d "
"(received %d shots)\n",
dev_dbg(fa->msgdev, "Free un-acquired block %d/%d (received %d shots)\n",
i + 1, fa->n_shots, fa->n_fires);
zio_buffer_free_block(bi, zfad_block[i].block);
}
......@@ -383,8 +382,7 @@ static int zfat_arm_trigger(struct zio_ti *ti)
if (size % 4) {
/* should never happen: increase the size accordling */
dev_warn(fa->msgdev,
"zio data block size should 32bit word aligned."
"original size:%d was increased by %d bytes\n",
"zio data block size should 32bit word aligned. original size:%d was increased by %d bytes\n",
size, size%4);
size += size % 4;
}
......@@ -496,8 +494,8 @@ int fa_trig_init(void)
err = zio_register_trig(&zfat_type, "adc-100m14b");
if (err)
pr_err("%s: Cannot register ZIO trigger type"
" \"adc-100m14b\" (error %i)\n", KBUILD_MODNAME, err);
pr_err("%s: Cannot register ZIO trigger type \"adc-100m14b\" (error %i)\n",
KBUILD_MODNAME, err);
return err;
}
......
......@@ -12,6 +12,8 @@
#include "platform_data/fmc-adc-100m14b4cha.h"
enum fa_spec_dev_offsets {
FA_SPEC_DBL_ADC_META_START = 0x00000000,
FA_SPEC_DBL_ADC_META_END = 0x00000040,
FA_SPEC_ADC_MEM_START = 0x000002000,
FA_SPEC_ADC_MEM_END = 0x000003FFF,
};
......@@ -23,7 +25,8 @@ static const struct fmc_adc_platform_data fmc_adc_pdata = {
.calib_trig_internal = 0,
};
static int fa_spec_probe(struct platform_device *pdev) {
static int fa_spec_probe(struct platform_device *pdev)
{
static struct resource fa_spec_fdt_res[] = {
{
.name = "fmc-adc-mem",
......@@ -36,7 +39,14 @@ static int fa_spec_probe(struct platform_device *pdev) {
{
.name = "fmc-adc-irq",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
}};
},
{
.name = "fmc-adc-meta",
.flags = IORESOURCE_MEM,
.start = FA_SPEC_DBL_ADC_META_START,
.end = FA_SPEC_DBL_ADC_META_END,
},
};
struct platform_device_info pdevinfo = {
.parent = &pdev->dev,
.name = "fmc-adc-100m",
......@@ -93,6 +103,8 @@ static int fa_spec_probe(struct platform_device *pdev) {
fa_spec_fdt_res[0].end = rmem->start + FA_SPEC_ADC_MEM_END;
fa_spec_fdt_res[1].start = dma_dev_chan;
fa_spec_fdt_res[2].start = irq;
fa_spec_fdt_res[3].start = rmem->start + FA_SPEC_DBL_ADC_META_START;
fa_spec_fdt_res[3].end = rmem->start + FA_SPEC_DBL_ADC_META_END;
pdev_child = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev_child))
......@@ -101,7 +113,8 @@ static int fa_spec_probe(struct platform_device *pdev) {
return 0;
}
static int fa_spec_remove(struct platform_device *pdev) {
static int fa_spec_remove(struct platform_device *pdev)
{
struct platform_device *pdev_child = platform_get_drvdata(pdev);
platform_device_unregister(pdev_child);
......@@ -133,11 +146,10 @@ static const struct platform_device_id fa_spec_id_table[] = {
};
static struct platform_driver fa_spec_driver = {
.driver =
{
.driver = {
.name = "fmc-adc-100m-spec",
.owner = THIS_MODULE,
},
},
.id_table = fa_spec_id_table,
.probe = fa_spec_probe,
.remove = fa_spec_remove,
......
......@@ -12,6 +12,7 @@
#include <linux/mfd/core.h>
#include <linux/fmc.h>
#include "fmc-adc-100m14b4cha-private.h"
#include "platform_data/fmc-adc-100m14b4cha.h"
#define SVEC_FMC_SLOTS 2
......@@ -28,10 +29,12 @@
#define SVEC_FPGA_DDR5_DMA (0x3000)
enum fa_svec_dev_offsets {
FA_SVEC_ADC1_MEM_START = 0x00002000,
FA_SVEC_ADC1_MEM_END = 0x00003FFF,
FA_SVEC_ADC2_MEM_START = 0x00004000,
FA_SVEC_ADC2_MEM_END = 0x00005FFF,
FA_SVEC_DBL_ADC_META_START = 0x00000000,
FA_SVEC_DBL_ADC_META_END = 0x00000040,
FA_SVEC_ADC1_MEM_START = 0x00002000,
FA_SVEC_ADC1_MEM_END = 0x00003FFF,
FA_SVEC_ADC2_MEM_START = 0x00004000,
FA_SVEC_ADC2_MEM_END = 0x00005FFF,
};
static inline struct platform_device *platform_device_register_resndata_mask(
......@@ -57,19 +60,19 @@ static inline struct platform_device *platform_device_register_resndata_mask(
static struct fmc_adc_platform_data fa_svec_adc_pdata[] = {
{
.flags = FMC_ADC_BIG_ENDIAN |
FMC_ADC_SVEC |
FMC_ADC_NOSQUASH_SCATTERLIST,
FMC_ADC_SVEC |
FMC_ADC_NOSQUASH_SCATTERLIST,
.vme_reg_offset = SVEC_FPGA_CSR_DDR4_ADDR,
.vme_dma_offset = SVEC_FPGA_DDR4_DMA,
.vme_dma_offset = SVEC_FPGA_DDR4_DMA,
.calib_trig_time = 0,
.calib_trig_threshold = 0,
.calib_trig_internal = 0,
}, {
.flags = FMC_ADC_BIG_ENDIAN |
FMC_ADC_SVEC |
FMC_ADC_NOSQUASH_SCATTERLIST,
FMC_ADC_SVEC |
FMC_ADC_NOSQUASH_SCATTERLIST,
.vme_reg_offset = SVEC_FPGA_CSR_DDR5_ADDR,
.vme_dma_offset = SVEC_FPGA_DDR5_DMA,
.vme_dma_offset = SVEC_FPGA_DDR5_DMA,
.calib_trig_time = 0,
.calib_trig_threshold = 0,
.calib_trig_internal = 0,
......@@ -93,6 +96,12 @@ static struct resource fa_svec_res1[] = {
.start = 0,
.end = 0,
},
{
.name = "fmc-adc-100m-meta.1",
.flags = IORESOURCE_MEM,
.start = FA_SVEC_DBL_ADC_META_START,
.end = FA_SVEC_DBL_ADC_META_END,
},
};
static struct resource fa_svec_res2[] = {
......@@ -112,6 +121,12 @@ static struct resource fa_svec_res2[] = {
.start = 1,
.end = 1,
},
{
.name = "fmc-adc-100m-meta.2",
.flags = IORESOURCE_MEM,
.start = FA_SVEC_DBL_ADC_META_START,
.end = FA_SVEC_DBL_ADC_META_END,
},
};
static struct resource *fa_svec_res[] = {
......@@ -131,7 +146,7 @@ static int fa_svec_probe(struct platform_device *pdev)
int irq;
int i;
rmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!rmem) {
dev_err(&pdev->dev, "Missing memory resource\n");
return -EINVAL;
......@@ -153,7 +168,7 @@ static int fa_svec_probe(struct platform_device *pdev)
struct fmc_slot *slot = fmc_slot_get(pdev->dev.parent, i + 1);
int present;
if (IS_ERR(slot)) {
if (IS_ERR(slot)) {
dev_err(&pdev->dev,
"Can't find FMC slot %d err: %ld\n",
i + 1, PTR_ERR(slot));
......@@ -169,10 +184,13 @@ static int fa_svec_probe(struct platform_device *pdev)
memcpy(res, fa_svec_res[i], sizeof(res));
res[0].parent = rmem;
res[0].parent = rmem;
res[0].start += rmem->start;
res[0].end += rmem->start;
res[2].start += irq;
res[3].start = rmem->start + FA_SVEC_DBL_ADC_META_START;
res[3].end = rmem->start + FA_SVEC_DBL_ADC_META_END;
pdev_data->adc[i] = platform_device_register_resndata_mask(&pdev->dev,
"fmc-adc-100m",
PLATFORM_DEVID_AUTO,
......@@ -190,7 +208,7 @@ static int fa_svec_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, pdev_data);
return 0;
return 0;
}
static int fa_svec_remove(struct platform_device *pdev)
......@@ -201,12 +219,12 @@ static int fa_svec_remove(struct platform_device *pdev)
if (!pdev_data)
return 0;
for (i = 0; i < SVEC_FMC_SLOTS; ++i)
for (i = 0; i < SVEC_FMC_SLOTS; ++i)
if (pdev_data->adc[i])
platform_device_unregister(pdev_data->adc[i]);
kfree(pdev_data);
return 0;
return 0;
}
/**
......
......@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/fmc.h>
#include <linux/completion.h>
#include <linux/version.h>
#include <linux/zio.h>
#include <linux/zio-dma.h>
......@@ -31,6 +32,20 @@ enum fa_versions {
ADC_VER = 0,
};
/**
* struct device_meta_id Metadata
*/
struct device_meta_id {
uint32_t vendor;
uint32_t device;
uint32_t version;
uint32_t bom;
uint32_t src[4];
uint32_t cap;
uint32_t uuid[4];
};
enum fa100m14b4c_trg_ext_attr_krn {
FA100M14B4C_TATTR_TRG_SU = __FA100M14B4C_TATTR_TRG_MAX,
FA100M14B4C_TATTR_TRG_SL,
......@@ -42,8 +57,7 @@ enum fa100m14b4c_trg_ext_attr_krn {
#define ADC_SPI_OFF 0x1800
#define ADC_UTC_OFF 0x1900
#define DAC_SAT_LOW -5000000
#define DAC_SAT_UP 5000000
#define DAC_VAL_MASK 0xFFFF
#define ADC_DMA 0
......@@ -57,6 +71,7 @@ enum fa_irq_resource {
enum fa_mem_resource {
ADC_MEM_BASE = 0,
ADC_MEM_META,
};
enum fa_bus_resource {
......@@ -64,7 +79,11 @@ enum fa_bus_resource {
};
struct fa_memory_ops {
#if KERNEL_VERSION(5, 8, 0) <= LINUX_VERSION_CODE
u32 (*read)(const void *addr);
#else
u32 (*read)(void *addr);
#endif
void (*write)(u32 value, void *addr);
};
......@@ -272,8 +291,8 @@ struct fa_dev; /* forward declaration */
* @n_fires: number of trigger fire occurred within an acquisition
*
* @n_dma_err: number of errors
* @user_offset: user offset (micro-Volts)
* @zero_offset: necessary offset to push the channel to zero (micro-Volts)
* @user_offset: user offset
* @zero_offset: necessary offset to push the channel to zero
*/
struct fa_dev {
unsigned long flags;
......@@ -289,6 +308,8 @@ struct fa_dev {
struct fmc_slot *slot;
struct fa_memory_ops memops;
struct device_meta_id meta;
/* carrier common base offset addresses */
void *fa_adc_csr_base;
void *fa_spi_base;
......@@ -319,8 +340,8 @@ struct fa_dev {
unsigned int n_dma_err;
/* Configuration */
int32_t user_offset[4]; /* one per channel */
int32_t zero_offset[FA100M14B4C_NCHAN];
uint16_t user_offset[FA100M14B4C_NCHAN]; /* one per channel */
uint16_t zero_offset[FA100M14B4C_NCHAN];
/* one-wire */
uint8_t ds18_id[8];
unsigned long next_t;
......@@ -336,7 +357,6 @@ struct fa_dev {
struct dentry *dbg_dir;
struct debugfs_regset32 dbg_reg32;
struct dentry *dbg_reg;
struct dentry *dbg_reg_spi;
struct dentry *dbg_trg_sw;
struct dentry *dbg_data_pattern;
......
......@@ -139,4 +139,15 @@ struct fa_calib {
struct fa_calib_stanza dac[FA_CALIB_STANZA_N]; /* For user offset, one per range */
};
#define FA_VERSION_DRV FA_VERSION_BLD
#define FA_VERSION_MAJ(_VER) ((_VER >> 24) & 0xFF)
#define FA_VERSION_MIN(_VER) ((_VER >> 16) & 0xFF)
#define FA_VERSION_PATCH(_VER) (_VER & 0xFFFF)
#define PCI_VENDOR_ID_CERN (0x10DC)
#define FA_META_VENDOR_ID PCI_VENDOR_ID_CERN
#define FA_META_DEVICE_ID_SPEC 0x41444301
#define FA_META_DEVICE_ID_SVEC_DBL_ADC 0x41444302
#endif /* FMC_ADC_H_ */
......@@ -7,9 +7,8 @@
#ifndef __FMC_ADC_PDATA_H__
#define __FMC_ADC_PDATA_H__
#define FMC_ADC_BIG_ENDIAN BIT(0)
#define FMC_ADC_BIG_ENDIAN BIT(0) /* Registers are in BIG ENDIAN */
#define FMC_ADC_NOSQUASH_SCATTERLIST BIT(1)
#define FMC_ADC_DATA_NO_SWAP BIT(2)
/*
* In principle this should not be necessary. The two variants should
......
......@@ -49,7 +49,7 @@ int fa_spi_xfer(struct fa_dev *fa, int cs, int num_bits,
/* Wait transfer complete */
while (fa_ioread(fa, fa->fa_spi_base + FA_SPI_CTRL)
& FA_SPI_CTRL_BUSY) {
if (jiffies > j) {
if (time_after(jiffies, j)) {
dev_err(fa->msgdev, "SPI transfer error cs:%d, ctrl: 0x%x\n",
cs, fa_ioread(fa, fa->fa_spi_base + FA_SPI_CTRL));
err = -EIO;
......@@ -77,11 +77,14 @@ int fa_spi_init(struct fa_dev *fa)
/* software reset the ADC chip (register 0) */
fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, BIT(7), &rx);
msleep(5);
msleep(20);
/* Force 2's complement data output (register 1, bit 5) */
fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, BIT(8) | BIT(5), &rx);
/* 2 lanes, 14-bit serializaion (register 2) */
fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, (2 << 8) | 0x1, &rx);
return 0;
}
......
......@@ -19,6 +19,7 @@ CFLAGS += -I../kernel -Wno-trigraphs -Wall -Werror -ggdb -O2 $(EXTRACFLAGS)
CFLAGS += -DGIT_VERSION="\"$(GIT_VERSION)\""
CPPCHECK ?= cppcheck
FLAWFINDER ?= flawfinder
CC ?= $(CROSS_COMPILE)gcc
progs := fau-trg-config
......@@ -45,3 +46,6 @@ install:
cppcheck:
$(CPPCHECK) -q -I. --suppress=missingIncludeSystem --enable=all *.c *.h --error-exitcode=1
flawfinder:
$(FLAWFINDER) -SQDC --error-level=4 .
......@@ -17,9 +17,10 @@
static char git_version[] = "version: " GIT_VERSION;
#define buf_len 50
#define path_len 200
#define base_len 40
/* user will edit by adding the device name */
char basepath[40] = "/sys/bus/zio/devices/";
char basepath[base_len] = "/sys/bus/zio/devices/";
enum fau_attribute {
FAU_UTR_STR_S,
......@@ -55,10 +56,10 @@ const char *attribute[] = {
/* Write a sysfs attribute */
int fau_read_attribute(enum fau_attribute attr, long *val)
{
char fullpath[200];
char fullpath[path_len];
FILE *f;
sprintf(fullpath, "%s%s", basepath, attribute[attr]);
snprintf(fullpath, path_len, "%s%s", basepath, attribute[attr]);
f = fopen(fullpath, "r");
if (!f)
return -1;
......@@ -153,7 +154,7 @@ int main(int argc, char *argv[])
exit(1);
}
strcat(basepath, argv[argc-1]);
strncat(basepath, argv[argc-1], base_len);
printf("Sysfs path to device is: %s\n", basepath);
if (last) {
......
......@@ -18,28 +18,32 @@
#include <fmc-adc-100m14b4cha.h>
#define MAX_OPT_NR 5
static char options[] = "hf:o:D:b";
static const char help_msg[] =
"Usage: fau-calibration [options]\n"
"\n"
"It reads calibration data from a file that contains it in binary\n"
"form and it shows it on STDOUT in binary form or in human readable\n"
"one (default).\n"
"This could be used to change the ADC calibration data at runtime\n"
"by redirectiong the binary output of this program to the proper \n"
"sysfs binary attribute\n"
"Rembember that we expect all values to be little endian\n"
"\n"
"General options:\n"
"-h Print this message\n"
"-b Show Calibration in binary form \n"
"\n"
"Read options:\n"
"-f Source file where to read calibration data from\n"
"-o Offset in bytes within the file (default 0)\n"
"Write options:\n"
"-D FMC ADC Target Device ID\n"
"\n";
static void fau_calibration_help(void)
{
fputs("Usage: fau-calibration [options]\n"
"\n"
"It reads calibration data from a file that contains it in binary\n"
"form and it shows it on STDOUT in binary form or in human readable\n"
"one (default).\n"
"This could be used to change the ADC calibration data at runtime\n"
"by redirectiong the binary output of this program to the proper \n"
"sysfs binary attribute\n"
"Rembember that we expect all values to be little endian\n"
"\n"
"General options:\n"
"-h Print this message\n"
"-b Show Calibration in binary form \n"
"\n"
"Read options:\n"
"-f Source file where to read calibration data from\n"
"-o Offset in bytes within the file (default 0)\n"
"Write options:\n"
"-D FMC ADC Target Device ID\n"
"\n", stdout);
}
/**
* Read calibration data from file
......@@ -49,7 +53,7 @@ static const char help_msg[] =
*
* Return: number of bytes read
*/
static int fau_calibration_read(char *path, struct fa_calib *calib,
static int fau_calibration_read(const char *path, struct fa_calib *calib,
off_t offset)
{
int fd;
......@@ -59,8 +63,13 @@ static int fau_calibration_read(char *path, struct fa_calib *calib,
if (fd < 0)
return -1;
ret = lseek(fd, offset, SEEK_SET);
if (ret >= 0)
if (ret >= 0) {
ret = read(fd, calib, sizeof(*calib));
if (ret != sizeof(*calib)) {
ret = -1;
errno =EINVAL;
}
}
close(fd);
return ret;
......@@ -121,11 +130,11 @@ static int fau_calibration_dump_machine(struct fa_calib *calib)
*/
static int fau_calibration_write(unsigned int devid, struct fa_calib *calib)
{
char path[128];
char path[55]; // store exactly the path we need
int fd;
int ret;
sprintf(path,
snprintf(path, sizeof(path),
"/sys/bus/zio/devices/adc-100m14b-%04x/calibration_data",
devid);
......@@ -148,11 +157,24 @@ int main(int argc, char *argv[])
int show_bin = 0, write = 0;
struct fa_calib calib;
if (argc <= 0) {
fprintf(stderr, "What is going on here?\n");
exit(EXIT_FAILURE);
}
if (argc > MAX_OPT_NR) {
fprintf(stderr, "This program accepts no more that %d arguments, you provides %d\n",
MAX_OPT_NR, argc);
fau_calibration_help();
exit(EXIT_FAILURE);
}
while ((c = getopt(argc, argv, options)) != -1) {
switch (c) {
default:
case 'h':
fprintf(stderr, help_msg);
case 'h':
fau_calibration_help();
exit(EXIT_SUCCESS);
case 'D':
ret = sscanf(optarg, "0x%x", &devid);
......
......@@ -12,16 +12,20 @@
#include <stdlib.h>
#include <stdint.h>
#include <getopt.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
static char git_version[] = "version: " GIT_VERSION;
#define path_len 200
#define buf_len 50
#define base_len 40
/* user will edit by adding the device name */
char basepath[40] = "/sys/bus/zio/devices/";
char basepath[base_len] = "/sys/bus/zio/devices/";
enum fau_attribute {
FAU_TRG_EN,
......@@ -54,19 +58,21 @@ const char *attribute[] = {
int fau_write_attribute(enum fau_attribute attr, uint32_t val)
{
int ret, fd;
char buf[buf_len], fullpath[200];
char buf[buf_len], fullpath[path_len];
/* convert val to string */
sprintf(buf,"%u",val);
snprintf(buf, buf_len, "%u",val);
/* build the attribute path */
strcpy(fullpath, basepath);
strcat(fullpath, attribute[attr]);
strncpy(fullpath, basepath, path_len);
if (path_len > 0)
fullpath[path_len -1] = '\0';
strncat(fullpath, attribute[attr], path_len);
/* Write the attribute */
printf("Writing %s in %s\n", buf, fullpath);
fd = open(fullpath, O_WRONLY);
if (fd < 0)
return -ENOENT;
ret = write(fd, buf, strlen(buf));
ret = write(fd, buf, strnlen(buf, buf_len));
close(fd);
return ret;
}
......@@ -102,6 +108,19 @@ static void print_version(char *pname)
printf("%s %s\n", pname, git_version);
}
static long strtol_or_die(const char *arg)
{
long val = strtol(arg, NULL, 0);
if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) {
fprintf(stderr, "Can't convert \"%s\" to integer\n", optarg);
exit(EXIT_FAILURE);
}
return val;
}
int main(int argc, char *argv[])
{
/* default attribute */
......@@ -137,22 +156,22 @@ int main(int argc, char *argv[])
options, &opt_index)) >=0 ){
switch(c){
case 'p':
attrval[FAU_TRG_PRE] = atoi(optarg);
attrval[FAU_TRG_PRE] = strtol_or_die(optarg);
break;
case 'P':
attrval[FAU_TRG_PST] = atoi(optarg);
attrval[FAU_TRG_PST] = strtol_or_die(optarg);
break;
case 'n':
attrval[FAU_TRG_RE_EN] = atoi(optarg);
attrval[FAU_TRG_RE_EN] = strtol_or_die(optarg);
break;
case 'd':
attrval[FAU_TRG_DLY] = atoi(optarg);
attrval[FAU_TRG_DLY] = strtol_or_die(optarg);
break;
case 't':
attrval[FAU_TRG_THR] = atoi(optarg);
attrval[FAU_TRG_THR] = strtol_or_die(optarg);
break;
case 'c':
attrval[FAU_TRG_CHN] = atoi(optarg);
attrval[FAU_TRG_CHN] = strtol_or_die(optarg);
break;
case 'V':
print_version(argv[0]);
......@@ -171,7 +190,7 @@ int main(int argc, char *argv[])
exit(1);
}
strcat(basepath, argv[optind]);
strncat(basepath, argv[optind], base_len);
printf("Sysfs path to device is: %s\n", basepath);
for (i = 0; i < FAU_TRIG_NUM_ATTR; ++i) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment