Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

misc: xilinx_sdfec: Store driver config and state

Stores configuration based on parameters from the DT
node and values from the SD-FEC core plus reads
the default state from the SD-FEC core. To obtain
values from the core register read, write capabilities
have been added plus related register map details.

Tested-by: Dragan Cvetic <dragan.cvetic@xilinx.com>
Signed-off-by: Derek Kiernan <derek.kiernan@xilinx.com>
Signed-off-by: Dragan Cvetic <dragan.cvetic@xilinx.com>
Link: https://lore.kernel.org/r/1564216438-322406-2-git-send-email-dragan.cvetic@xilinx.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Dragan Cvetic and committed by
Greg Kroah-Hartman
6d54e455 77e38c19

+437 -6
+299 -6
drivers/misc/xilinx_sdfec.c
··· 20 20 #include <linux/slab.h> 21 21 #include <linux/clk.h> 22 22 23 + #include <uapi/misc/xilinx_sdfec.h> 24 + 23 25 #define DEV_NAME_LEN 12 24 26 25 27 static struct idr dev_idr; 26 28 static struct mutex dev_idr_lock; 29 + 30 + /* Xilinx SDFEC Register Map */ 31 + /* CODE_WRI_PROTECT Register */ 32 + #define XSDFEC_CODE_WR_PROTECT_ADDR (0x4) 33 + 34 + /* ACTIVE Register */ 35 + #define XSDFEC_ACTIVE_ADDR (0x8) 36 + #define XSDFEC_IS_ACTIVITY_SET (0x1) 37 + 38 + /* AXIS_WIDTH Register */ 39 + #define XSDFEC_AXIS_WIDTH_ADDR (0xC) 40 + #define XSDFEC_AXIS_DOUT_WORDS_LSB (5) 41 + #define XSDFEC_AXIS_DOUT_WIDTH_LSB (3) 42 + #define XSDFEC_AXIS_DIN_WORDS_LSB (2) 43 + #define XSDFEC_AXIS_DIN_WIDTH_LSB (0) 44 + 45 + /* AXIS_ENABLE Register */ 46 + #define XSDFEC_AXIS_ENABLE_ADDR (0x10) 47 + #define XSDFEC_AXIS_OUT_ENABLE_MASK (0x38) 48 + #define XSDFEC_AXIS_IN_ENABLE_MASK (0x7) 49 + #define XSDFEC_AXIS_ENABLE_MASK \ 50 + (XSDFEC_AXIS_OUT_ENABLE_MASK | XSDFEC_AXIS_IN_ENABLE_MASK) 51 + 52 + /* FEC_CODE Register */ 53 + #define XSDFEC_FEC_CODE_ADDR (0x14) 54 + 55 + /* ORDER Register Map */ 56 + #define XSDFEC_ORDER_ADDR (0x18) 57 + 58 + /* Interrupt Status Register */ 59 + #define XSDFEC_ISR_ADDR (0x1C) 60 + /* Interrupt Status Register Bit Mask */ 61 + #define XSDFEC_ISR_MASK (0x3F) 62 + 63 + /* Write Only - Interrupt Enable Register */ 64 + #define XSDFEC_IER_ADDR (0x20) 65 + /* Write Only - Interrupt Disable Register */ 66 + #define XSDFEC_IDR_ADDR (0x24) 67 + /* Read Only - Interrupt Mask Register */ 68 + #define XSDFEC_IMR_ADDR (0x28) 69 + 70 + /* ECC Interrupt Status Register */ 71 + #define XSDFEC_ECC_ISR_ADDR (0x2C) 72 + /* Single Bit Errors */ 73 + #define XSDFEC_ECC_ISR_SBE_MASK (0x7FF) 74 + /* PL Initialize Single Bit Errors */ 75 + #define XSDFEC_PL_INIT_ECC_ISR_SBE_MASK (0x3C00000) 76 + /* Multi Bit Errors */ 77 + #define XSDFEC_ECC_ISR_MBE_MASK (0x3FF800) 78 + /* PL Initialize Multi Bit Errors */ 79 + #define XSDFEC_PL_INIT_ECC_ISR_MBE_MASK (0x3C000000) 80 + /* Multi Bit Error to Event Shift */ 81 + #define XSDFEC_ECC_ISR_MBE_TO_EVENT_SHIFT (11) 82 + /* PL Initialize Multi Bit Error to Event Shift */ 83 + #define XSDFEC_PL_INIT_ECC_ISR_MBE_TO_EVENT_SHIFT (4) 84 + /* ECC Interrupt Status Bit Mask */ 85 + #define XSDFEC_ECC_ISR_MASK (XSDFEC_ECC_ISR_SBE_MASK | XSDFEC_ECC_ISR_MBE_MASK) 86 + /* ECC Interrupt Status PL Initialize Bit Mask */ 87 + #define XSDFEC_PL_INIT_ECC_ISR_MASK \ 88 + (XSDFEC_PL_INIT_ECC_ISR_SBE_MASK | XSDFEC_PL_INIT_ECC_ISR_MBE_MASK) 89 + /* ECC Interrupt Status All Bit Mask */ 90 + #define XSDFEC_ALL_ECC_ISR_MASK \ 91 + (XSDFEC_ECC_ISR_MASK | XSDFEC_PL_INIT_ECC_ISR_MASK) 92 + /* ECC Interrupt Status Single Bit Errors Mask */ 93 + #define XSDFEC_ALL_ECC_ISR_SBE_MASK \ 94 + (XSDFEC_ECC_ISR_SBE_MASK | XSDFEC_PL_INIT_ECC_ISR_SBE_MASK) 95 + /* ECC Interrupt Status Multi Bit Errors Mask */ 96 + #define XSDFEC_ALL_ECC_ISR_MBE_MASK \ 97 + (XSDFEC_ECC_ISR_MBE_MASK | XSDFEC_PL_INIT_ECC_ISR_MBE_MASK) 98 + 99 + /* Write Only - ECC Interrupt Enable Register */ 100 + #define XSDFEC_ECC_IER_ADDR (0x30) 101 + /* Write Only - ECC Interrupt Disable Register */ 102 + #define XSDFEC_ECC_IDR_ADDR (0x34) 103 + /* Read Only - ECC Interrupt Mask Register */ 104 + #define XSDFEC_ECC_IMR_ADDR (0x38) 105 + 106 + /* BYPASS Register */ 107 + #define XSDFEC_BYPASS_ADDR (0x3C) 27 108 28 109 /** 29 110 * struct xsdfec_clks - For managing SD-FEC clocks ··· 130 49 131 50 /** 132 51 * struct xsdfec_dev - Driver data for SDFEC 52 + * @miscdev: Misc device handle 53 + * @clks: Clocks managed by the SDFEC driver 133 54 * @regs: device physical base address 134 55 * @dev: pointer to device struct 135 - * @miscdev: Misc device handle 136 - * @error_data_lock: Error counter and states spinlock 137 - * @clks: Clocks managed by the SDFEC driver 56 + * @config: Configuration of the SDFEC device 138 57 * @dev_name: Device name 58 + * @state: State of the SDFEC device 59 + * @error_data_lock: Error counter and states spinlock 139 60 * @dev_id: Device ID 140 61 * 141 62 * This structure contains necessary state for SDFEC driver to operate 142 63 */ 143 64 struct xsdfec_dev { 65 + struct miscdevice miscdev; 66 + struct xsdfec_clks clks; 144 67 void __iomem *regs; 145 68 struct device *dev; 146 - struct miscdevice miscdev; 69 + struct xsdfec_config config; 70 + char dev_name[DEV_NAME_LEN]; 71 + enum xsdfec_state state; 147 72 /* Spinlock to protect state_updated and stats_updated */ 148 73 spinlock_t error_data_lock; 149 - struct xsdfec_clks clks; 150 - char dev_name[DEV_NAME_LEN]; 151 74 int dev_id; 152 75 }; 76 + 77 + static inline void xsdfec_regwrite(struct xsdfec_dev *xsdfec, u32 addr, 78 + u32 value) 79 + { 80 + dev_dbg(xsdfec->dev, "Writing 0x%x to offset 0x%x", value, addr); 81 + iowrite32(value, xsdfec->regs + addr); 82 + } 83 + 84 + static inline u32 xsdfec_regread(struct xsdfec_dev *xsdfec, u32 addr) 85 + { 86 + u32 rval; 87 + 88 + rval = ioread32(xsdfec->regs + addr); 89 + dev_dbg(xsdfec->dev, "Read value = 0x%x from offset 0x%x", rval, addr); 90 + return rval; 91 + } 92 + 93 + static void update_bool_config_from_reg(struct xsdfec_dev *xsdfec, 94 + u32 reg_offset, u32 bit_num, 95 + char *config_value) 96 + { 97 + u32 reg_val; 98 + u32 bit_mask = 1 << bit_num; 99 + 100 + reg_val = xsdfec_regread(xsdfec, reg_offset); 101 + *config_value = (reg_val & bit_mask) > 0; 102 + } 103 + 104 + static void update_config_from_hw(struct xsdfec_dev *xsdfec) 105 + { 106 + u32 reg_value; 107 + bool sdfec_started; 108 + 109 + /* Update the Order */ 110 + reg_value = xsdfec_regread(xsdfec, XSDFEC_ORDER_ADDR); 111 + xsdfec->config.order = reg_value; 112 + 113 + update_bool_config_from_reg(xsdfec, XSDFEC_BYPASS_ADDR, 114 + 0, /* Bit Number, maybe change to mask */ 115 + &xsdfec->config.bypass); 116 + 117 + update_bool_config_from_reg(xsdfec, XSDFEC_CODE_WR_PROTECT_ADDR, 118 + 0, /* Bit Number */ 119 + &xsdfec->config.code_wr_protect); 120 + 121 + reg_value = xsdfec_regread(xsdfec, XSDFEC_IMR_ADDR); 122 + xsdfec->config.irq.enable_isr = (reg_value & XSDFEC_ISR_MASK) > 0; 123 + 124 + reg_value = xsdfec_regread(xsdfec, XSDFEC_ECC_IMR_ADDR); 125 + xsdfec->config.irq.enable_ecc_isr = 126 + (reg_value & XSDFEC_ECC_ISR_MASK) > 0; 127 + 128 + reg_value = xsdfec_regread(xsdfec, XSDFEC_AXIS_ENABLE_ADDR); 129 + sdfec_started = (reg_value & XSDFEC_AXIS_IN_ENABLE_MASK) > 0; 130 + if (sdfec_started) 131 + xsdfec->state = XSDFEC_STARTED; 132 + else 133 + xsdfec->state = XSDFEC_STOPPED; 134 + } 135 + 136 + static u32 137 + xsdfec_translate_axis_width_cfg_val(enum xsdfec_axis_width axis_width_cfg) 138 + { 139 + u32 axis_width_field = 0; 140 + 141 + switch (axis_width_cfg) { 142 + case XSDFEC_1x128b: 143 + axis_width_field = 0; 144 + break; 145 + case XSDFEC_2x128b: 146 + axis_width_field = 1; 147 + break; 148 + case XSDFEC_4x128b: 149 + axis_width_field = 2; 150 + break; 151 + } 152 + 153 + return axis_width_field; 154 + } 155 + 156 + static u32 xsdfec_translate_axis_words_cfg_val(enum xsdfec_axis_word_include 157 + axis_word_inc_cfg) 158 + { 159 + u32 axis_words_field = 0; 160 + 161 + if (axis_word_inc_cfg == XSDFEC_FIXED_VALUE || 162 + axis_word_inc_cfg == XSDFEC_IN_BLOCK) 163 + axis_words_field = 0; 164 + else if (axis_word_inc_cfg == XSDFEC_PER_AXI_TRANSACTION) 165 + axis_words_field = 1; 166 + 167 + return axis_words_field; 168 + } 169 + 170 + static int xsdfec_cfg_axi_streams(struct xsdfec_dev *xsdfec) 171 + { 172 + u32 reg_value; 173 + u32 dout_words_field; 174 + u32 dout_width_field; 175 + u32 din_words_field; 176 + u32 din_width_field; 177 + struct xsdfec_config *config = &xsdfec->config; 178 + 179 + /* translate config info to register values */ 180 + dout_words_field = 181 + xsdfec_translate_axis_words_cfg_val(config->dout_word_include); 182 + dout_width_field = 183 + xsdfec_translate_axis_width_cfg_val(config->dout_width); 184 + din_words_field = 185 + xsdfec_translate_axis_words_cfg_val(config->din_word_include); 186 + din_width_field = 187 + xsdfec_translate_axis_width_cfg_val(config->din_width); 188 + 189 + reg_value = dout_words_field << XSDFEC_AXIS_DOUT_WORDS_LSB; 190 + reg_value |= dout_width_field << XSDFEC_AXIS_DOUT_WIDTH_LSB; 191 + reg_value |= din_words_field << XSDFEC_AXIS_DIN_WORDS_LSB; 192 + reg_value |= din_width_field << XSDFEC_AXIS_DIN_WIDTH_LSB; 193 + 194 + xsdfec_regwrite(xsdfec, XSDFEC_AXIS_WIDTH_ADDR, reg_value); 195 + 196 + return 0; 197 + } 153 198 154 199 static const struct file_operations xsdfec_fops = { 155 200 .owner = THIS_MODULE, 156 201 }; 202 + 203 + static int xsdfec_parse_of(struct xsdfec_dev *xsdfec) 204 + { 205 + struct device *dev = xsdfec->dev; 206 + struct device_node *node = dev->of_node; 207 + int rval; 208 + const char *fec_code; 209 + u32 din_width; 210 + u32 din_word_include; 211 + u32 dout_width; 212 + u32 dout_word_include; 213 + 214 + rval = of_property_read_string(node, "xlnx,sdfec-code", &fec_code); 215 + if (rval < 0) 216 + return rval; 217 + 218 + if (!strcasecmp(fec_code, "ldpc")) 219 + xsdfec->config.code = XSDFEC_LDPC_CODE; 220 + else if (!strcasecmp(fec_code, "turbo")) 221 + xsdfec->config.code = XSDFEC_TURBO_CODE; 222 + else 223 + return -EINVAL; 224 + 225 + rval = of_property_read_u32(node, "xlnx,sdfec-din-words", 226 + &din_word_include); 227 + if (rval < 0) 228 + return rval; 229 + 230 + if (din_word_include < XSDFEC_AXIS_WORDS_INCLUDE_MAX) 231 + xsdfec->config.din_word_include = din_word_include; 232 + else 233 + return -EINVAL; 234 + 235 + rval = of_property_read_u32(node, "xlnx,sdfec-din-width", &din_width); 236 + if (rval < 0) 237 + return rval; 238 + 239 + switch (din_width) { 240 + /* Fall through and set for valid values */ 241 + case XSDFEC_1x128b: 242 + case XSDFEC_2x128b: 243 + case XSDFEC_4x128b: 244 + xsdfec->config.din_width = din_width; 245 + break; 246 + default: 247 + return -EINVAL; 248 + } 249 + 250 + rval = of_property_read_u32(node, "xlnx,sdfec-dout-words", 251 + &dout_word_include); 252 + if (rval < 0) 253 + return rval; 254 + 255 + if (dout_word_include < XSDFEC_AXIS_WORDS_INCLUDE_MAX) 256 + xsdfec->config.dout_word_include = dout_word_include; 257 + else 258 + return -EINVAL; 259 + 260 + rval = of_property_read_u32(node, "xlnx,sdfec-dout-width", &dout_width); 261 + if (rval < 0) 262 + return rval; 263 + 264 + switch (dout_width) { 265 + /* Fall through and set for valid values */ 266 + case XSDFEC_1x128b: 267 + case XSDFEC_2x128b: 268 + case XSDFEC_4x128b: 269 + xsdfec->config.dout_width = dout_width; 270 + break; 271 + default: 272 + return -EINVAL; 273 + } 274 + 275 + /* Write LDPC to CODE Register */ 276 + xsdfec_regwrite(xsdfec, XSDFEC_FEC_CODE_ADDR, xsdfec->config.code); 277 + 278 + xsdfec_cfg_axi_streams(xsdfec); 279 + 280 + return 0; 281 + } 157 282 158 283 static int xsdfec_clk_init(struct platform_device *pdev, 159 284 struct xsdfec_clks *clks) ··· 546 259 err = PTR_ERR(xsdfec->regs); 547 260 goto err_xsdfec_dev; 548 261 } 262 + 263 + err = xsdfec_parse_of(xsdfec); 264 + if (err < 0) 265 + goto err_xsdfec_dev; 266 + 267 + update_config_from_hw(xsdfec); 549 268 550 269 /* Save driver private data */ 551 270 platform_set_drvdata(pdev, xsdfec);
+138
include/uapi/misc/xilinx_sdfec.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ 2 + /* 3 + * Xilinx SD-FEC 4 + * 5 + * Copyright (C) 2019 Xilinx, Inc. 6 + * 7 + * Description: 8 + * This driver is developed for SDFEC16 IP. It provides a char device 9 + * in sysfs and supports file operations like open(), close() and ioctl(). 10 + */ 11 + #ifndef __XILINX_SDFEC_H__ 12 + #define __XILINX_SDFEC_H__ 13 + 14 + #include <linux/types.h> 15 + 16 + /** 17 + * enum xsdfec_code - Code Type. 18 + * @XSDFEC_TURBO_CODE: Driver is configured for Turbo mode. 19 + * @XSDFEC_LDPC_CODE: Driver is configured for LDPC mode. 20 + * 21 + * This enum is used to indicate the mode of the driver. The mode is determined 22 + * by checking which codes are set in the driver. Note that the mode cannot be 23 + * changed by the driver. 24 + */ 25 + enum xsdfec_code { 26 + XSDFEC_TURBO_CODE = 0, 27 + XSDFEC_LDPC_CODE, 28 + }; 29 + 30 + /** 31 + * enum xsdfec_order - Order 32 + * @XSDFEC_MAINTAIN_ORDER: Maintain order execution of blocks. 33 + * @XSDFEC_OUT_OF_ORDER: Out-of-order execution of blocks. 34 + * 35 + * This enum is used to indicate whether the order of blocks can change from 36 + * input to output. 37 + */ 38 + enum xsdfec_order { 39 + XSDFEC_MAINTAIN_ORDER = 0, 40 + XSDFEC_OUT_OF_ORDER, 41 + }; 42 + 43 + /** 44 + * enum xsdfec_state - State. 45 + * @XSDFEC_INIT: Driver is initialized. 46 + * @XSDFEC_STARTED: Driver is started. 47 + * @XSDFEC_STOPPED: Driver is stopped. 48 + * @XSDFEC_NEEDS_RESET: Driver needs to be reset. 49 + * @XSDFEC_PL_RECONFIGURE: Programmable Logic needs to be recofigured. 50 + * 51 + * This enum is used to indicate the state of the driver. 52 + */ 53 + enum xsdfec_state { 54 + XSDFEC_INIT = 0, 55 + XSDFEC_STARTED, 56 + XSDFEC_STOPPED, 57 + XSDFEC_NEEDS_RESET, 58 + XSDFEC_PL_RECONFIGURE, 59 + }; 60 + 61 + /** 62 + * enum xsdfec_axis_width - AXIS_WIDTH.DIN Setting for 128-bit width. 63 + * @XSDFEC_1x128b: DIN data input stream consists of a 128-bit lane 64 + * @XSDFEC_2x128b: DIN data input stream consists of two 128-bit lanes 65 + * @XSDFEC_4x128b: DIN data input stream consists of four 128-bit lanes 66 + * 67 + * This enum is used to indicate the AXIS_WIDTH.DIN setting for 128-bit width. 68 + * The number of lanes of the DIN data input stream depends upon the 69 + * AXIS_WIDTH.DIN parameter. 70 + */ 71 + enum xsdfec_axis_width { 72 + XSDFEC_1x128b = 1, 73 + XSDFEC_2x128b = 2, 74 + XSDFEC_4x128b = 4, 75 + }; 76 + 77 + /** 78 + * enum xsdfec_axis_word_include - Words Configuration. 79 + * @XSDFEC_FIXED_VALUE: Fixed, the DIN_WORDS AXI4-Stream interface is removed 80 + * from the IP instance and is driven with the specified 81 + * number of words. 82 + * @XSDFEC_IN_BLOCK: In Block, configures the IP instance to expect a single 83 + * DIN_WORDS value per input code block. The DIN_WORDS 84 + * interface is present. 85 + * @XSDFEC_PER_AXI_TRANSACTION: Per Transaction, configures the IP instance to 86 + * expect one DIN_WORDS value per input transaction on the DIN interface. The 87 + * DIN_WORDS interface is present. 88 + * @XSDFEC_AXIS_WORDS_INCLUDE_MAX: Used to indicate out of bound Words 89 + * Configurations. 90 + * 91 + * This enum is used to specify the DIN_WORDS configuration. 92 + */ 93 + enum xsdfec_axis_word_include { 94 + XSDFEC_FIXED_VALUE = 0, 95 + XSDFEC_IN_BLOCK, 96 + XSDFEC_PER_AXI_TRANSACTION, 97 + XSDFEC_AXIS_WORDS_INCLUDE_MAX, 98 + }; 99 + 100 + /** 101 + * struct xsdfec_irq - Enabling or Disabling Interrupts. 102 + * @enable_isr: If true enables the ISR 103 + * @enable_ecc_isr: If true enables the ECC ISR 104 + */ 105 + struct xsdfec_irq { 106 + __s8 enable_isr; 107 + __s8 enable_ecc_isr; 108 + }; 109 + 110 + /** 111 + * struct xsdfec_config - Configuration of SD-FEC core. 112 + * @code: The codes being used by the SD-FEC instance 113 + * @order: Order of Operation 114 + * @din_width: Width of the DIN AXI4-Stream 115 + * @din_word_include: How DIN_WORDS are inputted 116 + * @dout_width: Width of the DOUT AXI4-Stream 117 + * @dout_word_include: HOW DOUT_WORDS are outputted 118 + * @irq: Enabling or disabling interrupts 119 + * @bypass: Is the core being bypassed 120 + * @code_wr_protect: Is write protection of LDPC codes enabled 121 + */ 122 + struct xsdfec_config { 123 + __u32 code; 124 + __u32 order; 125 + __u32 din_width; 126 + __u32 din_word_include; 127 + __u32 dout_width; 128 + __u32 dout_word_include; 129 + struct xsdfec_irq irq; 130 + __s8 bypass; 131 + __s8 code_wr_protect; 132 + }; 133 + 134 + /* 135 + * XSDFEC IOCTL List 136 + */ 137 + #define XSDFEC_MAGIC 'f' 138 + #endif /* __XILINX_SDFEC_H__ */