The RTL8231 GPIO and LED expander can be configured for use as an MDIO or SMI
bus device. Currently only the MDIO mode is supported, although SMI mode
support should be fairly straightforward, once an SMI bus driver is available.
Provided features by the RTL8231:
- Up to 37 GPIOs
- Configurable drive strength: 8mA or 4mA (currently unsupported)
- Input debouncing on high GPIOs (currently unsupported)
- Up to 88 LEDs in multiple scan matrix groups
- On, off, or one of six toggling intervals
- "single-color mode": 2×36 single color LEDs + 8 bi-color LEDs
- "bi-color mode": (12 + 2×6) bi-color LEDs + 24 single color LEDs
- Up to one PWM output (currently unsupported)
- Fixed duty cycle, 8 selectable frequencies (1.2kHz - 4.8kHz)
There remain some log warnings when probing the device, possibly due to the way
I'm using the MFD subsystem. Would it be possible to avoid these?
[ 2.602242] rtl8231-pinctrl: Failed to locate of_node [id: -2]
[ 2.609380] rtl8231-pinctrl rtl8231-pinctrl.0.auto: no of_node; not parsing pinctrl DT
When no 'leds' sub-node is specified:
[ 2.922262] rtl8231-leds: Failed to locate of_node [id: -2]
[ 2.967149] rtl8231-leds rtl8231-leds.1.auto: no of_node; not parsing pinctrl DT
[ 2.975673] rtl8231-leds rtl8231-leds.1.auto: scan mode missing or invalid
[ 2.983531] rtl8231-leds: probe of rtl8231-leds.1.auto failed with error -22
Changes since RFC:
- Dropped MDIO regmap interface. I was unable to resolve the Kconfig
dependency issue, so have reverted to using regmap_config.reg_read/write.
- Added pinctrl support
- Added LED support
- Changed root device to MFD, with pinctrl and leds child devices. Root
device is now an mdio_device driver.
Sander Vanheule (5):
dt-bindings: leds: Binding for RTL8231 scan matrix
dt-bindings: mfd: Binding for RTL8231
mfd: Add RTL8231 core device
pinctrl: Add RTL8231 pin control and GPIO support
leds: Add support for RTL8231 LED scan matrix
.../bindings/leds/realtek,rtl8231-leds.yaml | 159 ++++++
.../bindings/mfd/realtek,rtl8231.yaml | 202 +++++++
drivers/leds/Kconfig | 10 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-rtl8231.c | 281 ++++++++++
drivers/mfd/Kconfig | 9 +
drivers/mfd/Makefile | 1 +
drivers/mfd/rtl8231.c | 163 ++++++
drivers/pinctrl/Kconfig | 10 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rtl8231.c | 497 ++++++++++++++++++
include/linux/mfd/rtl8231.h | 49 ++
12 files changed, 1383 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
create mode 100644 drivers/leds/leds-rtl8231.c
create mode 100644 drivers/mfd/rtl8231.c
create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c
create mode 100644 include/linux/mfd/rtl8231.h
--
2.31.1
The RTL8231 is implemented as an MDIO device, and provides a regmap
interface for register access by the core and child devices.
The chip can also be a device on an SMI bus, a proprietary I2C-like bus
by Realtek. Since kernel support for SMI is limited, and no real-world
SMI implementations have been encountered for this device, this is
currently unimplemented. The use of the regmap interface should make any
future support relatively straightforward.
After reset, all pins are muxed to GPIO inputs before the pin drivers
are enabled. This is done to prevent accidental system resets, when a
pin is connected to the parent SoC's reset line.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/mfd/Kconfig | 9 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/rtl8231.c | 163 ++++++++++++++++++++++++++++++++++++
include/linux/mfd/rtl8231.h | 49 +++++++++++
4 files changed, 222 insertions(+)
create mode 100644 drivers/mfd/rtl8231.c
create mode 100644 include/linux/mfd/rtl8231.h
@@ -0,0 +1,163 @@+// SPDX-License-Identifier: GPL-2.0-only++#include<linux/bits.h>+#include<linux/delay.h>+#include<linux/mfd/core.h>+#include<linux/mfd/rtl8231.h>+#include<linux/mdio.h>+#include<linux/module.h>+#include<linux/platform_device.h>+#include<linux/regmap.h>++staticconststructreg_fieldRTL8231_FIELD_LED_START=REG_FIELD(RTL8231_REG_FUNC0,1,1);+staticconststructreg_fieldRTL8231_FIELD_READY_CODE=REG_FIELD(RTL8231_REG_FUNC1,4,9);+staticconststructreg_fieldRTL8231_FIELD_SOFT_RESET=REG_FIELD(RTL8231_REG_PIN_HI_CFG,15,15);++staticconststructmfd_cellrtl8231_cells[]={+{+.name="rtl8231-pinctrl",+.of_compatible="realtek,rtl8231-pinctrl",+},+{+.name="rtl8231-leds",+.of_compatible="realtek,rtl8231-leds",+},+};++staticintrtl8231_init(structdevice*dev,structregmap*map)+{+structregmap_field*field_ready_code;+structregmap_field*field_soft_reset;+unsignedintv;+interr=0;++field_ready_code=regmap_field_alloc(map,RTL8231_FIELD_READY_CODE);+if(IS_ERR(field_ready_code))+returnPTR_ERR(field_ready_code);++field_soft_reset=regmap_field_alloc(map,RTL8231_FIELD_SOFT_RESET);+if(IS_ERR(field_soft_reset)){+err=PTR_ERR(field_soft_reset);+gotoinit_out;+}++err=regmap_field_read(field_ready_code,&v);++if(err){+dev_err(dev,"failed to read READY_CODE\n");+gotoinit_out;+}elseif(v!=RTL8231_FUNC1_READY_CODE_VALUE){+dev_err(dev,"RTL8231 not present or ready 0x%x != 0x%x\n",+v,RTL8231_FUNC1_READY_CODE_VALUE);+err=-ENODEV;+gotoinit_out;+}++// TODO Implement reset-gpios+regmap_field_write(field_soft_reset,1);+usleep_range(1000,10000);++/* Do not write LED_START before configuring pins */+/* Select GPIO functionality for all pins and set to input */+regmap_write(map,RTL8231_REG_PIN_MODE0,0xffff);+regmap_write(map,RTL8231_REG_GPIO_DIR0,0xffff);+regmap_write(map,RTL8231_REG_PIN_MODE1,0xffff);+regmap_write(map,RTL8231_REG_GPIO_DIR1,0xffff);+regmap_write(map,RTL8231_REG_PIN_HI_CFG,GENMASK(4,0)|GENMASK(9,5));++init_out:+regmap_field_free(field_ready_code);+regmap_field_free(field_soft_reset);+returnerr;+}++staticintrtl8231_mdio_reg_read(void*ctx,unsignedintreg,unsignedint*val)+{+structmdio_device*mdiodev=ctx;+intret;++ret=mdiobus_read(mdiodev->bus,mdiodev->addr,reg);+if(ret<0)+returnret;++*val=ret&0xffff;+return0;+}++staticintrtl8231_mdio_reg_write(void*ctx,unsignedintreg,unsignedintval)+{+structmdio_device*mdiodev=ctx;++returnmdiobus_write(mdiodev->bus,mdiodev->addr,reg,val);+}++staticconststructregmap_configrtl8231_regmap_config={+.val_bits=16,+.reg_bits=5,+.max_register=RTL8231_REG_COUNT-1,+.use_single_read=true,+.use_single_write=true,+.reg_format_endian=REGMAP_ENDIAN_BIG,+.val_format_endian=REGMAP_ENDIAN_BIG,+.reg_read=rtl8231_mdio_reg_read,+.reg_write=rtl8231_mdio_reg_write,+};++staticintrtl8231_mdio_probe(structmdio_device*mdiodev)+{+structdevice*dev=&mdiodev->dev;+structregmap_field*led_start;+structregmap*map;+interr;++map=devm_regmap_init(dev,NULL,mdiodev,&rtl8231_regmap_config);++if(IS_ERR(map)){+dev_err(dev,"failed to init regmap\n");+returnPTR_ERR(map);+}++led_start=devm_regmap_field_alloc(dev,map,RTL8231_FIELD_LED_START);+if(IS_ERR(led_start))+returnPTR_ERR(led_start);++dev_set_drvdata(dev,led_start);++err=rtl8231_init(dev,map);+if(err)+returnerr;++/* LED_START enables power to output pins, and starts the LED engine */+regmap_field_write(led_start,1);++returndevm_mfd_add_devices(dev,PLATFORM_DEVID_AUTO,rtl8231_cells,+ARRAY_SIZE(rtl8231_cells),NULL,0,NULL);+}++staticvoidrtl8231_mdio_remove(structmdio_device*mdiodev)+{+structregmap_field*led_start;++led_start=dev_get_drvdata(&mdiodev->dev);+regmap_field_write(led_start,0);+}++staticconststructof_device_idrtl8231_of_match[]={+{.compatible="realtek,rtl8231"},+{},+};+MODULE_DEVICE_TABLE(of,rtl8231_of_match);++staticstructmdio_driverrtl8231_mdio_driver={+.mdiodrv.driver={+.name="rtl8231-expander",+.of_match_table=rtl8231_of_match,+},+.probe=rtl8231_mdio_probe,+.remove=rtl8231_mdio_remove,+};+mdio_module_driver(rtl8231_mdio_driver);++MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");+MODULE_DESCRIPTION("Realtek RTL8231 GPIO and LED expander");+MODULE_LICENSE("GPL v2");
Both single and bi-color scanning modes are supported. The driver will
verify that the addresses are valid for the current mode, before
registering the LEDs.
LEDs can be turned on, off, or toggled at one of six predefined rates
from 40ms to 1280ms.
Implements a platform device for use as child device with RTL8231 MFD,
and uses the parent regmap to access the required registers.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/leds/Kconfig | 10 ++
drivers/leds/Makefile | 1 +
drivers/leds/leds-rtl8231.c | 281 ++++++++++++++++++++++++++++++++++++
3 files changed, 292 insertions(+)
create mode 100644 drivers/leds/leds-rtl8231.c
This driver implements the GPIO and pin muxing features provided by the
RTL8231. The device should be instantiated as an MFD child, where the
parent device has already configured the regmap used for register
access.
Although described in the bindings, pin debouncing and drive strength
selection are currently not implemented. Debouncing is only available
for the six highest GPIOs, and must be emulated when other pins are used
for (button) inputs anyway.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/pinctrl/Kconfig | 10 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rtl8231.c | 497 ++++++++++++++++++++++++++++++
3 files changed, 508 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c
@@ -0,0 +1,497 @@+// SPDX-License-Identifier: GPL-2.0-only++#include<linux/bitops.h>+#include<linux/gpio/driver.h>+#include<linux/mfd/rtl8231.h>+#include<linux/module.h>+#include<linux/pinctrl/pinconf-generic.h>+#include<linux/pinctrl/pinctrl.h>+#include<linux/pinctrl/pinmux.h>+#include<linux/platform_device.h>+#include<linux/regmap.h>++#define RTL8231_NUM_GPIOS 37++enumrtl8231_gpio_regfield{+RTL8231_FIELD_GPIO_DIR0,+RTL8231_FIELD_GPIO_DIR1,+RTL8231_FIELD_GPIO_DIR2,+RTL8231_FIELD_GPIO_DATA0,+RTL8231_FIELD_GPIO_DATA1,+RTL8231_FIELD_GPIO_DATA2,+RTL8231_FIELD_GPIO_MAX+};++staticstructreg_fieldrtl8231_gpio_fields[RTL8231_FIELD_GPIO_MAX]={+[RTL8231_FIELD_GPIO_DIR0]=REG_FIELD(RTL8231_REG_GPIO_DIR0,0,15),+[RTL8231_FIELD_GPIO_DIR1]=REG_FIELD(RTL8231_REG_GPIO_DIR1,0,15),+[RTL8231_FIELD_GPIO_DIR2]=REG_FIELD(RTL8231_REG_PIN_HI_CFG,5,9),+[RTL8231_FIELD_GPIO_DATA0]=REG_FIELD(RTL8231_REG_GPIO_DATA0,0,15),+[RTL8231_FIELD_GPIO_DATA1]=REG_FIELD(RTL8231_REG_GPIO_DATA1,0,15),+[RTL8231_FIELD_GPIO_DATA2]=REG_FIELD(RTL8231_REG_GPIO_DATA2,0,4),+};++structrtl8231_function{+constchar*name;+unsignedintngroups;+constchar**groups;+};++structrtl8231_pin_ctrl{+/* Pin controller */+structpinctrl_descpctl_desc;+unsignedintnfunctions;+structrtl8231_function*functions;+structregmap*map;+/* GPIO controller */+structgpio_chipgc;+structregmap_field*fields[RTL8231_FIELD_GPIO_MAX];+};++/*+*Pincontrollerfunctionality+*/+staticconstchar*constrtl8231_pin_function_names[]={+"gpio",+"led",+"pwm",+};++enumrtl8231_pin_function{+RTL8231_PIN_FUNCTION_GPIO=BIT(0),+RTL8231_PIN_FUNCTION_LED=BIT(1),+RTL8231_PIN_FUNCTION_PWM=BIT(2),+};++structrtl8231_pin_desc{+unsignedintnumber;+constchar*name;+enumrtl8231_pin_functionfunctions;+u8reg;+u8offset;+u8gpio_function_value;+};++#define RTL8231_PIN(_num, _func, _reg, _fld, _val) \+{\+.number=_num,\+.name="gpio"#_num,\+.functions=RTL8231_PIN_FUNCTION_GPIO|_func,\+.reg=_reg,\+.offset=_fld,\+.gpio_function_value=_val,\+}+#define RTL8231_GPIO_PIN(_num) \+RTL8231_PIN(_num,0,0,0,0)+#define RTL8231_LED_PIN(_num, _reg, _fld) \+RTL8231_PIN(_num,RTL8231_PIN_FUNCTION_LED,_reg,_fld,RTL8231_PIN_MODE_GPIO)+#define RTL8231_PWM_PIN(_num, _reg, _fld) \+RTL8231_PIN(_num,RTL8231_PIN_FUNCTION_PWM,_reg,_fld,0)++/* Pins always support GPIO, and may support one alternate function */+staticconststructrtl8231_pin_descrtl8231_pins[RTL8231_NUM_GPIOS]={+RTL8231_LED_PIN(0,RTL8231_REG_PIN_MODE0,0),+RTL8231_LED_PIN(1,RTL8231_REG_PIN_MODE0,1),+RTL8231_LED_PIN(2,RTL8231_REG_PIN_MODE0,2),+RTL8231_LED_PIN(3,RTL8231_REG_PIN_MODE0,3),+RTL8231_LED_PIN(4,RTL8231_REG_PIN_MODE0,4),+RTL8231_LED_PIN(5,RTL8231_REG_PIN_MODE0,5),+RTL8231_LED_PIN(6,RTL8231_REG_PIN_MODE0,6),+RTL8231_LED_PIN(7,RTL8231_REG_PIN_MODE0,7),+RTL8231_LED_PIN(8,RTL8231_REG_PIN_MODE0,8),+RTL8231_LED_PIN(9,RTL8231_REG_PIN_MODE0,9),+RTL8231_LED_PIN(10,RTL8231_REG_PIN_MODE0,10),+RTL8231_LED_PIN(11,RTL8231_REG_PIN_MODE0,11),+RTL8231_LED_PIN(12,RTL8231_REG_PIN_MODE0,12),+RTL8231_LED_PIN(13,RTL8231_REG_PIN_MODE0,13),+RTL8231_LED_PIN(14,RTL8231_REG_PIN_MODE0,14),+RTL8231_LED_PIN(15,RTL8231_REG_PIN_MODE0,15),+RTL8231_LED_PIN(16,RTL8231_REG_PIN_MODE1,0),+RTL8231_LED_PIN(17,RTL8231_REG_PIN_MODE1,1),+RTL8231_LED_PIN(18,RTL8231_REG_PIN_MODE1,2),+RTL8231_LED_PIN(19,RTL8231_REG_PIN_MODE1,3),+RTL8231_LED_PIN(20,RTL8231_REG_PIN_MODE1,4),+RTL8231_LED_PIN(21,RTL8231_REG_PIN_MODE1,5),+RTL8231_LED_PIN(22,RTL8231_REG_PIN_MODE1,6),+RTL8231_LED_PIN(23,RTL8231_REG_PIN_MODE1,7),+RTL8231_LED_PIN(24,RTL8231_REG_PIN_MODE1,8),+RTL8231_LED_PIN(25,RTL8231_REG_PIN_MODE1,9),+RTL8231_LED_PIN(26,RTL8231_REG_PIN_MODE1,10),+RTL8231_LED_PIN(27,RTL8231_REG_PIN_MODE1,11),+RTL8231_LED_PIN(28,RTL8231_REG_PIN_MODE1,12),+RTL8231_LED_PIN(29,RTL8231_REG_PIN_MODE1,13),+RTL8231_LED_PIN(30,RTL8231_REG_PIN_MODE1,14),+RTL8231_LED_PIN(31,RTL8231_REG_PIN_MODE1,15),+RTL8231_LED_PIN(32,RTL8231_REG_PIN_HI_CFG,0),+RTL8231_LED_PIN(33,RTL8231_REG_PIN_HI_CFG,1),+RTL8231_LED_PIN(34,RTL8231_REG_PIN_HI_CFG,2),+RTL8231_PWM_PIN(35,RTL8231_REG_FUNC1,3),+RTL8231_GPIO_PIN(36),+};++staticintrtl8231_get_groups_count(structpinctrl_dev*pctldev)+{+returnARRAY_SIZE(rtl8231_pins);+}++staticconstchar*rtl8231_get_group_name(structpinctrl_dev*pctldev,unsignedintselector)+{+returnrtl8231_pins[selector].name;+}++staticintrtl8231_get_group_pins(structpinctrl_dev*pctldev,unsignedintselector,+constunsignedint**pins,unsignedint*num_pins)+{+if(selector<ARRAY_SIZE(rtl8231_pins)){+*pins=&rtl8231_pins[selector].number;+*num_pins=1;+return0;+}++return-EINVAL;+}++staticconststructpinctrl_opsrtl8231_pinctrl_ops={+.get_groups_count=rtl8231_get_groups_count,+.get_group_name=rtl8231_get_group_name,+.get_group_pins=rtl8231_get_group_pins,+.dt_node_to_map=pinconf_generic_dt_node_to_map_all,+.dt_free_map=pinconf_generic_dt_free_map,+};++staticintrtl8231_get_functions_count(structpinctrl_dev*pctldev)+{+structrtl8231_pin_ctrl*ctrl=pinctrl_dev_get_drvdata(pctldev);++returnctrl->nfunctions;+}++staticconstchar*rtl8231_get_function_name(structpinctrl_dev*pctldev,unsignedintselector)+{+structrtl8231_pin_ctrl*ctrl=pinctrl_dev_get_drvdata(pctldev);++returnctrl->functions[selector].name;+}++staticintrtl8231_get_function_groups(structpinctrl_dev*pctldev,unsignedintselector,+constchar*const**groups,unsignedint*num_groups)+{+structrtl8231_pin_ctrl*ctrl=pinctrl_dev_get_drvdata(pctldev);++*groups=ctrl->functions[selector].groups;+*num_groups=ctrl->functions[selector].ngroups;+return0;+}++staticintrtl8231_set_mux(structpinctrl_dev*pctldev,unsignedintfunc_selector,+unsignedintgroup_selector)+{+structrtl8231_pin_ctrl*ctrl=pinctrl_dev_get_drvdata(pctldev);+conststructrtl8231_pin_desc*desc=&rtl8231_pins[group_selector];+unsignedintfunc_flag=BIT(func_selector);+unsignedintfunction_mask;+unsignedintgpio_function;+interr=0;++if(!(desc->functions&func_flag))+return-EINVAL;++function_mask=BIT(desc->offset);+gpio_function=desc->gpio_function_value<<desc->offset;++switch(func_flag){+caseRTL8231_PIN_FUNCTION_LED:+caseRTL8231_PIN_FUNCTION_PWM:+err=regmap_update_bits(ctrl->map,desc->reg,function_mask,~gpio_function);+break;+caseRTL8231_PIN_FUNCTION_GPIO:+err=regmap_update_bits(ctrl->map,desc->reg,function_mask,gpio_function);+break;+default:+return-EINVAL;+}++returnerr;+}++staticintrtl8231_gpio_request_enable(structpinctrl_dev*pctldev,+structpinctrl_gpio_range*range,unsignedintoffset)+{+structrtl8231_pin_ctrl*ctrl=pinctrl_dev_get_drvdata(pctldev);+conststructrtl8231_pin_desc*desc=&rtl8231_pins[offset];+unsignedintfunction_mask;+unsignedintgpio_function;++function_mask=BIT(desc->offset);+gpio_function=desc->gpio_function_value<<desc->offset;++returnregmap_update_bits(ctrl->map,desc->reg,function_mask,gpio_function);+}++staticconststructpinmux_opsrtl8231_pinmux_ops={+.set_mux=rtl8231_set_mux,+.get_functions_count=rtl8231_get_functions_count,+.get_function_name=rtl8231_get_function_name,+.get_function_groups=rtl8231_get_function_groups,+.gpio_request_enable=rtl8231_gpio_request_enable,+.strict=true+};+++staticintrtl8231_pinctrl_init_functions(structdevice*dev,structrtl8231_pin_ctrl*ctrl)+{+structrtl8231_function*function;+constchar**group_name;+unsignedintf_idx;+unsignedintpin;++ctrl->nfunctions=ARRAY_SIZE(rtl8231_pin_function_names);+ctrl->functions=devm_kcalloc(dev,ctrl->nfunctions,sizeof(*ctrl->functions),GFP_KERNEL);+if(IS_ERR(ctrl->functions)){+dev_err(dev,"failed to allocate pin function descriptors\n");+returnPTR_ERR(ctrl->functions);+}++for(f_idx=0;f_idx<ctrl->nfunctions;f_idx++){+function=&ctrl->functions[f_idx];+function->name=rtl8231_pin_function_names[f_idx];++for(pin=0;pin<ctrl->pctl_desc.npins;pin++)+if(rtl8231_pins[pin].functions&BIT(f_idx))+function->ngroups++;++function->groups=devm_kcalloc(dev,function->ngroups,+sizeof(*function->groups),GFP_KERNEL);+if(IS_ERR(function->groups)){+dev_err(dev,"failed to allocate pin function group names\n");+returnPTR_ERR(function->groups);+}++group_name=function->groups;+for(pin=0;pin<ctrl->pctl_desc.npins;pin++)+if(rtl8231_pins[pin].functions&BIT(f_idx))+*group_name++=rtl8231_pins[pin].name;+}++return0;+}++staticintrtl8231_pinctrl_init(structdevice*dev,structrtl8231_pin_ctrl*ctrl)+{+structpinctrl_dev*pctl;+structpinctrl_pin_desc*pins;+unsignedintpin;+interr=0;++ctrl->pctl_desc.name="rtl8231-pinctrl",+ctrl->pctl_desc.owner=THIS_MODULE,+ctrl->pctl_desc.pctlops=&rtl8231_pinctrl_ops,+ctrl->pctl_desc.pmxops=&rtl8231_pinmux_ops,++ctrl->pctl_desc.npins=ARRAY_SIZE(rtl8231_pins);+pins=devm_kcalloc(dev,ctrl->pctl_desc.npins,sizeof(*pins),GFP_KERNEL);+if(IS_ERR(pins)){+dev_err(dev,"failed to allocate pin descriptors\n");+returnPTR_ERR(pins);+}+ctrl->pctl_desc.pins=pins;++for(pin=0;pin<ctrl->pctl_desc.npins;pin++){+pins[pin].number=rtl8231_pins[pin].number;+pins[pin].name=rtl8231_pins[pin].name;+}++err=rtl8231_pinctrl_init_functions(dev,ctrl);+if(err)+returnerr;++err=devm_pinctrl_register_and_init(dev->parent,&ctrl->pctl_desc,ctrl,&pctl);+if(err){+dev_err(dev,"failed to register pin controller\n");+returnerr;+}++err=pinctrl_enable(pctl);+if(err)+dev_err(dev,"failed to enable pin controller\n");++returnerr;+}++/*+*GPIOcontrollerfunctionality+*/+staticintrtl8231_pin_read(structrtl8231_pin_ctrl*ctrl,intbase,intoffset)+{+intfield=base+offset/16;+intbit=offset%16;+unsignedintv;+interr;++err=regmap_field_read(ctrl->fields[field],&v);+if(err)+returnerr;++return!!(v&BIT(bit));+}++staticintrtl8231_pin_write(structrtl8231_pin_ctrl*ctrl,intbase,intoffset,intval)+{+intfield=base+offset/16;+intbit=offset%16;++returnregmap_field_update_bits(ctrl->fields[field],BIT(bit),val<<bit);+}++staticintrtl8231_direction_input(structgpio_chip*gc,unsignedintoffset)+{+structrtl8231_pin_ctrl*ctrl=gpiochip_get_data(gc);++returnrtl8231_pin_write(ctrl,RTL8231_FIELD_GPIO_DIR0,offset,RTL8231_GPIO_DIR_IN);+}++staticintrtl8231_direction_output(structgpio_chip*gc,unsignedintoffset,intvalue)+{+structrtl8231_pin_ctrl*ctrl=gpiochip_get_data(gc);+interr;++err=rtl8231_pin_write(ctrl,RTL8231_FIELD_GPIO_DIR0,offset,RTL8231_GPIO_DIR_OUT);+if(err)+returnerr;++returnrtl8231_pin_write(ctrl,RTL8231_FIELD_GPIO_DATA0,offset,value);+}++staticintrtl8231_get_direction(structgpio_chip*gc,unsignedintoffset)+{+structrtl8231_pin_ctrl*ctrl=gpiochip_get_data(gc);++returnrtl8231_pin_read(ctrl,RTL8231_FIELD_GPIO_DIR0,offset);+}++staticintrtl8231_gpio_get(structgpio_chip*gc,unsignedintoffset)+{+structrtl8231_pin_ctrl*ctrl=gpiochip_get_data(gc);++returnrtl8231_pin_read(ctrl,RTL8231_FIELD_GPIO_DATA0,offset);+}++staticvoidrtl8231_gpio_set(structgpio_chip*gc,unsignedintoffset,intvalue)+{+structrtl8231_pin_ctrl*ctrl=gpiochip_get_data(gc);++rtl8231_pin_write(ctrl,RTL8231_FIELD_GPIO_DATA0,offset,value);+}++staticintrtl8231_gpio_get_multiple(structgpio_chip*gc,+unsignedlong*mask,unsignedlong*bits)+{+structrtl8231_pin_ctrl*ctrl=gpiochip_get_data(gc);+unsignedlongsub_mask,bit_value;+structregmap_field**field;+unsignedintreg_value;+intoffset,shift;+intread;+interr;++field=&ctrl->fields[RTL8231_FIELD_GPIO_DATA0];++for(read=0;read<gc->ngpio;field++,read+=16){+shift=read%BITS_PER_TYPE(*bits);+offset=read/BITS_PER_TYPE(*bits);+sub_mask=mask[offset]&(0xffffUL<<shift);+if(sub_mask){+err=regmap_field_read(*field,®_value);+if(err)+returnerr;+bit_value=((unsignedlong)reg_value)<<shift;+bits[offset]=(bits[offset]&~sub_mask)|(bit_value&sub_mask);+}+}++returnerr;+}++staticvoidrtl8231_gpio_set_multiple(structgpio_chip*gc,+unsignedlong*mask,unsignedlong*bits)+{+structrtl8231_pin_ctrl*ctrl=gpiochip_get_data(gc);+unsignedlongsub_mask,value;+structregmap_field**field;+intoffset,shift;+intread;++field=&ctrl->fields[RTL8231_FIELD_GPIO_DATA0];++for(read=0;read<gc->ngpio;field++,read+=16){+shift=read%BITS_PER_TYPE(*bits);+offset=read/BITS_PER_TYPE(*bits);+sub_mask=(mask[offset]>>shift)&0xffff;+if(sub_mask){+value=bits[offset]>>shift;+regmap_field_update_bits(*field,sub_mask,value);+}+}+}++staticintrtl8231_pinctrl_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structrtl8231_pin_ctrl*ctrl;+interr;++ctrl=devm_kzalloc(dev,sizeof(*ctrl),GFP_KERNEL);+if(!ctrl)+return-ENOMEM;++ctrl->map=dev_get_regmap(dev->parent,NULL);+if(IS_ERR_OR_NULL(ctrl->map)){+dev_err(dev,"failed to retrieve regmap\n");+if(!ctrl->map)+return-ENODEV;+else+returnPTR_ERR(ctrl->map);+}++err=devm_regmap_field_bulk_alloc(dev,ctrl->map,ctrl->fields,rtl8231_gpio_fields,+ARRAY_SIZE(ctrl->fields));+if(err){+dev_err(dev,"unable to allocate gpio regmap fields\n");+returnerr;+}++err=rtl8231_pinctrl_init(dev,ctrl);+if(err)+returnerr;++ctrl->gc.base=-1;+ctrl->gc.ngpio=RTL8231_NUM_GPIOS;+ctrl->gc.label=pdev->name;+ctrl->gc.owner=THIS_MODULE;+ctrl->gc.can_sleep=true;+ctrl->gc.parent=dev->parent;++ctrl->gc.set=rtl8231_gpio_set;+ctrl->gc.set_multiple=rtl8231_gpio_set_multiple;+ctrl->gc.get=rtl8231_gpio_get;+ctrl->gc.get_multiple=rtl8231_gpio_get_multiple;+ctrl->gc.direction_input=rtl8231_direction_input;+ctrl->gc.direction_output=rtl8231_direction_output;+ctrl->gc.get_direction=rtl8231_get_direction;+ctrl->gc.request=gpiochip_generic_request;+ctrl->gc.free=gpiochip_generic_free;++returndevm_gpiochip_add_data(dev,&ctrl->gc,ctrl);+}++staticstructplatform_driverrtl8231_pinctrl_driver={+.driver={+.name="rtl8231-pinctrl",+},+.probe=rtl8231_pinctrl_probe,+};+module_platform_driver(rtl8231_pinctrl_driver);++MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");+MODULE_DESCRIPTION("Realtek RTL8231 pin control and GPIO support");+MODULE_LICENSE("GPL v2");
Add a binding description for the Realtek RTL8231's LED support, which
consists of up to 88 LEDs arranged in a number of scanning matrices.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
.../bindings/leds/realtek,rtl8231-leds.yaml | 159 ++++++++++++++++++
1 file changed, 159 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
@@ -0,0 +1,159 @@+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause+%YAML1.2+---+$id:http://devicetree.org/schemas/leds/realtek,rtl8231-leds.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Realtek RTL8231 LED scan matrix.++maintainers:+-Sander Vanheule <sander@svanheule.net>++description:|+The RTL8231 has support for driving a number of LED matrices, by scanning+over the LEDs pins, alternatingly lighting different columns and/or rows.++In single color scan mode, 88 LEDs are supported. These are grouped into+three output matrices:+-Group A of 6×6 single color LEDs. Rows and columns are driven by GPIO+pins 0-11.+L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]+| | | | | |+P0/P6 --<--------<--------<--------<--------<--------< (3)+| | | | | |+P1/P7 --<--------<--------<--------<--------<--------< (4)+| | | | | |+P2/P8 --<--------<--------<--------<--------<--------< (5)+| | | | | |+P3/P9 --<--------<--------<--------<--------<--------< (6)+| | | | | |+P4/P10 --<--------<--------<--------<--------<--------< (7)+| | | | | |+P5/P11 --<--------<--------<--------<--------<--------< (8)+(0) (1) (2) (9) (10) (11)+-Group B of 6×6 single color LEDs. Rows and columns are driven by GPIO+pins 12-23.+L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]+| | | | | |+P12/P18 --<--------<--------<--------<--------<--------< (15)+| | | | | |+P13/P19 --<--------<--------<--------<--------<--------< (16)+| | | | | |+P14/P20 --<--------<--------<--------<--------<--------< (17)+| | | | | |+P15/P21 --<--------<--------<--------<--------<--------< (18)+| | | | | |+P16/P22 --<--------<--------<--------<--------<--------< (19)+| | | | | |+P17/P23 --<--------<--------<--------<--------<--------< (20)+(12) (13) (14) (21) (22) (23)+-Group C of 8 pairs of anti-parallel (or bi-color) LEDs. LED selection is+provided by GPIO pins 24-27 and 29-32, polarity selection by GPIO 28.+P24 P25 ... P30 P31+| | | |+LED POL --X-------X---/\/---X-------X (28)+(24) (25) ... (31) (32)++In bi-color scan mode, 72 LEDs are supported. These are grouped into four+output matrices:+-Group A of 12 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 0-11, polarity selection by GPIO 12.+-Group B of 6 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 23-28, polarity selection by GPIO 21.+-Group C of 6 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 29-34, polarity selection by GPIO 22.+-Group of 4×6 single color LEDs. Rows are driven by GPIO pins 15-20,+columns by GPIO pins 13-14 and 21-22 (shared with groups B and C).+P[n] P[n+6] P[n+12] P[n+18]+| | | |++0 --<--------<--------<--------< (15)+| | | |++1 --<--------<--------<--------< (16)+| | | |++2 --<--------<--------<--------< (17)+| | | |++3 --<--------<--------<--------< (18)+| | | |++4 --<--------<--------<--------< (19)+| | | |++6 --<--------<--------<--------< (20)+(13) (14) (21) (22)++This node must always be a child of a 'realtek,rtl8231' node.++properties:+$nodename:+const:leds++compatible:+const:realtek,rtl8231-leds++"#address-cells":+const:2++"#size-cells":+const:0++realtek,led-scan-mode:+$ref:/schemas/types.yaml#/definitions/string+description:|+Specify the scanning mode the chip should run in. See general description+for how the scanning matrices are wired up.+enum:["single-color","bi-color"]++patternProperties:+"^led@[0-9]+,[0-2]$":+description:|+LEDs are addressed by their port index and led index. Ports 0-23 always+support three LEDs. Additionally, but only when used in single color scan+mode, ports 24-31 support two LEDs.+type:object++properties:+reg:+maxItems:1++allOf:+-$ref:../leds/common.yaml#++required:+-reg++required:+-compatible+-"#address-cells"+-"#size-cells"+-realtek,led-scan-mode++additionalProperties:false++examples:+-|+#include <dt-bindings/leds/common.h>+leds {+compatible = "realtek,rtl8231-leds";+#address-cells = <2>;+#size-cells = <0>;++realtek,led-scan-mode = "single-color";++led@0,0 {+reg = <0 0>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@0,1 {+reg = <0 1>;+color = <LED_COLOR_ID_AMBER>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@0,2 {+reg = <0 2>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_STATUS;+};+};
Add a binding description for the Realtek RTL8231, a GPIO and LED
expander chip commonly used in ethernet switches based on a Realtek
switch SoC. These chips can be addressed via an MDIO or SMI bus, or used
as a plain 36-bit shift register.
This binding only describes the feature set provided by the MDIO/SMI
configuration, and covers the GPIO, PWM, and pin control properties. The
LED properties are defined in a separate binding.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
.../bindings/mfd/realtek,rtl8231.yaml | 202 ++++++++++++++++++
1 file changed, 202 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
@@ -0,0 +1,202 @@+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause+%YAML1.2+---+$id:http://devicetree.org/schemas/mfd/realtek,rtl8231.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Realtek RTL8231 GPIO and LED expander.++maintainers:+-Sander Vanheule <sander@svanheule.net>++description:|+The RTL8231 is a GPIO and LED expander chip, providing up to 37 GPIOs, up to+88 LEDs, and up to one PWM output. This device is frequently used alongside+Realtek switch SoCs, to provide additional I/O capabilities.++To manage the RTL8231's features, its strapping pins can be used to configure+it in one of three modes:shift register, MDIO device, or SMI device. The+shift register mode does not need special support. In MDIO or SMI mode, most+pins can be configured as a GPIO output, LED matrix scan line/column, or as a+PWM output.++The GPIO and pin control are part of the main node. PWM and LED support are+configured as sub-nodes.++properties:+compatible:+const:realtek,rtl8231++reg:+description:MDIO or SMI device address.+maxItems:1++# GPIO support+gpio-controller:true++"#gpio-cells":+const:2+description:|+The first cell is the pin number and the second cell is used to specify+the gpio active state.++gpio-ranges:+description:|+Must reference itself, and provide a zero-based mapping for 37 pins.+maxItems:1++# Pin muxing and configuration+realtek,drive-strength:+$ref:/schemas/types.yaml#/definitions/uint32+description:|+Common drive strength used for all GPIO output pins, must be 4mA or 8mA.+On reset, this value will default to 8mA.+enum:[4,8]++# LED scanning matrix+leds:+$ref:../leds/realtek,rtl8231-leds.yaml#++# PWM output+pwm:+type:object+description:|+Subnode describing the PWM peripheral. To use the PWM output, gpio35 must+be muxed to its 'pwm' function. Valid frequency values for consumers are+1200, 1600, 2000, 2400, 2800, 3200, 4000, and 4800.++properties:+"#pwm-cells":+description:|+Twos cells with PWM index (must be 0) and PWM frequency in Hz.+const:2++required:+-"#pwm-cells"++patternProperties:+"-pins$":+type:object+$ref:../pinctrl/pinmux-node.yaml#++properties:+pins:+items:+oneOf:+-enum:["gpio0","gpio1","gpio2","gpio3","gpio4","gpio5","gpio6",+"gpio7","gpio8","gpio9","gpio10","gpio11","gpio12","gpio13",+"gpio14","gpio15","gpio16","gpio17","gpio18","gpio19","gpio20",+"gpio21","gpio22","gpio23","gpio24","gpio25","gpio26","gpio27",+"gpio28","gpio29","gpio30","gpio31","gpio32","gpio33","gpio34",+"gpio35","gpio36"]+minItems:1+maxItems:37+function:+description:|+Select which function to use. "gpio" is supported for all pins, "led" is supported+for pins 0-34, "pwm" is supported for for pin 35.+enum:["gpio","led","pwm"]++required:+-pins+-function++required:+-compatible+-reg+-gpio-controller+-"#gpio-cells"+-gpio-ranges++additionalProperties:false++examples:+-|+// Minimal example+mdio {+#address-cells = <1>;+#size-cells = <0>;++expander0:expander@0 {+compatible = "realtek,rtl8231";+reg = <0>;++gpio-controller;+#gpio-cells = <2>;+gpio-ranges = <&expander0 0 0 37>;+};+};+-|+// All bells and whistles included+#include <dt-bindings/leds/common.h>+mdio {+#address-cells = <1>;+#size-cells = <0>;++expander1:expander@1 {+compatible = "realtek,rtl8231";+reg = <1>;++gpio-controller;+#gpio-cells = <2>;+gpio-ranges = <&expander1 0 0 37>;++realtek,drive-strength = <4>;++button-pins {+pins = "gpio36";+function = "gpio";+input-debounce = "100000";+};++pwm-pins {+pins = "gpio35";+function = "pwm";+};++led-pins {+pins = "gpio0", "gpio1", "gpio3", "gpio4";+function = "led";+};++pwm {+#pwm-cells = <2>;+};++leds {+compatible = "realtek,rtl8231-leds";+#address-cells = <2>;+#size-cells = <0>;++realtek,led-scan-mode = "single-color";++led@0,0 {+reg = <0 0>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@0,1 {+reg = <0 1>;+color = <LED_COLOR_ID_AMBER>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@1,0 {+reg = <1 0>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_LAN;+function-enumerator = <1>;+};++led@1,1 {+reg = <1 1>;+color = <LED_COLOR_ID_AMBER>;+function = LED_FUNCTION_LAN;+function-enumerator = <1>;+};+};+};+};
From: kernel test robot <hidden> Date: 2021-05-12 12:29:55
Hi Sander,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on pavel-linux-leds/for-next]
[also build test ERROR on lee-mfd/for-mfd-next pinctrl/devel v5.13-rc1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Sander-Vanheule/RTL8231-GPIO-expander-support/20210511-202618
base: git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git for-next
config: microblaze-randconfig-r023-20210512 (attached as .config)
compiler: microblaze-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/e031cc2da2c2948230bacd1ca56cfe9990e1aefd
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Sander-Vanheule/RTL8231-GPIO-expander-support/20210511-202618
git checkout e031cc2da2c2948230bacd1ca56cfe9990e1aefd
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=microblaze
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
All errors (new ones prefixed by >>):
microblaze-linux-ld: drivers/mfd/rtl8231.o: in function `rtl8231_mdio_reg_write':
quoted
(.text+0x50): undefined reference to `mdiobus_write'
microblaze-linux-ld: drivers/mfd/rtl8231.o: in function `rtl8231_mdio_reg_read':
quoted
(.text+0x80): undefined reference to `mdiobus_read'
microblaze-linux-ld: drivers/mfd/rtl8231.o: in function `mdio_module_init':
quoted
(.init.text+0x10): undefined reference to `mdio_driver_register'
microblaze-linux-ld: drivers/mfd/rtl8231.o: in function `mdio_module_exit':
quoted
(.exit.text+0x10): undefined reference to `mdio_driver_unregister'
From: kernel test robot <hidden> Date: 2021-05-12 13:13:57
Hi Sander,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on pavel-linux-leds/for-next]
[also build test ERROR on lee-mfd/for-mfd-next pinctrl/devel v5.13-rc1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Sander-Vanheule/RTL8231-GPIO-expander-support/20210511-202618
base: git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git for-next
config: h8300-randconfig-r012-20210512 (attached as .config)
compiler: h8300-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/e031cc2da2c2948230bacd1ca56cfe9990e1aefd
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Sander-Vanheule/RTL8231-GPIO-expander-support/20210511-202618
git checkout e031cc2da2c2948230bacd1ca56cfe9990e1aefd
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=h8300
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
All errors (new ones prefixed by >>):
h8300-linux-ld: drivers/mfd/rtl8231.o: in function `rtl8231_mdio_reg_write':
quoted
rtl8231.c:(.text+0x4f): undefined reference to `mdiobus_write'
h8300-linux-ld: drivers/mfd/rtl8231.o: in function `rtl8231_mdio_reg_read':
quoted
rtl8231.c:(.text+0x75): undefined reference to `mdiobus_read'
h8300-linux-ld: drivers/mfd/rtl8231.o: in function `mdio_module_init':
quoted
rtl8231.c:(.init.text+0xd): undefined reference to `mdio_driver_register'
The RTL8231 GPIO and LED expander can be configured for use as an MDIO or SMI
bus device. Currently only the MDIO mode is supported, although SMI mode
support should be fairly straightforward, once an SMI bus driver is available.
Provided features by the RTL8231:
- Up to 37 GPIOs
- Configurable drive strength: 8mA or 4mA (currently unsupported)
- Input debouncing on high GPIOs (currently unsupported)
- Up to 88 LEDs in multiple scan matrix groups
- On, off, or one of six toggling intervals
- "single-color mode": 2×36 single color LEDs + 8 bi-color LEDs
- "bi-color mode": (12 + 2×6) bi-color LEDs + 24 single color LEDs
- Up to one PWM output (currently unsupported)
- Fixed duty cycle, 8 selectable frequencies (1.2kHz - 4.8kHz)
Register access is provided through a new MDIO regmap provider. The GPIO
controller uses gpio-regmap, although a patch is required to support a
limitation of the chip.
There remain some log warnings when probing the device, possibly due to the way
I'm using the MFD subsystem. Would it be possible to avoid these?
[ 2.602242] rtl8231-pinctrl: Failed to locate of_node [id: -2]
[ 2.609380] rtl8231-pinctrl rtl8231-pinctrl.0.auto: no of_node; not parsing pinctrl DT
When no 'leds' sub-node is specified:
[ 2.922262] rtl8231-leds: Failed to locate of_node [id: -2]
[ 2.967149] rtl8231-leds rtl8231-leds.1.auto: no of_node; not parsing pinctrl DT
[ 2.975673] rtl8231-leds rtl8231-leds.1.auto: scan mode missing or invalid
[ 2.983531] rtl8231-leds: probe of rtl8231-leds.1.auto failed with error -22
Changes since v1:
- Reintroduce MDIO regmap, with fixed Kconfig dependencies
- Add configurable dir/value order for gpio-regmap direction_out call
- Drop allocations for regmap fields that are used only on init
- Move some definitions to MFD header
- Add PM ops to replace driver remove for MFD
- Change pinctrl driver to (modified) gpio-regmap
- Change leds driver to use fwnode
Link: https://lore.kernel.org/lkml/cover.1620735871.git.sander@svanheule.net/
Changes since RFC:
- Dropped MDIO regmap interface. I was unable to resolve the Kconfig
dependency issue, so have reverted to using regmap_config.reg_read/write.
- Added pinctrl support
- Added LED support
- Changed root device to MFD, with pinctrl and leds child devices. Root
device is now an mdio_device driver.
Link: https://lore.kernel.org/linux-gpio/cover.1617914861.git.sander@svanheule.net/
Sander Vanheule (7):
regmap: Add MDIO bus support
gpio: regmap: Add configurable dir/value order
dt-bindings: leds: Binding for RTL8231 scan matrix
dt-bindings: mfd: Binding for RTL8231
mfd: Add RTL8231 core device
pinctrl: Add RTL8231 pin control and GPIO support
leds: Add support for RTL8231 LED scan matrix
.../bindings/leds/realtek,rtl8231-leds.yaml | 159 ++++++++
.../bindings/mfd/realtek,rtl8231.yaml | 202 ++++++++++
drivers/base/regmap/Kconfig | 6 +-
drivers/base/regmap/Makefile | 1 +
drivers/base/regmap/regmap-mdio.c | 57 +++
drivers/gpio/gpio-regmap.c | 20 +-
drivers/leds/Kconfig | 10 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-rtl8231.c | 293 ++++++++++++++
drivers/mfd/Kconfig | 9 +
drivers/mfd/Makefile | 1 +
drivers/mfd/rtl8231.c | 153 +++++++
drivers/pinctrl/Kconfig | 11 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rtl8231.c | 377 ++++++++++++++++++
include/linux/gpio/regmap.h | 3 +
include/linux/mfd/rtl8231.h | 57 +++
include/linux/regmap.h | 36 ++
18 files changed, 1393 insertions(+), 4 deletions(-)
create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
create mode 100644 drivers/base/regmap/regmap-mdio.c
create mode 100644 drivers/leds/leds-rtl8231.c
create mode 100644 drivers/mfd/rtl8231.c
create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c
create mode 100644 include/linux/mfd/rtl8231.h
--
2.31.1
@@ -4,8 +4,9 @@# subsystems should select the appropriate symbols.configREGMAP-defaultyif(REGMAP_I2C||REGMAP_SPI||REGMAP_SPMI||REGMAP_W1||REGMAP_AC97||REGMAP_MMIO||REGMAP_IRQ||REGMAP_SOUNDWIRE||REGMAP_SOUNDWIRE_MBQ||REGMAP_SCCB||REGMAP_I3C||REGMAP_SPI_AVMM)+defaultyif(REGMAP_I2C||REGMAP_SPI||REGMAP_SPMI||REGMAP_W1||REGMAP_AC97||REGMAP_MMIO||REGMAP_IRQ||REGMAP_SOUNDWIRE||REGMAP_SOUNDWIRE_MBQ||REGMAP_SCCB||REGMAP_I3C||REGMAP_SPI_AVMM||REGMAP_MDIO)selectIRQ_DOMAINifREGMAP_IRQ+selectMDIO_BUSifREGMAP_MDIOboolconfigREGCACHE_COMPRESSED
GPIO chips may not support setting the output value when a pin is
configured as an input, although the current implementation assumes this
is always possible.
Add support for setting pin direction before value. The order defaults
to setting the value first, but this can be reversed by setting the
regmap_config.no_set_on_input flag, similar to the corresponding flag in
the gpio-mmio driver.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/gpio/gpio-regmap.c | 20 +++++++++++++++++---
include/linux/gpio/regmap.h | 3 +++
2 files changed, 20 insertions(+), 3 deletions(-)
Add a binding description for the Realtek RTL8231's LED support, which
consists of up to 88 LEDs arranged in a number of scanning matrices.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
.../bindings/leds/realtek,rtl8231-leds.yaml | 159 ++++++++++++++++++
1 file changed, 159 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
@@ -0,0 +1,159 @@+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause+%YAML1.2+---+$id:http://devicetree.org/schemas/leds/realtek,rtl8231-leds.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Realtek RTL8231 LED scan matrix.++maintainers:+-Sander Vanheule <sander@svanheule.net>++description:|+The RTL8231 has support for driving a number of LED matrices, by scanning+over the LEDs pins, alternatingly lighting different columns and/or rows.++In single color scan mode, 88 LEDs are supported. These are grouped into+three output matrices:+-Group A of 6×6 single color LEDs. Rows and columns are driven by GPIO+pins 0-11.+L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]+| | | | | |+P0/P6 --<--------<--------<--------<--------<--------< (3)+| | | | | |+P1/P7 --<--------<--------<--------<--------<--------< (4)+| | | | | |+P2/P8 --<--------<--------<--------<--------<--------< (5)+| | | | | |+P3/P9 --<--------<--------<--------<--------<--------< (6)+| | | | | |+P4/P10 --<--------<--------<--------<--------<--------< (7)+| | | | | |+P5/P11 --<--------<--------<--------<--------<--------< (8)+(0) (1) (2) (9) (10) (11)+-Group B of 6×6 single color LEDs. Rows and columns are driven by GPIO+pins 12-23.+L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]+| | | | | |+P12/P18 --<--------<--------<--------<--------<--------< (15)+| | | | | |+P13/P19 --<--------<--------<--------<--------<--------< (16)+| | | | | |+P14/P20 --<--------<--------<--------<--------<--------< (17)+| | | | | |+P15/P21 --<--------<--------<--------<--------<--------< (18)+| | | | | |+P16/P22 --<--------<--------<--------<--------<--------< (19)+| | | | | |+P17/P23 --<--------<--------<--------<--------<--------< (20)+(12) (13) (14) (21) (22) (23)+-Group C of 8 pairs of anti-parallel (or bi-color) LEDs. LED selection is+provided by GPIO pins 24-27 and 29-32, polarity selection by GPIO 28.+P24 P25 ... P30 P31+| | | |+LED POL --X-------X---/\/---X-------X (28)+(24) (25) ... (31) (32)++In bi-color scan mode, 72 LEDs are supported. These are grouped into four+output matrices:+-Group A of 12 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 0-11, polarity selection by GPIO 12.+-Group B of 6 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 23-28, polarity selection by GPIO 21.+-Group C of 6 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 29-34, polarity selection by GPIO 22.+-Group of 4×6 single color LEDs. Rows are driven by GPIO pins 15-20,+columns by GPIO pins 13-14 and 21-22 (shared with groups B and C).+P[n] P[n+6] P[n+12] P[n+18]+| | | |++0 --<--------<--------<--------< (15)+| | | |++1 --<--------<--------<--------< (16)+| | | |++2 --<--------<--------<--------< (17)+| | | |++3 --<--------<--------<--------< (18)+| | | |++4 --<--------<--------<--------< (19)+| | | |++6 --<--------<--------<--------< (20)+(13) (14) (21) (22)++This node must always be a child of a 'realtek,rtl8231' node.++properties:+$nodename:+const:leds++compatible:+const:realtek,rtl8231-leds++"#address-cells":+const:2++"#size-cells":+const:0++realtek,led-scan-mode:+$ref:/schemas/types.yaml#/definitions/string+description:|+Specify the scanning mode the chip should run in. See general description+for how the scanning matrices are wired up.+enum:["single-color","bi-color"]++patternProperties:+"^led@[0-9]+,[0-2]$":+description:|+LEDs are addressed by their port index and led index. Ports 0-23 always+support three LEDs. Additionally, but only when used in single color scan+mode, ports 24-31 support two LEDs.+type:object++properties:+reg:+maxItems:1++allOf:+-$ref:../leds/common.yaml#++required:+-reg++required:+-compatible+-"#address-cells"+-"#size-cells"+-realtek,led-scan-mode++additionalProperties:false++examples:+-|+#include <dt-bindings/leds/common.h>+leds {+compatible = "realtek,rtl8231-leds";+#address-cells = <2>;+#size-cells = <0>;++realtek,led-scan-mode = "single-color";++led@0,0 {+reg = <0 0>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@0,1 {+reg = <0 1>;+color = <LED_COLOR_ID_AMBER>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@0,2 {+reg = <0 2>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_STATUS;+};+};
Add a binding description for the Realtek RTL8231, a GPIO and LED
expander chip commonly used in ethernet switches based on a Realtek
switch SoC. These chips can be addressed via an MDIO or SMI bus, or used
as a plain 36-bit shift register.
This binding only describes the feature set provided by the MDIO/SMI
configuration, and covers the GPIO, PWM, and pin control properties. The
LED properties are defined in a separate binding.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
.../bindings/mfd/realtek,rtl8231.yaml | 202 ++++++++++++++++++
1 file changed, 202 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
@@ -0,0 +1,202 @@+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause+%YAML1.2+---+$id:http://devicetree.org/schemas/mfd/realtek,rtl8231.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Realtek RTL8231 GPIO and LED expander.++maintainers:+-Sander Vanheule <sander@svanheule.net>++description:|+The RTL8231 is a GPIO and LED expander chip, providing up to 37 GPIOs, up to+88 LEDs, and up to one PWM output. This device is frequently used alongside+Realtek switch SoCs, to provide additional I/O capabilities.++To manage the RTL8231's features, its strapping pins can be used to configure+it in one of three modes:shift register, MDIO device, or SMI device. The+shift register mode does not need special support. In MDIO or SMI mode, most+pins can be configured as a GPIO output, LED matrix scan line/column, or as a+PWM output.++The GPIO and pin control are part of the main node. PWM and LED support are+configured as sub-nodes.++properties:+compatible:+const:realtek,rtl8231++reg:+description:MDIO or SMI device address.+maxItems:1++# GPIO support+gpio-controller:true++"#gpio-cells":+const:2+description:|+The first cell is the pin number and the second cell is used to specify+the gpio active state.++gpio-ranges:+description:|+Must reference itself, and provide a zero-based mapping for 37 pins.+maxItems:1++# Pin muxing and configuration+realtek,drive-strength:+$ref:/schemas/types.yaml#/definitions/uint32+description:|+Common drive strength used for all GPIO output pins, must be 4mA or 8mA.+On reset, this value will default to 8mA.+enum:[4,8]++# LED scanning matrix+leds:+$ref:../leds/realtek,rtl8231-leds.yaml#++# PWM output+pwm:+type:object+description:|+Subnode describing the PWM peripheral. To use the PWM output, gpio35 must+be muxed to its 'pwm' function. Valid frequency values for consumers are+1200, 1600, 2000, 2400, 2800, 3200, 4000, and 4800.++properties:+"#pwm-cells":+description:|+Twos cells with PWM index (must be 0) and PWM frequency in Hz.+const:2++required:+-"#pwm-cells"++patternProperties:+"-pins$":+type:object+$ref:../pinctrl/pinmux-node.yaml#++properties:+pins:+items:+oneOf:+-enum:["gpio0","gpio1","gpio2","gpio3","gpio4","gpio5","gpio6",+"gpio7","gpio8","gpio9","gpio10","gpio11","gpio12","gpio13",+"gpio14","gpio15","gpio16","gpio17","gpio18","gpio19","gpio20",+"gpio21","gpio22","gpio23","gpio24","gpio25","gpio26","gpio27",+"gpio28","gpio29","gpio30","gpio31","gpio32","gpio33","gpio34",+"gpio35","gpio36"]+minItems:1+maxItems:37+function:+description:|+Select which function to use. "gpio" is supported for all pins, "led" is supported+for pins 0-34, "pwm" is supported for pin 35.+enum:["gpio","led","pwm"]++required:+-pins+-function++required:+-compatible+-reg+-gpio-controller+-"#gpio-cells"+-gpio-ranges++additionalProperties:false++examples:+-|+// Minimal example+mdio {+#address-cells = <1>;+#size-cells = <0>;++expander0:expander@0 {+compatible = "realtek,rtl8231";+reg = <0>;++gpio-controller;+#gpio-cells = <2>;+gpio-ranges = <&expander0 0 0 37>;+};+};+-|+// All bells and whistles included+#include <dt-bindings/leds/common.h>+mdio {+#address-cells = <1>;+#size-cells = <0>;++expander1:expander@1 {+compatible = "realtek,rtl8231";+reg = <1>;++gpio-controller;+#gpio-cells = <2>;+gpio-ranges = <&expander1 0 0 37>;++realtek,drive-strength = <4>;++button-pins {+pins = "gpio36";+function = "gpio";+input-debounce = "100000";+};++pwm-pins {+pins = "gpio35";+function = "pwm";+};++led-pins {+pins = "gpio0", "gpio1", "gpio3", "gpio4";+function = "led";+};++pwm {+#pwm-cells = <2>;+};++leds {+compatible = "realtek,rtl8231-leds";+#address-cells = <2>;+#size-cells = <0>;++realtek,led-scan-mode = "single-color";++led@0,0 {+reg = <0 0>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@0,1 {+reg = <0 1>;+color = <LED_COLOR_ID_AMBER>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@1,0 {+reg = <1 0>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_LAN;+function-enumerator = <1>;+};++led@1,1 {+reg = <1 1>;+color = <LED_COLOR_ID_AMBER>;+function = LED_FUNCTION_LAN;+function-enumerator = <1>;+};+};+};+};
The RTL8231 is implemented as an MDIO device, and provides a regmap
interface for register access by the core and child devices.
The chip can also be a device on an SMI bus, an I2C-like bus by Realtek.
Since kernel support for SMI is limited, and no real-world SMI
implementations have been encountered for this device, this is currently
unimplemented. The use of the regmap interface should make any future
support relatively straightforward.
After reset, all pins are muxed to GPIO inputs before the pin drivers
are enabled. This is done to prevent accidental system resets, when a
pin is connected to the parent SoC's reset line.
[missing MDIO_BUS dependency, provided via REGMAP_MDIO]
Reported-by: kernel test robot <redacted>
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/mfd/Kconfig | 9 +++
drivers/mfd/Makefile | 1 +
drivers/mfd/rtl8231.c | 153 ++++++++++++++++++++++++++++++++++++
include/linux/mfd/rtl8231.h | 57 ++++++++++++++
4 files changed, 220 insertions(+)
create mode 100644 drivers/mfd/rtl8231.c
create mode 100644 include/linux/mfd/rtl8231.h
@@ -0,0 +1,153 @@+// SPDX-License-Identifier: GPL-2.0-only++#include<linux/bits.h>+#include<linux/bitfield.h>+#include<linux/delay.h>+#include<linux/gpio/consumer.h>+#include<linux/mfd/core.h>+#include<linux/mdio.h>+#include<linux/module.h>+#include<linux/platform_device.h>+#include<linux/property.h>+#include<linux/regmap.h>++#include<linux/mfd/rtl8231.h>++staticconststructreg_fieldRTL8231_FIELD_LED_START=REG_FIELD(RTL8231_REG_FUNC0,1,1);++staticconststructmfd_cellrtl8231_cells[]={+{+.name="rtl8231-pinctrl",+.of_compatible="realtek,rtl8231-pinctrl",+},+{+.name="rtl8231-leds",+.of_compatible="realtek,rtl8231-leds",+},+};++staticintrtl8231_init(structdevice*dev,structregmap*map)+{+unsignedintready_code;+unsignedintv;+interr=0;++err=regmap_read(map,RTL8231_REG_FUNC1,&v);+ready_code=FIELD_GET(RTL8231_FUNC1_READY_CODE_MASK,v);++if(err){+dev_err(dev,"failed to read READY_CODE\n");+returnerr;+}elseif(ready_code!=RTL8231_FUNC1_READY_CODE_VALUE){+dev_err(dev,"RTL8231 not present or ready 0x%x != 0x%x\n",+ready_code,RTL8231_FUNC1_READY_CODE_VALUE);+return-ENODEV;+}++/* SOFT_RESET bit self-clears when done */+regmap_update_bits(map,RTL8231_REG_PIN_HI_CFG,+RTL8231_PIN_HI_CFG_SOFT_RESET,RTL8231_PIN_HI_CFG_SOFT_RESET);+usleep_range(1000,10000);++/*+*ChipresetresultsinapinconfigurationthatisamixofLEDandGPIOoutputs.+*SelectGPIfunctionalityforallpinsbeforeenablingpinoutputs.+*/+regmap_write(map,RTL8231_REG_PIN_MODE0,0xffff);+regmap_write(map,RTL8231_REG_GPIO_DIR0,0xffff);+regmap_write(map,RTL8231_REG_PIN_MODE1,0xffff);+regmap_write(map,RTL8231_REG_GPIO_DIR1,0xffff);+regmap_write(map,RTL8231_REG_PIN_HI_CFG,+RTL8231_PIN_HI_CFG_MODE_MASK|RTL8231_PIN_HI_CFG_DIR_MASK);++returnerr;+}++staticconststructregmap_configrtl8231_mdio_regmap_config={+.val_bits=RTL8231_BITS_VAL,+.reg_bits=5,+.max_register=RTL8231_REG_COUNT-1,+.use_single_read=true,+.use_single_write=true,+.reg_format_endian=REGMAP_ENDIAN_BIG,+.val_format_endian=REGMAP_ENDIAN_BIG,+};++staticintrtl8231_mdio_probe(structmdio_device*mdiodev)+{+structdevice*dev=&mdiodev->dev;+structregmap_field*led_start;+structregmap*map;+interr;++map=devm_regmap_init_mdio(mdiodev,&rtl8231_mdio_regmap_config);++if(IS_ERR(map)){+dev_err(dev,"failed to init regmap\n");+returnPTR_ERR(map);+}++led_start=devm_regmap_field_alloc(dev,map,RTL8231_FIELD_LED_START);+if(IS_ERR(led_start))+returnPTR_ERR(led_start);++dev_set_drvdata(dev,led_start);++mdiodev->reset_gpio=gpiod_get_optional(dev,"reset",GPIOD_OUT_LOW);+device_property_read_u32(dev,"reset-assert-delay",&mdiodev->reset_assert_delay);+device_property_read_u32(dev,"reset-deassert-delay",&mdiodev->reset_deassert_delay);++err=rtl8231_init(dev,map);+if(err)+returnerr;++/* LED_START enables power to output pins, and starts the LED engine */+regmap_field_write(led_start,1);++returndevm_mfd_add_devices(dev,PLATFORM_DEVID_AUTO,rtl8231_cells,+ARRAY_SIZE(rtl8231_cells),NULL,0,NULL);+}++#ifdef CONFIG_PM+staticintrtl8231_suspend(structdevice*dev)+{+structregmap_field*led_start=dev_get_drvdata(dev);++returnregmap_field_write(led_start,0);+}++staticintrtl8231_resume(structdevice*dev)+{+structregmap_field*led_start=dev_get_drvdata(dev);++returnregmap_field_write(led_start,1);+}++staticconststructdev_pm_opsrtl8231_pm_ops={+.suspend=rtl8231_suspend,+.resume=rtl8231_resume,+};+#define RTL8231_PM_OPS (&rtl8231_pm_ops)+#else+#define RTL8231_PM_OPS NULL+#endif /* CONFIG_PM */++staticconststructof_device_idrtl8231_of_match[]={+{.compatible="realtek,rtl8231"},+{},+};+MODULE_DEVICE_TABLE(of,rtl8231_of_match);++staticstructmdio_driverrtl8231_mdio_driver={+.mdiodrv.driver={+.name="rtl8231-expander",+.of_match_table=rtl8231_of_match,+.pm=RTL8231_PM_OPS,+},+.probe=rtl8231_mdio_probe,+};+mdio_module_driver(rtl8231_mdio_driver);++MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");+MODULE_DESCRIPTION("Realtek RTL8231 GPIO and LED expander");+MODULE_LICENSE("GPL v2");
This driver implements the GPIO and pin muxing features provided by the
RTL8231. The device should be instantiated as an MFD child, where the
parent device has already configured the regmap used for register
access.
Although described in the bindings, pin debouncing and drive strength
selection are currently not implemented. Debouncing is only available
for the six highest GPIOs, and must be emulated when other pins are used
for (button) inputs anyway.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/pinctrl/Kconfig | 11 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rtl8231.c | 377 ++++++++++++++++++++++++++++++
3 files changed, 389 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c
@@ -0,0 +1,377 @@+// SPDX-License-Identifier: GPL-2.0-only++#include<linux/bitfield.h>+#include<linux/gpio/driver.h>+#include<linux/gpio/regmap.h>+#include<linux/module.h>+#include<linux/pinctrl/pinconf-generic.h>+#include<linux/pinctrl/pinctrl.h>+#include<linux/pinctrl/pinmux.h>+#include<linux/platform_device.h>+#include<linux/regmap.h>++#include<linux/mfd/rtl8231.h>++#define RTL8231_NUM_GPIOS 37++structrtl8231_function{+constchar*name;+unsignedintngroups;+constchar**groups;+};++structrtl8231_pin_ctrl{+structpinctrl_descpctl_desc;+unsignedintnfunctions;+structrtl8231_function*functions;+structregmap*map;+};++/*+*Pincontrollerfunctionality+*/+staticconstchar*constrtl8231_pin_function_names[]={+"gpio",+"led",+"pwm",+};++enumrtl8231_pin_function{+RTL8231_PIN_FUNCTION_GPIO=BIT(0),+RTL8231_PIN_FUNCTION_LED=BIT(1),+RTL8231_PIN_FUNCTION_PWM=BIT(2),+};++structrtl8231_pin_desc{+unsignedintnumber;+constchar*name;+enumrtl8231_pin_functionfunctions;+u8reg;+u8offset;+u8gpio_function_value;+};++#define RTL8231_PIN(_num, _func, _reg, _fld, _val) \+{\+.number=_num,\+.name="gpio"#_num,\+.functions=RTL8231_PIN_FUNCTION_GPIO|_func,\+.reg=_reg,\+.offset=_fld,\+.gpio_function_value=_val,\+}+#define RTL8231_GPIO_PIN(_num) \+RTL8231_PIN(_num,0,0,0,0)+#define RTL8231_LED_PIN(_num, _reg, _fld) \+RTL8231_PIN(_num,RTL8231_PIN_FUNCTION_LED,_reg,_fld,RTL8231_PIN_MODE_GPIO)+#define RTL8231_PWM_PIN(_num, _reg, _fld) \+RTL8231_PIN(_num,RTL8231_PIN_FUNCTION_PWM,_reg,_fld,0)++/* Pins always support GPIO, and may support one alternate function */+staticconststructrtl8231_pin_descrtl8231_pins[RTL8231_NUM_GPIOS]={+RTL8231_LED_PIN(0,RTL8231_REG_PIN_MODE0,0),+RTL8231_LED_PIN(1,RTL8231_REG_PIN_MODE0,1),+RTL8231_LED_PIN(2,RTL8231_REG_PIN_MODE0,2),+RTL8231_LED_PIN(3,RTL8231_REG_PIN_MODE0,3),+RTL8231_LED_PIN(4,RTL8231_REG_PIN_MODE0,4),+RTL8231_LED_PIN(5,RTL8231_REG_PIN_MODE0,5),+RTL8231_LED_PIN(6,RTL8231_REG_PIN_MODE0,6),+RTL8231_LED_PIN(7,RTL8231_REG_PIN_MODE0,7),+RTL8231_LED_PIN(8,RTL8231_REG_PIN_MODE0,8),+RTL8231_LED_PIN(9,RTL8231_REG_PIN_MODE0,9),+RTL8231_LED_PIN(10,RTL8231_REG_PIN_MODE0,10),+RTL8231_LED_PIN(11,RTL8231_REG_PIN_MODE0,11),+RTL8231_LED_PIN(12,RTL8231_REG_PIN_MODE0,12),+RTL8231_LED_PIN(13,RTL8231_REG_PIN_MODE0,13),+RTL8231_LED_PIN(14,RTL8231_REG_PIN_MODE0,14),+RTL8231_LED_PIN(15,RTL8231_REG_PIN_MODE0,15),+RTL8231_LED_PIN(16,RTL8231_REG_PIN_MODE1,0),+RTL8231_LED_PIN(17,RTL8231_REG_PIN_MODE1,1),+RTL8231_LED_PIN(18,RTL8231_REG_PIN_MODE1,2),+RTL8231_LED_PIN(19,RTL8231_REG_PIN_MODE1,3),+RTL8231_LED_PIN(20,RTL8231_REG_PIN_MODE1,4),+RTL8231_LED_PIN(21,RTL8231_REG_PIN_MODE1,5),+RTL8231_LED_PIN(22,RTL8231_REG_PIN_MODE1,6),+RTL8231_LED_PIN(23,RTL8231_REG_PIN_MODE1,7),+RTL8231_LED_PIN(24,RTL8231_REG_PIN_MODE1,8),+RTL8231_LED_PIN(25,RTL8231_REG_PIN_MODE1,9),+RTL8231_LED_PIN(26,RTL8231_REG_PIN_MODE1,10),+RTL8231_LED_PIN(27,RTL8231_REG_PIN_MODE1,11),+RTL8231_LED_PIN(28,RTL8231_REG_PIN_MODE1,12),+RTL8231_LED_PIN(29,RTL8231_REG_PIN_MODE1,13),+RTL8231_LED_PIN(30,RTL8231_REG_PIN_MODE1,14),+RTL8231_LED_PIN(31,RTL8231_REG_PIN_MODE1,15),+RTL8231_LED_PIN(32,RTL8231_REG_PIN_HI_CFG,0),+RTL8231_LED_PIN(33,RTL8231_REG_PIN_HI_CFG,1),+RTL8231_LED_PIN(34,RTL8231_REG_PIN_HI_CFG,2),+RTL8231_PWM_PIN(35,RTL8231_REG_FUNC1,3),+RTL8231_GPIO_PIN(36),+};++staticintrtl8231_get_groups_count(structpinctrl_dev*pctldev)+{+returnARRAY_SIZE(rtl8231_pins);+}++staticconstchar*rtl8231_get_group_name(structpinctrl_dev*pctldev,unsignedintselector)+{+returnrtl8231_pins[selector].name;+}++staticintrtl8231_get_group_pins(structpinctrl_dev*pctldev,unsignedintselector,+constunsignedint**pins,unsignedint*num_pins)+{+if(selector<ARRAY_SIZE(rtl8231_pins)){+*pins=&rtl8231_pins[selector].number;+*num_pins=1;+return0;+}++return-EINVAL;+}++staticconststructpinctrl_opsrtl8231_pinctrl_ops={+.get_groups_count=rtl8231_get_groups_count,+.get_group_name=rtl8231_get_group_name,+.get_group_pins=rtl8231_get_group_pins,+.dt_node_to_map=pinconf_generic_dt_node_to_map_all,+.dt_free_map=pinconf_generic_dt_free_map,+};++staticintrtl8231_get_functions_count(structpinctrl_dev*pctldev)+{+structrtl8231_pin_ctrl*ctrl=pinctrl_dev_get_drvdata(pctldev);++returnctrl->nfunctions;+}++staticconstchar*rtl8231_get_function_name(structpinctrl_dev*pctldev,unsignedintselector)+{+structrtl8231_pin_ctrl*ctrl=pinctrl_dev_get_drvdata(pctldev);++returnctrl->functions[selector].name;+}++staticintrtl8231_get_function_groups(structpinctrl_dev*pctldev,unsignedintselector,+constchar*const**groups,unsignedint*num_groups)+{+structrtl8231_pin_ctrl*ctrl=pinctrl_dev_get_drvdata(pctldev);++*groups=ctrl->functions[selector].groups;+*num_groups=ctrl->functions[selector].ngroups;+return0;+}++staticintrtl8231_set_mux(structpinctrl_dev*pctldev,unsignedintfunc_selector,+unsignedintgroup_selector)+{+structrtl8231_pin_ctrl*ctrl=pinctrl_dev_get_drvdata(pctldev);+conststructrtl8231_pin_desc*desc=&rtl8231_pins[group_selector];+unsignedintfunc_flag=BIT(func_selector);+unsignedintfunction_mask;+unsignedintgpio_function;+interr=0;++if(!(desc->functions&func_flag))+return-EINVAL;++function_mask=BIT(desc->offset);+gpio_function=desc->gpio_function_value<<desc->offset;++switch(func_flag){+caseRTL8231_PIN_FUNCTION_LED:+caseRTL8231_PIN_FUNCTION_PWM:+err=regmap_update_bits(ctrl->map,desc->reg,function_mask,~gpio_function);+break;+caseRTL8231_PIN_FUNCTION_GPIO:+err=regmap_update_bits(ctrl->map,desc->reg,function_mask,gpio_function);+break;+default:+return-EINVAL;+}++returnerr;+}++staticintrtl8231_gpio_request_enable(structpinctrl_dev*pctldev,+structpinctrl_gpio_range*range,unsignedintoffset)+{+structrtl8231_pin_ctrl*ctrl=pinctrl_dev_get_drvdata(pctldev);+conststructrtl8231_pin_desc*desc=&rtl8231_pins[offset];+unsignedintfunction_mask;+unsignedintgpio_function;++function_mask=BIT(desc->offset);+gpio_function=desc->gpio_function_value<<desc->offset;++returnregmap_update_bits(ctrl->map,desc->reg,function_mask,gpio_function);+}++staticconststructpinmux_opsrtl8231_pinmux_ops={+.set_mux=rtl8231_set_mux,+.get_functions_count=rtl8231_get_functions_count,+.get_function_name=rtl8231_get_function_name,+.get_function_groups=rtl8231_get_function_groups,+.gpio_request_enable=rtl8231_gpio_request_enable,+.strict=true+};+++staticintrtl8231_pinctrl_init_functions(structdevice*dev,structrtl8231_pin_ctrl*ctrl)+{+structrtl8231_function*function;+constchar**group_name;+unsignedintf_idx;+unsignedintpin;++ctrl->nfunctions=ARRAY_SIZE(rtl8231_pin_function_names);+ctrl->functions=devm_kcalloc(dev,ctrl->nfunctions,sizeof(*ctrl->functions),GFP_KERNEL);+if(IS_ERR(ctrl->functions)){+dev_err(dev,"failed to allocate pin function descriptors\n");+returnPTR_ERR(ctrl->functions);+}++for(f_idx=0;f_idx<ctrl->nfunctions;f_idx++){+function=&ctrl->functions[f_idx];+function->name=rtl8231_pin_function_names[f_idx];++for(pin=0;pin<ctrl->pctl_desc.npins;pin++)+if(rtl8231_pins[pin].functions&BIT(f_idx))+function->ngroups++;++function->groups=devm_kcalloc(dev,function->ngroups,+sizeof(*function->groups),GFP_KERNEL);+if(IS_ERR(function->groups)){+dev_err(dev,"failed to allocate pin function group names\n");+returnPTR_ERR(function->groups);+}++group_name=function->groups;+for(pin=0;pin<ctrl->pctl_desc.npins;pin++)+if(rtl8231_pins[pin].functions&BIT(f_idx))+*group_name++=rtl8231_pins[pin].name;+}++return0;+}++staticintrtl8231_pinctrl_init(structdevice*dev,structrtl8231_pin_ctrl*ctrl)+{+structpinctrl_dev*pctl;+structpinctrl_pin_desc*pins;+unsignedintpin;+interr=0;++ctrl->pctl_desc.name="rtl8231-pinctrl",+ctrl->pctl_desc.owner=THIS_MODULE,+ctrl->pctl_desc.pctlops=&rtl8231_pinctrl_ops,+ctrl->pctl_desc.pmxops=&rtl8231_pinmux_ops,++ctrl->pctl_desc.npins=ARRAY_SIZE(rtl8231_pins);+pins=devm_kcalloc(dev,ctrl->pctl_desc.npins,sizeof(*pins),GFP_KERNEL);+if(IS_ERR(pins)){+dev_err(dev,"failed to allocate pin descriptors\n");+returnPTR_ERR(pins);+}+ctrl->pctl_desc.pins=pins;++for(pin=0;pin<ctrl->pctl_desc.npins;pin++){+pins[pin].number=rtl8231_pins[pin].number;+pins[pin].name=rtl8231_pins[pin].name;+}++err=rtl8231_pinctrl_init_functions(dev,ctrl);+if(err)+returnerr;++err=devm_pinctrl_register_and_init(dev->parent,&ctrl->pctl_desc,ctrl,&pctl);+if(err){+dev_err(dev,"failed to register pin controller\n");+returnerr;+}++err=pinctrl_enable(pctl);+if(err)+dev_err(dev,"failed to enable pin controller\n");++returnerr;+}++/*+*GPIOcontrollerfunctionality+*/+staticintrtl8231_gpio_reg_mask_xlate(structgpio_regmap*gpio,unsignedintbase,+unsignedintoffset,unsignedint*reg,unsignedint*mask)+{+unsignedintpin_mask=BIT(offset%RTL8231_BITS_VAL);++if(base==RTL8231_REG_GPIO_DATA0||offset<32){+*reg=base+offset/RTL8231_BITS_VAL;+*mask=pin_mask;+}elseif(base==RTL8231_REG_GPIO_DIR0){+*reg=RTL8231_REG_PIN_HI_CFG;+*mask=FIELD_PREP(RTL8231_PIN_HI_CFG_DIR_MASK,pin_mask);+}else{+return-EINVAL;+}++return0;+}++staticintrtl8231_pinctrl_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structrtl8231_pin_ctrl*ctrl;+structgpio_regmap_configgpio_cfg={};+structgpio_regmap*gr;+interr;++ctrl=devm_kzalloc(dev,sizeof(*ctrl),GFP_KERNEL);+if(!ctrl)+return-ENOMEM;++ctrl->map=dev_get_regmap(dev->parent,NULL);+if(IS_ERR_OR_NULL(ctrl->map)){+dev_err(dev,"failed to retrieve regmap\n");+if(!ctrl->map)+return-ENODEV;+else+returnPTR_ERR(ctrl->map);+}++err=rtl8231_pinctrl_init(dev,ctrl);+if(err)+returnerr;++gpio_cfg.regmap=ctrl->map;+gpio_cfg.parent=dev->parent;+gpio_cfg.ngpio=RTL8231_NUM_GPIOS;+gpio_cfg.ngpio_per_reg=RTL8231_BITS_VAL;++gpio_cfg.reg_dat_base=GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0);+gpio_cfg.reg_set_base=GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0);+gpio_cfg.reg_dir_in_base=GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DIR0);+gpio_cfg.no_set_on_input=true;++gpio_cfg.reg_mask_xlate=rtl8231_gpio_reg_mask_xlate;++gr=devm_gpio_regmap_register(dev,&gpio_cfg);+if(IS_ERR(gr)){+dev_err(dev,"failed to register gpio controller\n");+returnPTR_ERR(gr);+}++return0;+}++staticstructplatform_driverrtl8231_pinctrl_driver={+.driver={+.name="rtl8231-pinctrl",+},+.probe=rtl8231_pinctrl_probe,+};+module_platform_driver(rtl8231_pinctrl_driver);++MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");+MODULE_DESCRIPTION("Realtek RTL8231 pin control and GPIO support");+MODULE_LICENSE("GPL v2");
Both single and bi-color scanning modes are supported. The driver will
verify that the addresses are valid for the current mode, before
registering the LEDs. LEDs can be turned on, off, or toggled at one of
six predefined rates from 40ms to 1280ms.
Implements a platform device for use as child device with RTL8231 MFD,
and uses the parent regmap to access the required registers.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/leds/Kconfig | 10 ++
drivers/leds/Makefile | 1 +
drivers/leds/leds-rtl8231.c | 293 ++++++++++++++++++++++++++++++++++++
3 files changed, 304 insertions(+)
create mode 100644 drivers/leds/leds-rtl8231.c
From: Andy Shevchenko <hidden> Date: 2021-05-17 21:07:14
On Mon, May 17, 2021 at 10:28 PM Sander Vanheule [off-list ref] wrote:
GPIO chips may not support setting the output value when a pin is
configured as an input, although the current implementation assumes this
is always possible.
But it's broken hardware.
Can it be rather marked as a quirk?
Add support for setting pin direction before value. The order defaults
to setting the value first, but this can be reversed by setting the
regmap_config.no_set_on_input flag, similar to the corresponding flag in
the gpio-mmio driver.
From: Andy Shevchenko <hidden> Date: 2021-05-17 21:19:03
On Mon, May 17, 2021 at 10:28 PM Sander Vanheule [off-list ref] wrote:
The RTL8231 is implemented as an MDIO device, and provides a regmap
interface for register access by the core and child devices.
The chip can also be a device on an SMI bus, an I2C-like bus by Realtek.
Since kernel support for SMI is limited, and no real-world SMI
implementations have been encountered for this device, this is currently
unimplemented. The use of the regmap interface should make any future
support relatively straightforward.
After reset, all pins are muxed to GPIO inputs before the pin drivers
are enabled. This is done to prevent accidental system resets, when a
pin is connected to the parent SoC's reset line.
[missing MDIO_BUS dependency, provided via REGMAP_MDIO]
Reported-by: kernel test robot <redacted>
What is the culprit? Shouldn't this have a Fixes tag?
...
+ Support for the Realtek RTL8231 GPIO and LED expander.
+ Provides up to 37 GPIOs, 88 LEDs, and one PWM output.
+ When built as a module, this module will be named rtl8231_expander.
The name is not the one it will be according to Makefile.
From: Andy Shevchenko <hidden> Date: 2021-05-17 21:43:03
On Mon, May 17, 2021 at 10:28 PM Sander Vanheule [off-list ref] wrote:
This driver implements the GPIO and pin muxing features provided by the
RTL8231. The device should be instantiated as an MFD child, where the
parent device has already configured the regmap used for register
access.
Although described in the bindings, pin debouncing and drive strength
selection are currently not implemented. Debouncing is only available
for the six highest GPIOs, and must be emulated when other pins are used
for (button) inputs anyway.
From: Andy Shevchenko <hidden> Date: 2021-05-17 22:01:03
On Mon, May 17, 2021 at 10:28 PM Sander Vanheule [off-list ref] wrote:
Both single and bi-color scanning modes are supported. The driver will
verify that the addresses are valid for the current mode, before
registering the LEDs. LEDs can be turned on, off, or toggled at one of
six predefined rates from 40ms to 1280ms.
Implements a platform device for use as child device with RTL8231 MFD,
as a child
and uses the parent regmap to access the required registers.
...
+ When built as a module, this module will be named rtl8231_leds.
Again, what it's written here is not what is in Makefile.
+obj-$(CONFIG_LEDS_RTL8231) += leds-rtl8231.o
...
quoted hunk
+/**+ * struct led_toggle_rate - description of an LED blinking mode+ * @interval: LED toggle rate in ms+ * @mode: Register field value used to active this mode
activate
quoted hunk
+ *+ * For LED hardware accelerated blinking, with equal on and off delay.+ * Both delays are given by @interval, so the interval at which the LED blinks+ * (i.e. turn on and off once) is double this value.+ */
...
quoted hunk
+static unsigned int rtl8231_led_current_interval(struct rtl8231_led *pled)+{+ unsigned int mode;
+ unsigned int i = 0;
This...
quoted hunk
+ if (regmap_field_read(pled->reg_field, &mode))+ return 0;++ while (i < pled->modes->num_toggle_rates && mode != pled->modes->toggle_rates[i].mode)+ i++;
...and this will be better as a for-loop.
+ if (i < pled->modes->num_toggle_rates)
+ return pled->modes->toggle_rates[i].interval;
+ else
Redundant.
+ return 0;
+}
...
+ unsigned int i = 0;
As per above.
...
+ interval = 500;
interval_ms
quoted hunk
+ /*+ * If the current mode is blinking, choose the delay that (likely) changed.+ * Otherwise, choose the interval that would have the same total delay.+ */+ interval = rtl8231_led_current_interval(pled);
+ map = dev_get_regmap(dev->parent, NULL);
+ if (IS_ERR_OR_NULL(map)) {
Split it into two conditionals.
quoted hunk
+ dev_err(dev, "failed to retrieve regmap\n");+ if (!map)+ return -ENODEV;+ else+ return PTR_ERR(map);+ }
...
+ if (!device_property_match_string(dev, "realtek,led-scan-mode", "single-color")) {
It seems that device_property_match_string() and accompanying
functions have wrong description of returned codes, i.e. it returns
the index of the matched string. It's possible that some APIs are
broken (but I believe that the former is the case).
That said, I think the proper comparison should be >= 0.
From: Rob Herring <robh@kernel.org> Date: 2021-05-17 22:31:18
On Tue, May 11, 2021 at 02:25:19PM +0200, Sander Vanheule wrote:
quoted hunk
Add a binding description for the Realtek RTL8231's LED support, which
consists of up to 88 LEDs arranged in a number of scanning matrices.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
.../bindings/leds/realtek,rtl8231-leds.yaml | 159 ++++++++++++++++++
1 file changed, 159 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
@@ -0,0 +1,159 @@+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause+%YAML1.2+---+$id:http://devicetree.org/schemas/leds/realtek,rtl8231-leds.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Realtek RTL8231 LED scan matrix.++maintainers:+-Sander Vanheule <sander@svanheule.net>++description:|+The RTL8231 has support for driving a number of LED matrices, by scanning+over the LEDs pins, alternatingly lighting different columns and/or rows.++In single color scan mode, 88 LEDs are supported. These are grouped into+three output matrices:+-Group A of 6×6 single color LEDs. Rows and columns are driven by GPIO+pins 0-11.+L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]+| | | | | |+P0/P6 --<--------<--------<--------<--------<--------< (3)+| | | | | |+P1/P7 --<--------<--------<--------<--------<--------< (4)+| | | | | |+P2/P8 --<--------<--------<--------<--------<--------< (5)+| | | | | |+P3/P9 --<--------<--------<--------<--------<--------< (6)+| | | | | |+P4/P10 --<--------<--------<--------<--------<--------< (7)+| | | | | |+P5/P11 --<--------<--------<--------<--------<--------< (8)+(0) (1) (2) (9) (10) (11)+-Group B of 6×6 single color LEDs. Rows and columns are driven by GPIO+pins 12-23.+L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]+| | | | | |+P12/P18 --<--------<--------<--------<--------<--------< (15)+| | | | | |+P13/P19 --<--------<--------<--------<--------<--------< (16)+| | | | | |+P14/P20 --<--------<--------<--------<--------<--------< (17)+| | | | | |+P15/P21 --<--------<--------<--------<--------<--------< (18)+| | | | | |+P16/P22 --<--------<--------<--------<--------<--------< (19)+| | | | | |+P17/P23 --<--------<--------<--------<--------<--------< (20)+(12) (13) (14) (21) (22) (23)+-Group C of 8 pairs of anti-parallel (or bi-color) LEDs. LED selection is+provided by GPIO pins 24-27 and 29-32, polarity selection by GPIO 28.+P24 P25 ... P30 P31+| | | |+LED POL --X-------X---/\/---X-------X (28)+(24) (25) ... (31) (32)++In bi-color scan mode, 72 LEDs are supported. These are grouped into four+output matrices:+-Group A of 12 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 0-11, polarity selection by GPIO 12.+-Group B of 6 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 23-28, polarity selection by GPIO 21.+-Group C of 6 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 29-34, polarity selection by GPIO 22.+-Group of 4×6 single color LEDs. Rows are driven by GPIO pins 15-20,+columns by GPIO pins 13-14 and 21-22 (shared with groups B and C).+P[n] P[n+6] P[n+12] P[n+18]+| | | |++0 --<--------<--------<--------< (15)+| | | |++1 --<--------<--------<--------< (16)+| | | |++2 --<--------<--------<--------< (17)+| | | |++3 --<--------<--------<--------< (18)+| | | |++4 --<--------<--------<--------< (19)+| | | |++6 --<--------<--------<--------< (20)+(13) (14) (21) (22)++This node must always be a child of a 'realtek,rtl8231' node.++properties:+$nodename:+const:leds
led-controller
quoted hunk
++ compatible:+ const: realtek,rtl8231-leds
How is this device controlled?
quoted hunk
++ "#address-cells":+ const: 2++ "#size-cells":+ const: 0++ realtek,led-scan-mode:+ $ref: /schemas/types.yaml#/definitions/string+ description: |+ Specify the scanning mode the chip should run in. See general description+ for how the scanning matrices are wired up.+ enum: ["single-color", "bi-color"]++patternProperties:+ "^led@[0-9]+,[0-2]$":+ description: |+ LEDs are addressed by their port index and led index. Ports 0-23 always+ support three LEDs. Additionally, but only when used in single color scan+ mode, ports 24-31 support two LEDs.
Normally unit-addresses are hex values.
quoted hunk
+ type: object++ properties:+ reg:+ maxItems: 1
This should have more constraints:
reg:
items:
- items:
- description: port index
maximum: 31
- description: led index
maximum: 2
From: Rob Herring <robh@kernel.org> Date: 2021-05-17 22:38:07
On Tue, May 11, 2021 at 02:25:20PM +0200, Sander Vanheule wrote:
quoted hunk
Add a binding description for the Realtek RTL8231, a GPIO and LED
expander chip commonly used in ethernet switches based on a Realtek
switch SoC. These chips can be addressed via an MDIO or SMI bus, or used
as a plain 36-bit shift register.
This binding only describes the feature set provided by the MDIO/SMI
configuration, and covers the GPIO, PWM, and pin control properties. The
LED properties are defined in a separate binding.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
.../bindings/mfd/realtek,rtl8231.yaml | 202 ++++++++++++++++++
1 file changed, 202 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
@@ -0,0 +1,202 @@+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause+%YAML1.2+---+$id:http://devicetree.org/schemas/mfd/realtek,rtl8231.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Realtek RTL8231 GPIO and LED expander.++maintainers:+-Sander Vanheule <sander@svanheule.net>++description:|+The RTL8231 is a GPIO and LED expander chip, providing up to 37 GPIOs, up to+88 LEDs, and up to one PWM output. This device is frequently used alongside+Realtek switch SoCs, to provide additional I/O capabilities.++To manage the RTL8231's features, its strapping pins can be used to configure+it in one of three modes:shift register, MDIO device, or SMI device. The+shift register mode does not need special support. In MDIO or SMI mode, most+pins can be configured as a GPIO output, LED matrix scan line/column, or as a+PWM output.++The GPIO and pin control are part of the main node. PWM and LED support are+configured as sub-nodes.++properties:+compatible:+const:realtek,rtl8231++reg:+description:MDIO or SMI device address.+maxItems:1++# GPIO support+gpio-controller:true++"#gpio-cells":+const:2+description:|+The first cell is the pin number and the second cell is used to specify+the gpio active state.++gpio-ranges:+description:|+Must reference itself, and provide a zero-based mapping for 37 pins.+maxItems:1++# Pin muxing and configuration+realtek,drive-strength:+$ref:/schemas/types.yaml#/definitions/uint32
Use the standard 'drive-strength' property.
quoted hunk
+ description: |+ Common drive strength used for all GPIO output pins, must be 4mA or 8mA.+ On reset, this value will default to 8mA.+ enum: [4, 8]++ # LED scanning matrix+ leds:+ $ref: ../leds/realtek,rtl8231-leds.yaml#++ # PWM output+ pwm:+ type: object+ description: |+ Subnode describing the PWM peripheral. To use the PWM output, gpio35 must+ be muxed to its 'pwm' function. Valid frequency values for consumers are+ 1200, 1600, 2000, 2400, 2800, 3200, 4000, and 4800.++ properties:+ "#pwm-cells":+ description: |+ Twos cells with PWM index (must be 0) and PWM frequency in Hz.+ const: 2++ required:+ - "#pwm-cells"
Just move this to the parent node. No reason for a child node or that 1
node can't be 2 providers.
From: Andrew Lunn <andrew@lunn.ch> Date: 2021-05-18 01:40:52
On Mon, May 17, 2021 at 09:28:04PM +0200, Sander Vanheule wrote:
GPIO chips may not support setting the output value when a pin is
configured as an input
Could you describe what happens with the hardware you are playing
with. Not being able to do this means you will get glitches when
enabling the output so you should not use these GPIOs with bit banging
busses like i2c.
Andrew
From: Michael Walle <hidden> Date: 2021-05-18 08:39:40
Hi,
Am 2021-05-17 21:28, schrieb Sander Vanheule:
quoted hunk
GPIO chips may not support setting the output value when a pin is
configured as an input, although the current implementation assumes this
is always possible.
Add support for setting pin direction before value. The order defaults
to setting the value first, but this can be reversed by setting the
regmap_config.no_set_on_input flag, similar to the corresponding flag in
the gpio-mmio driver.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/gpio/gpio-regmap.c | 20 +++++++++++++++++---
include/linux/gpio/regmap.h | 3 +++
2 files changed, 20 insertions(+), 3 deletions(-)
@@ -170,14 +170,25 @@ static int gpio_regmap_direction_input(struct
gpio_chip *chip,
return gpio_regmap_set_direction(chip, offset, false);
}
-static int gpio_regmap_direction_output(struct gpio_chip *chip,
- unsigned int offset, int value)
+static int gpio_regmap_dir_out_val_first(struct gpio_chip *chip,
+ unsigned int offset, int value)
Can we leave the name as is? TBH I find these two similar names
super confusing. Maybe its just me, though.
quoted hunk
{ gpio_regmap_set(chip, offset, value); return gpio_regmap_set_direction(chip, offset, true); }+static int gpio_regmap_dir_out_dir_first(struct gpio_chip *chip,+ unsigned int offset, int value)+{+ int err;
Instead of adding a new one, we can also just check no_set_on_input
in gpio_regmap_direction_output(), which I'd prefer.
static int gpio_regmap_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
int ret;
if (gpio->no_set_on_input) {
/* some smart comment here, also mention gliches */
ret = gpio_regmap_set_direction(chip, offset, true);
gpio_regmap_set(chip, offset, value);
} else {
gpio_regmap_set(chip, offset, value);
ret = gpio_regmap_set_direction(chip, offset, true);
}
return ret;
}
From: Andy Shevchenko <hidden> Date: 2021-05-18 10:39:47
+Matti
On Tue, May 18, 2021 at 11:39 AM Michael Walle [off-list ref] wrote:
Am 2021-05-17 21:28, schrieb Sander Vanheule:
...
Instead of adding a new one, we can also just check no_set_on_input
in gpio_regmap_direction_output(), which I'd prefer.
+! here.
static int gpio_regmap_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
int ret;
if (gpio->no_set_on_input) {
/* some smart comment here, also mention gliches */
ret = gpio_regmap_set_direction(chip, offset, true);
gpio_regmap_set(chip, offset, value);
} else {
gpio_regmap_set(chip, offset, value);
ret = gpio_regmap_set_direction(chip, offset, true);
}
return ret;
}
...
quoted
+ * @no_set_on_input: Set if output value can only be set when the
direction
+ * is configured as output.
set_direction_first ?
Perhaps we need to establish rather something like
/* Broken hardware can't set value on input pin, we have to set it to
output first */
#define GPIO_REGMAP_QUIRK_... BIT(0)
unsigned int quirks;
?
--
With Best Regards,
Andy Shevchenko
Hi Andrew,
On Tue, 2021-05-18 at 03:40 +0200, Andrew Lunn wrote:
On Mon, May 17, 2021 at 09:28:04PM +0200, Sander Vanheule wrote:
quoted
GPIO chips may not support setting the output value when a pin is
configured as an input
Could you describe what happens with the hardware you are playing
with. Not being able to do this means you will get glitches when
enabling the output so you should not use these GPIOs with bit banging
busses like i2c.
As I was testing this driver, I noticed that output settings for GPIO LEDs,
connected to the RTL8231, weren't being properly set. The actual LED brightness
didn't correspond to the one reported by sysfs. Changing the operation order
fixed this.
However, the vendor code uses I2C bitbanging quite extensively on these chips,
so I decided to have another look.
From u-boot on my device, I can manipulate the RTL8231 registeres relatively
easily. I performed the following short tests:
* Set pin to input, pull pin high, write output low, change direction: pin
output changes to low value
* Set pin to input pull pin low, write output high, change direction: pin
output changes to high value
Which seems to indicate that I _can_ set output values on input pins... I'll
need to look into this in more detail when I have a bit more time, later this
week.
Best,
Sander
On Mon, May 17, 2021 at 9:28 PM Sander Vanheule [off-list ref] wrote:
Add a binding description for the Realtek RTL8231, a GPIO and LED
expander chip commonly used in ethernet switches based on a Realtek
switch SoC. These chips can be addressed via an MDIO or SMI bus, or used
as a plain 36-bit shift register.
This binding only describes the feature set provided by the MDIO/SMI
configuration, and covers the GPIO, PWM, and pin control properties. The
LED properties are defined in a separate binding.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
This looks correct from the GPIO side of things:
Reviewed-by: Linus Walleij <redacted>
Yours,
Linus Walleij
From: Lee Jones <hidden> Date: 2021-05-19 14:58:15
On Wed, 12 May 2021, kernel test robot wrote:
Hi Sander,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on pavel-linux-leds/for-next]
[also build test ERROR on lee-mfd/for-mfd-next pinctrl/devel v5.13-rc1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Sander-Vanheule/RTL8231-GPIO-expander-support/20210511-202618
base: git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git for-next
config: h8300-randconfig-r012-20210512 (attached as .config)
compiler: h8300-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/e031cc2da2c2948230bacd1ca56cfe9990e1aefd
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Sander-Vanheule/RTL8231-GPIO-expander-support/20210511-202618
git checkout e031cc2da2c2948230bacd1ca56cfe9990e1aefd
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=h8300
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
All errors (new ones prefixed by >>):
h8300-linux-ld: drivers/mfd/rtl8231.o: in function `rtl8231_mdio_reg_write':
quoted
quoted
rtl8231.c:(.text+0x4f): undefined reference to `mdiobus_write'
h8300-linux-ld: drivers/mfd/rtl8231.o: in function `rtl8231_mdio_reg_read':
quoted
quoted
rtl8231.c:(.text+0x75): undefined reference to `mdiobus_read'
h8300-linux-ld: drivers/mfd/rtl8231.o: in function `mdio_module_init':
quoted
quoted
rtl8231.c:(.init.text+0xd): undefined reference to `mdio_driver_register'
Please could you take a look at these failures.
Either fix them up or report a false positive.
--
Lee Jones [李琼斯]
Senior Technical Lead - Developer Services
Linaro.org │ Open source software for Arm SoCs
Follow Linaro: Facebook | Twitter | Blog
On Wed, 2021-05-19 at 15:58 +0100, Lee Jones wrote:
On Wed, 12 May 2021, kernel test robot wrote:
quoted
Hi Sander,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on pavel-linux-leds/for-next]
[also build test ERROR on lee-mfd/for-mfd-next pinctrl/devel v5.13-rc1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url:
https://github.com/0day-ci/linux/commits/Sander-Vanheule/RTL8231-GPIO-expander-support/20210511-202618
base: git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git
for-next
config: h8300-randconfig-r012-20210512 (attached as .config)
compiler: h8300-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget
https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O
~/bin/make.cross
chmod +x ~/bin/make.cross
#
https://github.com/0day-ci/linux/commit/e031cc2da2c2948230bacd1ca56cfe9990e1aefd
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Sander-Vanheule/RTL8231-GPIO-
expander-support/20210511-202618
git checkout e031cc2da2c2948230bacd1ca56cfe9990e1aefd
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1
ARCH=h8300
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
All errors (new ones prefixed by >>):
h8300-linux-ld: drivers/mfd/rtl8231.o: in function
`rtl8231_mdio_reg_write':
quoted
quoted
rtl8231.c:(.text+0x4f): undefined reference to `mdiobus_write'
h8300-linux-ld: drivers/mfd/rtl8231.o: in function `rtl8231_mdio_reg_read':
quoted
quoted
rtl8231.c:(.text+0x75): undefined reference to `mdiobus_read'
h8300-linux-ld: drivers/mfd/rtl8231.o: in function `mdio_module_init':
quoted
quoted
rtl8231.c:(.init.text+0xd): undefined reference to `mdio_driver_register'
From: Mark Brown <broonie@kernel.org> Date: 2021-05-19 16:11:19
On Mon, 17 May 2021 21:28:02 +0200, Sander Vanheule wrote:
The RTL8231 GPIO and LED expander can be configured for use as an MDIO or SMI
bus device. Currently only the MDIO mode is supported, although SMI mode
support should be fairly straightforward, once an SMI bus driver is available.
Provided features by the RTL8231:
- Up to 37 GPIOs
- Configurable drive strength: 8mA or 4mA (currently unsupported)
- Input debouncing on high GPIOs (currently unsupported)
- Up to 88 LEDs in multiple scan matrix groups
- On, off, or one of six toggling intervals
- "single-color mode": 2×36 single color LEDs + 8 bi-color LEDs
- "bi-color mode": (12 + 2×6) bi-color LEDs + 24 single color LEDs
- Up to one PWM output (currently unsupported)
- Fixed duty cycle, 8 selectable frequencies (1.2kHz - 4.8kHz)
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git for-next
Thanks!
[1/7] regmap: Add MDIO bus support
commit: 1f89d2fe16072a74b34bdb895160910091427891
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
From: Mark Brown <broonie@kernel.org> Date: 2021-05-19 16:13:08
On Mon, May 17, 2021 at 09:28:03PM +0200, Sander Vanheule wrote:
Basic support for MDIO bus access. Support only includes clause-22
register access, with 5-bit addresses, and 16-bit wide registers.
The following changes since commit 6efb943b8616ec53a5e444193dccf1af9ad627b5:
Linux 5.13-rc1 (2021-05-09 14:17:44 -0700)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git tags/regmap-mdio
for you to fetch changes up to 1f89d2fe16072a74b34bdb895160910091427891:
regmap: Add MDIO bus support (2021-05-19 14:19:10 +0100)
----------------------------------------------------------------
regmap: Add MDIO bus support
----------------------------------------------------------------
Sander Vanheule (1):
regmap: Add MDIO bus support
drivers/base/regmap/Kconfig | 6 ++++-
drivers/base/regmap/Makefile | 1 +
drivers/base/regmap/regmap-mdio.c | 57 +++++++++++++++++++++++++++++++++++++++
include/linux/regmap.h | 36 +++++++++++++++++++++++++
4 files changed, 99 insertions(+), 1 deletion(-)
create mode 100644 drivers/base/regmap/regmap-mdio.c
On Mon, 2021-05-17 at 17:31 -0500, Rob Herring wrote:
On Tue, May 11, 2021 at 02:25:19PM +0200, Sander Vanheule wrote:
quoted
Add a binding description for the Realtek RTL8231's LED support, which
consists of up to 88 LEDs arranged in a number of scanning matrices.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
.../bindings/leds/realtek,rtl8231-leds.yaml | 159 ++++++++++++++++++
1 file changed, 159 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-
leds.yaml
@@ -0,0 +1,159 @@+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause+%YAML1.2+---+$id:http://devicetree.org/schemas/leds/realtek,rtl8231-leds.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Realtek RTL8231 LED scan matrix.++maintainers:+-Sander Vanheule <sander@svanheule.net>++description:|+The RTL8231 has support for driving a number of LED matrices, by scanning+over the LEDs pins, alternatingly lighting different columns and/or rows.++In single color scan mode, 88 LEDs are supported. These are grouped into+three output matrices:+-Group A of 6×6 single color LEDs. Rows and columns are driven by GPIO+pins 0-11.+L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]+| | | | | |+P0/P6 --<--------<--------<--------<--------<--------< (3)+| | | | | |+P1/P7 --<--------<--------<--------<--------<--------< (4)+| | | | | |+P2/P8 --<--------<--------<--------<--------<--------< (5)+| | | | | |+P3/P9 --<--------<--------<--------<--------<--------< (6)+| | | | | |+P4/P10 --<--------<--------<--------<--------<--------< (7)+| | | | | |+P5/P11 --<--------<--------<--------<--------<--------< (8)+(0) (1) (2) (9) (10) (11)+-Group B of 6×6 single color LEDs. Rows and columns are driven by GPIO+pins 12-23.+L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]+| | | | | |+P12/P18 --<--------<--------<--------<--------<--------< (15)+| | | | | |+P13/P19 --<--------<--------<--------<--------<--------< (16)+| | | | | |+P14/P20 --<--------<--------<--------<--------<--------< (17)+| | | | | |+P15/P21 --<--------<--------<--------<--------<--------< (18)+| | | | | |+P16/P22 --<--------<--------<--------<--------<--------< (19)+| | | | | |+P17/P23 --<--------<--------<--------<--------<--------< (20)+(12) (13) (14) (21) (22) (23)+-Group C of 8 pairs of anti-parallel (or bi-color) LEDs. LED selection
is
+ provided by GPIO pins 24-27 and 29-32, polarity selection by GPIO 28.
+ P24 P25 ... P30 P31
+ | | | |
+ LED POL --X-------X---/\/---X-------X (28)
+ (24) (25) ... (31) (32)
+
+ In bi-color scan mode, 72 LEDs are supported. These are grouped into four
+ output matrices:
+ - Group A of 12 pairs of anti-parallel LEDs. LED selection is provided
+ by GPIO pins 0-11, polarity selection by GPIO 12.
+ - Group B of 6 pairs of anti-parallel LEDs. LED selection is provided
+ by GPIO pins 23-28, polarity selection by GPIO 21.
+ - Group C of 6 pairs of anti-parallel LEDs. LED selection is provided
+ by GPIO pins 29-34, polarity selection by GPIO 22.
+ - Group of 4×6 single color LEDs. Rows are driven by GPIO pins 15-20,
+ columns by GPIO pins 13-14 and 21-22 (shared with groups B and C).
+ P[n] P[n+6] P[n+12] P[n+18]
+ | | | |
+ +0 --<--------<--------<--------< (15)
+ | | | |
+ +1 --<--------<--------<--------< (16)
+ | | | |
+ +2 --<--------<--------<--------< (17)
+ | | | |
+ +3 --<--------<--------<--------< (18)
+ | | | |
+ +4 --<--------<--------<--------< (19)
+ | | | |
+ +6 --<--------<--------<--------< (20)
+ (13) (14) (21) (22)
+
+ This node must always be a child of a 'realtek,rtl8231' node.
+
+properties:
+ $nodename:
+ const: leds
led-controller
Will update.
quoted
++ compatible:+ const: realtek,rtl8231-leds
How is this device controlled?
This device represents a part of the functionality of the RTL8231 GPIO/LED
expander, which sits on an MDIO bus or Realtek SMI bus. I'll add this bit to the
main description.
Does this answer your question, or were you looking for other details?
quoted
+patternProperties:
+ "^led@[0-9]+,[0-2]$":
+ description: |
+ LEDs are addressed by their port index and led index. Ports 0-23
always
+ support three LEDs. Additionally, but only when used in single color
scan
+ mode, ports 24-31 support two LEDs.
Normally unit-addresses are hex values.
quoted
+ type: object++ properties:+ reg:+ maxItems: 1
This should have more constraints:
reg:
items:
- items:
- description: port index
maximum: 31
- description: led index
maximum: 2
Thanks for the suggestion, I was wondering what the best approach was here. I
will change the pattern to "^led@" as well, so the address restrictions are only
defined by the reg property.
On Mon, 2021-05-17 at 17:38 -0500, Rob Herring wrote:
On Tue, May 11, 2021 at 02:25:20PM +0200, Sander Vanheule wrote:
quoted
Add a binding description for the Realtek RTL8231, a GPIO and LED
expander chip commonly used in ethernet switches based on a Realtek
switch SoC. These chips can be addressed via an MDIO or SMI bus, or used
as a plain 36-bit shift register.
This binding only describes the feature set provided by the MDIO/SMI
configuration, and covers the GPIO, PWM, and pin control properties. The
LED properties are defined in a separate binding.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
.../bindings/mfd/realtek,rtl8231.yaml | 202 ++++++++++++++++++
1 file changed, 202 insertions(+)
create mode 100644
Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
@@ -0,0 +1,202 @@+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause+%YAML1.2+---+$id:http://devicetree.org/schemas/mfd/realtek,rtl8231.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Realtek RTL8231 GPIO and LED expander.++maintainers:+-Sander Vanheule <sander@svanheule.net>++description:|+The RTL8231 is a GPIO and LED expander chip, providing up to 37 GPIOs, up
to
+ 88 LEDs, and up to one PWM output. This device is frequently used
alongside
+ Realtek switch SoCs, to provide additional I/O capabilities.
+
+ To manage the RTL8231's features, its strapping pins can be used to
configure
+ it in one of three modes: shift register, MDIO device, or SMI device. The
+ shift register mode does not need special support. In MDIO or SMI mode,
most
+ pins can be configured as a GPIO output, LED matrix scan line/column, or
as a
+ PWM output.
+
+ The GPIO and pin control are part of the main node. PWM and LED support
are
+ configured as sub-nodes.
+
+properties:
+ compatible:
+ const: realtek,rtl8231
+
+ reg:
+ description: MDIO or SMI device address.
+ maxItems: 1
+
+ # GPIO support
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+ description: |
+ The first cell is the pin number and the second cell is used to
specify
+ the gpio active state.
+
+ gpio-ranges:
+ description: |
+ Must reference itself, and provide a zero-based mapping for 37 pins.
+ maxItems: 1
+
+ # Pin muxing and configuration
+ realtek,drive-strength:
+ $ref: /schemas/types.yaml#/definitions/uint32
Use the standard 'drive-strength' property.
Ok, I wasn't sure I could do this, since it's normally used in a pin config, not
a pin controller config. I'll update this, as well as the suggested changes
below.
Best,
Sander
quoted
+ description: |
+ Common drive strength used for all GPIO output pins, must be 4mA or
8mA.
+ On reset, this value will default to 8mA.
+ enum: [4, 8]
+
+ # LED scanning matrix
+ leds:
+ $ref: ../leds/realtek,rtl8231-leds.yaml#
+
+ # PWM output
+ pwm:
+ type: object
+ description: |
+ Subnode describing the PWM peripheral. To use the PWM output, gpio35
must
+ be muxed to its 'pwm' function. Valid frequency values for consumers
are
+ 1200, 1600, 2000, 2400, 2800, 3200, 4000, and 4800.
+
+ properties:
+ "#pwm-cells":
+ description: |
+ Twos cells with PWM index (must be 0) and PWM frequency in Hz.
+ const: 2
+
+ required:
+ - "#pwm-cells"
Just move this to the parent node. No reason for a child node or that 1
node can't be 2 providers.
Hi Michael,
On Tue, 2021-05-18 at 10:39 +0200, Michael Walle wrote:
Hi,
Am 2021-05-17 21:28, schrieb Sander Vanheule:
quoted
GPIO chips may not support setting the output value when a pin is
configured as an input, although the current implementation assumes
this
is always possible.
Add support for setting pin direction before value. The order defaults
to setting the value first, but this can be reversed by setting the
regmap_config.no_set_on_input flag, similar to the corresponding flag
in
the gpio-mmio driver.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/gpio/gpio-regmap.c | 20 +++++++++++++++++---
include/linux/gpio/regmap.h | 3 +++
2 files changed, 20 insertions(+), 3 deletions(-)
@@ -170,14 +170,25 @@ static int gpio_regmap_direction_input(struct
gpio_chip *chip,
return gpio_regmap_set_direction(chip, offset, false);
}
-static int gpio_regmap_direction_output(struct gpio_chip *chip,
- unsigned int offset, int value)
+static int gpio_regmap_dir_out_val_first(struct gpio_chip *chip,
+ unsigned int offset, int value)
Can we leave the name as is? TBH I find these two similar names
super confusing. Maybe its just me, though.
Sure. This is the implementation used in gpio-mmio.c to provide the same
functionality, so I had used that for consistenty between the two drivers.
quoted
{
gpio_regmap_set(chip, offset, value);
return gpio_regmap_set_direction(chip, offset, true);
}
+static int gpio_regmap_dir_out_dir_first(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ int err;
Instead of adding a new one, we can also just check no_set_on_input
in gpio_regmap_direction_output(), which I'd prefer.
static int gpio_regmap_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
int ret;
if (gpio->no_set_on_input) {
/* some smart comment here, also mention gliches */
ret = gpio_regmap_set_direction(chip, offset, true);
gpio_regmap_set(chip, offset, value);
} else {
gpio_regmap_set(chip, offset, value);
ret = gpio_regmap_set_direction(chip, offset, true);
}
return ret;
}
This would certainly make the code a bit easier to follow when you're not
familiar with it :-)
I also see the other functions do checks on static values too, so I'll bring
this function in line with that style.
* @reg_dir_out_base: (Optional) out setting register base address
* @reg_stride: (Optional) May be set if the registers (of
the
* same type, dat, set, etc) are not consecutive.
+ * @no_set_on_input: Set if output value can only be set when the
direction
+ * is configured as output.
set_direction_first ?
This negation can indeed be a bit confusing, I'll change this. As Andy
suggested, I just went for a 'quirks' field, with currently only one defined
flag.
Best,
Sander
Hi Andy,
I've implemented the minor remarks (redundant assignments, if/else code
structure...). Some extra details below.
On Tue, 2021-05-18 at 00:18 +0300, Andy Shevchenko wrote:
On Mon, May 17, 2021 at 10:28 PM Sander Vanheule [off-list ref] wrote:
quoted
The RTL8231 is implemented as an MDIO device, and provides a regmap
interface for register access by the core and child devices.
The chip can also be a device on an SMI bus, an I2C-like bus by Realtek.
Since kernel support for SMI is limited, and no real-world SMI
implementations have been encountered for this device, this is currently
unimplemented. The use of the regmap interface should make any future
support relatively straightforward.
After reset, all pins are muxed to GPIO inputs before the pin drivers
are enabled. This is done to prevent accidental system resets, when a
pin is connected to the parent SoC's reset line.
quoted
[missing MDIO_BUS dependency, provided via REGMAP_MDIO]
Reported-by: kernel test robot <redacted>
What is the culprit? Shouldn't this have a Fixes tag?
But it doesn't actually fix an issue created by an existing commit, just
something that was wrong in the first version of the patch. This patch is not
dedicated to fixing that single issue though, it's just a part of it. Hence the
note above the Reported-by tag.
Hi Andy,
On Tue, 2021-05-18 at 00:42 +0300, Andy Shevchenko wrote:
On Mon, May 17, 2021 at 10:28 PM Sander Vanheule [off-list ref] wrote:
quoted
This driver implements the GPIO and pin muxing features provided by the
RTL8231. The device should be instantiated as an MFD child, where the
parent device has already configured the regmap used for register
access.
Although described in the bindings, pin debouncing and drive strength
selection are currently not implemented. Debouncing is only available
for the six highest GPIOs, and must be emulated when other pins are used
for (button) inputs anyway.
I would see rather
sturct pinctrl_pin_desc desc;
Where drv_data describes the rest of the data for pin
I've split up the definitions into two parts:
* pinctrl_pin_desc with the standard info, which has drv_data pointing to...
* a device-specific rtl8231_pin_desc, with the register field info and
alternate function
So the pin descriptions are now entirely static, and only the pin functions are
assembled at runtime.
quoted
+static int rtl8231_get_group_pins(struct pinctrl_dev *pctldev, unsigned int
selector,
+ const unsigned int **pins, unsigned int *num_pins)
+{
quoted
+ if (selector < ARRAY_SIZE(rtl8231_pins)) {
Can we use traditional pattern, i.e.
if (... >= ARRAY_SIZE(...))
return -EINVAL;
...
return 0;
?
I was somehow thinking that this would either return an error value or a valid
point. Don't know where I got that, but should be fixed here and for the other
kallocs.
Best,
Sander
If count returns 1? What's the point of counting if you always want two?
If count returns 1, or more than 2, that's an error. But this check was missing
in v2, so I added it in v3.
quoted
+ if (!device_property_match_string(dev, "realtek,led-scan-mode",
"single-color")) {
It seems that device_property_match_string() and accompanying
functions have wrong description of returned codes, i.e. it returns
the index of the matched string. It's possible that some APIs are
broken (but I believe that the former is the case).
That said, I think the proper comparison should be >= 0.
Hi Adrew,
On Tue, 2021-05-18 at 03:40 +0200, Andrew Lunn wrote:
On Mon, May 17, 2021 at 09:28:04PM +0200, Sander Vanheule wrote:
quoted
GPIO chips may not support setting the output value when a pin is
configured as an input
Could you describe what happens with the hardware you are playing
with. Not being able to do this means you will get glitches when
enabling the output so you should not use these GPIOs with bit banging
busses like i2c.
As I reported earlier, using value-before-direction breaks the GPIO driven LEDs
on one of my devices.
I've tried to use another device to test if I could reproduce this. Using the
gpioset and gpioget utilities, I can't seem to reproduce this however. Whether I
enable the (new) quirk or not, doesn't seem to make any difference.
The documentation we have on this chip is quite scarce, so I'm unaware of
different chip revisions, or how I would distinguish between revisions. As far
as I can see, Realtek's code (present in the GPL archives we got from some
vendors) set the pin direction before setting the value.
For now, I've made the implementation so that the quirk is always applied. Like
the behaviour that is implicit in the origal code. If prefered, I could also
supply a separate devicetree compatible or extra devictree flag.
Regarding bit banged I2C, glitches may not actually be an issue. If a pull-up
resistor is used for HIGH values, and an open drain for LOW values, the GPIO pin
doesn't actually have to change value, only direction (IN for HIGH, OUT for
LOW). A configuration like this would perhaps glitch once on the initial OUT-LOW
configuration. Like I mentioned, bit banged I2C is frequently used in ethernet
switches with these chips to talk to SFP modules.
Best,
Sander
The RTL8231 GPIO and LED expander can be configured for use as an MDIO or SMI
bus device. Currently only the MDIO mode is supported, although SMI mode
support should be fairly straightforward, once an SMI bus driver is available.
Provided features by the RTL8231:
- Up to 37 GPIOs
- Configurable drive strength: 8mA or 4mA (currently unsupported)
- Input debouncing on high GPIOs (currently unsupported)
- Up to 88 LEDs in multiple scan matrix groups
- On, off, or one of six toggling intervals
- "single-color mode": 2×36 single color LEDs + 8 bi-color LEDs
- "bi-color mode": (12 + 2×6) bi-color LEDs + 24 single color LEDs
- Up to one PWM output (currently unsupported)
- Fixed duty cycle, 8 selectable frequencies (1.2kHz - 4.8kHz)
Register access is provided through a new MDIO regmap provider. The GPIO
controller uses gpio-regmap, although a patch is required to support a
limitation of the chip.
Changes since v2:
- MDIO regmap support was merged, so patch is dropped here
- Implement feedback for DT bindings
- Use correct module names in Kconfigs
- Fix k*alloc return value checks
- Introduce GPIO regmap quirks to set output direction first
- pinctrl: Use static pin descriptions for pin controller
- pinctrl: Fix gpio consumer resource leak
- mfd: Replace CONFIG_PM-ifdef'ery
- leds: Rename interval to interval_ms
Changes since v1:
- Reintroduce MDIO regmap, with fixed Kconfig dependencies
- Add configurable dir/value order for gpio-regmap direction_out call
- Drop allocations for regmap fields that are used only on init
- Move some definitions to MFD header
- Add PM ops to replace driver remove for MFD
- Change pinctrl driver to (modified) gpio-regmap
- Change leds driver to use fwnode
Changes since RFC:
- Dropped MDIO regmap interface. I was unable to resolve the Kconfig
dependency issue, so have reverted to using regmap_config.reg_read/write.
- Added pinctrl support
- Added LED support
- Changed root device to MFD, with pinctrl and leds child devices. Root
device is now an mdio_device driver.
Sander Vanheule (6):
gpio: regmap: Add quirk for output data register
dt-bindings: leds: Binding for RTL8231 scan matrix
dt-bindings: mfd: Binding for RTL8231
mfd: Add RTL8231 core device
pinctrl: Add RTL8231 pin control and GPIO support
leds: Add support for RTL8231 LED scan matrix
.../bindings/leds/realtek,rtl8231-leds.yaml | 166 ++++++++
.../bindings/mfd/realtek,rtl8231.yaml | 190 +++++++++
drivers/gpio/gpio-regmap.c | 15 +-
drivers/leds/Kconfig | 10 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-rtl8231.c | 291 +++++++++++++
drivers/mfd/Kconfig | 9 +
drivers/mfd/Makefile | 1 +
drivers/mfd/rtl8231.c | 143 +++++++
drivers/pinctrl/Kconfig | 11 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rtl8231.c | 398 ++++++++++++++++++
include/linux/gpio/regmap.h | 13 +
include/linux/mfd/rtl8231.h | 57 +++
14 files changed, 1304 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
create mode 100644 drivers/leds/leds-rtl8231.c
create mode 100644 drivers/mfd/rtl8231.c
create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c
create mode 100644 include/linux/mfd/rtl8231.h
--
2.31.1
GPIO chips may not support setting the output value when a pin is
configured as an input, although the current implementation assumes this
is always possible.
Add support for setting pin direction before value. The order defaults
to setting the value first, but this can be reversed by setting the
GPIO_REGMAP_QUIRK_SET_DIRECTION_FIRST flag in regmap_config.quirks.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/gpio/gpio-regmap.c | 15 +++++++++++++--
include/linux/gpio/regmap.h | 13 +++++++++++++
2 files changed, 26 insertions(+), 2 deletions(-)
Add a binding description for the Realtek RTL8231's LED support, which
consists of up to 88 LEDs arranged in a number of scanning matrices.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
.../bindings/leds/realtek,rtl8231-leds.yaml | 166 ++++++++++++++++++
1 file changed, 166 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/realtek,rtl8231-leds.yaml
@@ -0,0 +1,166 @@+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause+%YAML1.2+---+$id:http://devicetree.org/schemas/leds/realtek,rtl8231-leds.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Realtek RTL8231 LED scan matrix.++maintainers:+-Sander Vanheule <sander@svanheule.net>++description:|+The RTL8231 has support for driving a number of LED matrices, by scanning+over the LEDs pins, alternatingly lighting different columns and/or rows.++This functionality is available on an RTL8231, when it is configured for use+as an MDIO device, or SMI device.++In single color scan mode, 88 LEDs are supported. These are grouped into+three output matrices:+-Group A of 6×6 single color LEDs. Rows and columns are driven by GPIO+pins 0-11.+L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]+| | | | | |+P0/P6 --<--------<--------<--------<--------<--------< (3)+| | | | | |+P1/P7 --<--------<--------<--------<--------<--------< (4)+| | | | | |+P2/P8 --<--------<--------<--------<--------<--------< (5)+| | | | | |+P3/P9 --<--------<--------<--------<--------<--------< (6)+| | | | | |+P4/P10 --<--------<--------<--------<--------<--------< (7)+| | | | | |+P5/P11 --<--------<--------<--------<--------<--------< (8)+(0) (1) (2) (9) (10) (11)+-Group B of 6×6 single color LEDs. Rows and columns are driven by GPIO+pins 12-23.+L0[n] L1[n] L2[n] L0[n+6] L1[n+6] L2[n+6]+| | | | | |+P12/P18 --<--------<--------<--------<--------<--------< (15)+| | | | | |+P13/P19 --<--------<--------<--------<--------<--------< (16)+| | | | | |+P14/P20 --<--------<--------<--------<--------<--------< (17)+| | | | | |+P15/P21 --<--------<--------<--------<--------<--------< (18)+| | | | | |+P16/P22 --<--------<--------<--------<--------<--------< (19)+| | | | | |+P17/P23 --<--------<--------<--------<--------<--------< (20)+(12) (13) (14) (21) (22) (23)+-Group C of 8 pairs of anti-parallel (or bi-color) LEDs. LED selection is+provided by GPIO pins 24-27 and 29-32, polarity selection by GPIO 28.+P24 P25 ... P30 P31+| | | |+LED POL --X-------X---/\/---X-------X (28)+(24) (25) ... (31) (32)++In bi-color scan mode, 72 LEDs are supported. These are grouped into four+output matrices:+-Group A of 12 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 0-11, polarity selection by GPIO 12.+-Group B of 6 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 23-28, polarity selection by GPIO 21.+-Group C of 6 pairs of anti-parallel LEDs. LED selection is provided+by GPIO pins 29-34, polarity selection by GPIO 22.+-Group of 4×6 single color LEDs. Rows are driven by GPIO pins 15-20,+columns by GPIO pins 13-14 and 21-22 (shared with groups B and C).+L2[n] L2[n+6] L2[n+12] L2[n+18]+| | | |++0 --<--------<---------<---------< (15)+| | | |++1 --<--------<---------<---------< (16)+| | | |++2 --<--------<---------<---------< (17)+| | | |++3 --<--------<---------<---------< (18)+| | | |++4 --<--------<---------<---------< (19)+| | | |++6 --<--------<---------<---------< (20)+(13) (14) (21) (22)++This node must always be a child of a 'realtek,rtl8231' node.++properties:+$nodename:+const:led-controller++compatible:+const:realtek,rtl8231-leds++"#address-cells":+const:2++"#size-cells":+const:0++realtek,led-scan-mode:+$ref:/schemas/types.yaml#/definitions/string+description:|+Specify the scanning mode the chip should run in. See general description+for how the scanning matrices are wired up.+enum:["single-color","bi-color"]++patternProperties:+"^led@":+description:|+LEDs are addressed by their port index and led index. Ports 0-23 always+support three LEDs. Additionally, but only when used in single color scan+mode, ports 24-31 support two LEDs.+type:object++properties:+reg:+items:+-description:port index+maximum:31+-description:led index+maximum:2++allOf:+-$ref:../leds/common.yaml#++required:+-reg++required:+-compatible+-"#address-cells"+-"#size-cells"+-realtek,led-scan-mode++additionalProperties:false++examples:+-|+#include <dt-bindings/leds/common.h>+led-controller {+compatible = "realtek,rtl8231-leds";+#address-cells = <2>;+#size-cells = <0>;++realtek,led-scan-mode = "single-color";++led@0,0 {+reg = <0 0>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@0,1 {+reg = <0 1>;+color = <LED_COLOR_ID_AMBER>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@0,2 {+reg = <0 2>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_STATUS;+};+};
Add a binding description for the Realtek RTL8231, a GPIO and LED
expander chip commonly used in ethernet switches based on a Realtek
switch SoC. These chips can be addressed via an MDIO or SMI bus, or used
as a plain 36-bit shift register.
This binding only describes the feature set provided by the MDIO/SMI
configuration, and covers the GPIO, PWM, and pin control properties. The
LED properties are defined in a separate binding.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
.../bindings/mfd/realtek,rtl8231.yaml | 190 ++++++++++++++++++
1 file changed, 190 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/realtek,rtl8231.yaml
@@ -0,0 +1,190 @@+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause+%YAML1.2+---+$id:http://devicetree.org/schemas/mfd/realtek,rtl8231.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Realtek RTL8231 GPIO and LED expander.++maintainers:+-Sander Vanheule <sander@svanheule.net>++description:|+The RTL8231 is a GPIO and LED expander chip, providing up to 37 GPIOs, up to+88 LEDs, and up to one PWM output. This device is frequently used alongside+Realtek switch SoCs, to provide additional I/O capabilities.++To manage the RTL8231's features, its strapping pins can be used to configure+it in one of three modes:shift register, MDIO device, or SMI device. The+shift register mode does not need special support. In MDIO or SMI mode, most+pins can be configured as a GPIO output, LED matrix scan line/column, or as a+PWM output.++The GPIO, PWM, and pin control are part of the main node. LED support is+configured as a sub-node.++properties:+compatible:+const:realtek,rtl8231++reg:+description:MDIO or SMI device address.+maxItems:1++# GPIO support+gpio-controller:true++"#gpio-cells":+const:2+description:|+The first cell is the pin number and the second cell is used to specify+the GPIO active state.++gpio-ranges:+description:|+Must reference itself, and provide a zero-based mapping for 37 pins.+maxItems:1++# Pin muxing and configuration+drive-strength:+description:|+Common drive strength used for all GPIO output pins, must be 4mA or 8mA.+On reset, this value will default to 8mA.+enum:[4,8]++# LED scanning matrix+led-controller:+$ref:../leds/realtek,rtl8231-leds.yaml#++# PWM output+"#pwm-cells":+description:|+Twos cells with PWM index (must be 0) and PWM frequency in Hz. To use+the PWM output, gpio35 must be muxed to its 'pwm' function. Valid+frequency values for consumers are 1200, 1600, 2000, 2400, 2800, 3200,+4000, and 4800.+const:2++patternProperties:+"-pins$":+type:object+$ref:../pinctrl/pinmux-node.yaml#++properties:+pins:+items:+enum:["gpio0","gpio1","gpio2","gpio3","gpio4","gpio5","gpio6",+"gpio7","gpio8","gpio9","gpio10","gpio11","gpio12","gpio13",+"gpio14","gpio15","gpio16","gpio17","gpio18","gpio19","gpio20",+"gpio21","gpio22","gpio23","gpio24","gpio25","gpio26","gpio27",+"gpio28","gpio29","gpio30","gpio31","gpio32","gpio33","gpio34",+"gpio35","gpio36"]+minItems:1+maxItems:37+function:+description:|+Select which function to use. "gpio" is supported for all pins, "led" is supported+for pins 0-34, "pwm" is supported for pin 35.+enum:["gpio","led","pwm"]++required:+-pins+-function++required:+-compatible+-reg+-gpio-controller+-"#gpio-cells"+-gpio-ranges++additionalProperties:false++examples:+-|+// Minimal example+mdio {+#address-cells = <1>;+#size-cells = <0>;++expander0:expander@0 {+compatible = "realtek,rtl8231";+reg = <0>;++gpio-controller;+#gpio-cells = <2>;+gpio-ranges = <&expander0 0 0 37>;+};+};+-|+// All bells and whistles included+#include <dt-bindings/leds/common.h>+mdio {+#address-cells = <1>;+#size-cells = <0>;++expander1:expander@1 {+compatible = "realtek,rtl8231";+reg = <1>;++gpio-controller;+#gpio-cells = <2>;+gpio-ranges = <&expander1 0 0 37>;++#pwm-cells = <2>;++drive-strength = <4>;++button-pins {+pins = "gpio36";+function = "gpio";+input-debounce = "100000";+};++pwm-pins {+pins = "gpio35";+function = "pwm";+};++led-pins {+pins = "gpio0", "gpio1", "gpio3", "gpio4";+function = "led";+};++led-controller {+compatible = "realtek,rtl8231-leds";+#address-cells = <2>;+#size-cells = <0>;++realtek,led-scan-mode = "single-color";++led@0,0 {+reg = <0 0>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@0,1 {+reg = <0 1>;+color = <LED_COLOR_ID_AMBER>;+function = LED_FUNCTION_LAN;+function-enumerator = <0>;+};++led@1,0 {+reg = <1 0>;+color = <LED_COLOR_ID_GREEN>;+function = LED_FUNCTION_LAN;+function-enumerator = <1>;+};++led@1,1 {+reg = <1 1>;+color = <LED_COLOR_ID_AMBER>;+function = LED_FUNCTION_LAN;+function-enumerator = <1>;+};+};+};+};
The RTL8231 is implemented as an MDIO device, and provides a regmap
interface for register access by the core and child devices.
The chip can also be a device on an SMI bus, an I2C-like bus by Realtek.
Since kernel support for SMI is limited, and no real-world SMI
implementations have been encountered for this device, this is currently
unimplemented. The use of the regmap interface should make any future
support relatively straightforward.
After reset, all pins are muxed to GPIO inputs before the pin drivers
are enabled. This is done to prevent accidental system resets, when a
pin is connected to the parent SoC's reset line.
[missing MDIO_BUS dependency, provided via REGMAP_MDIO]
Reported-by: kernel test robot <redacted>
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/mfd/Kconfig | 9 +++
drivers/mfd/Makefile | 1 +
drivers/mfd/rtl8231.c | 143 ++++++++++++++++++++++++++++++++++++
include/linux/mfd/rtl8231.h | 57 ++++++++++++++
4 files changed, 210 insertions(+)
create mode 100644 drivers/mfd/rtl8231.c
create mode 100644 include/linux/mfd/rtl8231.h
@@ -0,0 +1,143 @@+// SPDX-License-Identifier: GPL-2.0-only++#include<linux/bits.h>+#include<linux/bitfield.h>+#include<linux/delay.h>+#include<linux/gpio/consumer.h>+#include<linux/mfd/core.h>+#include<linux/mdio.h>+#include<linux/module.h>+#include<linux/platform_device.h>+#include<linux/property.h>+#include<linux/regmap.h>++#include<linux/mfd/rtl8231.h>++staticconststructreg_fieldRTL8231_FIELD_LED_START=REG_FIELD(RTL8231_REG_FUNC0,1,1);++staticconststructmfd_cellrtl8231_cells[]={+{+.name="rtl8231-pinctrl",+},+{+.name="rtl8231-leds",+.of_compatible="realtek,rtl8231-leds",+},+};++staticintrtl8231_init(structdevice*dev,structregmap*map)+{+unsignedintval;+interr;++err=regmap_read(map,RTL8231_REG_FUNC1,&val);+if(err){+dev_err(dev,"failed to read READY_CODE\n");+returnerr;+}++val=FIELD_GET(RTL8231_FUNC1_READY_CODE_MASK,val);+if(val!=RTL8231_FUNC1_READY_CODE_VALUE){+dev_err(dev,"RTL8231 not present or ready 0x%x != 0x%x\n",+val,RTL8231_FUNC1_READY_CODE_VALUE);+return-ENODEV;+}++/* SOFT_RESET bit self-clears when done */+regmap_update_bits(map,RTL8231_REG_PIN_HI_CFG,+RTL8231_PIN_HI_CFG_SOFT_RESET,RTL8231_PIN_HI_CFG_SOFT_RESET);+usleep_range(1000,10000);++/*+*ChipresetresultsinapinconfigurationthatisamixofLEDandGPIOoutputs.+*SelectGPIfunctionalityforallpinsbeforeenablingpinoutputs.+*/+regmap_write(map,RTL8231_REG_PIN_MODE0,0xffff);+regmap_write(map,RTL8231_REG_GPIO_DIR0,0xffff);+regmap_write(map,RTL8231_REG_PIN_MODE1,0xffff);+regmap_write(map,RTL8231_REG_GPIO_DIR1,0xffff);+regmap_write(map,RTL8231_REG_PIN_HI_CFG,+RTL8231_PIN_HI_CFG_MODE_MASK|RTL8231_PIN_HI_CFG_DIR_MASK);++return0;+}++staticconststructregmap_configrtl8231_mdio_regmap_config={+.val_bits=RTL8231_BITS_VAL,+.reg_bits=5,+.max_register=RTL8231_REG_COUNT-1,+.use_single_read=true,+.use_single_write=true,+.reg_format_endian=REGMAP_ENDIAN_BIG,+.val_format_endian=REGMAP_ENDIAN_BIG,+};++staticintrtl8231_mdio_probe(structmdio_device*mdiodev)+{+structdevice*dev=&mdiodev->dev;+structregmap_field*led_start;+structregmap*map;+interr;++map=devm_regmap_init_mdio(mdiodev,&rtl8231_mdio_regmap_config);+if(IS_ERR(map)){+dev_err(dev,"failed to init regmap\n");+returnPTR_ERR(map);+}++led_start=devm_regmap_field_alloc(dev,map,RTL8231_FIELD_LED_START);+if(IS_ERR(led_start))+returnPTR_ERR(led_start);++dev_set_drvdata(dev,led_start);++mdiodev->reset_gpio=devm_gpiod_get_optional(dev,"reset",GPIOD_OUT_LOW);+device_property_read_u32(dev,"reset-assert-delay",&mdiodev->reset_assert_delay);+device_property_read_u32(dev,"reset-deassert-delay",&mdiodev->reset_deassert_delay);++err=rtl8231_init(dev,map);+if(err)+returnerr;++/* LED_START enables power to output pins, and starts the LED engine */+regmap_field_write(led_start,1);++returndevm_mfd_add_devices(dev,PLATFORM_DEVID_AUTO,rtl8231_cells,+ARRAY_SIZE(rtl8231_cells),NULL,0,NULL);+}++__maybe_unusedstaticintrtl8231_suspend(structdevice*dev)+{+structregmap_field*led_start=dev_get_drvdata(dev);++returnregmap_field_write(led_start,0);+}++__maybe_unusedstaticintrtl8231_resume(structdevice*dev)+{+structregmap_field*led_start=dev_get_drvdata(dev);++returnregmap_field_write(led_start,1);+}++staticSIMPLE_DEV_PM_OPS(rtl8231_pm_ops,rtl8231_suspend,rtl8231_resume);++staticconststructof_device_idrtl8231_of_match[]={+{.compatible="realtek,rtl8231"},+{}+};+MODULE_DEVICE_TABLE(of,rtl8231_of_match);++staticstructmdio_driverrtl8231_mdio_driver={+.mdiodrv.driver={+.name="rtl8231-expander",+.of_match_table=rtl8231_of_match,+.pm=pm_ptr(&rtl8231_pm_ops),+},+.probe=rtl8231_mdio_probe,+};+mdio_module_driver(rtl8231_mdio_driver);++MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");+MODULE_DESCRIPTION("Realtek RTL8231 GPIO and LED expander");+MODULE_LICENSE("GPL v2");
This driver implements the GPIO and pin muxing features provided by the
RTL8231. The device should be instantiated as an MFD child, where the
parent device has already configured the regmap used for register
access.
Although described in the bindings, pin debouncing and drive strength
selection are currently not implemented. Debouncing is only available
for the six highest GPIOs, and must be emulated when other pins are used
for (button) inputs anyway.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/pinctrl/Kconfig | 11 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rtl8231.c | 398 ++++++++++++++++++++++++++++++
3 files changed, 410 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c
Both single and bi-color scanning modes are supported. The driver will
verify that the addresses are valid for the current mode, before
registering the LEDs. LEDs can be turned on, off, or toggled at one of
six predefined rates from 40ms to 1280ms.
Implements a platform device for use as a child device with RTL8231 MFD,
and uses the parent regmap to access the required registers.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
drivers/leds/Kconfig | 10 ++
drivers/leds/Makefile | 1 +
drivers/leds/leds-rtl8231.c | 291 ++++++++++++++++++++++++++++++++++++
3 files changed, 302 insertions(+)
create mode 100644 drivers/leds/leds-rtl8231.c
From: Andy Shevchenko <hidden> Date: 2021-05-24 07:50:02
On Mon, May 24, 2021 at 12:28 AM Sander Vanheule [off-list ref] wrote:
On Tue, 2021-05-18 at 00:18 +0300, Andy Shevchenko wrote:
quoted
On Mon, May 17, 2021 at 10:28 PM Sander Vanheule [off-list ref] wrote:
quoted
The RTL8231 is implemented as an MDIO device, and provides a regmap
interface for register access by the core and child devices.
The chip can also be a device on an SMI bus, an I2C-like bus by Realtek.
Since kernel support for SMI is limited, and no real-world SMI
implementations have been encountered for this device, this is currently
unimplemented. The use of the regmap interface should make any future
support relatively straightforward.
After reset, all pins are muxed to GPIO inputs before the pin drivers
are enabled. This is done to prevent accidental system resets, when a
pin is connected to the parent SoC's reset line.
quoted
[missing MDIO_BUS dependency, provided via REGMAP_MDIO]
Reported-by: kernel test robot <redacted>
What is the culprit? Shouldn't this have a Fixes tag?
But it doesn't actually fix an issue created by an existing commit, just
something that was wrong in the first version of the patch.
Then why is it in the tag block?
If you want to give a credit to LKP, do it in the comments block
(after '---' cutter line).
This patch is not
dedicated to fixing that single issue though, it's just a part of it. Hence the
note above the Reported-by tag.
If we got an error why we need a read_core, what for?
The chip has a static 5-bit field in register 0x01, called READY_CODE according
to the datasheet. If a device is present, and a read from register 0x01
succeeds, I still check that this field has the correct value. For the RTL8231,
it should return 0x37. If this isn't the case, I assume this isn't an RTL8231,
so the driver probe stops and returns an error value.
Best,
Sander
If we got an error why we need a read_core, what for?
The chip has a static 5-bit field in register 0x01, called READY_CODE according
to the datasheet. If a device is present, and a read from register 0x01
succeeds, I still check that this field has the correct value. For the RTL8231,
it should return 0x37. If this isn't the case, I assume this isn't an RTL8231,
so the driver probe stops and returns an error value.
Right. And why do you get ready_code if you know that there is an error?
--
With Best Regards,
Andy Shevchenko
From: Andy Shevchenko <hidden> Date: 2021-05-24 08:02:39
On Mon, May 24, 2021 at 1:34 AM Sander Vanheule [off-list ref] wrote:
The RTL8231 is implemented as an MDIO device, and provides a regmap
interface for register access by the core and child devices.
The chip can also be a device on an SMI bus, an I2C-like bus by Realtek.
Since kernel support for SMI is limited, and no real-world SMI
implementations have been encountered for this device, this is currently
unimplemented. The use of the regmap interface should make any future
support relatively straightforward.
After reset, all pins are muxed to GPIO inputs before the pin drivers
are enabled. This is done to prevent accidental system resets, when a
pin is connected to the parent SoC's reset line.
...
[missing MDIO_BUS dependency, provided via REGMAP_MDIO]
Reported-by: kernel test robot <redacted>
What does this fix? Shouldn't it have a Fixes tag? (Yes, I know that
you answered in the other email, but here is a hint: before settling
these kinds of things do not send a new version. Instead of speeding
up the review you are closer to the chance to have this been not
applied for v5.14 at all)
...
quoted hunk
+ /* SOFT_RESET bit self-clears when done */+ regmap_update_bits(map, RTL8231_REG_PIN_HI_CFG,+ RTL8231_PIN_HI_CFG_SOFT_RESET, RTL8231_PIN_HI_CFG_SOFT_RESET);
+ usleep_range(1000, 10000);
It's strange to see this big range of minimum and maximum sleep.
Usually the ratio should not be bigger than ~3-4 between the values.
...
If we got an error why we need a read_core, what for?
The chip has a static 5-bit field in register 0x01, called READY_CODE
according
to the datasheet. If a device is present, and a read from register 0x01
succeeds, I still check that this field has the correct value. For the
RTL8231,
it should return 0x37. If this isn't the case, I assume this isn't an
RTL8231,
so the driver probe stops and returns an error value.
Right. And why do you get ready_code if you know that there is an error?
This has changed in v3. I now check if there was an error reading the register,
and return if there was. Only if there wasn't an error, the code continues to
extract and verify the READY_CODE value.
Best,
Sander
Hi Andy,
On Mon, 2021-05-24 at 11:02 +0300, Andy Shevchenko wrote:
On Mon, May 24, 2021 at 1:34 AM Sander Vanheule [off-list ref] wrote:
quoted
The RTL8231 is implemented as an MDIO device, and provides a regmap
interface for register access by the core and child devices.
The chip can also be a device on an SMI bus, an I2C-like bus by Realtek.
Since kernel support for SMI is limited, and no real-world SMI
implementations have been encountered for this device, this is currently
unimplemented. The use of the regmap interface should make any future
support relatively straightforward.
After reset, all pins are muxed to GPIO inputs before the pin drivers
are enabled. This is done to prevent accidental system resets, when a
pin is connected to the parent SoC's reset line.
...
quoted
[missing MDIO_BUS dependency, provided via REGMAP_MDIO]
Reported-by: kernel test robot <redacted>
What does this fix? Shouldn't it have a Fixes tag? (Yes, I know that
you answered in the other email, but here is a hint: before settling
these kinds of things do not send a new version. Instead of speeding
up the review you are closer to the chance to have this been not
applied for v5.14 at all)
I'll drop this from the commit message, if this isn't appropriate without a
Fixes-tag (which I can't provide anyway).
...
quoted
+ /* SOFT_RESET bit self-clears when done */
+ regmap_update_bits(map, RTL8231_REG_PIN_HI_CFG,
+ RTL8231_PIN_HI_CFG_SOFT_RESET,
RTL8231_PIN_HI_CFG_SOFT_RESET);
quoted
+ usleep_range(1000, 10000);
It's strange to see this big range of minimum and maximum sleep.
Usually the ratio should not be bigger than ~3-4 between the values.
I could also change this from a usleep to a polling loop that checks (with a
loop limit) if the reset bit has self-cleared already.
The datasheet that I have doesn't mention how fast it should self-clear. So I
checked, and it appears to be done after one loop iteration already. So,
certainly faster than the current usleep.
Would a polling loop (with maybe like max. 10 iterations) be a good alternative
for you?
From: Andy Shevchenko <hidden> Date: 2021-05-24 10:19:16
On Mon, May 24, 2021 at 11:23 AM Sander Vanheule [off-list ref] wrote:
On Mon, 2021-05-24 at 11:02 +0300, Andy Shevchenko wrote:
quoted
On Mon, May 24, 2021 at 1:34 AM Sander Vanheule [off-list ref] wrote:
...
quoted
quoted
+ usleep_range(1000, 10000);
It's strange to see this big range of minimum and maximum sleep.
Usually the ratio should not be bigger than ~3-4 between the values.
I could also change this from a usleep to a polling loop that checks (with a
loop limit) if the reset bit has self-cleared already.
The datasheet that I have doesn't mention how fast it should self-clear. So I
checked, and it appears to be done after one loop iteration already. So,
certainly faster than the current usleep.
Would a polling loop (with maybe like max. 10 iterations) be a good alternative
for you?
I guess it's the right way to go. Just check the iopoll.h for helpers.
Also regmap has regmap_read_poll_timeout().
--
With Best Regards,
Andy Shevchenko
From: Andy Shevchenko <hidden> Date: 2021-05-24 10:24:54
On Mon, May 24, 2021 at 1:34 AM Sander Vanheule [off-list ref] wrote:
Both single and bi-color scanning modes are supported. The driver will
verify that the addresses are valid for the current mode, before
registering the LEDs. LEDs can be turned on, off, or toggled at one of
six predefined rates from 40ms to 1280ms.
Implements a platform device for use as a child device with RTL8231 MFD,
and uses the parent regmap to access the required registers.
...
+ This options enables support for using the LED scanning matrix output
option
+ of the RTL8231 GPIO and LED expander chip.
+ When built as a module, this module will be named leds-rtl8231.
...
+ interval_ms = 500;
Does this deserve a #define?
...
quoted hunk
+ ret = fwnode_property_count_u32(fwnode, "reg");+ if (ret < 0)+ return ret;+ if (ret != 2)+ return -ENODEV;
I would say -EINVAL, but -ENODEV is similarly okay.
...
+ int err;
ret or err? Be consistent across a single driver.
...
On Mon, 2021-05-24 at 13:18 +0300, Andy Shevchenko wrote:
On Mon, May 24, 2021 at 11:23 AM Sander Vanheule [off-list ref] wrote:
quoted
On Mon, 2021-05-24 at 11:02 +0300, Andy Shevchenko wrote:
quoted
On Mon, May 24, 2021 at 1:34 AM Sander Vanheule [off-list ref]
wrote:
...
quoted
quoted
quoted
+ usleep_range(1000, 10000);
It's strange to see this big range of minimum and maximum sleep.
Usually the ratio should not be bigger than ~3-4 between the values.
I could also change this from a usleep to a polling loop that checks (with a
loop limit) if the reset bit has self-cleared already.
The datasheet that I have doesn't mention how fast it should self-clear. So
I
checked, and it appears to be done after one loop iteration already. So,
certainly faster than the current usleep.
Would a polling loop (with maybe like max. 10 iterations) be a good
alternative
for you?
I guess it's the right way to go. Just check the iopoll.h for helpers.
Also regmap has regmap_read_poll_timeout().
Thanks for the pointers. Replaced the usleep by regmap_read_poll_timeout.
Best,
Sander
- Introduce GPIO regmap quirks to set output direction first
I thought you had determined it was possible to set output before
direction?
Same thoughts when I saw an updated version of that patch. My
anticipation was to not see it at all.
The two devices I've been trying to test the behaviour on are:
* Netgear GS110TPP: has an RTL8231 with three LEDs, each driven via a pin
configured as (active-low) GPIO. The LEDs are easy for a quick visual check.
* Zyxel GS1900-8: RTL8231 used for the front panel button, and an active-low
GPIO used to hard reset the main SoC (an RTL8380). I've modified this board
to change some of the strapping pin values, but testing with the jumpers and
pull-up/down resistors is a bit more tedious.
On the Netgear, I tested the following with and without the quirk:
# Set as OUT-LOW twice, to avoid the quirk. Always turns the LED on
gpioset 1 32=0; gpioset 1 32=0
# Get value to change to input, turns the LED off (high impedance)
# Will return 1 due to (weak) internal pull-up
gpioget 1 32
# Set as OUT-HIGH, should result in LED off
# When the quirk is disabled, the LED turns on (i.e. old OUT-LOW value)
# When the quirk is enabled, the LED remains off (i.e. correct OUT-HIGH value)
gpioset 1 32=1
Now, what's confusing (to me) is that the inverse doesn't depend on the quirk:
# Set as OUT-HIGH twice
gpioset 1 32=1; gpioset 1 32=1
# Change to high-Z
gpioget 1 32
# Set to OUT-LOW, always results in LED on, with or without quirk
gpioset 1 32=0
Any idea why this would be (or appear) broken on the former case, but not on the
latter?
I was trying to reproduce this behaviour on the Zyxel, but using the strapping
pins that are also used to configure the device's address. So perhaps the pull-
ups/-downs were confusing the results. Using a separate pin on the Zyxel's
RTL8231, I've now been able to confirm the same behaviour as on the Netgear,
including capturing the resulting glitch (with my simple logic analyser) when
enabling the quirk in the first test case.
I hope this explains why I've still included the quirk in this revision. If not,
please let me know what isn't clear.
Best,
Sander
On Mon, 2021-05-24 at 13:24 +0300, Andy Shevchenko wrote:
On Mon, May 24, 2021 at 1:34 AM Sander Vanheule [off-list ref] wrote:
quoted
Both single and bi-color scanning modes are supported. The driver will
verify that the addresses are valid for the current mode, before
registering the LEDs. LEDs can be turned on, off, or toggled at one of
six predefined rates from 40ms to 1280ms.
Implements a platform device for use as a child device with RTL8231 MFD,
and uses the parent regmap to access the required registers.
...
quoted
+ This options enables support for using the LED scanning matrix
output
option
Fixed.
quoted
+ of the RTL8231 GPIO and LED expander chip.
+ When built as a module, this module will be named leds-rtl8231.
...
quoted
+ interval_ms = 500;
Does this deserve a #define?
Fine by me. Doesn't make a difference for the binary anyway, but it helps
document the code a bit.
...
quoted
+ ret = fwnode_property_count_u32(fwnode, "reg");+ if (ret < 0)+ return ret;+ if (ret != 2)+ return -ENODEV;
I would say -EINVAL, but -ENODEV is similarly okay.
Any specific reason you think EINVAL is more appropriate than ENODEV?
...
quoted
+ int err;
ret or err? Be consistent across a single driver.
I had first used 'err' for both fwnode_property_count_u32() and
fwnode_property_read_u32_array(). The former returns "actual count or error
code", while the latter is only "error code". And I found it weird to read the
code as "does error code equal 2", if I used 'err' as variable name.
I've split this up:
* addr_count for fwnode_property_count_u32's result
* err for fwnode_property_read_u32_array's result
Since addr_count is only used before err is touched, I guess the compiler will
optimize this out anyway?
Best,
Sander
From: Andy Shevchenko <hidden> Date: 2021-05-24 12:48:07
On Mon, May 24, 2021 at 3:04 PM Sander Vanheule [off-list ref] wrote:
On Mon, 2021-05-24 at 13:24 +0300, Andy Shevchenko wrote:
quoted
On Mon, May 24, 2021 at 1:34 AM Sander Vanheule [off-list ref] wrote:
...
quoted
quoted
+ if (ret != 2)
+ return -ENODEV;
I would say -EINVAL, but -ENODEV is similarly okay.
Any specific reason you think EINVAL is more appropriate than ENODEV?
My logic is that the initial values (from resource provider) are incorrect.
But as I said, I'm fine with either.
...
quoted
quoted
+ int err;
ret or err? Be consistent across a single driver.
I had first used 'err' for both fwnode_property_count_u32() and
fwnode_property_read_u32_array(). The former returns "actual count or error
code", while the latter is only "error code". And I found it weird to read the
code as "does error code equal 2", if I used 'err' as variable name.
I've split this up:
* addr_count for fwnode_property_count_u32's result
* err for fwnode_property_read_u32_array's result
Since addr_count is only used before err is touched, I guess the compiler will
optimize this out anyway?
Usually we do this pattern (and it seems you missed the point, name of
variable is ret in some functions and err in the rest):
err /* ret */ = foo();
if (err < 0)
return err;
count = err;
--
With Best Regards,
Andy Shevchenko
Also works but you have to provide this information in the cover letter.
...
quoted
quoted
quoted
- Introduce GPIO regmap quirks to set output direction first
I thought you had determined it was possible to set output before
direction?
Same thoughts when I saw an updated version of that patch. My
anticipation was to not see it at all.
The two devices I've been trying to test the behaviour on are:
* Netgear GS110TPP: has an RTL8231 with three LEDs, each driven via a pin
configured as (active-low) GPIO. The LEDs are easy for a quick visual check.
* Zyxel GS1900-8: RTL8231 used for the front panel button, and an active-low
GPIO used to hard reset the main SoC (an RTL8380). I've modified this board
to change some of the strapping pin values, but testing with the jumpers and
pull-up/down resistors is a bit more tedious.
On the Netgear, I tested the following with and without the quirk:
# Set as OUT-LOW twice, to avoid the quirk. Always turns the LED on
gpioset 1 32=0; gpioset 1 32=0
# Get value to change to input, turns the LED off (high impedance)
# Will return 1 due to (weak) internal pull-up
gpioget 1 32
# Set as OUT-HIGH, should result in LED off
# When the quirk is disabled, the LED turns on (i.e. old OUT-LOW value)
# When the quirk is enabled, the LED remains off (i.e. correct OUT-HIGH value)
gpioset 1 32=1
Now, what's confusing (to me) is that the inverse doesn't depend on the quirk:
# Set as OUT-HIGH twice
gpioset 1 32=1; gpioset 1 32=1
# Change to high-Z
gpioget 1 32
# Set to OUT-LOW, always results in LED on, with or without quirk
gpioset 1 32=0
Any idea why this would be (or appear) broken on the former case, but not on the
latter?
GPIO tools for the shell are context-less. Can you reproduce this with
the legacy sysfs interface?
I was trying to reproduce this behaviour on the Zyxel, but using the strapping
pins that are also used to configure the device's address. So perhaps the pull-
ups/-downs were confusing the results. Using a separate pin on the Zyxel's
RTL8231, I've now been able to confirm the same behaviour as on the Netgear,
including capturing the resulting glitch (with my simple logic analyser) when
enabling the quirk in the first test case.
I hope this explains why I've still included the quirk in this revision. If not,
please let me know what isn't clear.
Do you possess a schematic of either of the devices and a link to the
RTL datasheet (Btw, if it's publicly available, or you have a link
that will ask for necessary sign-in it would be nice to include the
link to it as a Datasheet: tag)?
--
With Best Regards,
Andy Shevchenko
On Mon, 2021-05-24 at 15:47 +0300, Andy Shevchenko wrote:
On Mon, May 24, 2021 at 3:04 PM Sander Vanheule [off-list ref] wrote:
quoted
On Mon, 2021-05-24 at 13:24 +0300, Andy Shevchenko wrote:
quoted
On Mon, May 24, 2021 at 1:34 AM Sander Vanheule [off-list ref]
wrote:
...
quoted
quoted
quoted
+ if (ret != 2)
+ return -ENODEV;
I would say -EINVAL, but -ENODEV is similarly okay.
Any specific reason you think EINVAL is more appropriate than ENODEV?
My logic is that the initial values (from resource provider) are incorrect.
But as I said, I'm fine with either.
Ok, that makes sense. Actually, I'm already using "address invalid" in the error
messages when reading the address fails, so I'll change to EINVAL for
consistency.
quoted
quoted
quoted
+ int err;
ret or err? Be consistent across a single driver.
I had first used 'err' for both fwnode_property_count_u32() and
fwnode_property_read_u32_array(). The former returns "actual count or error
code", while the latter is only "error code". And I found it weird to read
the
code as "does error code equal 2", if I used 'err' as variable name.
I've split this up:
* addr_count for fwnode_property_count_u32's result
* err for fwnode_property_read_u32_array's result
Since addr_count is only used before err is touched, I guess the compiler
will
optimize this out anyway?
Usually we do this pattern (and it seems you missed the point, name of
variable is ret in some functions and err in the rest):
err /* ret */ = foo();
if (err < 0)
return err;
count = err;
I had only used 'ret' specifically in this one function, because I didn't like
"if (err != 2)" (and I apparently decided that I disliked that more than the
inconsistency introduced by using 'ret'). I'll stick to calling the variable
'err', and change the clause to (err != ARRAY_SIZE(addr)) to make it more
obvious that 2 isn't just some random return value.
Best,
Sander
Also works but you have to provide this information in the cover letter.
Ok, I will add the link to the cover letter for the next version. Does it need
to be in a Link-tag, or can just be a reference?
...
quoted
quoted
quoted
quoted
- Introduce GPIO regmap quirks to set output direction first
I thought you had determined it was possible to set output before
direction?
Same thoughts when I saw an updated version of that patch. My
anticipation was to not see it at all.
The two devices I've been trying to test the behaviour on are:
* Netgear GS110TPP: has an RTL8231 with three LEDs, each driven via a pin
configured as (active-low) GPIO. The LEDs are easy for a quick visual
check.
* Zyxel GS1900-8: RTL8231 used for the front panel button, and an active-
low
GPIO used to hard reset the main SoC (an RTL8380). I've modified this
board
to change some of the strapping pin values, but testing with the jumpers
and
pull-up/down resistors is a bit more tedious.
On the Netgear, I tested the following with and without the quirk:
# Set as OUT-LOW twice, to avoid the quirk. Always turns the LED on
gpioset 1 32=0; gpioset 1 32=0
# Get value to change to input, turns the LED off (high impedance)
# Will return 1 due to (weak) internal pull-up
gpioget 1 32
# Set as OUT-HIGH, should result in LED off
# When the quirk is disabled, the LED turns on (i.e. old OUT-LOW value)
# When the quirk is enabled, the LED remains off (i.e. correct OUT-HIGH
value)
gpioset 1 32=1
Now, what's confusing (to me) is that the inverse doesn't depend on the
quirk:
# Set as OUT-HIGH twice
gpioset 1 32=1; gpioset 1 32=1
# Change to high-Z
gpioget 1 32
# Set to OUT-LOW, always results in LED on, with or without quirk
gpioset 1 32=0
Any idea why this would be (or appear) broken on the former case, but not on
the
latter?
GPIO tools for the shell are context-less. Can you reproduce this with
the legacy sysfs interface?
quoted
I was trying to reproduce this behaviour on the Zyxel, but using the
strapping
pins that are also used to configure the device's address. So perhaps the
pull-
ups/-downs were confusing the results. Using a separate pin on the Zyxel's
RTL8231, I've now been able to confirm the same behaviour as on the Netgear,
including capturing the resulting glitch (with my simple logic analyser)
when
enabling the quirk in the first test case.
I hope this explains why I've still included the quirk in this revision. If
not,
please let me know what isn't clear.
Do you possess a schematic of either of the devices and a link to the
RTL datasheet (Btw, if it's publicly available, or you have a link
that will ask for necessary sign-in it would be nice to include the
link to it as a Datasheet: tag)?
Sadly, I don't. Most of the info we have comes from code archives of switch
vendors (Zyxel, Cisco etc). Boards need to be reverse engineered, and the few
leaked datasheets that can be found on the internet aren't exactly thick in
information.
The RTL8231 datasheet is actually quite useful, but makes no mention of the
output value isse. Since this isn't an official resource, I don't think it would
be appropriate to link it via a Datasheet: tag.
https://github.com/libc0607/Realtek_switch_hacking/blob/files/RTL8231_Datasheet_
1.2.pdf
Looking at the datasheet again, I came up with a... terrible hack to work around
the output value issue.
The chip also has GPIO_INVERT registers that I hadn't used until now, because
the logical inversion is handled in the kernel. However, these inversion
registers only apply to the output values. So, I could implement glitch-free
output behaviour in the following way:
* After chip reset, and before enabling the output driver (MFD initialisation):
- Mux all pins as GPIO
- Change all pins to outputs, so the data registers (0x1c-0x1e) become writable
- Write value 0 to all pins
- Change all pins to GPI to change them into high-Z
* In the pinctrl/gpio driver:
- Use data registers as input-only
- Use inversion register to determine output value (can be written any time)
The above gives glitch-free outputs, but the values that are read back (when
configured as output), come from the data registers. They should now be coming
from the inversion (reg_set_base) registers, but the code prefers to use the
data registers (reg_dat_base).
Best,
Sander
Hi Andy,
Forgot to reply to the sysfs suggestion.
On Mon, 2021-05-24 at 15:54 +0300, Andy Shevchenko wrote:
On Mon, May 24, 2021 at 2:41 PM Sander Vanheule [off-list ref] wrote:
quoted
On Mon, 2021-05-24 at 10:53 +0300, Andy Shevchenko wrote:
quoted
On Mon, May 24, 2021 at 4:11 AM Andrew Lunn [off-list ref] wrote:
quoted
quoted
quoted
quoted
- Introduce GPIO regmap quirks to set output direction first
I thought you had determined it was possible to set output before
direction?
Same thoughts when I saw an updated version of that patch. My
anticipation was to not see it at all.
The two devices I've been trying to test the behaviour on are:
* Netgear GS110TPP: has an RTL8231 with three LEDs, each driven via a pin
configured as (active-low) GPIO. The LEDs are easy for a quick visual
check.
* Zyxel GS1900-8: RTL8231 used for the front panel button, and an active-
low
GPIO used to hard reset the main SoC (an RTL8380). I've modified this
board
to change some of the strapping pin values, but testing with the jumpers
and
pull-up/down resistors is a bit more tedious.
On the Netgear, I tested the following with and without the quirk:
# Set as OUT-LOW twice, to avoid the quirk. Always turns the LED on
gpioset 1 32=0; gpioset 1 32=0
# Get value to change to input, turns the LED off (high impedance)
# Will return 1 due to (weak) internal pull-up
gpioget 1 32
# Set as OUT-HIGH, should result in LED off
# When the quirk is disabled, the LED turns on (i.e. old OUT-LOW value)
# When the quirk is enabled, the LED remains off (i.e. correct OUT-HIGH
value)
gpioset 1 32=1
Now, what's confusing (to me) is that the inverse doesn't depend on the
quirk:
# Set as OUT-HIGH twice
gpioset 1 32=1; gpioset 1 32=1
# Change to high-Z
gpioget 1 32
# Set to OUT-LOW, always results in LED on, with or without quirk
gpioset 1 32=0
Any idea why this would be (or appear) broken on the former case, but not on
the
latter?
GPIO tools for the shell are context-less. Can you reproduce this with
the legacy sysfs interface?
Using the sysfs interface produced the same behaviour for both test cases.
E.g. case 1:
# Set to output low
echo out > direction; echo 0 > value
# Change to input (with weak pull-up)
echo in > direction
# Try to set to output high
# Fails to go high if the pin value is set before the direction
echo high > direction
Best,
Sander
From: Andy Shevchenko <hidden> Date: 2021-05-24 16:30:28
On Mon, May 24, 2021 at 6:03 PM Sander Vanheule [off-list ref] wrote:
On Mon, 2021-05-24 at 15:54 +0300, Andy Shevchenko wrote:
quoted
On Mon, May 24, 2021 at 2:41 PM Sander Vanheule [off-list ref] wrote:
quoted
On Mon, 2021-05-24 at 10:53 +0300, Andy Shevchenko wrote:
quoted
On Mon, May 24, 2021 at 4:11 AM Andrew Lunn [off-list ref] wrote:
...
Ok, I will add the link to the cover letter for the next version. Does it need
to be in a Link-tag, or can just be a reference?
Some kind of reference, no need to have a special tag in the cover letter.
...
quoted
quoted
quoted
quoted
quoted
- Introduce GPIO regmap quirks to set output direction first
I thought you had determined it was possible to set output before
direction?
Same thoughts when I saw an updated version of that patch. My
anticipation was to not see it at all.
The two devices I've been trying to test the behaviour on are:
* Netgear GS110TPP: has an RTL8231 with three LEDs, each driven via a pin
configured as (active-low) GPIO. The LEDs are easy for a quick visual
check.
* Zyxel GS1900-8: RTL8231 used for the front panel button, and an active-
low
GPIO used to hard reset the main SoC (an RTL8380). I've modified this
board
to change some of the strapping pin values, but testing with the jumpers
and
pull-up/down resistors is a bit more tedious.
On the Netgear, I tested the following with and without the quirk:
# Set as OUT-LOW twice, to avoid the quirk. Always turns the LED on
gpioset 1 32=0; gpioset 1 32=0
# Get value to change to input, turns the LED off (high impedance)
# Will return 1 due to (weak) internal pull-up
gpioget 1 32
# Set as OUT-HIGH, should result in LED off
# When the quirk is disabled, the LED turns on (i.e. old OUT-LOW value)
# When the quirk is enabled, the LED remains off (i.e. correct OUT-HIGH
value)
gpioset 1 32=1
Now, what's confusing (to me) is that the inverse doesn't depend on the
quirk:
# Set as OUT-HIGH twice
gpioset 1 32=1; gpioset 1 32=1
# Change to high-Z
gpioget 1 32
# Set to OUT-LOW, always results in LED on, with or without quirk
gpioset 1 32=0
Any idea why this would be (or appear) broken on the former case, but not on
the
latter?
GPIO tools for the shell are context-less. Can you reproduce this with
the legacy sysfs interface?
quoted
I was trying to reproduce this behaviour on the Zyxel, but using the
strapping
pins that are also used to configure the device's address. So perhaps the
pull-
ups/-downs were confusing the results. Using a separate pin on the Zyxel's
RTL8231, I've now been able to confirm the same behaviour as on the Netgear,
including capturing the resulting glitch (with my simple logic analyser)
when
enabling the quirk in the first test case.
I hope this explains why I've still included the quirk in this revision. If
not,
please let me know what isn't clear.
Do you possess a schematic of either of the devices and a link to the
RTL datasheet (Btw, if it's publicly available, or you have a link
that will ask for necessary sign-in it would be nice to include the
link to it as a Datasheet: tag)?
Sadly, I don't. Most of the info we have comes from code archives of switch
vendors (Zyxel, Cisco etc). Boards need to be reverse engineered, and the few
leaked datasheets that can be found on the internet aren't exactly thick in
information.
The RTL8231 datasheet is actually quite useful, but makes no mention of the
output value isse. Since this isn't an official resource, I don't think it would
be appropriate to link it via a Datasheet: tag.
https://github.com/libc0607/Realtek_switch_hacking/blob/files/RTL8231_Datasheet_
1.2.pdf
Looking at the datasheet again, I came up with a... terrible hack to work around
the output value issue.
The chip also has GPIO_INVERT registers that I hadn't used until now, because
the logical inversion is handled in the kernel. However, these inversion
registers only apply to the output values. So, I could implement glitch-free
output behaviour in the following way:
* After chip reset, and before enabling the output driver (MFD initialisation):
- Mux all pins as GPIO
- Change all pins to outputs,
No. no, no. This is much worse than the glitches. You never know what
the hardware is connected there and it's potential breakage (on hw
level) possible.
so the data registers (0x1c-0x1e) become writable
- Write value 0 to all pins
- Change all pins to GPI to change them into high-Z
* In the pinctrl/gpio driver:
- Use data registers as input-only
- Use inversion register to determine output value (can be written any time)
The above gives glitch-free outputs, but the values that are read back (when
configured as output), come from the data registers. They should now be coming
from the inversion (reg_set_base) registers, but the code prefers to use the
data registers (reg_dat_base).
Lemme read the datasheet and see if I find any clue for the hw behaviour.
--
With Best Regards,
Andy Shevchenko
From: Andy Shevchenko <hidden> Date: 2021-05-25 17:12:09
On Mon, May 24, 2021 at 7:30 PM Andy Shevchenko
[off-list ref] wrote:
On Mon, May 24, 2021 at 6:03 PM Sander Vanheule [off-list ref] wrote:
quoted
On Mon, 2021-05-24 at 15:54 +0300, Andy Shevchenko wrote:
...
quoted
Sadly, I don't. Most of the info we have comes from code archives of switch
vendors (Zyxel, Cisco etc). Boards need to be reverse engineered, and the few
leaked datasheets that can be found on the internet aren't exactly thick in
information.
The RTL8231 datasheet is actually quite useful, but makes no mention of the
output value isse. Since this isn't an official resource, I don't think it would
be appropriate to link it via a Datasheet: tag.
https://github.com/libc0607/Realtek_switch_hacking/blob/files/RTL8231_Datasheet_
1.2.pdf
Looking at the datasheet again, I came up with a... terrible hack to work around
the output value issue.
The chip also has GPIO_INVERT registers that I hadn't used until now, because
the logical inversion is handled in the kernel. However, these inversion
registers only apply to the output values. So, I could implement glitch-free
output behaviour in the following way:
* After chip reset, and before enabling the output driver (MFD initialisation):
- Mux all pins as GPIO
- Change all pins to outputs,
No. no, no. This is much worse than the glitches. You never know what
the hardware is connected there and it's potential breakage (on hw
level) possible.
quoted
so the data registers (0x1c-0x1e) become writable
- Write value 0 to all pins
- Change all pins to GPI to change them into high-Z
* In the pinctrl/gpio driver:
- Use data registers as input-only
- Use inversion register to determine output value (can be written any time)
The above gives glitch-free outputs, but the values that are read back (when
configured as output), come from the data registers. They should now be coming
from the inversion (reg_set_base) registers, but the code prefers to use the
data registers (reg_dat_base).
Lemme read the datasheet and see if I find any clue for the hw behaviour.
Thank you for your patience!
Have you explored the possibility of using En_Sync_GPIO?
--
With Best Regards,
Andy Shevchenko
On Tue, 2021-05-25 at 20:11 +0300, Andy Shevchenko wrote:
On Mon, May 24, 2021 at 7:30 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Mon, May 24, 2021 at 6:03 PM Sander Vanheule [off-list ref]
wrote:
quoted
On Mon, 2021-05-24 at 15:54 +0300, Andy Shevchenko wrote:
...
quoted
quoted
Sadly, I don't. Most of the info we have comes from code archives of
switch
vendors (Zyxel, Cisco etc). Boards need to be reverse engineered, and the
few
leaked datasheets that can be found on the internet aren't exactly thick
in
information.
The RTL8231 datasheet is actually quite useful, but makes no mention of
the
output value isse. Since this isn't an official resource, I don't think it
would
be appropriate to link it via a Datasheet: tag.
https://github.com/libc0607/Realtek_switch_hacking/blob/files/RTL8231_Datasheet_
1.2.pdf
Looking at the datasheet again, I came up with a... terrible hack to work
around
the output value issue.
The chip also has GPIO_INVERT registers that I hadn't used until now,
because
the logical inversion is handled in the kernel. However, these inversion
registers only apply to the output values. So, I could implement glitch-
free
output behaviour in the following way:
* After chip reset, and before enabling the output driver (MFD
initialisation):
- Mux all pins as GPIO
- Change all pins to outputs,
No. no, no. This is much worse than the glitches. You never know what
the hardware is connected there and it's potential breakage (on hw
level) possible.
quoted
so the data registers (0x1c-0x1e) become writable
- Write value 0 to all pins
- Change all pins to GPI to change them into high-Z
* In the pinctrl/gpio driver:
- Use data registers as input-only
- Use inversion register to determine output value (can be written any
time)
The above gives glitch-free outputs, but the values that are read back
(when
configured as output), come from the data registers. They should now be
coming
from the inversion (reg_set_base) registers, but the code prefers to use
the
data registers (reg_dat_base).
Lemme read the datasheet and see if I find any clue for the hw behaviour.
Thank you for your patience!
Have you explored the possibility of using En_Sync_GPIO?
I haven't (output latching doesn't really appear to be a thing in the gpio
framework?), but I did notice that the main SoC's RTL8231 integration uses it.
Let me play around with it to see if it also latches the pin direction, or if
that's always an immediate change.
Best,
Sander
On Tue, 2021-05-25 at 20:11 +0300, Andy Shevchenko wrote:
On Mon, May 24, 2021 at 7:30 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Mon, May 24, 2021 at 6:03 PM Sander Vanheule [off-list ref]
wrote:
quoted
On Mon, 2021-05-24 at 15:54 +0300, Andy Shevchenko wrote:
...
quoted
quoted
Sadly, I don't. Most of the info we have comes from code archives of
switch
vendors (Zyxel, Cisco etc). Boards need to be reverse engineered, and the
few
leaked datasheets that can be found on the internet aren't exactly thick
in
information.
The RTL8231 datasheet is actually quite useful, but makes no mention of
the
output value isse. Since this isn't an official resource, I don't think it
would
be appropriate to link it via a Datasheet: tag.
https://github.com/libc0607/Realtek_switch_hacking/blob/files/RTL8231_Datasheet_
1.2.pdf
Looking at the datasheet again, I came up with a... terrible hack to work
around
the output value issue.
The chip also has GPIO_INVERT registers that I hadn't used until now,
because
the logical inversion is handled in the kernel. However, these inversion
registers only apply to the output values. So, I could implement glitch-
free
output behaviour in the following way:
* After chip reset, and before enabling the output driver (MFD
initialisation):
- Mux all pins as GPIO
- Change all pins to outputs,
No. no, no. This is much worse than the glitches. You never know what
the hardware is connected there and it's potential breakage (on hw
level) possible.
quoted
so the data registers (0x1c-0x1e) become writable
- Write value 0 to all pins
- Change all pins to GPI to change them into high-Z
* In the pinctrl/gpio driver:
- Use data registers as input-only
- Use inversion register to determine output value (can be written any
time)
The above gives glitch-free outputs, but the values that are read back
(when
configured as output), come from the data registers. They should now be
coming
from the inversion (reg_set_base) registers, but the code prefers to use
the
data registers (reg_dat_base).
Lemme read the datasheet and see if I find any clue for the hw behaviour.
Thank you for your patience!
Have you explored the possibility of using En_Sync_GPIO?
Got around to testing things.
If En_Sync_GPIO is enabled, it's still possible to change the pin direction
without also writing the Sync_GPIO bit. So even with the latching, glitches are
still produced.
As long as Sync_GPIO is not set to latch the new values, it also appears that
reads of the data registers result in the current output value, not the new one.
As a different test, I've added a pull-down, to make the input level low. Now I
see the opposite behaviour as before (with set-value-before-direction):
* OUT-HIGH > IN (low) > OUT-LOW: results in a high level (i.e. old value)
* OUT-HIGH > IN (low) > OUT-HIGH: results in a high level (new/old value)
* OUT-LOW > IN (low) > OUT-HIGH: results in a high level (new value, or toggled
old value?)
* OUT-LOW > IN (low) > OUT-LOW: results in a low level (new/old value)
For reference, with a pull-up:
* OUT-HIGH > IN (high) > OUT-HIGH: high result
* OUT-HIGH > IN (high) > OUT-LOW: low result
* OUT-LOW > IN (high) > OUT-HIGH: low result
* OUT-LOW > IN (high) > OUT-LOW: low result
I've only tested this with the sysfs interface, so I don't know what the result
would be on multiple writes to the data register (during input, but probably not
very relevant). Nor have I tested direction changes if the input has changed
between two output values.
I may have some time tomorrow for more testing, but otherwise it'll have to wait
until the weekend. Any other ideas in the meantime?
Best,
Sander
From: Andy Shevchenko <hidden> Date: 2021-05-27 10:39:01
+Cc: Hans
Hans, sorry for disturbing you later too much. Here we have "nice"
hardware which can't be used in a glitch-free mode (somehow it reminds
me lynxpoint, baytrail, cherryview designs). If you have any ideas to
share (no need to dive deep or look at it if you have no time), you're
welcome.
On Thu, May 27, 2021 at 12:02 AM Sander Vanheule [off-list ref] wrote:
On Tue, 2021-05-25 at 20:11 +0300, Andy Shevchenko wrote:
quoted
On Mon, May 24, 2021 at 7:30 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Mon, May 24, 2021 at 6:03 PM Sander Vanheule [off-list ref]
wrote:
quoted
On Mon, 2021-05-24 at 15:54 +0300, Andy Shevchenko wrote:
...
quoted
quoted
Sadly, I don't. Most of the info we have comes from code archives of
switch
vendors (Zyxel, Cisco etc). Boards need to be reverse engineered, and the
few
leaked datasheets that can be found on the internet aren't exactly thick
in
information.
The RTL8231 datasheet is actually quite useful, but makes no mention of
the
output value isse. Since this isn't an official resource, I don't think it
would
be appropriate to link it via a Datasheet: tag.
https://github.com/libc0607/Realtek_switch_hacking/blob/files/RTL8231_Datasheet_
1.2.pdf
Looking at the datasheet again, I came up with a... terrible hack to work
around
the output value issue.
The chip also has GPIO_INVERT registers that I hadn't used until now,
because
the logical inversion is handled in the kernel. However, these inversion
registers only apply to the output values. So, I could implement glitch-
free
output behaviour in the following way:
* After chip reset, and before enabling the output driver (MFD
initialisation):
- Mux all pins as GPIO
- Change all pins to outputs,
No. no, no. This is much worse than the glitches. You never know what
the hardware is connected there and it's potential breakage (on hw
level) possible.
quoted
so the data registers (0x1c-0x1e) become writable
- Write value 0 to all pins
- Change all pins to GPI to change them into high-Z
* In the pinctrl/gpio driver:
- Use data registers as input-only
- Use inversion register to determine output value (can be written any
time)
The above gives glitch-free outputs, but the values that are read back
(when
configured as output), come from the data registers. They should now be
coming
from the inversion (reg_set_base) registers, but the code prefers to use
the
data registers (reg_dat_base).
Lemme read the datasheet and see if I find any clue for the hw behaviour.
Thank you for your patience!
Have you explored the possibility of using En_Sync_GPIO?
Got around to testing things.
If En_Sync_GPIO is enabled, it's still possible to change the pin direction
without also writing the Sync_GPIO bit. So even with the latching, glitches are
still produced.
As long as Sync_GPIO is not set to latch the new values, it also appears that
reads of the data registers result in the current output value, not the new one.
As a different test, I've added a pull-down, to make the input level low. Now I
see the opposite behaviour as before (with set-value-before-direction):
* OUT-HIGH > IN (low) > OUT-LOW: results in a high level (i.e. old value)
* OUT-HIGH > IN (low) > OUT-HIGH: results in a high level (new/old value)
* OUT-LOW > IN (low) > OUT-HIGH: results in a high level (new value, or toggled
old value?)
* OUT-LOW > IN (low) > OUT-LOW: results in a low level (new/old value)
For reference, with a pull-up:
* OUT-HIGH > IN (high) > OUT-HIGH: high result
* OUT-HIGH > IN (high) > OUT-LOW: low result
* OUT-LOW > IN (high) > OUT-HIGH: low result
* OUT-LOW > IN (high) > OUT-LOW: low result
I've only tested this with the sysfs interface, so I don't know what the result
would be on multiple writes to the data register (during input, but probably not
very relevant). Nor have I tested direction changes if the input has changed
between two output values.
I may have some time tomorrow for more testing, but otherwise it'll have to wait
until the weekend. Any other ideas in the meantime?
No ideas so far. In x86 we used to have something similar (baytrail,
cherryview, lynxpoint), but it's firmware assisted. I think that this
hardware (realtek) is supposed either
- to be firmware / bootloader assisted, so in a way that platform is
preconfigured when Linux starts and any GPIO request won't be harmful
as long as it doesn't change direction on the pins (which is usually
guaranteed by DT and corresponding drivers to do the correct things)
- be used for glitch-tolerant hardware (LEDs, for example, where
nobody usually will noticed 1ms blink)
That said, I have not been convinced we have to quirk gpio-regmap for
this one. Just describe the issues with hardware in the accompanying
documentation.
But if maintainers or somebody comes with a better / different
approach I am all ears.
--
With Best Regards,
Andy Shevchenko
From: Hans de Goede <hidden> Date: 2021-05-27 10:41:47
Hi,
On 5/27/21 12:38 PM, Andy Shevchenko wrote:
+Cc: Hans
Hans, sorry for disturbing you later too much. Here we have "nice"
hardware which can't be used in a glitch-free mode (somehow it reminds
me lynxpoint, baytrail, cherryview designs). If you have any ideas to
share (no need to dive deep or look at it if you have no time), you're
welcome.
I'm afraid I've no ideas how to solve this nicely. Documenting the
issue might be the best we can do.
Regards,
Hans
On Thu, May 27, 2021 at 12:02 AM Sander Vanheule [off-list ref] wrote:
quoted
On Tue, 2021-05-25 at 20:11 +0300, Andy Shevchenko wrote:
quoted
On Mon, May 24, 2021 at 7:30 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Mon, May 24, 2021 at 6:03 PM Sander Vanheule [off-list ref]
wrote:
quoted
On Mon, 2021-05-24 at 15:54 +0300, Andy Shevchenko wrote:
...
quoted
quoted
Sadly, I don't. Most of the info we have comes from code archives of
switch
vendors (Zyxel, Cisco etc). Boards need to be reverse engineered, and the
few
leaked datasheets that can be found on the internet aren't exactly thick
in
information.
The RTL8231 datasheet is actually quite useful, but makes no mention of
the
output value isse. Since this isn't an official resource, I don't think it
would
be appropriate to link it via a Datasheet: tag.
https://github.com/libc0607/Realtek_switch_hacking/blob/files/RTL8231_Datasheet_
1.2.pdf
Looking at the datasheet again, I came up with a... terrible hack to work
around
the output value issue.
The chip also has GPIO_INVERT registers that I hadn't used until now,
because
the logical inversion is handled in the kernel. However, these inversion
registers only apply to the output values. So, I could implement glitch-
free
output behaviour in the following way:
* After chip reset, and before enabling the output driver (MFD
initialisation):
- Mux all pins as GPIO
- Change all pins to outputs,
No. no, no. This is much worse than the glitches. You never know what
the hardware is connected there and it's potential breakage (on hw
level) possible.
quoted
so the data registers (0x1c-0x1e) become writable
- Write value 0 to all pins
- Change all pins to GPI to change them into high-Z
* In the pinctrl/gpio driver:
- Use data registers as input-only
- Use inversion register to determine output value (can be written any
time)
The above gives glitch-free outputs, but the values that are read back
(when
configured as output), come from the data registers. They should now be
coming
from the inversion (reg_set_base) registers, but the code prefers to use
the
data registers (reg_dat_base).
Lemme read the datasheet and see if I find any clue for the hw behaviour.
Thank you for your patience!
Have you explored the possibility of using En_Sync_GPIO?
Got around to testing things.
If En_Sync_GPIO is enabled, it's still possible to change the pin direction
without also writing the Sync_GPIO bit. So even with the latching, glitches are
still produced.
As long as Sync_GPIO is not set to latch the new values, it also appears that
reads of the data registers result in the current output value, not the new one.
As a different test, I've added a pull-down, to make the input level low. Now I
see the opposite behaviour as before (with set-value-before-direction):
* OUT-HIGH > IN (low) > OUT-LOW: results in a high level (i.e. old value)
* OUT-HIGH > IN (low) > OUT-HIGH: results in a high level (new/old value)
* OUT-LOW > IN (low) > OUT-HIGH: results in a high level (new value, or toggled
old value?)
* OUT-LOW > IN (low) > OUT-LOW: results in a low level (new/old value)
For reference, with a pull-up:
* OUT-HIGH > IN (high) > OUT-HIGH: high result
* OUT-HIGH > IN (high) > OUT-LOW: low result
* OUT-LOW > IN (high) > OUT-HIGH: low result
* OUT-LOW > IN (high) > OUT-LOW: low result
I've only tested this with the sysfs interface, so I don't know what the result
would be on multiple writes to the data register (during input, but probably not
very relevant). Nor have I tested direction changes if the input has changed
between two output values.
I may have some time tomorrow for more testing, but otherwise it'll have to wait
until the weekend. Any other ideas in the meantime?
No ideas so far. In x86 we used to have something similar (baytrail,
cherryview, lynxpoint), but it's firmware assisted. I think that this
hardware (realtek) is supposed either
- to be firmware / bootloader assisted, so in a way that platform is
preconfigured when Linux starts and any GPIO request won't be harmful
as long as it doesn't change direction on the pins (which is usually
guaranteed by DT and corresponding drivers to do the correct things)
- be used for glitch-tolerant hardware (LEDs, for example, where
nobody usually will noticed 1ms blink)
That said, I have not been convinced we have to quirk gpio-regmap for
this one. Just describe the issues with hardware in the accompanying
documentation.
But if maintainers or somebody comes with a better / different
approach I am all ears.
On Mon, May 24, 2021 at 12:34 AM Sander Vanheule [off-list ref] wrote:
Add a binding description for the Realtek RTL8231, a GPIO and LED
expander chip commonly used in ethernet switches based on a Realtek
switch SoC. These chips can be addressed via an MDIO or SMI bus, or used
as a plain 36-bit shift register.
This binding only describes the feature set provided by the MDIO/SMI
configuration, and covers the GPIO, PWM, and pin control properties. The
LED properties are defined in a separate binding.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
This looks good to me from a GPIO and pin control PoV:
Reviewed-by: Linus Walleij <redacted>
Yours,
Linus Walleij
Btw. you'd only need GPIO_REGMAP_ADDR(x) if x might be 0. Because you have
a constant != 0 there, you could save the GPIO_REGMAP_ADDR() call. You
could drop this if you like, but no need to respin the series for this.
-michael
- Introduce GPIO regmap quirks to set output direction first
I thought you had determined it was possible to set output before
direction?
Same thoughts when I saw an updated version of that patch. My
anticipation was to not see it at all.
The two devices I've been trying to test the behaviour on are:
* Netgear GS110TPP: has an RTL8231 with three LEDs, each driven via a pin
configured as (active-low) GPIO. The LEDs are easy for a quick visual check.
* Zyxel GS1900-8: RTL8231 used for the front panel button, and an active-low
GPIO used to hard reset the main SoC (an RTL8380). I've modified this board
to change some of the strapping pin values, but testing with the jumpers and
pull-up/down resistors is a bit more tedious.
On the Netgear, I tested the following with and without the quirk:
# Set as OUT-LOW twice, to avoid the quirk. Always turns the LED on
gpioset 1 32=0; gpioset 1 32=0
# Get value to change to input, turns the LED off (high impedance)
# Will return 1 due to (weak) internal pull-up
gpioget 1 32
# Set as OUT-HIGH, should result in LED off
# When the quirk is disabled, the LED turns on (i.e. old OUT-LOW value)
# When the quirk is enabled, the LED remains off (i.e. correct
OUT-HIGH value)
gpioset 1 32=1
Now, what's confusing (to me) is that the inverse doesn't depend on the quirk:
# Set as OUT-HIGH twice
gpioset 1 32=1; gpioset 1 32=1
# Change to high-Z
gpioget 1 32
# Set to OUT-LOW, always results in LED on, with or without quirk
gpioset 1 32=0
Any idea why this would be (or appear) broken on the former case, but not on the
latter?
Before reading this, I'd have guessed that they switch the internal register
depending on the GPIO direction; I mean there is only one register address
for both the input and the output register. Hm.
Did you try playing around with raw register accesses and see if the value
of the GPIO data register is changing when you switch GPIOs to input/output.
Eg. you could try https://github.com/kontron/miitool to access the registers
from userspace (your ethernet controller has to have support for the ioctl's
though, see commit a613bafec516 ("enetc: add ioctl() support for PHY-related
ops") for an example).
-michael
From: Michael Walle <hidden> Date: 2021-05-28 06:40:56
Am 2021-05-24 00:33, schrieb Sander Vanheule:
GPIO chips may not support setting the output value when a pin is
configured as an input, although the current implementation assumes this
is always possible.
Add support for setting pin direction before value. The order defaults
to setting the value first, but this can be reversed by setting the
GPIO_REGMAP_QUIRK_SET_DIRECTION_FIRST flag in regmap_config.quirks.
Nice! If this is really needed:
Reviewed-by: Michael Walle <redacted>
-michael
Btw. you'd only need GPIO_REGMAP_ADDR(x) if x might be 0. Because you
have
a constant != 0 there, you could save the GPIO_REGMAP_ADDR() call. You
could drop this if you like, but no need to respin the series for this.
I will need to respin this series anyway, so I can drop the GPIO_REGMAP_ADDR()
calls. I was aware they are no-ops in this case, as register address 0 is not
used for the GPIO functions, so mainly included them as a form of documentation.
Best,
Sander