Skip to content

Commit fb38af4

Browse files
William Breathitt Graybrgl
authored andcommitted
gpio: i8255: Introduce the Intel 8255 interface library module
Exposes consumer library functions providing support for interfaces compatible with the venerable Intel 8255 Programmable Peripheral Interface (PPI). The Intel 8255 PPI first appeared in the early 1970s, initially for the Intel 8080 and later appearing in the original IBM-PC. The popularity of the original Intel 8255 chip led to many subsequent variants and clones of the interface in various chips and integrated circuits. Although still popular, interfaces compatible with the Intel 8255 PPI are nowdays typically found embedded in larger VLSI processing chips and FPGA components rather than as discrete ICs. A CONFIG_GPIO_I8255 Kconfig option is introduced by this patch. Modules wanting access to these i8255 library functions should select this Kconfig option, and import the I8255 symbol namespace. Tested-by: Fred Eckert <Frede@cmslaser.com> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Cc: John Hentges <jhentges@accesio.com> Cc: Jay Dolan <jay.dolan@accesio.com> Cc: Andy Shevchenko <andy.shevchenko@gmail.com> Signed-off-by: William Breathitt Gray <william.gray@linaro.org> Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
1 parent cc442e4 commit fb38af4

5 files changed

Lines changed: 352 additions & 0 deletions

File tree

MAINTAINERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9902,6 +9902,12 @@ L: linux-fbdev@vger.kernel.org
99029902
S: Maintained
99039903
F: drivers/video/fbdev/i810/
99049904

9905+
INTEL 8255 GPIO DRIVER
9906+
M: William Breathitt Gray <william.gray@linaro.org>
9907+
L: linux-gpio@vger.kernel.org
9908+
S: Maintained
9909+
F: drivers/gpio/gpio-i8255.c
9910+
99059911
INTEL ASoC DRIVERS
99069912
M: Cezary Rojewski <cezary.rojewski@intel.com>
99079913
M: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>

drivers/gpio/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,18 @@ endmenu
824824
menu "Port-mapped I/O GPIO drivers"
825825
depends on X86 # Unconditional I/O space access
826826

827+
config GPIO_I8255
828+
tristate
829+
help
830+
Enables support for the i8255 interface library functions. The i8255
831+
interface library provides functions to facilitate communication with
832+
interfaces compatible with the venerable Intel 8255 Programmable
833+
Peripheral Interface (PPI). The Intel 8255 PPI chip was first released
834+
in the early 1970s but compatible interfaces are nowadays typically
835+
found embedded in larger VLSI processing chips and FPGA components.
836+
837+
If built as a module its name will be gpio-i8255.
838+
827839
config GPIO_104_DIO_48E
828840
tristate "ACCES 104-DIO-48E GPIO support"
829841
depends on PC104

drivers/gpio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o
6767
obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o
6868
obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o
6969
obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o
70+
obj-$(CONFIG_GPIO_I8255) += gpio-i8255.o
7071
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
7172
obj-$(CONFIG_GPIO_IDT3243X) += gpio-idt3243x.o
7273
obj-$(CONFIG_GPIO_IOP) += gpio-iop.o

drivers/gpio/gpio-i8255.c

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
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+
25+
static 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+
34+
static 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+
56+
static 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+
*/
85+
void 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+
}
102+
EXPORT_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+
*/
116+
void 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+
}
136+
EXPORT_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+
*/
146+
int 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+
}
153+
EXPORT_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+
*/
162+
int 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+
}
170+
EXPORT_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+
*/
182+
void 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+
}
200+
EXPORT_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+
*/
209+
void i8255_mode0_output(struct i8255 __iomem *const ppi)
210+
{
211+
iowrite8(I8255_CONTROL_MODE_SET, &ppi->control);
212+
}
213+
EXPORT_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+
*/
225+
void 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+
}
235+
EXPORT_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+
*/
248+
void 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+
}
265+
EXPORT_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+
*/
275+
void 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+
}
283+
EXPORT_SYMBOL_NS_GPL(i8255_state_init, I8255);
284+
285+
MODULE_AUTHOR("William Breathitt Gray");
286+
MODULE_DESCRIPTION("Intel 8255 Programmable Peripheral Interface");
287+
MODULE_LICENSE("GPL");

drivers/gpio/gpio-i8255.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/* Copyright 2022 William Breathitt Gray */
3+
#ifndef _I8255_H_
4+
#define _I8255_H_
5+
6+
#include <linux/spinlock.h>
7+
#include <linux/types.h>
8+
9+
/**
10+
* struct i8255 - Intel 8255 register structure
11+
* @port: Port A, B, and C
12+
* @control: Control register
13+
*/
14+
struct i8255 {
15+
u8 port[3];
16+
u8 control;
17+
};
18+
19+
/**
20+
* struct i8255_state - Intel 8255 state structure
21+
* @lock: synchronization lock for accessing device state
22+
* @control_state: Control register state
23+
*/
24+
struct i8255_state {
25+
spinlock_t lock;
26+
u8 control_state;
27+
};
28+
29+
void i8255_direction_input(struct i8255 __iomem *ppi, struct i8255_state *state,
30+
unsigned long offset);
31+
void i8255_direction_output(struct i8255 __iomem *ppi,
32+
struct i8255_state *state, unsigned long offset,
33+
unsigned long value);
34+
int i8255_get(struct i8255 __iomem *ppi, unsigned long offset);
35+
int i8255_get_direction(const struct i8255_state *state, unsigned long offset);
36+
void i8255_get_multiple(struct i8255 __iomem *ppi, const unsigned long *mask,
37+
unsigned long *bits, unsigned long ngpio);
38+
void i8255_mode0_output(struct i8255 __iomem *const ppi);
39+
void i8255_set(struct i8255 __iomem *ppi, struct i8255_state *state,
40+
unsigned long offset, unsigned long value);
41+
void i8255_set_multiple(struct i8255 __iomem *ppi, struct i8255_state *state,
42+
const unsigned long *mask, const unsigned long *bits,
43+
unsigned long ngpio);
44+
void i8255_state_init(struct i8255_state *const state, unsigned long nbanks);
45+
46+
#endif /* _I8255_H_ */

0 commit comments

Comments
 (0)