diff options
author | Andrey Smirnov <andrew.smirnov@gmail.com> | 2016-11-07 08:53:17 -0800 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2016-11-08 09:42:19 +0100 |
commit | 6489677f86c330404ae47703bf59d30ec4c46de9 (patch) | |
tree | 8a7be9957dd6b71d93e55df26e11d1f178b29d86 /drivers/pinctrl | |
parent | 0db0f26c2c5d439e07883cd8e7f22fe24e6a0101 (diff) |
pinctrl-sx150x: Replace sx150x_*_cfg by means of regmap API
The difference between 8 and 16 pin GPIO expanders can be accomodated by
the means of regmap API without resorting to using driver-specific
read/write accessors. This change, IMHO, brings the following benefits:
- Replaces driver's idiosyncratic way of dealing with
mult-register fields with regmap API, which, hopefuly,
makes the code a bit easier for a new reader to understand
- Removes various multi-read for-loop register read logic
from various places in the code and puts it in a signle
place
- Removes ad-hoc IRQ register caching code in
sx150x_irq_bus_sync_unlock, since that functionality is
provided by regmap
Besided aforementioned benefits this change also implements necessary
RegSense byte swap necessary for SX1503 and SX1506 variants of the chip.
Tested-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r-- | drivers/pinctrl/pinctrl-sx150x.c | 527 |
1 files changed, 284 insertions, 243 deletions
diff --git a/drivers/pinctrl/pinctrl-sx150x.c b/drivers/pinctrl/pinctrl-sx150x.c index 2eb233fe218b..4725fac3ede5 100644 --- a/drivers/pinctrl/pinctrl-sx150x.c +++ b/drivers/pinctrl/pinctrl-sx150x.c @@ -106,11 +106,8 @@ struct sx150x_pinctrl { struct irq_chip irq_chip; struct regmap *regmap; struct { - int update; u32 sense; u32 masked; - u32 dev_sense; - u32 dev_masked; } irq; struct mutex lock; const struct sx150x_device_data *data; @@ -171,16 +168,16 @@ static const struct sx150x_device_data sx1508q_device_data = { static const struct sx150x_device_data sx1509q_device_data = { .model = SX150X_789, - .reg_pullup = 0x07, - .reg_pulldn = 0x09, - .reg_dir = 0x0f, - .reg_data = 0x11, - .reg_irq_mask = 0x13, - .reg_irq_src = 0x19, - .reg_sense = 0x17, + .reg_pullup = 0x06, + .reg_pulldn = 0x08, + .reg_dir = 0x0e, + .reg_data = 0x10, + .reg_irq_mask = 0x12, + .reg_irq_src = 0x18, + .reg_sense = 0x14, .pri.x789 = { - .reg_drain = 0x0b, - .reg_polarity = 0x0d, + .reg_drain = 0x0a, + .reg_polarity = 0x0c, .reg_clock = 0x1e, .reg_misc = 0x1f, .reg_reset = 0x7d, @@ -192,20 +189,20 @@ static const struct sx150x_device_data sx1509q_device_data = { static const struct sx150x_device_data sx1506q_device_data = { .model = SX150X_456, - .reg_pullup = 0x05, - .reg_pulldn = 0x07, - .reg_dir = 0x03, - .reg_data = 0x01, - .reg_irq_mask = 0x09, - .reg_irq_src = 0x0f, - .reg_sense = 0x0d, + .reg_pullup = 0x04, + .reg_pulldn = 0x06, + .reg_dir = 0x02, + .reg_data = 0x00, + .reg_irq_mask = 0x08, + .reg_irq_src = 0x0e, + .reg_sense = 0x0a, .pri.x456 = { - .reg_pld_mode = 0x21, - .reg_pld_table0 = 0x23, - .reg_pld_table1 = 0x25, - .reg_pld_table2 = 0x27, - .reg_pld_table3 = 0x29, - .reg_pld_table4 = 0x2b, + .reg_pld_mode = 0x20, + .reg_pld_table0 = 0x22, + .reg_pld_table1 = 0x24, + .reg_pld_table2 = 0x26, + .reg_pld_table3 = 0x28, + .reg_pld_table4 = 0x2a, .reg_advance = 0xad, }, .ngpios = 16, @@ -238,20 +235,20 @@ static const struct sx150x_device_data sx1502q_device_data = { static const struct sx150x_device_data sx1503q_device_data = { .model = SX150X_123, - .reg_pullup = 0x05, - .reg_pulldn = 0x07, - .reg_dir = 0x03, - .reg_data = 0x01, - .reg_irq_mask = 0x09, - .reg_irq_src = 0x0f, - .reg_sense = 0x07, + .reg_pullup = 0x04, + .reg_pulldn = 0x06, + .reg_dir = 0x02, + .reg_data = 0x00, + .reg_irq_mask = 0x08, + .reg_irq_src = 0x0e, + .reg_sense = 0x0a, .pri.x123 = { - .reg_pld_mode = 0x10, - .reg_pld_table0 = 0x11, - .reg_pld_table1 = 0x12, - .reg_pld_table2 = 0x13, - .reg_pld_table3 = 0x14, - .reg_pld_table4 = 0x15, + .reg_pld_mode = 0x20, + .reg_pld_table0 = 0x22, + .reg_pld_table1 = 0x24, + .reg_pld_table2 = 0x26, + .reg_pld_table3 = 0x28, + .reg_pld_table4 = 0x2a, .reg_advance = 0xad, }, .ngpios = 16, @@ -259,70 +256,6 @@ static const struct sx150x_device_data sx1503q_device_data = { .npins = 16, /* oscio not available */ }; -/* - * These utility functions solve the common problem of locating and setting - * configuration bits. Configuration bits are grouped into registers - * whose indexes increase downwards. For example, with eight-bit registers, - * sixteen gpios would have their config bits grouped in the following order: - * REGISTER N-1 [ f e d c b a 9 8 ] - * N [ 7 6 5 4 3 2 1 0 ] - * - * For multi-bit configurations, the pattern gets wider: - * REGISTER N-3 [ f f e e d d c c ] - * N-2 [ b b a a 9 9 8 8 ] - * N-1 [ 7 7 6 6 5 5 4 4 ] - * N [ 3 3 2 2 1 1 0 0 ] - * - * Given the address of the starting register 'N', the index of the gpio - * whose configuration we seek to change, and the width in bits of that - * configuration, these functions allow us to locate the correct - * register and mask the correct bits. - */ -static inline void sx150x_find_cfg(u8 offset, u8 width, - u8 *reg, u8 *mask, u8 *shift) -{ - *reg -= offset * width / 8; - *mask = (1 << width) - 1; - *shift = (offset * width) % 8; - *mask <<= *shift; -} - -static int sx150x_write_cfg(struct i2c_client *client, - u8 offset, u8 width, u8 reg, u8 val) -{ - u8 mask; - unsigned int data; - u8 shift; - int err; - struct sx150x_pinctrl *pctl = i2c_get_clientdata(client); - - sx150x_find_cfg(offset, width, ®, &mask, &shift); - err = regmap_read(pctl->regmap, reg, &data); - if (err < 0) - return err; - - data &= ~mask; - data |= (val << shift) & mask; - return regmap_write(pctl->regmap, reg, data); -} - -static int sx150x_read_cfg(struct i2c_client *client, - u8 offset, u8 width, u8 reg) -{ - u8 mask; - unsigned int data; - u8 shift; - int err; - struct sx150x_pinctrl *pctl = i2c_get_clientdata(client); - - sx150x_find_cfg(offset, width, ®, &mask, &shift); - err = regmap_read(pctl->regmap, reg, &data); - if (err < 0) - return err; - - return (data & mask); -} - static int sx150x_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) { return 0; @@ -368,31 +301,33 @@ static int sx150x_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { struct sx150x_pinctrl *pctl = gpiochip_get_data(chip); - int status; + unsigned int value; + int ret; if (sx150x_pin_is_oscio(pctl, offset)) return false; - status = sx150x_read_cfg(pctl->client, offset, 1, pctl->data->reg_dir); - if (status >= 0) - status = !!status; + ret = regmap_read(pctl->regmap, pctl->data->reg_dir, &value); + if (ret < 0) + return ret; - return status; + return !!(value & BIT(offset)); } static int sx150x_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct sx150x_pinctrl *pctl = gpiochip_get_data(chip); - int status; + unsigned int value; + int ret; if (sx150x_pin_is_oscio(pctl, offset)) return -EINVAL; - status = sx150x_read_cfg(pctl->client, offset, 1, pctl->data->reg_data); - if (status >= 0) - status = !!status; + ret = regmap_read(pctl->regmap, pctl->data->reg_data, &value); + if (ret < 0) + return ret; - return status; + return !!(value & BIT(offset)); } static int sx150x_gpio_set_single_ended(struct gpio_chip *chip, @@ -409,9 +344,9 @@ static int sx150x_gpio_set_single_ended(struct gpio_chip *chip, return 0; mutex_lock(&pctl->lock); - ret = sx150x_write_cfg(pctl->client, offset, 1, - pctl->data->pri.x789.reg_drain, - 0); + ret = regmap_write_bits(pctl->regmap, + pctl->data->pri.x789.reg_drain, + BIT(offset), 0); mutex_unlock(&pctl->lock); if (ret < 0) return ret; @@ -423,9 +358,9 @@ static int sx150x_gpio_set_single_ended(struct gpio_chip *chip, return -ENOTSUPP; mutex_lock(&pctl->lock); - ret = sx150x_write_cfg(pctl->client, offset, 1, - pctl->data->pri.x789.reg_drain, - 1); + ret = regmap_write_bits(pctl->regmap, + pctl->data->pri.x789.reg_drain, + BIT(offset), BIT(offset)); mutex_unlock(&pctl->lock); if (ret < 0) return ret; @@ -438,6 +373,13 @@ static int sx150x_gpio_set_single_ended(struct gpio_chip *chip, return 0; } +static int __sx150x_gpio_set(struct sx150x_pinctrl *pctl, unsigned int offset, + int value) +{ + return regmap_write_bits(pctl->regmap, pctl->data->reg_data, + BIT(offset), value ? BIT(offset) : 0); +} + static void sx150x_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { @@ -451,9 +393,7 @@ static void sx150x_gpio_set(struct gpio_chip *chip, unsigned int offset, mutex_unlock(&pctl->lock); } else { mutex_lock(&pctl->lock); - sx150x_write_cfg(pctl->client, offset, 1, - pctl->data->reg_data, - (value ? 1 : 0)); + __sx150x_gpio_set(pctl, offset, value); mutex_unlock(&pctl->lock); } } @@ -468,8 +408,9 @@ static int sx150x_gpio_direction_input(struct gpio_chip *chip, return -EINVAL; mutex_lock(&pctl->lock); - ret = sx150x_write_cfg(pctl->client, offset, 1, - pctl->data->reg_dir, 1); + ret = regmap_write_bits(pctl->regmap, + pctl->data->reg_dir, + BIT(offset), BIT(offset)); mutex_unlock(&pctl->lock); return ret; @@ -487,12 +428,11 @@ static int sx150x_gpio_direction_output(struct gpio_chip *chip, } mutex_lock(&pctl->lock); - status = sx150x_write_cfg(pctl->client, offset, 1, - pctl->data->reg_data, - (value ? 1 : 0)); + status = __sx150x_gpio_set(pctl, offset, value); if (status >= 0) - status = sx150x_write_cfg(pctl->client, offset, 1, - pctl->data->reg_dir, 0); + status = regmap_write_bits(pctl->regmap, + pctl->data->reg_dir, + BIT(offset), 0); mutex_unlock(&pctl->lock); return status; @@ -504,8 +444,7 @@ static void sx150x_irq_mask(struct irq_data *d) gpiochip_get_data(irq_data_get_irq_chip_data(d)); unsigned int n = d->hwirq; - pctl->irq.masked |= (1 << n); - pctl->irq.update = n; + pctl->irq.masked |= BIT(n); } static void sx150x_irq_unmask(struct irq_data *d) @@ -514,8 +453,7 @@ static void sx150x_irq_unmask(struct irq_data *d) gpiochip_get_data(irq_data_get_irq_chip_data(d)); unsigned int n = d->hwirq; - pctl->irq.masked &= ~(1 << n); - pctl->irq.update = n; + pctl->irq.masked &= ~BIT(n); } static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type) @@ -536,7 +474,6 @@ static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type) pctl->irq.sense &= ~(3UL << (n * 2)); pctl->irq.sense |= val << (n * 2); - pctl->irq.update = n; return 0; } @@ -548,29 +485,20 @@ static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id) unsigned int n; s32 err; unsigned int val; - int i; - for (i = (pctl->data->ngpios / 8) - 1; i >= 0; --i) { - err = regmap_read(pctl->regmap, - pctl->data->reg_irq_src - i, - &val); - if (err < 0) - continue; + err = regmap_read(pctl->regmap, pctl->data->reg_irq_src, &val); + if (err < 0) + return IRQ_NONE; - err = regmap_write(pctl->regmap, - pctl->data->reg_irq_src - i, - val); - if (err < 0) - continue; - - for (n = 0; n < 8; ++n) { - if (val & (1 << n)) { - sub_irq = irq_find_mapping( - pctl->gpio.irqdomain, - (i * 8) + n); - handle_nested_irq(sub_irq); - ++nhandled; - } + err = regmap_write(pctl->regmap, pctl->data->reg_irq_src, val); + if (err < 0) + return IRQ_NONE; + + for (n = 0; n < pctl->data->ngpios; ++n) { + if (val & BIT(n)) { + sub_irq = irq_find_mapping(pctl->gpio.irqdomain, n); + handle_nested_irq(sub_irq); + ++nhandled; } } @@ -589,35 +517,9 @@ static void sx150x_irq_bus_sync_unlock(struct irq_data *d) { struct sx150x_pinctrl *pctl = gpiochip_get_data(irq_data_get_irq_chip_data(d)); - unsigned int n; - - if (pctl->irq.update < 0) - goto out; - n = pctl->irq.update; - pctl->irq.update = -1; - - /* Avoid updates if nothing changed */ - if (pctl->irq.dev_sense == pctl->irq.sense && - pctl->irq.dev_masked == pctl->irq.masked) - goto out; - - pctl->irq.dev_sense = pctl->irq.sense; - pctl->irq.dev_masked = pctl->irq.masked; - - if (pctl->irq.masked & (1 << n)) { - sx150x_write_cfg(pctl->client, n, 1, - pctl->data->reg_irq_mask, 1); - sx150x_write_cfg(pctl->client, n, 2, - pctl->data->reg_sense, 0); - } else { - sx150x_write_cfg(pctl->client, n, 1, - pctl->data->reg_irq_mask, 0); - sx150x_write_cfg(pctl->client, n, 2, - pctl->data->reg_sense, - pctl->irq.sense >> (n * 2)); - } -out: + regmap_write(pctl->regmap, pctl->data->reg_irq_mask, pctl->irq.masked); + regmap_write(pctl->regmap, pctl->data->reg_sense, pctl->irq.sense); mutex_unlock(&pctl->lock); } @@ -628,10 +530,9 @@ static int sx150x_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, unsigned int param = pinconf_to_config_param(*config); int ret; u32 arg; + unsigned int data; if (sx150x_pin_is_oscio(pctl, pin)) { - unsigned int data; - switch (param) { case PIN_CONFIG_DRIVE_PUSH_PULL: case PIN_CONFIG_OUTPUT: @@ -666,8 +567,10 @@ static int sx150x_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, switch (param) { case PIN_CONFIG_BIAS_PULL_DOWN: mutex_lock(&pctl->lock); - ret = sx150x_read_cfg(pctl->client, pin, 1, - pctl->data->reg_pulldn); + ret = regmap_read(pctl->regmap, + pctl->data->reg_pulldn, + &data); + data &= BIT(pin); mutex_unlock(&pctl->lock); if (ret < 0) @@ -681,8 +584,10 @@ static int sx150x_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, case PIN_CONFIG_BIAS_PULL_UP: mutex_lock(&pctl->lock); - ret = sx150x_read_cfg(pctl->client, pin, 1, - pctl->data->reg_pullup); + ret = regmap_read(pctl->regmap, + pctl->data->reg_pullup, + &data); + data &= BIT(pin); mutex_unlock(&pctl->lock); if (ret < 0) @@ -699,14 +604,16 @@ static int sx150x_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, return -ENOTSUPP; mutex_lock(&pctl->lock); - ret = sx150x_read_cfg(pctl->client, pin, 1, - pctl->data->pri.x789.reg_drain); + ret = regmap_read(pctl->regmap, + pctl->data->pri.x789.reg_drain, + &data); + data &= BIT(pin); mutex_unlock(&pctl->lock); if (ret < 0) return ret; - if (!ret) + if (!data) return -EINVAL; arg = 1; @@ -717,14 +624,16 @@ static int sx150x_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, arg = true; else { mutex_lock(&pctl->lock); - ret = sx150x_read_cfg(pctl->client, pin, 1, - pctl->data->pri.x789.reg_drain); + ret = regmap_read(pctl->regmap, + pctl->data->pri.x789.reg_drain, + &data); + data &= BIT(pin); mutex_unlock(&pctl->lock); if (ret < 0) return ret; - if (ret) + if (data) return -EINVAL; arg = 1; @@ -785,15 +694,17 @@ static int sx150x_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: case PIN_CONFIG_BIAS_DISABLE: mutex_lock(&pctl->lock); - ret = sx150x_write_cfg(pctl->client, pin, 1, - pctl->data->reg_pulldn, 0); + ret = regmap_write_bits(pctl->regmap, + pctl->data->reg_pulldn, + BIT(pin), 0); mutex_unlock(&pctl->lock); if (ret < 0) return ret; mutex_lock(&pctl->lock); - ret = sx150x_write_cfg(pctl->client, pin, 1, - pctl->data->reg_pullup, 0); + ret = regmap_write_bits(pctl->regmap, + pctl->data->reg_pullup, + BIT(pin), 0); mutex_unlock(&pctl->lock); if (ret < 0) return ret; @@ -802,9 +713,9 @@ static int sx150x_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, case PIN_CONFIG_BIAS_PULL_UP: mutex_lock(&pctl->lock); - ret = sx150x_write_cfg(pctl->client, pin, 1, - pctl->data->reg_pullup, - 1); + ret = regmap_write_bits(pctl->regmap, + pctl->data->reg_pullup, + BIT(pin), BIT(pin)); mutex_unlock(&pctl->lock); if (ret < 0) return ret; @@ -813,9 +724,9 @@ static int sx150x_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, case PIN_CONFIG_BIAS_PULL_DOWN: mutex_lock(&pctl->lock); - ret = sx150x_write_cfg(pctl->client, pin, 1, - pctl->data->reg_pulldn, - 1); + ret = regmap_write_bits(pctl->regmap, + pctl->data->reg_pulldn, + BIT(pin), BIT(pin)); mutex_unlock(&pctl->lock); if (ret < 0) return ret; @@ -878,16 +789,6 @@ static const struct of_device_id sx150x_of_match[] = { {}, }; -static int sx150x_init_io(struct sx150x_pinctrl *pctl, u8 base, u16 cfg) -{ - int err = 0; - unsigned int n; - - for (n = 0; err >= 0 && n < (pctl->data->ngpios / 8); ++n) - err = regmap_write(pctl->regmap, base - n, cfg >> (n * 8)); - return err; -} - static int sx150x_reset(struct sx150x_pinctrl *pctl) { int err; @@ -933,11 +834,16 @@ static int sx150x_init_misc(struct sx150x_pinctrl *pctl) return -EINVAL; } - return i2c_smbus_write_byte_data(pctl->client, reg, value); + return regmap_write(pctl->regmap, reg, value); } static int sx150x_init_hw(struct sx150x_pinctrl *pctl) { + const u8 reg[] = { + [SX150X_789] = pctl->data->pri.x789.reg_polarity, + [SX150X_456] = pctl->data->pri.x456.reg_pld_mode, + [SX150X_123] = pctl->data->pri.x123.reg_pld_mode, + }; int err; if (pctl->data->model == SX150X_789 && @@ -952,28 +858,165 @@ static int sx150x_init_hw(struct sx150x_pinctrl *pctl) return err; /* Set all pins to work in normal mode */ - if (pctl->data->model == SX150X_789) { - err = sx150x_init_io(pctl, - pctl->data->pri.x789.reg_polarity, - 0); - if (err < 0) - return err; - } else if (pctl->data->model == SX150X_456) { - /* Set all pins to work in normal mode */ - err = sx150x_init_io(pctl, - pctl->data->pri.x456.reg_pld_mode, - 0); - if (err < 0) - return err; + return regmap_write(pctl->regmap, reg[pctl->data->model], 0); +} + +static int sx150x_regmap_reg_width(struct sx150x_pinctrl *pctl, + unsigned int reg) +{ + const struct sx150x_device_data *data = pctl->data; + + if (reg == data->reg_sense) { + /* + * RegSense packs two bits of configuration per GPIO, + * so we'd need to read twice as many bits as there + * are GPIO in our chip + */ + return 2 * data->ngpios; + } else if ((data->model == SX150X_789 && + (reg == data->pri.x789.reg_misc || + reg == data->pri.x789.reg_clock || + reg == data->pri.x789.reg_reset)) + || + (data->model == SX150X_123 && + reg == data->pri.x123.reg_advance) + || + (data->model == SX150X_456 && + reg == data->pri.x456.reg_advance)) { + return 8; } else { - /* Set all pins to work in normal mode */ - err = sx150x_init_io(pctl, - pctl->data->pri.x123.reg_pld_mode, - 0); - if (err < 0) - return err; + return data->ngpios; + } +} + +static unsigned int sx150x_maybe_swizzle(struct sx150x_pinctrl *pctl, + unsigned int reg, unsigned int val) +{ + unsigned int a, b; + const struct sx150x_device_data *data = pctl->data; + + /* + * Whereas SX1509 presents RegSense in a simple layout as such: + * reg [ f f e e d d c c ] + * reg + 1 [ b b a a 9 9 8 8 ] + * reg + 2 [ 7 7 6 6 5 5 4 4 ] + * reg + 3 [ 3 3 2 2 1 1 0 0 ] + * + * SX1503 and SX1506 deviate from that data layout, instead storing + * thier contents as follows: + * + * reg [ f f e e d d c c ] + * reg + 1 [ 7 7 6 6 5 5 4 4 ] + * reg + 2 [ b b a a 9 9 8 8 ] + * reg + 3 [ 3 3 2 2 1 1 0 0 ] + * + * so, taking that into account, we swap two + * inner bytes of a 4-byte result + */ + + if (reg == data->reg_sense && + data->ngpios == 16 && + (data->model == SX150X_123 || + data->model == SX150X_456)) { + a = val & 0x00ff0000; + b = val & 0x0000ff00; + + val &= 0xff0000ff; + val |= b << 8; + val |= a >> 8; } + return val; +} + +/* + * In order to mask the differences between 16 and 8 bit expander + * devices we set up a sligthly ficticious regmap that pretends to be + * a set of 32-bit (to accomodate RegSenseLow/RegSenseHigh + * pair/quartet) registers and transparently reconstructs those + * registers via multiple I2C/SMBus reads + * + * This way the rest of the driver code, interfacing with the chip via + * regmap API, can work assuming that each GPIO pin is represented by + * a group of bits at an offset proportioan to GPIO number within a + * given register. + * + */ +static int sx150x_regmap_reg_read(void *context, unsigned int reg, + unsigned int *result) +{ + int ret, n; + struct sx150x_pinctrl *pctl = context; + struct i2c_client *i2c = pctl->client; + const int width = sx150x_regmap_reg_width(pctl, reg); + unsigned int idx, val; + + /* + * There are four potential cases coverd by this function: + * + * 1) 8-pin chip, single configuration bit register + * + * This is trivial the code below just needs to read: + * reg [ 7 6 5 4 3 2 1 0 ] + * + * 2) 8-pin chip, double configuration bit register (RegSense) + * + * The read will be done as follows: + * reg [ 7 7 6 6 5 5 4 4 ] + * reg + 1 [ 3 3 2 2 1 1 0 0 ] + * + * 3) 16-pin chip, single configuration bit register + * + * The read will be done as follows: + * reg [ f e d c b a 9 8 ] + * reg + 1 [ 7 6 5 4 3 2 1 0 ] + * + * 4) 16-pin chip, double configuration bit register (RegSense) + * + * The read will be done as follows: + * reg [ f f e e d d c c ] + * reg + 1 [ b b a a 9 9 8 8 ] + * reg + 2 [ 7 7 6 6 5 5 4 4 ] + * reg + 3 [ 3 3 2 2 1 1 0 0 ] + */ + + for (n = width, val = 0, idx = reg; n > 0; n -= 8, idx++) { + val <<= 8; + + ret = i2c_smbus_read_byte_data(i2c, idx); + if (ret < 0) + return ret; + + val |= ret; + } + + *result = sx150x_maybe_swizzle(pctl, reg, val); + + return 0; +} + +static int sx150x_regmap_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + int ret, n; + struct sx150x_pinctrl *pctl = context; + struct i2c_client *i2c = pctl->client; + const int width = sx150x_regmap_reg_width(pctl, reg); + + val = sx150x_maybe_swizzle(pctl, reg, val); + + n = width - 8; + do { + const u8 byte = (val >> n) & 0xff; + + ret = i2c_smbus_write_byte_data(i2c, reg, byte); + if (ret < 0) + return ret; + + reg++; + n -= 8; + } while (n >= 0); + return 0; } @@ -981,18 +1024,18 @@ static bool sx150x_reg_volatile(struct device *dev, unsigned int reg) { struct sx150x_pinctrl *pctl = i2c_get_clientdata(to_i2c_client(dev)); - return reg == pctl->data->reg_irq_src || - reg == pctl->data->reg_irq_src - 1 || - reg == pctl->data->reg_data || - reg == pctl->data->reg_data - 1; + return reg == pctl->data->reg_irq_src || reg == pctl->data->reg_data; } const struct regmap_config sx150x_regmap_config = { .reg_bits = 8, - .val_bits = 8, + .val_bits = 32, .cache_type = REGCACHE_RBTREE, + .reg_read = sx150x_regmap_reg_read, + .reg_write = sx150x_regmap_reg_write, + .max_register = SX150X_MAX_REGISTER, .volatile_reg = sx150x_reg_volatile, }; @@ -1026,7 +1069,8 @@ static int sx150x_probe(struct i2c_client *client, if (!pctl->data) return -EINVAL; - pctl->regmap = devm_regmap_init_i2c(client, &sx150x_regmap_config); + pctl->regmap = devm_regmap_init(dev, NULL, pctl, + &sx150x_regmap_config); if (IS_ERR(pctl->regmap)) { ret = PTR_ERR(pctl->regmap); dev_err(dev, "Failed to allocate register map: %d\n", @@ -1072,9 +1116,6 @@ static int sx150x_probe(struct i2c_client *client, pctl->irq.masked = ~0; pctl->irq.sense = 0; - pctl->irq.dev_masked = ~0; - pctl->irq.dev_sense = 0; - pctl->irq.update = -1; ret = gpiochip_irqchip_add(&pctl->gpio, &pctl->irq_chip, 0, |