[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <508cc1c01cdfa8192e553ac290995ffe8e5681a2.1667472555.git.william.gray@linaro.org>
Date: Thu, 3 Nov 2022 07:20:49 -0400
From: William Breathitt Gray <william.gray@...aro.org>
To: linus.walleij@...aro.org, brgl@...ev.pl
Cc: andriy.shevchenko@...ux.intel.com, linux-gpio@...r.kernel.org,
linux-kernel@...r.kernel.org,
William Breathitt Gray <william.gray@...aro.org>
Subject: [PATCH 3/3] gpio: i8255: Migrate to regmap API
The regmap API supports IO port accessors so we can take advantage of
regmap abstractions rather than handling access to the device registers
directly in the driver. The 104-dio-48e, 104-idi-48, and gpio-mm modules
depend on the i8255 library and are thus updated accordingly.
The struct i8255_state data structure is removed and its role merged
into the struct i8255 data structure which is updated to hold regmap
register maps. Similarly, the i8255_state_init() function is repurposed
to the i8255_init() function; the other i8255 library functions are
adjusted to utilize the regmap API. The I8255_REGMAP_CONFIG macro is
introduced to facilitate the initialization of struct regmap_config
data structures for i8255 register mappings.
Cc: Andy Shevchenko <andriy.shevchenko@...ux.intel.com>
Signed-off-by: William Breathitt Gray <william.gray@...aro.org>
---
drivers/gpio/gpio-104-dio-48e.c | 111 ++++++++++------
drivers/gpio/gpio-104-idi-48.c | 60 ++++++---
drivers/gpio/gpio-gpio-mm.c | 97 ++++++++++----
drivers/gpio/gpio-i8255.c | 218 +++++++++++++++++++++-----------
drivers/gpio/gpio-i8255.h | 54 ++++----
5 files changed, 353 insertions(+), 187 deletions(-)
diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c
index 134e3dd12ae9..e460d66800cb 100644
--- a/drivers/gpio/gpio-104-dio-48e.c
+++ b/drivers/gpio/gpio-104-dio-48e.c
@@ -52,28 +52,18 @@ MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers");
#define DIO48E_NUM_PPI 2
-/**
- * struct dio48e_reg - device register structure
- * @ppi: Programmable Peripheral Interface groups
- */
-struct dio48e_reg {
- struct i8255 ppi[DIO48E_NUM_PPI];
-};
-
/**
* struct dio48e_gpio - GPIO device private data structure
* @chip: instance of the gpio_chip
- * @ppi_state: PPI device states
+ * @ppi: Programmable Peripheral Interface device structures
* @lock: synchronization lock to prevent I/O race conditions
- * @reg: I/O address offset for the device registers
* @map: device register map
* @irq_mask: I/O bits affected by interrupts
*/
struct dio48e_gpio {
struct gpio_chip chip;
- struct i8255_state ppi_state[DIO48E_NUM_PPI];
+ struct i8255 ppi[DIO48E_NUM_PPI];
raw_spinlock_t lock;
- struct dio48e_reg __iomem *reg;
struct regmap *map;
unsigned char irq_mask;
};
@@ -82,7 +72,7 @@ static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned int offset
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
- if (i8255_get_direction(dio48egpio->ppi_state, offset))
+ if (i8255_get_direction(dio48egpio->ppi, offset))
return GPIO_LINE_DIRECTION_IN;
return GPIO_LINE_DIRECTION_OUT;
@@ -92,10 +82,7 @@ static int dio48e_gpio_direction_input(struct gpio_chip *chip, unsigned int offs
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
- i8255_direction_input(dio48egpio->reg->ppi, dio48egpio->ppi_state,
- offset);
-
- return 0;
+ return i8255_direction_input(dio48egpio->ppi, offset);
}
static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned int offset,
@@ -103,17 +90,20 @@ static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned int off
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
- i8255_direction_output(dio48egpio->reg->ppi, dio48egpio->ppi_state,
- offset, value);
-
- return 0;
+ return i8255_direction_output(dio48egpio->ppi, offset, value);
}
static int dio48e_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ bool value;
+ int err;
- return i8255_get(dio48egpio->reg->ppi, offset);
+ err = i8255_get(dio48egpio->ppi, offset, &value);
+ if (err)
+ return err;
+
+ return value;
}
static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
@@ -121,25 +111,32 @@ static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
- i8255_get_multiple(dio48egpio->reg->ppi, mask, bits, chip->ngpio);
-
- return 0;
+ return i8255_get_multiple(dio48egpio->ppi, mask, bits, chip->ngpio);
}
static void dio48e_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ int err;
- i8255_set(dio48egpio->reg->ppi, dio48egpio->ppi_state, offset, value);
+ err = i8255_set(dio48egpio->ppi, offset, value);
+ if (err)
+ dev_warn(chip->parent,
+ "Unable to set GPIO offset %u to value %d (errno %d)\n",
+ offset, value, err);
}
static void dio48e_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ int err;
- i8255_set_multiple(dio48egpio->reg->ppi, dio48egpio->ppi_state, mask,
- bits, chip->ngpio);
+ err = i8255_set_multiple(dio48egpio->ppi, mask, bits, chip->ngpio);
+ if (err)
+ dev_warn(chip->parent,
+ "Unable to set GPIO with mask %lu and bits %lu (errno %d)\n",
+ *mask, *bits, err);
}
static void dio48e_irq_ack(struct irq_data *data)
@@ -271,19 +268,52 @@ static int dio48e_irq_init_hw(struct gpio_chip *gc)
return regmap_read(dio48egpio->map, DIO48E_DISABLE_INTERRUPT, &val);
}
-static void dio48e_init_ppi(struct i8255 __iomem *const ppi,
- struct i8255_state *const ppi_state)
+static const struct regmap_range dio48e_ppi_rd_ranges[] = {
+ regmap_reg_range(0x0, 0x2),
+};
+static const struct regmap_access_table dio48e_ppi_rd_table = {
+ .yes_ranges = dio48e_ppi_rd_ranges,
+ .n_yes_ranges = ARRAY_SIZE(dio48e_ppi_rd_ranges),
+};
+
+static int devm_dio48e_init_ppi_maps(struct device *const dev,
+ struct regmap **const maps,
+ void __iomem *const regs)
+{
+ struct regmap_config config = I8255_REGMAP_CONFIG;
+ unsigned long i;
+
+ config.io_port = true;
+ config.rd_table = &dio48e_ppi_rd_table;
+
+ for (i = 0; i < DIO48E_NUM_PPI; i++) {
+ maps[i] = devm_regmap_init_mmio(dev, regs + i * 4, &config);
+ if (IS_ERR(maps[i]))
+ return PTR_ERR(maps[i]);
+ }
+
+ return 0;
+}
+
+static int dio48e_init_ppi(struct i8255 *const ppi)
{
const unsigned long ngpio = 24;
const unsigned long mask = GENMASK(ngpio - 1, 0);
const unsigned long bits = 0;
unsigned long i;
+ int err;
/* Initialize all GPIO to output 0 */
for (i = 0; i < DIO48E_NUM_PPI; i++) {
- i8255_mode0_output(&ppi[i]);
- i8255_set_multiple(&ppi[i], &ppi_state[i], &mask, &bits, ngpio);
+ err = i8255_mode0_output(&ppi[i]);
+ if (err)
+ return err;
+ err = i8255_set_multiple(&ppi[i], &mask, &bits, ngpio);
+ if (err)
+ return err;
}
+
+ return 0;
}
static const struct regmap_range dio48e_wr_ranges[] = {
@@ -322,6 +352,7 @@ static int dio48e_probe(struct device *dev, unsigned int id)
struct dio48e_gpio *dio48egpio;
const char *const name = dev_name(dev);
void __iomem *regs;
+ struct regmap *ppi_maps[DIO48E_NUM_PPI];
struct gpio_irq_chip *girq;
int err;
@@ -338,7 +369,18 @@ static int dio48e_probe(struct device *dev, unsigned int id)
regs = devm_ioport_map(dev, base[id], DIO48E_EXTENT);
if (!regs)
return -ENOMEM;
- dio48egpio->reg = regs;
+
+ err = devm_dio48e_init_ppi_maps(dev, ppi_maps, regs);
+ if (err) {
+ dev_err(dev, "Regmaps initialization failed (%d)\n", err);
+ return err;
+ }
+ i8255_init(dio48egpio->ppi, DIO48E_NUM_PPI, ppi_maps);
+ err = dio48e_init_ppi(dio48egpio->ppi);
+ if (err) {
+ dev_err(dev, "PPI initialization failed (%d)\n", err);
+ return err;
+ }
dio48egpio->map = devm_regmap_init_mmio(dev, regs + DIO48E_REGS_OFFSET,
&dio48e_regmap_config);
@@ -371,9 +413,6 @@ static int dio48e_probe(struct device *dev, unsigned int id)
raw_spin_lock_init(&dio48egpio->lock);
- i8255_state_init(dio48egpio->ppi_state, DIO48E_NUM_PPI);
- dio48e_init_ppi(dio48egpio->reg->ppi, dio48egpio->ppi_state);
-
err = devm_gpiochip_add_data(dev, &dio48egpio->chip, dio48egpio);
if (err) {
dev_err(dev, "GPIO registering failed (%d)\n", err);
diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c
index 5d9de5b5e7af..e44dfbb68142 100644
--- a/drivers/gpio/gpio-104-idi-48.c
+++ b/drivers/gpio/gpio-104-idi-48.c
@@ -39,18 +39,6 @@ static unsigned int num_irq;
module_param_hw_array(irq, uint, irq, &num_irq, 0);
MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers");
-/**
- * struct idi_48_reg - device register structure
- * @port0: Port 0 Inputs
- * @unused: Unused
- * @port1: Port 1 Inputs
- */
-struct idi_48_reg {
- u8 port0[3];
- u8 unused;
- u8 port1[3];
-};
-
#define IDI48_NAME "104-idi-48"
#define IDI48_REGS_OFFSET 0x3
@@ -58,12 +46,14 @@ struct idi_48_reg {
#define IDI48_IRQ_CLEAR 0x0
#define IDI48_IRQ_ENABLE 0x0
+#define IDI48_NUM_PPI 2
+
/**
* struct idi_48_gpio - GPIO device private data structure
* @chip: instance of the gpio_chip
* @lock: synchronization lock to prevent I/O race conditions
* @irq_mask: input bits affected by interrupts
- * @reg: I/O address offset for the device registers
+ * @ppi: Programmable Peripheral Interface device structures
* @map: device register map
* @cos_enb: Change-Of-State IRQ enable boundaries mask
*/
@@ -71,7 +61,7 @@ struct idi_48_gpio {
struct gpio_chip chip;
spinlock_t lock;
unsigned char irq_mask[6];
- struct idi_48_reg __iomem *reg;
+ struct i8255 ppi[IDI48_NUM_PPI];
struct regmap *map;
unsigned char cos_enb;
};
@@ -89,20 +79,22 @@ static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned int offs
static int idi_48_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
- void __iomem *const ppi = idi48gpio->reg;
+ bool value;
+ int err;
- return i8255_get(ppi, offset);
+ err = i8255_get(idi48gpio->ppi, offset, &value);
+ if (err)
+ return err;
+
+ return value;
}
static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits)
{
struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
- void __iomem *const ppi = idi48gpio->reg;
-
- i8255_get_multiple(ppi, mask, bits, chip->ngpio);
- return 0;
+ return i8255_get_multiple(idi48gpio->ppi, mask, bits, chip->ngpio);
}
static void idi_48_irq_ack(struct irq_data *data)
@@ -237,6 +229,25 @@ static const char *idi48_names[IDI48_NGPIO] = {
"Bit 18 B", "Bit 19 B", "Bit 20 B", "Bit 21 B", "Bit 22 B", "Bit 23 B"
};
+static int devm_idi_48_init_ppi_maps(struct device *const dev,
+ struct regmap **const maps,
+ void __iomem *const regs)
+{
+ struct regmap_config config = I8255_REGMAP_CONFIG;
+ unsigned long i;
+
+ config.io_port = true;
+ config.max_register = 0x2;
+
+ for (i = 0; i < IDI48_NUM_PPI; i++) {
+ maps[i] = devm_regmap_init_mmio(dev, regs + i * 4, &config);
+ if (IS_ERR(maps[i]))
+ return PTR_ERR(maps[i]);
+ }
+
+ return 0;
+}
+
static int idi_48_irq_init_hw(struct gpio_chip *gc)
{
struct idi_48_gpio *const idi48gpio = gpiochip_get_data(gc);
@@ -261,6 +272,7 @@ static int idi_48_probe(struct device *dev, unsigned int id)
struct idi_48_gpio *idi48gpio;
const char *const name = dev_name(dev);
void __iomem *regs;
+ struct regmap *ppi_maps[IDI48_NUM_PPI];
struct gpio_irq_chip *girq;
int err;
@@ -277,7 +289,13 @@ static int idi_48_probe(struct device *dev, unsigned int id)
regs = devm_ioport_map(dev, base[id], IDI_48_EXTENT);
if (!regs)
return -ENOMEM;
- idi48gpio->reg = regs;
+
+ err = devm_idi_48_init_ppi_maps(dev, ppi_maps, regs);
+ if (err) {
+ dev_err(dev, "Regmaps initialization failed (%d)\n", err);
+ return err;
+ }
+ i8255_init(idi48gpio->ppi, IDI48_NUM_PPI, ppi_maps);
idi48gpio->map = devm_regmap_init_mmio(dev, regs + IDI48_REGS_OFFSET,
&idi48_regmap_config);
diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c
index 2689671b6b01..2efcf63fb0f6 100644
--- a/drivers/gpio/gpio-gpio-mm.c
+++ b/drivers/gpio/gpio-gpio-mm.c
@@ -33,13 +33,11 @@ MODULE_PARM_DESC(base, "Diamond Systems GPIO-MM base addresses");
/**
* struct gpiomm_gpio - GPIO device private data structure
* @chip: instance of the gpio_chip
- * @ppi_state: Programmable Peripheral Interface group states
- * @ppi: Programmable Peripheral Interface groups
+ * @ppi: Programmable Peripheral Interface device structures
*/
struct gpiomm_gpio {
struct gpio_chip chip;
- struct i8255_state ppi_state[GPIOMM_NUM_PPI];
- struct i8255 __iomem *ppi;
+ struct i8255 ppi[GPIOMM_NUM_PPI];
};
static int gpiomm_gpio_get_direction(struct gpio_chip *chip,
@@ -47,7 +45,7 @@ static int gpiomm_gpio_get_direction(struct gpio_chip *chip,
{
struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
- if (i8255_get_direction(gpiommgpio->ppi_state, offset))
+ if (i8255_get_direction(gpiommgpio->ppi, offset))
return GPIO_LINE_DIRECTION_IN;
return GPIO_LINE_DIRECTION_OUT;
@@ -58,9 +56,7 @@ static int gpiomm_gpio_direction_input(struct gpio_chip *chip,
{
struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
- i8255_direction_input(gpiommgpio->ppi, gpiommgpio->ppi_state, offset);
-
- return 0;
+ return i8255_direction_input(gpiommgpio->ppi, offset);
}
static int gpiomm_gpio_direction_output(struct gpio_chip *chip,
@@ -68,17 +64,20 @@ static int gpiomm_gpio_direction_output(struct gpio_chip *chip,
{
struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
- i8255_direction_output(gpiommgpio->ppi, gpiommgpio->ppi_state, offset,
- value);
-
- return 0;
+ return i8255_direction_output(gpiommgpio->ppi, offset, value);
}
static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
+ bool value;
+ int err;
+
+ err = i8255_get(gpiommgpio->ppi, offset, &value);
+ if (err)
+ return err;
- return i8255_get(gpiommgpio->ppi, offset);
+ return value;
}
static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
@@ -86,26 +85,33 @@ static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
{
struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
- i8255_get_multiple(gpiommgpio->ppi, mask, bits, chip->ngpio);
-
- return 0;
+ return i8255_get_multiple(gpiommgpio->ppi, mask, bits, chip->ngpio);
}
static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
+ int err;
- i8255_set(gpiommgpio->ppi, gpiommgpio->ppi_state, offset, value);
+ err = i8255_set(gpiommgpio->ppi, offset, value);
+ if (err)
+ dev_warn(chip->parent,
+ "Unable to set GPIO offset %u to value %d (errno %d)\n",
+ offset, value, err);
}
static void gpiomm_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
+ int err;
- i8255_set_multiple(gpiommgpio->ppi, gpiommgpio->ppi_state, mask, bits,
- chip->ngpio);
+ err = i8255_set_multiple(gpiommgpio->ppi, mask, bits, chip->ngpio);
+ if (err)
+ dev_warn(chip->parent,
+ "Unable to set GPIO with mask %lu and bits %lu (errno %d)\n",
+ *mask, *bits, err);
}
#define GPIOMM_NGPIO 48
@@ -120,25 +126,51 @@ static const char *gpiomm_names[GPIOMM_NGPIO] = {
"Port 2C2", "Port 2C3", "Port 2C4", "Port 2C5", "Port 2C6", "Port 2C7",
};
-static void gpiomm_init_dio(struct i8255 __iomem *const ppi,
- struct i8255_state *const ppi_state)
+static int devm_gpiomm_init_ppi_maps(struct device *const dev,
+ struct regmap **const maps,
+ void __iomem *const regs)
+{
+ struct regmap_config config = I8255_REGMAP_CONFIG;
+ unsigned long i;
+
+ config.io_port = true;
+
+ for (i = 0; i < GPIOMM_NUM_PPI; i++) {
+ maps[i] = devm_regmap_init_mmio(dev, regs + i * 4, &config);
+ if (IS_ERR(maps[i]))
+ return PTR_ERR(maps[i]);
+ }
+
+ return 0;
+}
+
+static int gpiomm_init_dio(struct i8255 *const ppi)
{
const unsigned long ngpio = 24;
const unsigned long mask = GENMASK(ngpio - 1, 0);
const unsigned long bits = 0;
unsigned long i;
+ int err;
/* Initialize all GPIO to output 0 */
for (i = 0; i < GPIOMM_NUM_PPI; i++) {
- i8255_mode0_output(&ppi[i]);
- i8255_set_multiple(&ppi[i], &ppi_state[i], &mask, &bits, ngpio);
+ err = i8255_mode0_output(&ppi[i]);
+ if (err)
+ return err;
+ err = i8255_set_multiple(&ppi[i], &mask, &bits, ngpio);
+ if (err)
+ return err;
}
+
+ return 0;
}
static int gpiomm_probe(struct device *dev, unsigned int id)
{
struct gpiomm_gpio *gpiommgpio;
const char *const name = dev_name(dev);
+ void __iomem *regs;
+ struct regmap *ppi_maps[GPIOMM_NUM_PPI];
int err;
gpiommgpio = devm_kzalloc(dev, sizeof(*gpiommgpio), GFP_KERNEL);
@@ -151,10 +183,22 @@ static int gpiomm_probe(struct device *dev, unsigned int id)
return -EBUSY;
}
- gpiommgpio->ppi = devm_ioport_map(dev, base[id], GPIOMM_EXTENT);
- if (!gpiommgpio->ppi)
+ regs = devm_ioport_map(dev, base[id], GPIOMM_EXTENT);
+ if (!regs)
return -ENOMEM;
+ err = devm_gpiomm_init_ppi_maps(dev, ppi_maps, regs);
+ if (err) {
+ dev_err(dev, "Regmaps initialization failed (%d)\n", err);
+ return err;
+ }
+ i8255_init(gpiommgpio->ppi, GPIOMM_NUM_PPI, ppi_maps);
+ err = gpiomm_init_dio(gpiommgpio->ppi);
+ if (err) {
+ dev_err(dev, "PPI initialization failed (%d)\n", err);
+ return err;
+ }
+
gpiommgpio->chip.label = name;
gpiommgpio->chip.parent = dev;
gpiommgpio->chip.owner = THIS_MODULE;
@@ -169,9 +213,6 @@ static int gpiomm_probe(struct device *dev, unsigned int id)
gpiommgpio->chip.set = gpiomm_gpio_set;
gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple;
- i8255_state_init(gpiommgpio->ppi_state, GPIOMM_NUM_PPI);
- gpiomm_init_dio(gpiommgpio->ppi, gpiommgpio->ppi_state);
-
err = devm_gpiochip_add_data(dev, &gpiommgpio->chip, gpiommgpio);
if (err) {
dev_err(dev, "GPIO registering failed (%d)\n", err);
diff --git a/drivers/gpio/gpio-i8255.c b/drivers/gpio/gpio-i8255.c
index 9b97db418df1..5ff8d90c0fe3 100644
--- a/drivers/gpio/gpio-i8255.c
+++ b/drivers/gpio/gpio-i8255.c
@@ -6,8 +6,8 @@
#include <linux/bitmap.h>
#include <linux/err.h>
#include <linux/export.h>
-#include <linux/io.h>
#include <linux/module.h>
+#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/types.h>
@@ -21,14 +21,22 @@
#define I8255_PORTA 0
#define I8255_PORTB 1
#define I8255_PORTC 2
+#define I8255_CONTROL 3
-static int i8255_get_port(struct i8255 __iomem *const ppi,
- const unsigned long io_port, const unsigned long mask)
+static int i8255_get_port(struct i8255 *const ppi, const unsigned long io_port,
+ const unsigned long mask, unsigned int *val)
{
const unsigned long bank = io_port / 3;
const unsigned long ppi_port = io_port % 3;
+ int err;
- return ioread8(&ppi[bank].port[ppi_port]) & mask;
+ err = regmap_read(ppi[bank].map, ppi_port, val);
+ if (err)
+ return err;
+
+ *val &= mask;
+
+ return 0;
}
static u8 i8255_direction_mask(const unsigned long offset)
@@ -53,85 +61,90 @@ static u8 i8255_direction_mask(const unsigned long offset)
}
}
-static void i8255_set_port(struct i8255 __iomem *const ppi,
- struct i8255_state *const state,
- const unsigned long io_port,
- const unsigned long mask, const unsigned long bits)
+static int i8255_set_port(struct i8255 *const ppi, const unsigned long io_port,
+ const unsigned long mask, const unsigned long bits)
{
const unsigned long bank = io_port / 3;
const unsigned long ppi_port = io_port % 3;
- unsigned long flags;
- unsigned long out_state;
+ unsigned int out_state;
+ int err;
- spin_lock_irqsave(&state[bank].lock, flags);
+ err = regmap_read(ppi[bank].map, ppi_port, &out_state);
+ if (err)
+ return err;
- out_state = ioread8(&ppi[bank].port[ppi_port]);
out_state = (out_state & ~mask) | (bits & mask);
- iowrite8(out_state, &ppi[bank].port[ppi_port]);
-
- spin_unlock_irqrestore(&state[bank].lock, flags);
+ return regmap_write(ppi[bank].map, ppi_port, out_state);
}
/**
* i8255_direction_input - configure signal offset as input
* @ppi: Intel 8255 Programmable Peripheral Interface banks
- * @state: devices states of the respective PPI banks
* @offset: signal offset to configure as input
*
* Configures a signal @offset as input for the respective Intel 8255
- * Programmable Peripheral Interface (@ppi) banks. The @state control_state
- * values are updated to reflect the new configuration.
+ * Programmable Peripheral Interface (@ppi) banks. Returns 0 on success and
+ * negative error number on failure.
*/
-void i8255_direction_input(struct i8255 __iomem *const ppi,
- struct i8255_state *const state,
- const unsigned long offset)
+int i8255_direction_input(struct i8255 *const ppi, const unsigned long offset)
{
const unsigned long io_port = offset / 8;
const unsigned long bank = io_port / 3;
unsigned long flags;
+ int err;
- spin_lock_irqsave(&state[bank].lock, flags);
+ spin_lock_irqsave(&ppi[bank].lock, flags);
- state[bank].control_state |= I8255_CONTROL_MODE_SET;
- state[bank].control_state |= i8255_direction_mask(offset);
+ ppi[bank].control_state |= I8255_CONTROL_MODE_SET;
+ ppi[bank].control_state |= i8255_direction_mask(offset);
- iowrite8(state[bank].control_state, &ppi[bank].control);
+ err = regmap_write(ppi[bank].map, I8255_CONTROL,
+ ppi[bank].control_state);
+ if (err) {
+ spin_unlock_irqrestore(&ppi[bank].lock, flags);
+ return err;
+ }
+
+ spin_unlock_irqrestore(&ppi[bank].lock, flags);
- spin_unlock_irqrestore(&state[bank].lock, flags);
+ return 0;
}
EXPORT_SYMBOL_NS_GPL(i8255_direction_input, I8255);
/**
* i8255_direction_output - configure signal offset as output
* @ppi: Intel 8255 Programmable Peripheral Interface banks
- * @state: devices states of the respective PPI banks
* @offset: signal offset to configure as output
* @value: signal value to output
*
* Configures a signal @offset as output for the respective Intel 8255
* Programmable Peripheral Interface (@ppi) banks and sets the respective signal
- * output to the desired @value. The @state control_state values are updated to
- * reflect the new configuration.
+ * output to the desired @value. Returns 0 on success and negative error number
+ * on failure.
*/
-void i8255_direction_output(struct i8255 __iomem *const ppi,
- struct i8255_state *const state,
- const unsigned long offset,
- const unsigned long value)
+int i8255_direction_output(struct i8255 *const ppi, const unsigned long offset,
+ const unsigned long value)
{
const unsigned long io_port = offset / 8;
const unsigned long bank = io_port / 3;
unsigned long flags;
+ int err;
- spin_lock_irqsave(&state[bank].lock, flags);
+ spin_lock_irqsave(&ppi[bank].lock, flags);
- state[bank].control_state |= I8255_CONTROL_MODE_SET;
- state[bank].control_state &= ~i8255_direction_mask(offset);
+ ppi[bank].control_state |= I8255_CONTROL_MODE_SET;
+ ppi[bank].control_state &= ~i8255_direction_mask(offset);
- iowrite8(state[bank].control_state, &ppi[bank].control);
+ err = regmap_write(ppi[bank].map, I8255_CONTROL,
+ ppi[bank].control_state);
+ if (err) {
+ spin_unlock_irqrestore(&ppi[bank].lock, flags);
+ return err;
+ }
- spin_unlock_irqrestore(&state[bank].lock, flags);
+ spin_unlock_irqrestore(&ppi[bank].lock, flags);
- i8255_set(ppi, state, offset, value);
+ return i8255_set(ppi, offset, value);
}
EXPORT_SYMBOL_NS_GPL(i8255_direction_output, I8255);
@@ -139,16 +152,28 @@ EXPORT_SYMBOL_NS_GPL(i8255_direction_output, I8255);
* i8255_get - get signal value at signal offset
* @ppi: Intel 8255 Programmable Peripheral Interface banks
* @offset: offset of signal to get
+ * @value: pointer to store read signal value
*
- * Returns the signal value (0=low, 1=high) for the signal at @offset for the
- * respective Intel 8255 Programmable Peripheral Interface (@ppi) banks.
+ * Gets the signal value for the signal at @offset for the
+ * respective Intel 8255 Programmable Peripheral Interface (@ppi) banks and
+ * stores it at the location pointed to by @value. Returns 0 on success and
+ * negative error number on failure.
*/
-int i8255_get(struct i8255 __iomem *const ppi, const unsigned long offset)
+int i8255_get(struct i8255 *const ppi, const unsigned long offset,
+ bool *const value)
{
const unsigned long io_port = offset / 8;
const unsigned long offset_mask = BIT(offset % 8);
+ unsigned int port_val;
+ int err;
- return !!i8255_get_port(ppi, io_port, offset_mask);
+ err = i8255_get_port(ppi, io_port, offset_mask, &port_val);
+ if (err)
+ return err;
+
+ *value = port_val;
+
+ return 0;
}
EXPORT_SYMBOL_NS_GPL(i8255_get, I8255);
@@ -159,13 +184,13 @@ EXPORT_SYMBOL_NS_GPL(i8255_get, I8255);
*
* Returns the signal direction (0=output, 1=input) for the signal at @offset.
*/
-int i8255_get_direction(const struct i8255_state *const state,
+int i8255_get_direction(const struct i8255 *const ppi,
const unsigned long offset)
{
const unsigned long io_port = offset / 8;
const unsigned long bank = io_port / 3;
- return !!(state[bank].control_state & i8255_direction_mask(offset));
+ return !!(ppi[bank].control_state & i8255_direction_mask(offset));
}
EXPORT_SYMBOL_NS_GPL(i8255_get_direction, I8255);
@@ -178,24 +203,29 @@ EXPORT_SYMBOL_NS_GPL(i8255_get_direction, I8255);
*
* Stores in @bits the values (0=low, 1=high) for the signals defined by @mask
* for the respective Intel 8255 Programmable Peripheral Interface (@ppi) banks.
+ * Returns 0 on success and negative error number on failure.
*/
-void i8255_get_multiple(struct i8255 __iomem *const ppi,
- const unsigned long *const mask,
- unsigned long *const bits, const unsigned long ngpio)
+int i8255_get_multiple(struct i8255 *const ppi, const unsigned long *const mask,
+ unsigned long *const bits, const unsigned long ngpio)
{
unsigned long offset;
unsigned long port_mask;
unsigned long io_port;
- unsigned long port_state;
+ unsigned int port_state;
+ int err;
bitmap_zero(bits, ngpio);
for_each_set_clump8(offset, port_mask, mask, ngpio) {
io_port = offset / 8;
- port_state = i8255_get_port(ppi, io_port, port_mask);
+ err = i8255_get_port(ppi, io_port, port_mask, &port_state);
+ if (err)
+ return err;
bitmap_set_value8(bits, port_state, offset);
}
+
+ return 0;
}
EXPORT_SYMBOL_NS_GPL(i8255_get_multiple, I8255);
@@ -204,83 +234,123 @@ EXPORT_SYMBOL_NS_GPL(i8255_get_multiple, I8255);
* @ppi: Intel 8255 Programmable Peripheral Interface bank
*
* Configures all Intel 8255 Programmable Peripheral Interface (@ppi) ports to
- * MODE 0 (Basic Input/Output) output mode.
+ * MODE 0 (Basic Input/Output) output mode. Returns 0 on success and negative
+ * error number on failure.
*/
-void i8255_mode0_output(struct i8255 __iomem *const ppi)
+int i8255_mode0_output(struct i8255 *const ppi)
{
- iowrite8(I8255_CONTROL_MODE_SET, &ppi->control);
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&ppi->lock, flags);
+
+ err = regmap_write(ppi->map, I8255_CONTROL, I8255_CONTROL_MODE_SET);
+ if (err) {
+ spin_unlock_irqrestore(&ppi->lock, flags);
+ return err;
+ }
+
+ spin_unlock_irqrestore(&ppi->lock, flags);
+
+ return 0;
}
EXPORT_SYMBOL_NS_GPL(i8255_mode0_output, I8255);
/**
* i8255_set - set signal value at signal offset
* @ppi: Intel 8255 Programmable Peripheral Interface banks
- * @state: devices states of the respective PPI banks
* @offset: offset of signal to set
* @value: value of signal to set
*
* Assigns output @value for the signal at @offset for the respective Intel 8255
- * Programmable Peripheral Interface (@ppi) banks.
+ * Programmable Peripheral Interface (@ppi) banks. Returns 0 on success and
+ * negative error number on failure.
*/
-void i8255_set(struct i8255 __iomem *const ppi, struct i8255_state *const state,
- const unsigned long offset, const unsigned long value)
+int i8255_set(struct i8255 *const ppi, const unsigned long offset,
+ const unsigned long value)
{
const unsigned long io_port = offset / 8;
const unsigned long port_offset = offset % 8;
const unsigned long mask = BIT(port_offset);
const unsigned long bits = value << port_offset;
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&ppi->lock, flags);
- i8255_set_port(ppi, state, io_port, mask, bits);
+ err = i8255_set_port(ppi, io_port, mask, bits);
+ if (err) {
+ spin_unlock_irqrestore(&ppi->lock, flags);
+ return err;
+ }
+
+ spin_unlock_irqrestore(&ppi->lock, flags);
+
+ return 0;
}
EXPORT_SYMBOL_NS_GPL(i8255_set, I8255);
/**
* i8255_set_multiple - set signal values at multiple signal offsets
* @ppi: Intel 8255 Programmable Peripheral Interface banks
- * @state: devices states of the respective PPI banks
* @mask: mask of signals to set
* @bits: bitmap of signal output values
* @ngpio: number of GPIO signals of the respective PPI banks
*
* Assigns output values defined by @bits for the signals defined by @mask for
* the respective Intel 8255 Programmable Peripheral Interface (@ppi) banks.
+ * Returns 0 on success and negative error number on failure.
*/
-void i8255_set_multiple(struct i8255 __iomem *const ppi,
- struct i8255_state *const state,
- const unsigned long *const mask,
- const unsigned long *const bits,
- const unsigned long ngpio)
+int i8255_set_multiple(struct i8255 *const ppi, const unsigned long *const mask,
+ const unsigned long *const bits,
+ const unsigned long ngpio)
{
unsigned long offset;
unsigned long port_mask;
unsigned long io_port;
unsigned long value;
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&ppi->lock, flags);
for_each_set_clump8(offset, port_mask, mask, ngpio) {
io_port = offset / 8;
value = bitmap_get_value8(bits, offset);
- i8255_set_port(ppi, state, io_port, port_mask, value);
+
+ err = i8255_set_port(ppi, io_port, port_mask, value);
+ if (err) {
+ spin_unlock_irqrestore(&ppi->lock, flags);
+ return err;
+ }
}
+
+ spin_unlock_irqrestore(&ppi->lock, flags);
+
+ return 0;
}
EXPORT_SYMBOL_NS_GPL(i8255_set_multiple, I8255);
/**
- * i8255_state_init - initialize i8255_state structure
- * @state: devices states of the respective PPI banks
+ * i8255_init - initialize i8255 device structures
+ * @ppi: Intel 8255 Programmable Peripheral Interface banks
* @nbanks: number of Intel 8255 Programmable Peripheral Interface banks
+ * @maps: array of regmaps for respective PPI banks
*
- * Initializes the @state of each Intel 8255 Programmable Peripheral Interface
- * bank for use in i8255 library functions.
+ * Initializes the Intel 8255 Programmable Peripheral Interface device structure
+ * (@ppi) for use in i8255 library functions.
*/
-void i8255_state_init(struct i8255_state *const state,
- const unsigned long nbanks)
+void i8255_init(struct i8255 *const ppi, const unsigned long nbanks,
+ struct regmap **const maps)
{
unsigned long bank;
- for (bank = 0; bank < nbanks; bank++)
- spin_lock_init(&state[bank].lock);
+ for (bank = 0; bank < nbanks; bank++) {
+ ppi[bank].map = maps[bank];
+ spin_lock_init(&ppi[bank].lock);
+ }
}
-EXPORT_SYMBOL_NS_GPL(i8255_state_init, I8255);
+EXPORT_SYMBOL_NS_GPL(i8255_init, I8255);
MODULE_AUTHOR("William Breathitt Gray");
MODULE_DESCRIPTION("Intel 8255 Programmable Peripheral Interface");
diff --git a/drivers/gpio/gpio-i8255.h b/drivers/gpio/gpio-i8255.h
index d9084aae9446..f0773ae458c8 100644
--- a/drivers/gpio/gpio-i8255.h
+++ b/drivers/gpio/gpio-i8255.h
@@ -3,44 +3,42 @@
#ifndef _I8255_H_
#define _I8255_H_
+#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/types.h>
/**
- * struct i8255 - Intel 8255 register structure
- * @port: Port A, B, and C
- * @control: Control register
- */
-struct i8255 {
- u8 port[3];
- u8 control;
-};
-
-/**
- * struct i8255_state - Intel 8255 state structure
+ * struct i8255 - Intel 8255 device structure
+ * @map: device register map
* @lock: synchronization lock for accessing device state
* @control_state: Control register state
*/
-struct i8255_state {
+struct i8255 {
+ struct regmap *map;
spinlock_t lock;
u8 control_state;
};
-void i8255_direction_input(struct i8255 __iomem *ppi, struct i8255_state *state,
- unsigned long offset);
-void i8255_direction_output(struct i8255 __iomem *ppi,
- struct i8255_state *state, unsigned long offset,
- unsigned long value);
-int i8255_get(struct i8255 __iomem *ppi, unsigned long offset);
-int i8255_get_direction(const struct i8255_state *state, unsigned long offset);
-void i8255_get_multiple(struct i8255 __iomem *ppi, const unsigned long *mask,
- unsigned long *bits, unsigned long ngpio);
-void i8255_mode0_output(struct i8255 __iomem *const ppi);
-void i8255_set(struct i8255 __iomem *ppi, struct i8255_state *state,
- unsigned long offset, unsigned long value);
-void i8255_set_multiple(struct i8255 __iomem *ppi, struct i8255_state *state,
- const unsigned long *mask, const unsigned long *bits,
- unsigned long ngpio);
-void i8255_state_init(struct i8255_state *const state, unsigned long nbanks);
+int i8255_direction_input(struct i8255 *ppi, unsigned long offset);
+int i8255_direction_output(struct i8255 *ppi, unsigned long offset,
+ unsigned long value);
+int i8255_get(struct i8255 *ppi, unsigned long offset, bool *value);
+int i8255_get_direction(const struct i8255 *ppi, unsigned long offset);
+int i8255_get_multiple(struct i8255 *ppi, const unsigned long *mask,
+ unsigned long *bits, unsigned long ngpio);
+int i8255_mode0_output(struct i8255 *ppi);
+int i8255_set(struct i8255 *ppi, unsigned long offset, unsigned long value);
+int i8255_set_multiple(struct i8255 *ppi, const unsigned long *mask,
+ const unsigned long *bits, unsigned long ngpio);
+void i8255_init(struct i8255 *ppi, unsigned long nbanks, struct regmap **maps);
+
+#define I8255_REGMAP_CONFIG \
+{ \
+ .name = "i8255", \
+ .reg_bits = 8, \
+ .val_bits = 8, \
+ .reg_stride = 1, \
+ .max_register = 0x3, \
+}
#endif /* _I8255_H_ */
--
2.37.3
Powered by blists - more mailing lists