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.

at v4.6 249 lines 7.3 kB view raw
1/* 2 * Intel MIC Platform Software Stack (MPSS) 3 * 4 * Copyright(c) 2014 Intel Corporation. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License, version 2, as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 * 15 * Intel SCIF driver. 16 * 17 */ 18#include <linux/circ_buf.h> 19#include <linux/types.h> 20#include <linux/io.h> 21#include <linux/errno.h> 22 23#include "scif_rb.h" 24 25#define scif_rb_ring_cnt(head, tail, size) CIRC_CNT(head, tail, size) 26#define scif_rb_ring_space(head, tail, size) CIRC_SPACE(head, tail, size) 27 28/** 29 * scif_rb_init - Initializes the ring buffer 30 * @rb: ring buffer 31 * @read_ptr: A pointer to the read offset 32 * @write_ptr: A pointer to the write offset 33 * @rb_base: A pointer to the base of the ring buffer 34 * @size: The size of the ring buffer in powers of two 35 */ 36void scif_rb_init(struct scif_rb *rb, u32 *read_ptr, u32 *write_ptr, 37 void *rb_base, u8 size) 38{ 39 rb->rb_base = rb_base; 40 rb->size = (1 << size); 41 rb->read_ptr = read_ptr; 42 rb->write_ptr = write_ptr; 43 rb->current_read_offset = *read_ptr; 44 rb->current_write_offset = *write_ptr; 45} 46 47/* Copies a message to the ring buffer -- handles the wrap around case */ 48static void memcpy_torb(struct scif_rb *rb, void *header, 49 void *msg, u32 size) 50{ 51 u32 size1, size2; 52 53 if (header + size >= rb->rb_base + rb->size) { 54 /* Need to call two copies if it wraps around */ 55 size1 = (u32)(rb->rb_base + rb->size - header); 56 size2 = size - size1; 57 memcpy_toio((void __iomem __force *)header, msg, size1); 58 memcpy_toio((void __iomem __force *)rb->rb_base, 59 msg + size1, size2); 60 } else { 61 memcpy_toio((void __iomem __force *)header, msg, size); 62 } 63} 64 65/* Copies a message from the ring buffer -- handles the wrap around case */ 66static void memcpy_fromrb(struct scif_rb *rb, void *header, 67 void *msg, u32 size) 68{ 69 u32 size1, size2; 70 71 if (header + size >= rb->rb_base + rb->size) { 72 /* Need to call two copies if it wraps around */ 73 size1 = (u32)(rb->rb_base + rb->size - header); 74 size2 = size - size1; 75 memcpy_fromio(msg, (void __iomem __force *)header, size1); 76 memcpy_fromio(msg + size1, 77 (void __iomem __force *)rb->rb_base, size2); 78 } else { 79 memcpy_fromio(msg, (void __iomem __force *)header, size); 80 } 81} 82 83/** 84 * scif_rb_space - Query space available for writing to the RB 85 * @rb: ring buffer 86 * 87 * Return: size available for writing to RB in bytes. 88 */ 89u32 scif_rb_space(struct scif_rb *rb) 90{ 91 rb->current_read_offset = *rb->read_ptr; 92 /* 93 * Update from the HW read pointer only once the peer has exposed the 94 * new empty slot. This barrier is paired with the memory barrier 95 * scif_rb_update_read_ptr() 96 */ 97 mb(); 98 return scif_rb_ring_space(rb->current_write_offset, 99 rb->current_read_offset, rb->size); 100} 101 102/** 103 * scif_rb_write - Write a message to the RB 104 * @rb: ring buffer 105 * @msg: buffer to send the message. Must be at least size bytes long 106 * @size: the size (in bytes) to be copied to the RB 107 * 108 * This API does not block if there isn't enough space in the RB. 109 * Returns: 0 on success or -ENOMEM on failure 110 */ 111int scif_rb_write(struct scif_rb *rb, void *msg, u32 size) 112{ 113 void *header; 114 115 if (scif_rb_space(rb) < size) 116 return -ENOMEM; 117 header = rb->rb_base + rb->current_write_offset; 118 memcpy_torb(rb, header, msg, size); 119 /* 120 * Wait until scif_rb_commit(). Update the local ring 121 * buffer data, not the shared data until commit. 122 */ 123 rb->current_write_offset = 124 (rb->current_write_offset + size) & (rb->size - 1); 125 return 0; 126} 127 128/** 129 * scif_rb_commit - To submit the message to let the peer fetch it 130 * @rb: ring buffer 131 */ 132void scif_rb_commit(struct scif_rb *rb) 133{ 134 /* 135 * We must ensure ordering between the all the data committed 136 * previously before we expose the new message to the peer by 137 * updating the write_ptr. This write barrier is paired with 138 * the read barrier in scif_rb_count(..) 139 */ 140 wmb(); 141 ACCESS_ONCE(*rb->write_ptr) = rb->current_write_offset; 142#ifdef CONFIG_INTEL_MIC_CARD 143 /* 144 * X100 Si bug: For the case where a Core is performing an EXT_WR 145 * followed by a Doorbell Write, the Core must perform two EXT_WR to the 146 * same address with the same data before it does the Doorbell Write. 147 * This way, if ordering is violated for the Interrupt Message, it will 148 * fall just behind the first Posted associated with the first EXT_WR. 149 */ 150 ACCESS_ONCE(*rb->write_ptr) = rb->current_write_offset; 151#endif 152} 153 154/** 155 * scif_rb_get - To get next message from the ring buffer 156 * @rb: ring buffer 157 * @size: Number of bytes to be read 158 * 159 * Return: NULL if no bytes to be read from the ring buffer, otherwise the 160 * pointer to the next byte 161 */ 162static void *scif_rb_get(struct scif_rb *rb, u32 size) 163{ 164 void *header = NULL; 165 166 if (scif_rb_count(rb, size) >= size) 167 header = rb->rb_base + rb->current_read_offset; 168 return header; 169} 170 171/* 172 * scif_rb_get_next - Read from ring buffer. 173 * @rb: ring buffer 174 * @msg: buffer to hold the message. Must be at least size bytes long 175 * @size: Number of bytes to be read 176 * 177 * Return: number of bytes read if available bytes are >= size, otherwise 178 * returns zero. 179 */ 180u32 scif_rb_get_next(struct scif_rb *rb, void *msg, u32 size) 181{ 182 void *header = NULL; 183 int read_size = 0; 184 185 header = scif_rb_get(rb, size); 186 if (header) { 187 u32 next_cmd_offset = 188 (rb->current_read_offset + size) & (rb->size - 1); 189 190 read_size = size; 191 rb->current_read_offset = next_cmd_offset; 192 memcpy_fromrb(rb, header, msg, size); 193 } 194 return read_size; 195} 196 197/** 198 * scif_rb_update_read_ptr 199 * @rb: ring buffer 200 */ 201void scif_rb_update_read_ptr(struct scif_rb *rb) 202{ 203 u32 new_offset; 204 205 new_offset = rb->current_read_offset; 206 /* 207 * We must ensure ordering between the all the data committed or read 208 * previously before we expose the empty slot to the peer by updating 209 * the read_ptr. This barrier is paired with the memory barrier in 210 * scif_rb_space(..) 211 */ 212 mb(); 213 ACCESS_ONCE(*rb->read_ptr) = new_offset; 214#ifdef CONFIG_INTEL_MIC_CARD 215 /* 216 * X100 Si Bug: For the case where a Core is performing an EXT_WR 217 * followed by a Doorbell Write, the Core must perform two EXT_WR to the 218 * same address with the same data before it does the Doorbell Write. 219 * This way, if ordering is violated for the Interrupt Message, it will 220 * fall just behind the first Posted associated with the first EXT_WR. 221 */ 222 ACCESS_ONCE(*rb->read_ptr) = new_offset; 223#endif 224} 225 226/** 227 * scif_rb_count 228 * @rb: ring buffer 229 * @size: Number of bytes expected to be read 230 * 231 * Return: number of bytes that can be read from the RB 232 */ 233u32 scif_rb_count(struct scif_rb *rb, u32 size) 234{ 235 if (scif_rb_ring_cnt(rb->current_write_offset, 236 rb->current_read_offset, 237 rb->size) < size) { 238 rb->current_write_offset = *rb->write_ptr; 239 /* 240 * Update from the HW write pointer if empty only once the peer 241 * has exposed the new message. This read barrier is paired 242 * with the write barrier in scif_rb_commit(..) 243 */ 244 smp_rmb(); 245 } 246 return scif_rb_ring_cnt(rb->current_write_offset, 247 rb->current_read_offset, 248 rb->size); 249}