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 v6.0-rc4 287 lines 9.3 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Intel 8255 Programmable Peripheral Interface 4 * Copyright (C) 2022 William Breathitt Gray 5 */ 6#include <linux/bitmap.h> 7#include <linux/err.h> 8#include <linux/export.h> 9#include <linux/io.h> 10#include <linux/module.h> 11#include <linux/spinlock.h> 12#include <linux/types.h> 13 14#include "gpio-i8255.h" 15 16#define I8255_CONTROL_PORTC_LOWER_DIRECTION BIT(0) 17#define I8255_CONTROL_PORTB_DIRECTION BIT(1) 18#define I8255_CONTROL_PORTC_UPPER_DIRECTION BIT(3) 19#define I8255_CONTROL_PORTA_DIRECTION BIT(4) 20#define I8255_CONTROL_MODE_SET BIT(7) 21#define I8255_PORTA 0 22#define I8255_PORTB 1 23#define I8255_PORTC 2 24 25static int i8255_get_port(struct i8255 __iomem *const ppi, 26 const unsigned long io_port, const unsigned long mask) 27{ 28 const unsigned long bank = io_port / 3; 29 const unsigned long ppi_port = io_port % 3; 30 31 return ioread8(&ppi[bank].port[ppi_port]) & mask; 32} 33 34static u8 i8255_direction_mask(const unsigned long offset) 35{ 36 const unsigned long port_offset = offset % 8; 37 const unsigned long io_port = offset / 8; 38 const unsigned long ppi_port = io_port % 3; 39 40 switch (ppi_port) { 41 case I8255_PORTA: 42 return I8255_CONTROL_PORTA_DIRECTION; 43 case I8255_PORTB: 44 return I8255_CONTROL_PORTB_DIRECTION; 45 case I8255_PORTC: 46 /* Port C can be configured by nibble */ 47 if (port_offset >= 4) 48 return I8255_CONTROL_PORTC_UPPER_DIRECTION; 49 return I8255_CONTROL_PORTC_LOWER_DIRECTION; 50 default: 51 /* Should never reach this path */ 52 return 0; 53 } 54} 55 56static void i8255_set_port(struct i8255 __iomem *const ppi, 57 struct i8255_state *const state, 58 const unsigned long io_port, 59 const unsigned long mask, const unsigned long bits) 60{ 61 const unsigned long bank = io_port / 3; 62 const unsigned long ppi_port = io_port % 3; 63 unsigned long flags; 64 unsigned long out_state; 65 66 spin_lock_irqsave(&state[bank].lock, flags); 67 68 out_state = ioread8(&ppi[bank].port[ppi_port]); 69 out_state = (out_state & ~mask) | (bits & mask); 70 iowrite8(out_state, &ppi[bank].port[ppi_port]); 71 72 spin_unlock_irqrestore(&state[bank].lock, flags); 73} 74 75/** 76 * i8255_direction_input - configure signal offset as input 77 * @ppi: Intel 8255 Programmable Peripheral Interface banks 78 * @state: devices states of the respective PPI banks 79 * @offset: signal offset to configure as input 80 * 81 * Configures a signal @offset as input for the respective Intel 8255 82 * Programmable Peripheral Interface (@ppi) banks. The @state control_state 83 * values are updated to reflect the new configuration. 84 */ 85void i8255_direction_input(struct i8255 __iomem *const ppi, 86 struct i8255_state *const state, 87 const unsigned long offset) 88{ 89 const unsigned long io_port = offset / 8; 90 const unsigned long bank = io_port / 3; 91 unsigned long flags; 92 93 spin_lock_irqsave(&state[bank].lock, flags); 94 95 state[bank].control_state |= I8255_CONTROL_MODE_SET; 96 state[bank].control_state |= i8255_direction_mask(offset); 97 98 iowrite8(state[bank].control_state, &ppi[bank].control); 99 100 spin_unlock_irqrestore(&state[bank].lock, flags); 101} 102EXPORT_SYMBOL_NS_GPL(i8255_direction_input, I8255); 103 104/** 105 * i8255_direction_output - configure signal offset as output 106 * @ppi: Intel 8255 Programmable Peripheral Interface banks 107 * @state: devices states of the respective PPI banks 108 * @offset: signal offset to configure as output 109 * @value: signal value to output 110 * 111 * Configures a signal @offset as output for the respective Intel 8255 112 * Programmable Peripheral Interface (@ppi) banks and sets the respective signal 113 * output to the desired @value. The @state control_state values are updated to 114 * reflect the new configuration. 115 */ 116void i8255_direction_output(struct i8255 __iomem *const ppi, 117 struct i8255_state *const state, 118 const unsigned long offset, 119 const unsigned long value) 120{ 121 const unsigned long io_port = offset / 8; 122 const unsigned long bank = io_port / 3; 123 unsigned long flags; 124 125 spin_lock_irqsave(&state[bank].lock, flags); 126 127 state[bank].control_state |= I8255_CONTROL_MODE_SET; 128 state[bank].control_state &= ~i8255_direction_mask(offset); 129 130 iowrite8(state[bank].control_state, &ppi[bank].control); 131 132 spin_unlock_irqrestore(&state[bank].lock, flags); 133 134 i8255_set(ppi, state, offset, value); 135} 136EXPORT_SYMBOL_NS_GPL(i8255_direction_output, I8255); 137 138/** 139 * i8255_get - get signal value at signal offset 140 * @ppi: Intel 8255 Programmable Peripheral Interface banks 141 * @offset: offset of signal to get 142 * 143 * Returns the signal value (0=low, 1=high) for the signal at @offset for the 144 * respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. 145 */ 146int i8255_get(struct i8255 __iomem *const ppi, const unsigned long offset) 147{ 148 const unsigned long io_port = offset / 8; 149 const unsigned long offset_mask = BIT(offset % 8); 150 151 return !!i8255_get_port(ppi, io_port, offset_mask); 152} 153EXPORT_SYMBOL_NS_GPL(i8255_get, I8255); 154 155/** 156 * i8255_get_direction - get the I/O direction for a signal offset 157 * @state: devices states of the respective PPI banks 158 * @offset: offset of signal to get direction 159 * 160 * Returns the signal direction (0=output, 1=input) for the signal at @offset. 161 */ 162int i8255_get_direction(const struct i8255_state *const state, 163 const unsigned long offset) 164{ 165 const unsigned long io_port = offset / 8; 166 const unsigned long bank = io_port / 3; 167 168 return !!(state[bank].control_state & i8255_direction_mask(offset)); 169} 170EXPORT_SYMBOL_NS_GPL(i8255_get_direction, I8255); 171 172/** 173 * i8255_get_multiple - get multiple signal values at multiple signal offsets 174 * @ppi: Intel 8255 Programmable Peripheral Interface banks 175 * @mask: mask of signals to get 176 * @bits: bitmap to store signal values 177 * @ngpio: number of GPIO signals of the respective PPI banks 178 * 179 * Stores in @bits the values (0=low, 1=high) for the signals defined by @mask 180 * for the respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. 181 */ 182void i8255_get_multiple(struct i8255 __iomem *const ppi, 183 const unsigned long *const mask, 184 unsigned long *const bits, const unsigned long ngpio) 185{ 186 unsigned long offset; 187 unsigned long port_mask; 188 unsigned long io_port; 189 unsigned long port_state; 190 191 bitmap_zero(bits, ngpio); 192 193 for_each_set_clump8(offset, port_mask, mask, ngpio) { 194 io_port = offset / 8; 195 port_state = i8255_get_port(ppi, io_port, port_mask); 196 197 bitmap_set_value8(bits, port_state, offset); 198 } 199} 200EXPORT_SYMBOL_NS_GPL(i8255_get_multiple, I8255); 201 202/** 203 * i8255_mode0_output - configure all PPI ports to MODE 0 output mode 204 * @ppi: Intel 8255 Programmable Peripheral Interface bank 205 * 206 * Configures all Intel 8255 Programmable Peripheral Interface (@ppi) ports to 207 * MODE 0 (Basic Input/Output) output mode. 208 */ 209void i8255_mode0_output(struct i8255 __iomem *const ppi) 210{ 211 iowrite8(I8255_CONTROL_MODE_SET, &ppi->control); 212} 213EXPORT_SYMBOL_NS_GPL(i8255_mode0_output, I8255); 214 215/** 216 * i8255_set - set signal value at signal offset 217 * @ppi: Intel 8255 Programmable Peripheral Interface banks 218 * @state: devices states of the respective PPI banks 219 * @offset: offset of signal to set 220 * @value: value of signal to set 221 * 222 * Assigns output @value for the signal at @offset for the respective Intel 8255 223 * Programmable Peripheral Interface (@ppi) banks. 224 */ 225void i8255_set(struct i8255 __iomem *const ppi, struct i8255_state *const state, 226 const unsigned long offset, const unsigned long value) 227{ 228 const unsigned long io_port = offset / 8; 229 const unsigned long port_offset = offset % 8; 230 const unsigned long mask = BIT(port_offset); 231 const unsigned long bits = value << port_offset; 232 233 i8255_set_port(ppi, state, io_port, mask, bits); 234} 235EXPORT_SYMBOL_NS_GPL(i8255_set, I8255); 236 237/** 238 * i8255_set_multiple - set signal values at multiple signal offsets 239 * @ppi: Intel 8255 Programmable Peripheral Interface banks 240 * @state: devices states of the respective PPI banks 241 * @mask: mask of signals to set 242 * @bits: bitmap of signal output values 243 * @ngpio: number of GPIO signals of the respective PPI banks 244 * 245 * Assigns output values defined by @bits for the signals defined by @mask for 246 * the respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. 247 */ 248void i8255_set_multiple(struct i8255 __iomem *const ppi, 249 struct i8255_state *const state, 250 const unsigned long *const mask, 251 const unsigned long *const bits, 252 const unsigned long ngpio) 253{ 254 unsigned long offset; 255 unsigned long port_mask; 256 unsigned long io_port; 257 unsigned long value; 258 259 for_each_set_clump8(offset, port_mask, mask, ngpio) { 260 io_port = offset / 8; 261 value = bitmap_get_value8(bits, offset); 262 i8255_set_port(ppi, state, io_port, port_mask, value); 263 } 264} 265EXPORT_SYMBOL_NS_GPL(i8255_set_multiple, I8255); 266 267/** 268 * i8255_state_init - initialize i8255_state structure 269 * @state: devices states of the respective PPI banks 270 * @nbanks: number of Intel 8255 Programmable Peripheral Interface banks 271 * 272 * Initializes the @state of each Intel 8255 Programmable Peripheral Interface 273 * bank for use in i8255 library functions. 274 */ 275void i8255_state_init(struct i8255_state *const state, 276 const unsigned long nbanks) 277{ 278 unsigned long bank; 279 280 for (bank = 0; bank < nbanks; bank++) 281 spin_lock_init(&state[bank].lock); 282} 283EXPORT_SYMBOL_NS_GPL(i8255_state_init, I8255); 284 285MODULE_AUTHOR("William Breathitt Gray"); 286MODULE_DESCRIPTION("Intel 8255 Programmable Peripheral Interface"); 287MODULE_LICENSE("GPL");