This series adds support for STMicroelectronics MultiFunction eXpander
(ST MFX), used on some STM32 discovery and evaluation boards.
ST MFX is an STM32L152 slave controller whose firmware embeds the following
features:
- I/O expander (16 GPIOs + 8 extra if the other features are not enabled),
- resistive touchscreen controller,
- IDD measurement.
Using an I2C bus, the main MCU can control the MFX. MFX internal/external
events are sent to the main MCU by the MFX_IRQ_OUT_PIN.
Amelie Delaunay (6):
dt-bindings: mfd: Add ST Multi-Function eXpander driver
mfd: Add ST Multi-Function eXpander core driver
gpio: Add GPIO support for the ST Multi-Function eXpander
ARM: dts: stm32: add MFX support on I2C1 on stm32746g-eval
ARM: dts: stm32: add joystick support on stm32746g-eval
ARM: configs: stm32: enable ST MFX and its GPIO expander feature
Documentation/devicetree/bindings/mfd/st-mfx.txt | 51 ++
arch/arm/boot/dts/stm32746g-eval.dts | 48 ++
arch/arm/configs/stm32_defconfig | 2 +
drivers/gpio/Kconfig | 10 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-st-mfx.c | 589 +++++++++++++++++++++++
drivers/mfd/Kconfig | 15 +
drivers/mfd/Makefile | 1 +
drivers/mfd/st-mfx.c | 526 ++++++++++++++++++++
include/dt-bindings/gpio/st-mfx-gpio.h | 24 +
include/linux/mfd/st-mfx.h | 116 +++++
11 files changed, 1383 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/st-mfx.txt
create mode 100644 drivers/gpio/gpio-st-mfx.c
create mode 100644 drivers/mfd/st-mfx.c
create mode 100644 include/dt-bindings/gpio/st-mfx-gpio.h
create mode 100644 include/linux/mfd/st-mfx.h
--
2.7.4
@@ -52,9 +52,11 @@ CONFIG_I2C_CHARDEV=y CONFIG_I2C_STM32F4=y CONFIG_I2C_STM32F7=y CONFIG_GPIO_STMPE=y+CONFIG_GPIO_ST_MFX=y # CONFIG_HWMON is not set CONFIG_WATCHDOG=y CONFIG_MFD_STMPE=y+CONFIG_MFD_ST_MFX=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y # CONFIG_USB_SUPPORT is not set
ST Multi-Function eXpander (MFX) is a slave controller using I2C
for communication with the main MCU. Main features are:
- 16 fast GPIOs individually configurable in input/output
- 8 alternate GPIOs individually configurable in input/output
- Main MCU IDD measurement
- Resistive touchscreen controller
Only GPIO expander (16 fast GPIOs + 8 alternate) feature is
supported for the moment.
Signed-off-by: Amelie Delaunay <redacted>
---
drivers/mfd/Kconfig | 15 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/st-mfx.c | 526 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/st-mfx.h | 116 ++++++++++
4 files changed, 658 insertions(+)
create mode 100644 drivers/mfd/st-mfx.c
create mode 100644 include/linux/mfd/st-mfx.h
@@ -0,0 +1,526 @@+/*+*STMicroelectronicsMulti-FunctioneXpander(ST-MFX)CoreDriver+*+*Copyright(C)2017,STMicroelectronics-AllRightsReserved+*Author(s):AmelieDelaunay<amelie.delaunay@st.com>forSTMicroelectronics.+*+*Licenseterms:GPLV2.0.+*+*st-mfxCoreDriverisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseversion2aspublishedby+*theFreeSoftwareFoundation.+*+*st-mfxCoreDriverisdistributedinthehopethatitwillbeuseful,but+*WITHOUTANYWARRANTY;withouteventheimpliedwarrantyofMERCHANTABILITYor+*FITNESSFORAPARTICULARPURPOSE.SeetheGNUGeneralPublicLicenseformore+*details.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicensealongwith+*st-mfxCoreDriver.Ifnot,see<http://www.gnu.org/licenses/>.+*/+#include<linux/bitfield.h>+#include<linux/gpio/consumer.h>+#include<linux/i2c.h>+#include<linux/interrupt.h>+#include<linux/module.h>+#include<linux/mfd/core.h>+#include<linux/mfd/st-mfx.h>+#include<linux/of_device.h>+#include<linux/of_irq.h>+#include<linux/regmap.h>++/**+*structmfx_priv-MFXMFDprivatestructure+*@dev:device,mostlyforlogs+*@regmap:registermap+*@mfx:MFXMFDpublicstructure,toshareinformationwithsubdrivers+*@irq_domain:IRQdomain+*@irq:IRQnumberformfx+*@irq_lock:IRQbuslock+*@irqen:cacheofIRQ_SRC_ENregisterforbus_lock+*@oldirqen:cacheofIRQ_SRC_ENregisterforbus_lock+*/+structmfx_priv{+structdevice*dev;+structregmap*regmap;+structmfxmfx;+structirq_domain*irq_domain;+intirq;+structmutexirq_lock;/* IRQ bus lock */+u8irqen;+u8oldirqen;+};++#define to_mfx_priv(_mfx) container_of(_mfx, struct mfx_priv, mfx)++/* MFX boot time is around 10ms, so after reset, we have to wait this delay */+#define MFX_BOOT_TIME 10++staticu8mfx_blocks_to_mask(u32blocks)+{+u8mask=0;++if(blocks&MFX_BLOCK_GPIO)+mask|=MFX_REG_SYS_CTRL_GPIO_EN;+else+mask&=~MFX_REG_SYS_CTRL_GPIO_EN;++if(blocks&MFX_BLOCK_TS)+mask|=MFX_REG_SYS_CTRL_TS_EN;+else+mask&=~MFX_REG_SYS_CTRL_TS_EN;++if(blocks&MFX_BLOCK_IDD)+mask|=MFX_REG_SYS_CTRL_IDD_EN;+else+mask&=~MFX_REG_SYS_CTRL_IDD_EN;++if(blocks&MFX_BLOCK_ALTGPIO)+mask|=MFX_REG_SYS_CTRL_ALTGPIO_EN;+else+mask&=~MFX_REG_SYS_CTRL_ALTGPIO_EN;++returnmask;+}++staticintmfx_reset(structmfx*mfx)+{+structmfx_priv*mfx_priv=to_mfx_priv(mfx);+intret;++ret=regmap_update_bits(mfx_priv->regmap,MFX_REG_SYS_CTRL,+MFX_REG_SYS_CTRL_SWRST,+MFX_REG_SYS_CTRL_SWRST);++if(ret<0)+returnret;++msleep(MFX_BOOT_TIME);++returnret;+}++intmfx_block_read(structmfx*mfx,u8reg,u8length,u8*values)+{+structmfx_priv*mfx_priv=to_mfx_priv(mfx);++returnregmap_bulk_read(mfx_priv->regmap,reg,values,length);+}+EXPORT_SYMBOL_GPL(mfx_block_read);++intmfx_block_write(structmfx*mfx,u8reg,u8length,constu8*values)+{+structmfx_priv*mfx_priv=to_mfx_priv(mfx);++returnregmap_bulk_write(mfx_priv->regmap,reg,values,length);+}+EXPORT_SYMBOL_GPL(mfx_block_write);++intmfx_reg_read(structmfx*mfx,u8reg)+{+structmfx_priv*mfx_priv=to_mfx_priv(mfx);+u32val;+intret;++ret=regmap_read(mfx_priv->regmap,reg,&val);++returnret?ret:val;+}+EXPORT_SYMBOL_GPL(mfx_reg_read);++intmfx_reg_write(structmfx*mfx,u8reg,u8val)+{+structmfx_priv*mfx_priv=to_mfx_priv(mfx);++returnregmap_write(mfx_priv->regmap,reg,val);+}+EXPORT_SYMBOL_GPL(mfx_reg_write);++intmfx_set_bits(structmfx*mfx,u8reg,u8mask,u8val)+{+structmfx_priv*mfx_priv=to_mfx_priv(mfx);++returnregmap_update_bits(mfx_priv->regmap,reg,mask,val);+}+EXPORT_SYMBOL_GPL(mfx_set_bits);++intmfx_enable(structmfx*mfx,u32blocks)+{+structmfx_priv*mfx_priv=to_mfx_priv(mfx);+u8mask=mfx_blocks_to_mask(blocks);++returnregmap_update_bits(mfx_priv->regmap,MFX_REG_SYS_CTRL,+mask,mask);+}+EXPORT_SYMBOL_GPL(mfx_enable);++intmfx_disable(structmfx*mfx,u32blocks)+{+structmfx_priv*mfx_priv=to_mfx_priv(mfx);+u8mask=mfx_blocks_to_mask(blocks);++returnregmap_update_bits(mfx_priv->regmap,MFX_REG_SYS_CTRL,+mask,0);+}+EXPORT_SYMBOL_GPL(mfx_disable);++staticvoidmfx_irq_lock(structirq_data*data)+{+structmfx_priv*mfx_priv=irq_data_get_irq_chip_data(data);++mutex_lock(&mfx_priv->irq_lock);+}++staticvoidmfx_irq_sync_unlock(structirq_data*data)+{+structmfx_priv*mfx_priv=irq_data_get_irq_chip_data(data);+u8new=mfx_priv->irqen;+u8old=mfx_priv->oldirqen;++if(new==old)+gotounlock;++mfx_priv->oldirqen=new;+mfx_reg_write(&mfx_priv->mfx,MFX_REG_IRQ_SRC_EN,new);+unlock:+mutex_unlock(&mfx_priv->irq_lock);+}++staticvoidmfx_irq_mask(structirq_data*data)+{+structmfx_priv*mfx_priv=irq_data_get_irq_chip_data(data);++mfx_priv->irqen&=~BIT(data->hwirq%8);+}++staticvoidmfx_irq_unmask(structirq_data*data)+{+structmfx_priv*mfx_priv=irq_data_get_irq_chip_data(data);++mfx_priv->irqen|=BIT(data->hwirq%8);+}++staticstructirq_chipmfx_irq_chip={+.name="mfx",+.irq_bus_lock=mfx_irq_lock,+.irq_bus_sync_unlock=mfx_irq_sync_unlock,+.irq_mask=mfx_irq_mask,+.irq_unmask=mfx_irq_unmask,+};++staticirqreturn_tmfx_irq(intirq,void*data)+{+structmfx_priv*mfx_priv=data;+unsignedlongstatus,bit;+u8ack;+intret;++ret=mfx_reg_read(&mfx_priv->mfx,MFX_REG_IRQ_PENDING);+if(ret<0){+dev_err(mfx_priv->dev,"can't get IRQ_PENDING: %d\n",ret);+returnIRQ_NONE;+}++/* It can be GPIO, IDD, ERROR, TS* IRQs */+status=ret&mfx_priv->irqen;++/*+*ThereisnoACKforGPIO,MFX_REG_IRQ_PENDING_GPIOisalogicalOR+*ofMFX_REG_IRQ_GPI_PENDING1/_PENDING2/_PENDING3+*/+ack=status&~MFX_REG_IRQ_PENDING_GPIO;++for_each_set_bit(bit,&status,8)+handle_nested_irq(irq_find_mapping(mfx_priv->irq_domain,bit));++if(ack)+mfx_reg_write(&mfx_priv->mfx,MFX_REG_IRQ_ACK,ack);++returnIRQ_HANDLED;+}++staticintmfx_irq_map(structirq_domain*d,unsignedintvirq,+irq_hw_number_thwirq)+{+structmfx_priv*mfx_priv=d->host_data;++irq_set_chip_data(virq,mfx_priv);+irq_set_chip(virq,&mfx_irq_chip);+irq_set_nested_thread(virq,1);+irq_set_parent(virq,mfx_priv->irq);+irq_set_noprobe(virq);++return0;+}++staticvoidmfx_irq_unmap(structirq_domain*d,unsignedintvirq)+{+irq_set_chip_and_handler(virq,NULL,NULL);+irq_set_chip_data(virq,NULL);+}++staticconststructirq_domain_opsmfx_irq_ops={+.map=mfx_irq_map,+.unmap=mfx_irq_unmap,+.xlate=irq_domain_xlate_onecell,+};++staticintmfx_irq_init(structmfx_priv*mfx_priv,structdevice_node*np)+{+intirqoutpin=MFX_REG_IRQ_OUT_PIN_TYPE;/* Push-Pull */+intirqtrigger,ret;++mfx_priv->irq=of_irq_get(np,0);+if(mfx_priv->irq>0){+irqtrigger=irq_get_trigger_type(mfx_priv->irq);+}else{+dev_err(mfx_priv->dev,"failed to get irq: %d\n",+mfx_priv->irq);+returnmfx_priv->irq;+}++if((irqtrigger&IRQ_TYPE_EDGE_FALLING)||+(irqtrigger&IRQ_TYPE_LEVEL_LOW))+irqoutpin&=~MFX_REG_IRQ_OUT_PIN_POL;/* Active Low */+else+irqoutpin|=MFX_REG_IRQ_OUT_PIN_POL;/* Active High */++mfx_reg_write(&mfx_priv->mfx,MFX_REG_IRQ_OUT_PIN,irqoutpin);++mfx_priv->irq_domain=irq_domain_add_linear(np,MFX_IRQ_SRC_NR,+&mfx_irq_ops,mfx_priv);+if(!mfx_priv->irq_domain){+dev_err(mfx_priv->dev,"failed to create irq domain\n");+return-ENOMEM;+}++ret=devm_request_threaded_irq(mfx_priv->dev,mfx_priv->irq,+NULL,mfx_irq,+irqtrigger|IRQF_ONESHOT,"mfx",+mfx_priv);+if(ret){+dev_err(mfx_priv->dev,"failed to request irq: %d\n",ret);+irq_domain_remove(mfx_priv->irq_domain);+returnret;+}++return0;+}++staticvoidmfx_irq_remove(structmfx_priv*mfx_priv)+{+inthwirq;++for(hwirq=0;hwirq<MFX_IRQ_SRC_NR;hwirq++)+irq_dispose_mapping(irq_find_mapping(mfx_priv->irq_domain,+hwirq));+irq_domain_remove(mfx_priv->irq_domain);+}++staticintmfx_chip_init(structmfx_priv*mfx_priv,u16i2c_addr)+{+structmfx*mfx=&mfx_priv->mfx;+intret;+intid;+u8version[2];++id=mfx_reg_read(mfx,MFX_REG_CHIP_ID);+if(id<0){+dev_err(mfx_priv->dev,"error reading chip id: %d\n",id);+returnid;+}++ret=mfx_block_read(mfx,MFX_REG_FW_VERSION_MSB,+ARRAY_SIZE(version),version);+if(ret<0){+dev_err(mfx_priv->dev,"error reading fw version: %d\n",ret);+returnret;+}++/*+*CheckthatIDisthecomplementoftheI2Caddress:+*MFXI2Caddressfollowsthe7-bitformat(MSB),that'swhy+*i2c_addrisshifted.+*+*MFX_I2C_ADDR|MFX|Linux+*inputpin|I2Cdeviceaddress|I2Cdeviceaddress+*--------------------------------------------------------+*0|b:1000010xh:0x84|0x42+*1|b:1000011xh:0x86|0x43+*/+if(FIELD_GET(MFX_REG_CHIP_ID_MASK,~id)!=(i2c_addr<<1)){+dev_err(mfx_priv->dev,"unknown chip id: %#x\n",id);+return-EINVAL;+}++dev_info(mfx_priv->dev,"ST-MFX chip id: %#x, fw version: %x.%02x\n",+id,version[0],version[1]);++/* Disable all features, subdrivers should enable what they need */+ret=mfx_disable(mfx,~0);+if(ret)+returnret;++returnmfx_reset(mfx);+}++staticconststructregmap_configmfx_regmap_config={+.reg_bits=8,+.val_bits=8,+};++staticconststructof_device_idmfx_of_match[]={+{.compatible="st,mfx"},+{}+};+MODULE_DEVICE_TABLE(of,mfx_of_match);++staticintmfx_of_probe(structdevice_node*np,structmfx_priv*mfx_priv)+{+structdevice_node*child;++if(!np)+return-ENODEV;++for_each_child_of_node(np,child){+if(of_device_is_compatible(child,"st,mfx-gpio")){+mfx_priv->mfx.blocks|=MFX_BLOCK_GPIO;+mfx_priv->mfx.num_gpio=16;+}+/*+*Herewecouldfindotherchildrenlike"st,mfx-ts"or+*"st,mfx-idd.+*/+}++if(!(mfx_priv->mfx.blocks&MFX_BLOCK_TS)&&+!(mfx_priv->mfx.blocks&MFX_BLOCK_IDD)){+mfx_priv->mfx.blocks|=MFX_BLOCK_ALTGPIO;+mfx_priv->mfx.num_gpio+=8;+}++/*+*TODO:aGPIO[3:0]andaGPIO[7:4]canbeusedindependently:+*-ifIDDisusedbutnotTS,aGPIO[3:0]canbeusedasGPIO,+*-ifTSisusedbutnotIDD:aGPIO[7:4]canbeusedasGPIO.+*/++returnof_platform_populate(np,NULL,NULL,mfx_priv->dev);+}++intmfx_probe(structi2c_client*client,conststructi2c_device_id*id)+{+structdevice_node*np=client->dev.of_node;+structmfx_priv*mfx_priv;+intret;++mfx_priv=devm_kzalloc(&client->dev,sizeof(structmfx_priv),+GFP_KERNEL);+if(!mfx_priv)+return-ENOMEM;++mfx_priv->regmap=devm_regmap_init_i2c(client,&mfx_regmap_config);+if(IS_ERR(mfx_priv->regmap)){+ret=PTR_ERR(mfx_priv->regmap);+dev_err(&client->dev,"failed to allocate register map: %d\n",+ret);+returnret;+}++mfx_priv->dev=&client->dev;+i2c_set_clientdata(client,&mfx_priv->mfx);++mutex_init(&mfx_priv->irq_lock);++ret=mfx_chip_init(mfx_priv,client->addr);+if(ret){+if(ret==-ETIMEDOUT)+return-EPROBE_DEFER;++returnret;+}++ret=mfx_irq_init(mfx_priv,np);+if(ret<0)+returnret;++ret=mfx_of_probe(np,mfx_priv);+if(ret<0)+returnret;++dev_info(mfx_priv->dev,"ST-MFX (CORE) initialized\n");++return0;+}++staticintmfx_remove(structi2c_client*client)+{+structmfx_priv*mfx_priv=to_mfx_priv(i2c_get_clientdata(client));++mfx_irq_remove(mfx_priv);+of_platform_depopulate(mfx_priv->dev);++return0;+}++#ifdef CONFIG_PM_SLEEP+staticintmfx_suspend(structdevice*dev)+{+structmfx_priv*mfx_priv=to_mfx_priv(dev_get_drvdata(dev));++if(device_may_wakeup(dev))+enable_irq_wake(mfx_priv->irq);++/*+*TODO:DoweputMFXinSTANDBYmode?+*(WakeupbyrisingedgeonMFX_wakeuppin)+*/++return0;+}++staticintmfx_resume(structdevice*dev)+{+structmfx_priv*mfx_priv=to_mfx_priv(dev_get_drvdata(dev));++if(device_may_wakeup(dev))+disable_irq_wake(mfx_priv->irq);++return0;+}+#endif++staticSIMPLE_DEV_PM_OPS(mfx_dev_pm_ops,mfx_suspend,mfx_resume);++staticconststructi2c_device_idmfx_i2c_id[]={+{"mfx",},+{},+};+MODULE_DEVICE_TABLE(i2c,mfx_id);++staticstructi2c_drivermfx_driver={+.driver={+.name="st-mfx",+.pm=&mfx_dev_pm_ops,+.of_match_table=mfx_of_match,+},+.probe=mfx_probe,+.remove=mfx_remove,+.id_table=mfx_i2c_id,+};++staticint__initmfx_init(void)+{+returni2c_add_driver(&mfx_driver);+}+subsys_initcall(mfx_init);++staticvoid__exitmfx_exit(void)+{+i2c_del_driver(&mfx_driver);+}+module_exit(mfx_exit);++MODULE_DESCRIPTION("STMicroelectronics Multi-Function eXpander MFD core driver");+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");+MODULE_LICENSE("GPL v2");
@@ -0,0 +1,51 @@+STMicroelectronics Multi-Function eXpander++ST Multi-Function eXpander (MFX) is a slave controller using I2C for+communication with the main MCU. Its main features are gpio expansion, main+MCU IDD measurement (IDD is the amount of current that flows through VDD)+and resistive touchscreen controller.++Required properties:+- compatible: must be "st,mfx"+- reg: I2C address of the device+- interrupts: interrupt triggered by MFX_IRQ_OUT signal+- interrupt-parent: interrupt controller MFX is connected to+- interrupt-controller: marks the device as an interrupt controller+- #interrupt-cells: should be <1>, index of the interrupt within the+ controller, in accordance with the "one cell" variant of+ <devicetree/bindings/interrupt-controller/interrupt.txt>++Optional nodes:++* GPIO eXpander+MFX provides 16 programmable GPIOs, and it is also possible to recover 8+alternate GPIOs if the main functions are not used (touchscreen controller and+IDD measurement not enabled).++Required properties:+- compatible : must be "st,mfx-gpio"+- interrupt-parent : must be <&mfx>+- interrupts = must be <0>+- gpio-controller: marks the device node as a GPIO controller+- #gpio-cells: should be <2>, the first cell is the GPIO offset on this GPIO+ controller, the second cell is the gpio flags in accordance with+ <dt-bindings/gpio/st-mfx-gpio.h>.++Example:++ mfx: mfx at 42 {+ compatible = "st,mfx";+ reg = <0x42>;+ interrupts = <8 IRQ_TYPE_EDGE_RISING>;+ interrupt-parent = <&gpioi>;+ interrupt-controller;+ #interrupt-cells = <1>;++ mfxgpio: mfx_gpio {+ compatible = "st,mfx-gpio";+ interrupt-parent = <&mfx>;+ interrupts = <0>;+ gpio-controller;+ #gpio-cells = <2>;+ };+ };
ST Multi-Function eXpander (MFX) can be used as GPIO expander.
It has 16 fast GPIOs and can have 8 extra alternate GPIOs
when other MFX features are not enabled.
Signed-off-by: Amelie Delaunay <redacted>
---
drivers/gpio/Kconfig | 10 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-st-mfx.c | 589 +++++++++++++++++++++++++++++++++
include/dt-bindings/gpio/st-mfx-gpio.h | 24 ++
4 files changed, 624 insertions(+)
create mode 100644 drivers/gpio/gpio-st-mfx.c
create mode 100644 include/dt-bindings/gpio/st-mfx-gpio.h
@@ -0,0 +1,589 @@+/*+*STMicroelectronicsMulti-FunctioneXpander(ST-MFX)-GPIOexpanderdriver.+*+*Copyright(C)2017,STMicroelectronics-AllRightsReserved+*Author(s):AmelieDelaunay<amelie.delaunay@st.com>forSTMicroelectronics.+*+*Licenseterms:GPLV2.0.+*+*st-mfx-gpioisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseversion2aspublishedby+*theFreeSoftwareFoundation.+*+*st-mfx-gpioisdistributedinthehopethatitwillbeuseful,but+*WITHOUTANYWARRANTY;withouteventheimpliedwarrantyofMERCHANTABILITYor+*FITNESSFORAPARTICULARPURPOSE.SeetheGNUGeneralPublicLicenseformore+*details.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicensealongwith+*st-mfx-gpio.Ifnot,see<http://www.gnu.org/licenses/>.+*/++#include<linux/interrupt.h>+#include<linux/mfd/st-mfx.h>+#include<linux/module.h>+#include<linux/of_gpio.h>+#include<linux/platform_device.h>+#include<linux/seq_file.h>++/* MFX has a maximum of 24 gpios, with 8 gpios per bank, so 3 banks maximum */+#define NR_MAX_GPIOS 24+#define NR_GPIOS_PER_BANK 8+#define NR_MAX_BANKS (NR_MAX_GPIOS / NR_GPIOS_PER_BANK)+#define get_bank(offset) ((u8)((offset) / NR_GPIOS_PER_BANK))+#define get_mask(offset) ((u8)BIT((offset) % NR_GPIOS_PER_BANK))++/*+*Theseregistersaremodifiedunderthe.irq_bus_lockandcachedtoavoid+*unnecessarywritesin.irq_bus_sync_unlock.+*/+enum{REG_IRQ_SRC,REG_IRQ_EVT,REG_IRQ_TYPE,NR_CACHE_IRQ_REGS};++/*+*ThisisMFX-specificflags,overloadingLinux-specificof_gpio_flags,+*neededinof_xlatecallback.+*onMFX:-ifGPIOisoutput:+**(0)meansPUSH_PULL+**OF_GPIO_SINGLE_ENDED(=2)meansOPEN-DRAIN+*-ifGPIOisinput:+**(0)meansNO_PULL+**OF_MFX_GPI_PUSH_PULL(=2)meansPUSH_PULL+*+**OF_MFX_GPIO_PULL_UPprogramspullupresistorontheGPIO,+*whateveritsdirection,withoutthisflag,dependingon+*GPIOtypeanddirection,itprogramseithernopullor+*pulldownresistor.+*/+enumof_mfx_gpio_flags{+OF_MFX_GPI_PUSH_PULL=0x2,+OF_MFX_GPIO_PULL_UP=0x4,+};++structmfx_gpio{+structgpio_chipgc;+structmfx*mfx;+structdevice*dev;+structmutexirq_lock;/* IRQ bus lock */+u32flags[NR_MAX_GPIOS];+/* Caches of interrupt control registers for bus_lock */+u8regs[NR_CACHE_IRQ_REGS][NR_MAX_BANKS];+u8oldregs[NR_CACHE_IRQ_REGS][NR_MAX_BANKS];+};++staticintmfx_gpio_get(structgpio_chip*gc,unsignedintoffset)+{+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+structmfx*mfx=mfx_gpio->mfx;+u8reg=MFX_REG_GPIO_STATE+get_bank(offset);+u8mask=get_mask(offset);+intret;++ret=mfx_reg_read(mfx,reg);+if(ret<0)+returnret;++return!!(ret&mask);+}++staticvoidmfx_gpio_set(structgpio_chip*gc,unsignedintoffset,intval)+{+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+structmfx*mfx=mfx_gpio->mfx;+intwhich=val?MFX_REG_GPO_SET:MFX_REG_GPO_CLR;+u8reg=which+get_bank(offset);+u8mask=get_mask(offset);++mfx_reg_write(mfx,reg,mask);+}++staticintmfx_gpio_get_direction(structgpio_chip*gc,+unsignedintoffset)+{+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+structmfx*mfx=mfx_gpio->mfx;+u8reg=MFX_REG_GPIO_DIR+get_bank(offset);+u8mask=get_mask(offset);+intret;++ret=mfx_reg_read(mfx,reg);+if(ret<0)+returnret;++return!(ret&mask);+}++staticintmfx_gpio_direction_input(structgpio_chip*gc,+unsignedintoffset)+{+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+structmfx*mfx=mfx_gpio->mfx;+u8reg_type=MFX_REG_GPIO_TYPE+get_bank(offset);+u8reg_pupd=MFX_REG_GPIO_PUPD+get_bank(offset);+u8reg_dir=MFX_REG_GPIO_DIR+get_bank(offset);+u8mask=get_mask(offset);+intret;++/*+*Incaseofinputdirection,thereisactuallynowaytoapplysome+*configurationinhardware,asitcanbedonewith+*.set_configincaseofoutputdirection.That'swhyweapply+*heretheMFXspecific-flags.+*/+if(mfx_gpio->flags[offset]&OF_MFX_GPI_PUSH_PULL)+ret=mfx_set_bits(mfx,reg_type,mask,mask);+else+ret=mfx_set_bits(mfx,reg_type,mask,0);++if(ret)+returnret;++if(mfx_gpio->flags[offset]&OF_MFX_GPIO_PULL_UP)+ret=mfx_set_bits(mfx,reg_pupd,mask,mask);+else+ret=mfx_set_bits(mfx,reg_pupd,mask,0);++if(ret)+returnret;++returnmfx_set_bits(mfx,reg_dir,mask,0);+}++staticintmfx_gpio_direction_output(structgpio_chip*gc,+unsignedintoffset,intval)+{+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+structmfx*mfx=mfx_gpio->mfx;+u8reg=MFX_REG_GPIO_DIR+get_bank(offset);+u8mask=get_mask(offset);++mfx_gpio_set(gc,offset,val);++returnmfx_set_bits(mfx,reg,mask,mask);+}++staticintmfx_gpio_set_config(structgpio_chip*gc,unsignedintoffset,+unsignedlongconfig)+{+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+structmfx*mfx=mfx_gpio->mfx;+u8reg_type=MFX_REG_GPIO_TYPE+get_bank(offset);+u8reg_pupd=MFX_REG_GPIO_PUPD+get_bank(offset);+u8mask=get_mask(offset);+intret;++switch(pinconf_to_config_param(config)){+casePIN_CONFIG_DRIVE_OPEN_DRAIN:+ret=mfx_set_bits(mfx,reg_type,mask,mask);+if(ret)+returnret;+returnmfx_set_bits(mfx,reg_pupd,mask,0);+casePIN_CONFIG_DRIVE_OPEN_SOURCE:+ret=mfx_set_bits(mfx,reg_type,mask,mask);+if(ret)+returnret;+returnmfx_set_bits(mfx,reg_pupd,mask,mask);+casePIN_CONFIG_DRIVE_PUSH_PULL:+ret=mfx_set_bits(mfx,reg_type,mask,0);+if(ret)+returnret;+returnmfx_set_bits(mfx,reg_pupd,mask,0);+default:+break;+}++return-ENOTSUPP;+}++staticvoidmfx_gpio_dbg_show(structseq_file*s,structgpio_chip*gc)+{+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+structmfx*mfx=mfx_gpio->mfx;+u16i;++for(i=0;i<gc->ngpio;i++){+intgpio=i+gc->base;+constchar*label=gpiochip_is_requested(gc,i);+intdir=mfx_gpio_get_direction(gc,i);+intval=mfx_gpio_get(gc,i);+u8mask=get_mask(i);+u8reg;+inttype,pupd;+intirqsrc,irqevt,irqtype,irqpending;++if(!label)+label="Unrequested";++seq_printf(s," gpio-%-3d (%-20.20s) ",gpio,label);++reg=MFX_REG_GPIO_TYPE+get_bank(i);+type=mfx_reg_read(mfx,reg);+if(type<0)+return;+type=!!(type&mask);++reg=MFX_REG_GPIO_PUPD+get_bank(i);+pupd=mfx_reg_read(mfx,reg);+if(pupd<0)+return;+pupd=!!(pupd&mask);++reg=MFX_REG_IRQ_GPI_SRC+get_bank(i);+irqsrc=mfx_reg_read(mfx,reg);+if(irqsrc<0)+return;+irqsrc=!!(irqsrc&mask);++reg=MFX_REG_IRQ_GPI_EVT+get_bank(i);+irqevt=mfx_reg_read(mfx,reg);+if(irqevt<0)+return;+irqevt=!!(irqevt&mask);++reg=MFX_REG_IRQ_GPI_TYPE+get_bank(i);+irqtype=mfx_reg_read(mfx,reg);+if(irqtype<0)+return;+irqtype=!!(irqtype&mask);++reg=MFX_REG_IRQ_GPI_PENDING+get_bank(i);+irqpending=mfx_reg_read(mfx,reg);+if(irqpending<0)+return;+irqpending=!!(irqpending&mask);++if(!dir){+seq_printf(s,"out %s ",val?"high":"low");+if(type)+seq_puts(s,"open drain ");+else+seq_puts(s,"push pull ");+if(pupd&&type)+seq_puts(s,"with internal pull-up ");+else+seq_puts(s,"without internal pull-up ");+}else{+seq_printf(s,"in %s ",val?"high":"low");+if(type)+seq_printf(s,"with internal pull-%s ",+pupd?"up":"down");+else+seq_printf(s,"%s ",+pupd?"floating":"analog");+}++if(irqsrc){+seq_printf(s,"IRQ generated on %s %s",+!irqevt?+(!irqtype?"Low level":"High level"):+(!irqtype?"Falling edge":"Rising edge"),+irqpending?"(pending)":"");+}++seq_puts(s,"\n");+}+}++intmfx_gpio_of_xlate(structgpio_chip*gc,+conststructof_phandle_args*gpiospec,u32*flags)+{+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+intret;++ret=of_gpio_simple_xlate(gc,gpiospec,flags);+if(ret<0)+returnret;++/*+*Inatomiccontexthere,wecan'taccessregistersoverI2C,+*that'swhygpioflagsarestoredtobeappliedlater.+*/+mfx_gpio->flags[gpiospec->args[0]]=gpiospec->args[1];++returnret;+}++staticconststructgpio_chipmfx_gpio_chip={+.label="MFX_GPIO",+.owner=THIS_MODULE,+.get_direction=mfx_gpio_get_direction,+.direction_input=mfx_gpio_direction_input,+.direction_output=mfx_gpio_direction_output,+.get=mfx_gpio_get,+.set=mfx_gpio_set,+.set_config=mfx_gpio_set_config,+.dbg_show=mfx_gpio_dbg_show,+.can_sleep=true,+.of_gpio_n_cells=2,+.of_xlate=mfx_gpio_of_xlate,+};++staticvoidmfx_gpio_irq_toggle_trigger(structgpio_chip*gc,intoffset)+{+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+structmfx*mfx=mfx_gpio->mfx;+u8bank=get_bank(offset);+u8mask=get_mask(offset);+intval=mfx_gpio_get(gc,offset);++if(val<0){+dev_err(mfx_gpio->dev,"can't get value of gpio%d: ret=%d\n",+offset,val);+return;+}++if(val){+mfx_gpio->regs[REG_IRQ_TYPE][bank]&=~mask;+mfx_set_bits(mfx,MFX_REG_IRQ_GPI_TYPE+bank,mask,0);+}else{+mfx_gpio->regs[REG_IRQ_TYPE][bank]|=mask;+mfx_set_bits(mfx,MFX_REG_IRQ_GPI_TYPE+bank,mask,mask);+}+}++staticintmfx_gpio_irq_set_type(structirq_data*d,unsignedinttype)+{+structgpio_chip*gc=irq_data_get_irq_chip_data(d);+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+intbank=get_bank(d->hwirq);+intmask=get_mask(d->hwirq);++if((type&IRQ_TYPE_LEVEL_LOW)||(type&IRQ_TYPE_LEVEL_HIGH))+mfx_gpio->regs[REG_IRQ_EVT][bank]&=~mask;+else+mfx_gpio->regs[REG_IRQ_EVT][bank]|=mask;++if((type&IRQ_TYPE_EDGE_RISING)||(type&IRQ_TYPE_LEVEL_HIGH))+mfx_gpio->regs[REG_IRQ_TYPE][bank]|=mask;+else+mfx_gpio->regs[REG_IRQ_TYPE][bank]&=~mask;++/*+*Incaseof(type&IRQ_TYPE_EDGE_BOTH),weneedtoknowcurrent+*GPIOvaluetosettherightedgetrigger.Butinatomiccontext+*herewecan'taccessregistersoverI2C.That'swhy(type&+*IRQ_TYPE_EDGE_BOTH)willbemanagedin.irq_sync_unlock.+*/++return0;+}++staticvoidmfx_gpio_irq_lock(structirq_data*d)+{+structgpio_chip*gc=irq_data_get_irq_chip_data(d);+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);++mutex_lock(&mfx_gpio->irq_lock);+}++staticvoidmfx_gpio_irq_sync_unlock(structirq_data*d)+{+structgpio_chip*gc=irq_data_get_irq_chip_data(d);+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+structmfx*mfx=mfx_gpio->mfx;+staticconstu8cache_regs[]={+[REG_IRQ_SRC]=MFX_REG_IRQ_GPI_SRC,+[REG_IRQ_EVT]=MFX_REG_IRQ_GPI_EVT,+[REG_IRQ_TYPE]=MFX_REG_IRQ_GPI_TYPE,+};+u8i,bank;++/*+*Incaseof(type&IRQ_TYPE_EDGE_BOTH),readthecurrentGPIOvalue+*(thiscouldn'tbedonein.irq_set_typebecauseofatomiccontext)+*tosettherightirqtriggertype.+*/+if(irqd_get_trigger_type(d)&IRQ_TYPE_EDGE_BOTH){+inttype;++if(mfx_gpio_get(gc,d->hwirq))+type=IRQ_TYPE_EDGE_FALLING;+else+type=IRQ_TYPE_EDGE_RISING;++mfx_gpio_irq_set_type(d,type);+}++for(i=0;i<NR_CACHE_IRQ_REGS;i++){+for(bank=0;bank<NR_MAX_BANKS;bank++){+u8old=mfx_gpio->oldregs[i][bank];+u8new=mfx_gpio->regs[i][bank];++if(new==old)+continue;++mfx_gpio->oldregs[i][bank]=new;+mfx_reg_write(mfx,cache_regs[i]+bank,new);+}+}++mutex_unlock(&mfx_gpio->irq_lock);+}++staticvoidmfx_gpio_irq_mask(structirq_data*d)+{+structgpio_chip*gc=irq_data_get_irq_chip_data(d);+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+intoffset=d->hwirq;+u8bank=get_bank(offset);+u8mask=get_mask(offset);++mfx_gpio->regs[REG_IRQ_SRC][bank]&=~mask;+}++staticvoidmfx_gpio_irq_unmask(structirq_data*d)+{+structgpio_chip*gc=irq_data_get_irq_chip_data(d);+structmfx_gpio*mfx_gpio=gpiochip_get_data(gc);+intoffset=d->hwirq;+u8bank=get_bank(offset);+u8mask=get_mask(offset);++mfx_gpio->regs[REG_IRQ_SRC][bank]|=mask;+}++staticstructirq_chipmfx_gpio_irq_chip={+.name="mfx-gpio",+.irq_bus_lock=mfx_gpio_irq_lock,+.irq_bus_sync_unlock=mfx_gpio_irq_sync_unlock,+.irq_mask=mfx_gpio_irq_mask,+.irq_unmask=mfx_gpio_irq_unmask,+.irq_set_type=mfx_gpio_irq_set_type,+};++staticirqreturn_tmfx_gpio_irq(intirq,void*dev)+{+structmfx_gpio*mfx_gpio=dev;+structmfx*mfx=mfx_gpio->mfx;+intnum_banks=mfx->num_gpio/8;+u8status[num_banks];+intret;+u8bank;++ret=mfx_block_read(mfx,MFX_REG_IRQ_GPI_PENDING,ARRAY_SIZE(status),+status);+if(ret<0){+dev_err(mfx_gpio->dev,"can't get IRQ_GPI_PENDING: %d\n",ret);+returnIRQ_NONE;+}++for(bank=0;bank<ARRAY_SIZE(status);bank++){+u8stat=status[bank];++if(!stat)+continue;++while(stat){+intbit=__ffs(stat);+intoffset=bank*NR_GPIOS_PER_BANK+bit;+intgpio_irq=irq_find_mapping(mfx_gpio->gc.irq.domain,+offset);+intirq_trigger=irq_get_trigger_type(gpio_irq);++handle_nested_irq(gpio_irq);+stat&=~(BIT(bit));++if(irq_trigger&IRQ_TYPE_EDGE_BOTH)+mfx_gpio_irq_toggle_trigger(&mfx_gpio->gc,+offset);+}++mfx_reg_write(mfx,MFX_REG_IRQ_GPI_ACK+bank,status[bank]);+}++returnIRQ_HANDLED;+}++staticintmfx_gpio_probe(structplatform_device*pdev)+{+structmfx*mfx=dev_get_drvdata(pdev->dev.parent);+structmfx_gpio*mfx_gpio;+intirq;+intret;++irq=platform_get_irq(pdev,0);+if(irq<0){+dev_err(&pdev->dev,"failed to get irq: %d\n",irq);++returnirq;+}++mfx_gpio=devm_kzalloc(&pdev->dev,sizeof(structmfx_gpio),+GFP_KERNEL);+if(!mfx_gpio)+return-ENOMEM;++mutex_init(&mfx_gpio->irq_lock);++mfx_gpio->dev=&pdev->dev;+mfx_gpio->mfx=mfx;++mfx_gpio->gc=mfx_gpio_chip;+mfx_gpio->gc.ngpio=mfx->num_gpio;+mfx_gpio->gc.parent=&pdev->dev;+mfx_gpio->gc.base=-1;+mfx_gpio->gc.of_node=pdev->dev.of_node;++if(mfx->blocks&MFX_BLOCK_ALTGPIO)+ret=mfx_enable(mfx,MFX_BLOCK_GPIO|MFX_BLOCK_ALTGPIO);+else+ret=mfx_enable(mfx,MFX_BLOCK_GPIO);++if(ret<0)+returnret;++ret=devm_request_threaded_irq(&pdev->dev,irq,+NULL,mfx_gpio_irq,IRQF_ONESHOT,+"mfx-gpio",mfx_gpio);+if(ret){+dev_err(&pdev->dev,"unable to request irq: %d\n",ret);+returnret;+}++ret=devm_gpiochip_add_data(&pdev->dev,&mfx_gpio->gc,mfx_gpio);+if(ret){+dev_err(&pdev->dev,"unable to add gpiochip: %d\n",ret);+returnret;+}++ret=gpiochip_irqchip_add_nested(&mfx_gpio->gc,&mfx_gpio_irq_chip,0,+handle_simple_irq,IRQ_TYPE_NONE);+if(ret){+dev_err(&pdev->dev,"could not connect irqchip to gpiochip\n");+returnret;+}++gpiochip_set_nested_irqchip(&mfx_gpio->gc,&mfx_gpio_irq_chip,irq);++platform_set_drvdata(pdev,mfx_gpio);++dev_info(&pdev->dev,"ST-MFX (GPIO) initialized\n");++return0;+}++staticintmfx_gpio_remove(structplatform_device*pdev)+{+structmfx*mfx=dev_get_drvdata(pdev->dev.parent);++returnmfx_disable(mfx,MFX_BLOCK_GPIO|MFX_BLOCK_ALTGPIO);+}++staticconststructof_device_idmfx_gpio_of_match[]={+{.compatible="st,mfx-gpio"},+{}+};+MODULE_DEVICE_TABLE(of,mfx_of_match);++staticstructplatform_drivermfx_gpio_driver={+.driver={+.name="st-mfx-gpio",+.of_match_table=mfx_gpio_of_match,+},+.probe=mfx_gpio_probe,+.remove=mfx_gpio_remove,+};+module_platform_driver(mfx_gpio_driver);++MODULE_DESCRIPTION("STMicroelectronics Multi-Function eXpander GPIO driver");+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");+MODULE_LICENSE("GPL v2");
From: Lee Jones <hidden> Date: 2018-02-12 12:06:09
On Thu, 08 Feb 2018, Amelie Delaunay wrote:
quoted hunk
ST Multi-Function eXpander (MFX) is a slave controller using I2C
for communication with the main MCU. Main features are:
- 16 fast GPIOs individually configurable in input/output
- 8 alternate GPIOs individually configurable in input/output
- Main MCU IDD measurement
- Resistive touchscreen controller
Only GPIO expander (16 fast GPIOs + 8 alternate) feature is
supported for the moment.
Signed-off-by: Amelie Delaunay <redacted>
---
drivers/mfd/Kconfig | 15 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/st-mfx.c | 526 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/st-mfx.h | 116 ++++++++++
4 files changed, 658 insertions(+)
create mode 100644 drivers/mfd/st-mfx.c
create mode 100644 include/linux/mfd/st-mfx.h
Is that really what this device is called?
Is there a datasheet?
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the functionality
+ of the device. Currently available sub drivers are:
+
+ GPIO: mfx-gpio
This driver would be easier to review if I could picture the device as
a whole. What other functionality does it have? What is it that
makes this an MFD?
quoted hunk
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
You don't need to put "for STMicroelectronics". This was something we
made up when submitting from a different (!st.com) email address.
+ * License terms: GPL V2.0.
+ *
+ * st-mfx Core Driver is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * st-mfx Core Driver is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * st-mfx Core Driver. If not, see <http://www.gnu.org/licenses/>.
You should be able to use the short version of the licensing
agreement. Also, please grep for "SPDX".
+ */
+#include <linux/bitfield.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/st-mfx.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+
+/**
+ * struct mfx_priv - MFX MFD private structure
+ * @dev: device, mostly for logs
+ * @regmap: register map
+ * @mfx: MFX MFD public structure, to share information with subdrivers
+ * @irq_domain: IRQ domain
+ * @irq: IRQ number for mfx
+ * @irq_lock: IRQ bus lock
+ * @irqen: cache of IRQ_SRC_EN register for bus_lock
+ * @oldirqen: cache of IRQ_SRC_EN register for bus_lock
+ */
/**
* struct mfx_priv - MFX MFD private structure
* @dev: device, mostly for logs
* @regmap: register map
* @mfx: MFX MFD public structure, to share information with subdrivers
* @irq_domain: IRQ domain
* @irq: IRQ number for mfx
* @irq_lock: IRQ bus lock
* @irqen: cache of IRQ_SRC_EN register for bus_lock
* @oldirqen: cache of IRQ_SRC_EN register for bus_lock
*/
Easier to read?
+ }
+ /*
+ * Here we could find other children like "st,mfx-ts" or
+ * "st,mfx-idd.
+ */
+ }
+
+ if (!(mfx_priv->mfx.blocks & MFX_BLOCK_TS) &&
+ !(mfx_priv->mfx.blocks & MFX_BLOCK_IDD)) {
+ mfx_priv->mfx.blocks |= MFX_BLOCK_ALTGPIO;
+ mfx_priv->mfx.num_gpio += 8;
+ }
What are TS and IDD?
This is starting to look like a GPIO driver, and nothing more.
+ /*
+ * TODO: aGPIO[3:0] and aGPIO[7:4] can be used independently:
+ * - if IDD is used but not TS, aGPIO[3:0] can be used as GPIO,
+ * - if TS is used but not IDD: aGPIO[7:4] can be used as GPIO.
+ */
+
+ return of_platform_populate(np, NULL, NULL, mfx_priv->dev);
+}
+
+int mfx_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct device_node *np = client->dev.of_node;
+ struct mfx_priv *mfx_priv;
+ int ret;
+
+ mfx_priv = devm_kzalloc(&client->dev, sizeof(struct mfx_priv),
+ GFP_KERNEL);
+ if (!mfx_priv)
+ return -ENOMEM;
+
+ mfx_priv->regmap = devm_regmap_init_i2c(client, &mfx_regmap_config);
+ if (IS_ERR(mfx_priv->regmap)) {
+ ret = PTR_ERR(mfx_priv->regmap);
+ dev_err(&client->dev, "failed to allocate register map: %d\n",
+ ret);
Nit: Prefer if you broke the line after '&client->dev,'.
From: Philippe Ombredanne <hidden> Date: 2018-02-12 14:16:24
Amelie,
On Mon, Feb 12, 2018 at 1:06 PM, Lee Jones [off-list ref] wrote:
On Thu, 08 Feb 2018, Amelie Delaunay wrote:
quoted
ST Multi-Function eXpander (MFX) is a slave controller using I2C
for communication with the main MCU. Main features are:
- 16 fast GPIOs individually configurable in input/output
- 8 alternate GPIOs individually configurable in input/output
- Main MCU IDD measurement
- Resistive touchscreen controller
Only GPIO expander (16 fast GPIOs + 8 alternate) feature is
supported for the moment.
Signed-off-by: Amelie Delaunay <redacted>
You don't need to put "for STMicroelectronics". This was something we
made up when submitting from a different (!st.com) email address.
quoted
+ * License terms: GPL V2.0.
+ *
+ * st-mfx Core Driver is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * st-mfx Core Driver is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * st-mfx Core Driver. If not, see <http://www.gnu.org/licenses/>.
You should be able to use the short version of the licensing
agreement. Also, please grep for "SPDX".
From: Andy Shevchenko <hidden> Date: 2018-02-14 15:30:26
On Thu, Feb 8, 2018 at 4:27 PM, Amelie Delaunay [off-list ref] wrote:
ST Multi-Function eXpander (MFX) can be used as GPIO expander.
It has 16 fast GPIOs and can have 8 extra alternate GPIOs
when other MFX features are not enabled.
+config GPIO_ST_MFX
+ bool "ST-MFX GPIOs"
+ depends on MFD_ST_MFX
+ depends on OF_GPIO
+ select GPIOLIB_IRQCHIP
+ help
+ GPIO driver for STMicroelectronics Multi-Function eXpander.
+
+ This provides GPIO interface supporting inputs and outputs.
+ * License terms: GPL V2.0.
+ *
+ * st-mfx-gpio is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * st-mfx-gpio is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * st-mfx-gpio. If not, see <http://www.gnu.org/licenses/>.
Use SPDX instead.
+ */
+#include <linux/of_gpio.h>
+/* MFX has a maximum of 24 gpios, with 8 gpios per bank, so 3 banks maximum */
+#define NR_MAX_GPIOS 24
+#define NR_GPIOS_PER_BANK 8
+#define NR_MAX_BANKS (NR_MAX_GPIOS / NR_GPIOS_PER_BANK)
+/*
+ * This is MFX-specific flags, overloading Linux-specific of_gpio_flags,
+ * needed in of_xlate callback.
+ * on MFX: - if GPIO is output:
Split to two lines.
+ * * (0) means PUSH_PULL
+ * * OF_GPIO_SINGLE_ENDED (=2) means OPEN-DRAIN
+ * - if GPIO is input:
+ * * (0) means NO_PULL
+ * * OF_MFX_GPI_PUSH_PULL (=2) means PUSH_PULL
+ *
+ * * OF_MFX_GPIO_PULL_UP programs pull up resistor on the GPIO,
+ * whatever its direction, without this flag, depending on
+ * GPIO type and direction, it programs either no pull or
+ * pull down resistor.
+ */
+enum of_mfx_gpio_flags {
+ OF_MFX_GPI_PUSH_PULL = 0x2,
+ OF_MFX_GPIO_PULL_UP = 0x4,
These have misleading prefix. OF_ is reserved for general OF stuff.
+ struct mutex irq_lock; /* IRQ bus lock */
+ u32 flags[NR_MAX_GPIOS];
+ /* Caches of interrupt control registers for bus_lock */
+ u8 regs[NR_CACHE_IRQ_REGS][NR_MAX_BANKS];
+ u8 oldregs[NR_CACHE_IRQ_REGS][NR_MAX_BANKS];
+};
+static int mfx_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct mfx_gpio *mfx_gpio = gpiochip_get_data(gc);
+ struct mfx *mfx = mfx_gpio->mfx;
+ u8 reg_type = MFX_REG_GPIO_TYPE + get_bank(offset);
+ u8 reg_pupd = MFX_REG_GPIO_PUPD + get_bank(offset);
+ u8 reg_dir = MFX_REG_GPIO_DIR + get_bank(offset);
+ u8 mask = get_mask(offset);
+ int ret;
+
+ /*
+ * In case of input direction, there is actually no way to apply some
+ * configuration in hardware, as it can be done with
+ * .set_config in case of output direction. That's why we apply
+ * here the MFX specific-flags.
+ */
+ if (mfx_gpio->flags[offset] & OF_MFX_GPI_PUSH_PULL)
+ ret = mfx_set_bits(mfx, reg_type, mask, mask);
+ else
+ ret = mfx_set_bits(mfx, reg_type, mask, 0);
+
Redundant empty line.
+ if (ret)
+ return ret;
+
+ if (mfx_gpio->flags[offset] & OF_MFX_GPIO_PULL_UP)
+ ret = mfx_set_bits(mfx, reg_pupd, mask, mask);
+ else
+ ret = mfx_set_bits(mfx, reg_pupd, mask, 0);
@@ -0,0 +1,51 @@+STMicroelectronics Multi-Function eXpander++ST Multi-Function eXpander (MFX) is a slave controller using I2C for+communication with the main MCU. Its main features are gpio expansion, main+MCU IDD measurement (IDD is the amount of current that flows through VDD)+and resistive touchscreen controller.
You don't have to implement all the drivers now, but please completely
describe the device. As is, there is no reason to have a child GPIO
node.
+
+Required properties:
+- compatible: must be "st,mfx"
Kind of generic. Only 1 single version ever?
+- reg: I2C address of the device
+- interrupts: interrupt triggered by MFX_IRQ_OUT signal
+- interrupt-parent: interrupt controller MFX is connected to
+- interrupt-controller: marks the device as an interrupt controller
+- #interrupt-cells: should be <1>, index of the interrupt within the
+ controller, in accordance with the "one cell" variant of
+ <devicetree/bindings/interrupt-controller/interrupt.txt>
+
+Optional nodes:
+
+* GPIO eXpander
+MFX provides 16 programmable GPIOs, and it is also possible to recover 8
+alternate GPIOs if the main functions are not used (touchscreen controller and
+IDD measurement not enabled).
+
+Required properties:
+- compatible : must be "st,mfx-gpio"
+- interrupt-parent : must be <&mfx>
Not necessary. A parent node with 'interrupt-controller' property is the
interrupt's parent.
+- interrupts = must be <0>
+- gpio-controller: marks the device node as a GPIO controller
+- #gpio-cells: should be <2>, the first cell is the GPIO offset on this GPIO
+ controller, the second cell is the gpio flags in accordance with
+ <dt-bindings/gpio/st-mfx-gpio.h>.
Custom flags? Use standard flags.
DT binding headers should be part of this patch.
@@ -0,0 +1,51 @@+STMicroelectronics Multi-Function eXpander++ST Multi-Function eXpander (MFX) is a slave controller using I2C for+communication with the main MCU. Its main features are gpio expansion, main+MCU IDD measurement (IDD is the amount of current that flows through VDD)+and resistive touchscreen controller.
You don't have to implement all the drivers now, but please completely
describe the device. As is, there is no reason to have a child GPIO
node.
The MFD driver will be abandoned as only GPIO part is used. I'll send a
V2 soon.
quoted
+
+Required properties:
+- compatible: must be "st,mfx"
Kind of generic. Only 1 single version ever?
"st-mfx" compatible will disappear in V2 (we keep only GPIO driver). MFX
version can be read in MFX FW_VERSION register. So do I keep
"st,mfx-gpio" compatible or you want to see mfx version ?
quoted
+- reg: I2C address of the device
+- interrupts: interrupt triggered by MFX_IRQ_OUT signal
+- interrupt-parent: interrupt controller MFX is connected to
+- interrupt-controller: marks the device as an interrupt controller
+- #interrupt-cells: should be <1>, index of the interrupt within the
+ controller, in accordance with the "one cell" variant of
+ <devicetree/bindings/interrupt-controller/interrupt.txt>
+
+Optional nodes:
+
+* GPIO eXpander
+MFX provides 16 programmable GPIOs, and it is also possible to recover 8
+alternate GPIOs if the main functions are not used (touchscreen controller and
+IDD measurement not enabled).
+
+Required properties:
+- compatible : must be "st,mfx-gpio"
+- interrupt-parent : must be <&mfx>
Not necessary. A parent node with 'interrupt-controller' property is the
interrupt's parent.
I will keep it in mind.
quoted
+- interrupts = must be <0>
+- gpio-controller: marks the device node as a GPIO controller
+- #gpio-cells: should be <2>, the first cell is the GPIO offset on this GPIO
+ controller, the second cell is the gpio flags in accordance with
+ <dt-bindings/gpio/st-mfx-gpio.h>.
Custom flags? Use standard flags.
DT binding headers should be part of this patch.
Custom flags because MFX GPIOs have several types:
- Output open drain with internal pull-up resistor
- Output open drain without internal pull-up resistor
- Output push pull without internal pull resistor
- Input with internal pull-up resistor
- Input with internal pull-down resistor
- Input floating
- Input analog.
Standard flags don't have pull up or down information. That's why I am
using custom flags, they overloads standard flags.
I will add DT bindings header in this patch V2.
Thanks Philippe,
I will take this into account for V2.
Regards,
Amelie
On 02/12/2018 03:15 PM, Philippe Ombredanne wrote:
Amelie,
On Mon, Feb 12, 2018 at 1:06 PM, Lee Jones [off-list ref] wrote:
quoted
On Thu, 08 Feb 2018, Amelie Delaunay wrote:
quoted
ST Multi-Function eXpander (MFX) is a slave controller using I2C
for communication with the main MCU. Main features are:
- 16 fast GPIOs individually configurable in input/output
- 8 alternate GPIOs individually configurable in input/output
- Main MCU IDD measurement
- Resistive touchscreen controller
Only GPIO expander (16 fast GPIOs + 8 alternate) feature is
supported for the moment.
Signed-off-by: Amelie Delaunay <redacted>
You don't need to put "for STMicroelectronics". This was something we
made up when submitting from a different (!st.com) email address.
quoted
+ * License terms: GPL V2.0.
+ *
+ * st-mfx Core Driver is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * st-mfx Core Driver is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * st-mfx Core Driver. If not, see <http://www.gnu.org/licenses/>.
You should be able to use the short version of the licensing
agreement. Also, please grep for "SPDX".
ST Multi-Function eXpander (MFX) is a slave controller using I2C
for communication with the main MCU. Main features are:
- 16 fast GPIOs individually configurable in input/output
- 8 alternate GPIOs individually configurable in input/output
- Main MCU IDD measurement
- Resistive touchscreen controller
Only GPIO expander (16 fast GPIOs + 8 alternate) feature is
supported for the moment.
Signed-off-by: Amelie Delaunay <redacted>
---
drivers/mfd/Kconfig | 15 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/st-mfx.c | 526 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/st-mfx.h | 116 ++++++++++
4 files changed, 658 insertions(+)
create mode 100644 drivers/mfd/st-mfx.c
create mode 100644 include/linux/mfd/st-mfx.h
Yes, all STMicroelectronics Evaluation boards and Discovery kits (using
an expansion device) user manuals refer to "MFX (Multi Function eXpander)".
Is there a datasheet?
quoted
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the functionality
+ of the device. Currently available sub drivers are:
+
+ GPIO: mfx-gpio
This driver would be easier to review if I could picture the device as
a whole. What other functionality does it have? What is it that
makes this an MFD?
MFX is a slave controller based on the STM32L152CCT6 MCU.
MFX firmware integrates 3 features:
- main MCU IDD measurement: this allows measurement of the IDD current
consumed by the main MCU, especially for static measurement in low power
mode.
- resistive touchscreen controller, using ADC inputs of STM32L152CCT6.
- GPIO expander: 16 programmable input/output + 8 alternate GPIO
(Touchscreen controller uses the four first gpios of this bank when
enabled, IDD used the 4 last ones when enabled).
IDD measurement driver and touchscreen driver have not been developed
yet because I have no hardware to test it. But anyway, I will send a V2
with gpio driver only.
quoted
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
You don't need to put "for STMicroelectronics". This was something we
made up when submitting from a different (!st.com) email address.
OK, I will fix it in gpio driver for V2.
quoted
+ * License terms: GPL V2.0.
+ *
+ * st-mfx Core Driver is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * st-mfx Core Driver is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * st-mfx Core Driver. If not, see <http://www.gnu.org/licenses/>.
You should be able to use the short version of the licensing
agreement. Also, please grep for "SPDX".
OK.
quoted
+ */
+#include <linux/bitfield.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/st-mfx.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+
+/**
+ * struct mfx_priv - MFX MFD private structure
+ * @dev: device, mostly for logs
+ * @regmap: register map
+ * @mfx: MFX MFD public structure, to share information with subdrivers
+ * @irq_domain: IRQ domain
+ * @irq: IRQ number for mfx
+ * @irq_lock: IRQ bus lock
+ * @irqen: cache of IRQ_SRC_EN register for bus_lock
+ * @oldirqen: cache of IRQ_SRC_EN register for bus_lock
+ */
/**
* struct mfx_priv - MFX MFD private structure
* @dev: device, mostly for logs
* @regmap: register map
* @mfx: MFX MFD public structure, to share information with subdrivers
* @irq_domain: IRQ domain
* @irq: IRQ number for mfx
* @irq_lock: IRQ bus lock
* @irqen: cache of IRQ_SRC_EN register for bus_lock
* @oldirqen: cache of IRQ_SRC_EN register for bus_lock
*/
Easier to read?
I will pay attention to the indentation in gpio driver V2.
+ unsigned long status, bit;
+ u8 ack;
+ int ret;
+
+ ret = mfx_reg_read(&mfx_priv->mfx, MFX_REG_IRQ_PENDING);
+ if (ret < 0) {
+ dev_err(mfx_priv->dev, "can't get IRQ_PENDING: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ /* It can be GPIO, IDD, ERROR, TS* IRQs */
+ status = ret & mfx_priv->irqen;
+
+ /*
+ * There is no ACK for GPIO, MFX_REG_IRQ_PENDING_GPIO is a logical OR
+ * of MFX_REG_IRQ_GPI _PENDING1/_PENDING2/_PENDING3
+ */
+ ack = status & ~MFX_REG_IRQ_PENDING_GPIO;
+
+ for_each_set_bit(bit, &status, 8)
+ handle_nested_irq(irq_find_mapping(mfx_priv->irq_domain, bit));
+
+ if (ack)
+ mfx_reg_write(&mfx_priv->mfx, MFX_REG_IRQ_ACK, ack);
+
+ return IRQ_HANDLED;
+}
+
+static int mfx_irq_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ struct mfx_priv *mfx_priv = d->host_data;
+
+ irq_set_chip_data(virq, mfx_priv);
+ irq_set_chip(virq, &mfx_irq_chip);
+ irq_set_nested_thread(virq, 1);
+ irq_set_parent(virq, mfx_priv->irq);
+ irq_set_noprobe(virq);
+
+ return 0;
+}
+
+static void mfx_irq_unmap(struct irq_domain *d, unsigned int virq)
+{
+ irq_set_chip_and_handler(virq, NULL, NULL);
+ irq_set_chip_data(virq, NULL);
+}
+
+static const struct irq_domain_ops mfx_irq_ops = {
+ .map = mfx_irq_map,
+ .unmap = mfx_irq_unmap,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int mfx_irq_init(struct mfx_priv *mfx_priv, struct device_node *np)
+{
+ int irqoutpin = MFX_REG_IRQ_OUT_PIN_TYPE; /* Push-Pull */
+ int irqtrigger, ret;
+
+ mfx_priv->irq = of_irq_get(np, 0);
+ if (mfx_priv->irq > 0) {
+ irqtrigger = irq_get_trigger_type(mfx_priv->irq);
+ } else {
+ dev_err(mfx_priv->dev, "failed to get irq: %d\n",
+ mfx_priv->irq);
+ return mfx_priv->irq;
+ }
+
+ if ((irqtrigger & IRQ_TYPE_EDGE_FALLING) ||
+ (irqtrigger & IRQ_TYPE_LEVEL_LOW))
+ irqoutpin &= ~MFX_REG_IRQ_OUT_PIN_POL; /* Active Low */
+ else
+ irqoutpin |= MFX_REG_IRQ_OUT_PIN_POL; /* Active High */
+
+ mfx_reg_write(&mfx_priv->mfx, MFX_REG_IRQ_OUT_PIN, irqoutpin);
+
+ mfx_priv->irq_domain = irq_domain_add_linear(np, MFX_IRQ_SRC_NR,
+ &mfx_irq_ops, mfx_priv);
+ if (!mfx_priv->irq_domain) {
+ dev_err(mfx_priv->dev, "failed to create irq domain\n");
+ return -ENOMEM;
+ }
+
+ ret = devm_request_threaded_irq(mfx_priv->dev, mfx_priv->irq,
+ NULL, mfx_irq,
+ irqtrigger | IRQF_ONESHOT, "mfx",
+ mfx_priv);
+ if (ret) {
+ dev_err(mfx_priv->dev, "failed to request irq: %d\n", ret);
+ irq_domain_remove(mfx_priv->irq_domain);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void mfx_irq_remove(struct mfx_priv *mfx_priv)
+{
+ int hwirq;
+
+ for (hwirq = 0; hwirq < MFX_IRQ_SRC_NR; hwirq++)
+ irq_dispose_mapping(irq_find_mapping(mfx_priv->irq_domain,
+ hwirq));
+ irq_domain_remove(mfx_priv->irq_domain);
+}
Is there any reason why this IRQ stuff can't live in the GPIO driver?
MFX has a first level of interrupts (8 sources), with touscreen
interrupts, idd interrupt and gpio interrupt. Then gpio has its own
level of interrupts, with 24 possible sources.
GPIO interrupts are raised through MFX_IRQ_OUT pin only if at least one
source is enabled at gpio level, and gpio interrupt is enabled at MFX level.
_TS_OVF
|_TS_FULL
|_TS_TH
|_TS_NE
MFX_IRQ_OUT < _TS_DET
|_ERROR(IDD) _ GPIO0
|_IDD |
|_GPIO---------<...
|_ GPIO23
quoted
+static int mfx_chip_init(struct mfx_priv *mfx_priv, u16 i2c_addr)
+{
+ struct mfx *mfx = &mfx_priv->mfx;
+ int ret;
+ int id;
+ u8 version[2];
+
+ id = mfx_reg_read(mfx, MFX_REG_CHIP_ID);
+ if (id < 0) {
+ dev_err(mfx_priv->dev, "error reading chip id: %d\n", id);
+ return id;
+ }
+
+ ret = mfx_block_read(mfx, MFX_REG_FW_VERSION_MSB,
+ ARRAY_SIZE(version), version);
+ if (ret < 0) {
+ dev_err(mfx_priv->dev, "error reading fw version: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Check that ID is the complement of the I2C address:
+ * MFX I2C address follows the 7-bit format (MSB), that's why
+ * i2c_addr is shifted.
+ *
+ * MFX_I2C_ADDR | MFX | Linux
+ * input pin | I2C device address | I2C device address
+ *--------------------------------------------------------
+ * 0 | b: 1000 010x h:0x84 | 0x42
+ * 1 | b: 1000 011x h:0x86 | 0x43
+ */
+ if (FIELD_GET(MFX_REG_CHIP_ID_MASK, ~id) != (i2c_addr << 1)) {
+ dev_err(mfx_priv->dev, "unknown chip id: %#x\n", id);
+ return -EINVAL;
+ }
+
+ dev_info(mfx_priv->dev, "ST-MFX chip id: %#x, fw version: %x.%02x\n",
+ id, version[0], version[1]);
+
+ /* Disable all features, subdrivers should enable what they need */
+ ret = mfx_disable(mfx, ~0);
+ if (ret)
+ return ret;
+
+ return mfx_reset(mfx);
+}
+
+static const struct regmap_config mfx_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct of_device_id mfx_of_match[] = {
+ { .compatible = "st,mfx" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mfx_of_match);
+
+static int mfx_of_probe(struct device_node *np, struct mfx_priv *mfx_priv)
+{
+ struct device_node *child;
+
+ if (!np)
+ return -ENODEV;
Was used in gpio driver. But anyway this will disappear in V2.
quoted
+ }
+ /*
+ * Here we could find other children like "st,mfx-ts" or
+ * "st,mfx-idd.
+ */
+ }
+
+ if (!(mfx_priv->mfx.blocks & MFX_BLOCK_TS) &&
+ !(mfx_priv->mfx.blocks & MFX_BLOCK_IDD)) {
+ mfx_priv->mfx.blocks |= MFX_BLOCK_ALTGPIO;
+ mfx_priv->mfx.num_gpio += 8;
+ }
What are TS and IDD?
This is starting to look like a GPIO driver, and nothing more.
Without touschreen controller driver and idd measurement driver,
definitely yes.
quoted
+ /*
+ * TODO: aGPIO[3:0] and aGPIO[7:4] can be used independently:
+ * - if IDD is used but not TS, aGPIO[3:0] can be used as GPIO,
+ * - if TS is used but not IDD: aGPIO[7:4] can be used as GPIO.
+ */
+
+ return of_platform_populate(np, NULL, NULL, mfx_priv->dev);
+}
+
+int mfx_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct device_node *np = client->dev.of_node;
+ struct mfx_priv *mfx_priv;
+ int ret;
+
+ mfx_priv = devm_kzalloc(&client->dev, sizeof(struct mfx_priv),
+ GFP_KERNEL);
+ if (!mfx_priv)
+ return -ENOMEM;
+
+ mfx_priv->regmap = devm_regmap_init_i2c(client, &mfx_regmap_config);
+ if (IS_ERR(mfx_priv->regmap)) {
+ ret = PTR_ERR(mfx_priv->regmap);
+ dev_err(&client->dev, "failed to allocate register map: %d\n",
+ ret);
Nit: Prefer if you broke the line after '&client->dev,'.
On Thu, Feb 8, 2018 at 4:27 PM, Amelie Delaunay [off-list ref] wrote:
quoted
ST Multi-Function eXpander (MFX) can be used as GPIO expander.
It has 16 fast GPIOs and can have 8 extra alternate GPIOs
when other MFX features are not enabled.
quoted
+config GPIO_ST_MFX
+ bool "ST-MFX GPIOs"
+ depends on MFD_ST_MFX
+ depends on OF_GPIO
+ select GPIOLIB_IRQCHIP
+ help
+ GPIO driver for STMicroelectronics Multi-Function eXpander.
+
+ This provides GPIO interface supporting inputs and outputs.
+ * License terms: GPL V2.0.
+ *
+ * st-mfx-gpio is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * st-mfx-gpio is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * st-mfx-gpio. If not, see <http://www.gnu.org/licenses/>.
Use SPDX instead.
OK.
quoted
+ */
quoted
+#include <linux/of_gpio.h>
quoted
+/* MFX has a maximum of 24 gpios, with 8 gpios per bank, so 3 banks maximum */
+#define NR_MAX_GPIOS 24
+#define NR_GPIOS_PER_BANK 8
+#define NR_MAX_BANKS (NR_MAX_GPIOS / NR_GPIOS_PER_BANK)
I would rather keep it consistent, i.e.
get_bank() [as is]
get_mask -> get_shift() [w/o BIT() macro]
#define get_shift(offset) ((u8)((offset) % NR_GPIOS_PER_BANK))
u8 mask = (u8)BIT(get_shift(offset));
I'm not sure consistency is more important than readability and
convenience ?
+/*
+ * This is MFX-specific flags, overloading Linux-specific of_gpio_flags,
+ * needed in of_xlate callback.
quoted
+ * on MFX: - if GPIO is output:
Split to two lines.
OK.
quoted
+ * * (0) means PUSH_PULL
+ * * OF_GPIO_SINGLE_ENDED (=2) means OPEN-DRAIN
+ * - if GPIO is input:
+ * * (0) means NO_PULL
+ * * OF_MFX_GPI_PUSH_PULL (=2) means PUSH_PULL
+ *
+ * * OF_MFX_GPIO_PULL_UP programs pull up resistor on the GPIO,
+ * whatever its direction, without this flag, depending on
+ * GPIO type and direction, it programs either no pull or
+ * pull down resistor.
+ */
+enum of_mfx_gpio_flags {
+ OF_MFX_GPI_PUSH_PULL = 0x2,
+ OF_MFX_GPIO_PULL_UP = 0x4,
These have misleading prefix. OF_ is reserved for general OF stuff.
+ struct mutex irq_lock; /* IRQ bus lock */
+ u32 flags[NR_MAX_GPIOS];
+ /* Caches of interrupt control registers for bus_lock */
+ u8 regs[NR_CACHE_IRQ_REGS][NR_MAX_BANKS];
+ u8 oldregs[NR_CACHE_IRQ_REGS][NR_MAX_BANKS];
+};
quoted
+static int mfx_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct mfx_gpio *mfx_gpio = gpiochip_get_data(gc);
+ struct mfx *mfx = mfx_gpio->mfx;
+ u8 reg_type = MFX_REG_GPIO_TYPE + get_bank(offset);
+ u8 reg_pupd = MFX_REG_GPIO_PUPD + get_bank(offset);
+ u8 reg_dir = MFX_REG_GPIO_DIR + get_bank(offset);
+ u8 mask = get_mask(offset);
+ int ret;
+
+ /*
+ * In case of input direction, there is actually no way to apply some
+ * configuration in hardware, as it can be done with
+ * .set_config in case of output direction. That's why we apply
+ * here the MFX specific-flags.
+ */
+ if (mfx_gpio->flags[offset] & OF_MFX_GPI_PUSH_PULL)
+ ret = mfx_set_bits(mfx, reg_type, mask, mask);
+ else
+ ret = mfx_set_bits(mfx, reg_type, mask, 0);
quoted
+
Redundant empty line.
OK.
quoted
+ if (ret)
+ return ret;
+
+ if (mfx_gpio->flags[offset] & OF_MFX_GPIO_PULL_UP)
+ ret = mfx_set_bits(mfx, reg_pupd, mask, mask);
+ else
+ ret = mfx_set_bits(mfx, reg_pupd, mask, 0);
It is .irq_bus_lock and .irq_bus_sync_unlock. Description is available
in include/linux/irq.h:
* @irq_bus_lock: function to lock access to slow bus (i2c) chips
* @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
@@ -0,0 +1,51 @@+STMicroelectronics Multi-Function eXpander++ST Multi-Function eXpander (MFX) is a slave controller using I2C for+communication with the main MCU. Its main features are gpio expansion, main+MCU IDD measurement (IDD is the amount of current that flows through VDD)+and resistive touchscreen controller.
You don't have to implement all the drivers now, but please completely
describe the device. As is, there is no reason to have a child GPIO
node.
The MFD driver will be abandoned as only GPIO part is used. I'll send a
V2 soon.
quoted
quoted
+
+Required properties:
+- compatible: must be "st,mfx"
Kind of generic. Only 1 single version ever?
"st-mfx" compatible will disappear in V2 (we keep only GPIO driver). MFX
version can be read in MFX FW_VERSION register. So do I keep
"st,mfx-gpio" compatible or you want to see mfx version ?
That sounds a bit worrying. The other functions will *never* get
accessed? The DT should reflect the h/w including any future needs,
not just what you have a driver for today.
quoted
quoted
+- reg: I2C address of the device
+- interrupts: interrupt triggered by MFX_IRQ_OUT signal
+- interrupt-parent: interrupt controller MFX is connected to
+- interrupt-controller: marks the device as an interrupt controller
+- #interrupt-cells: should be <1>, index of the interrupt within the
+ controller, in accordance with the "one cell" variant of
+ <devicetree/bindings/interrupt-controller/interrupt.txt>
+
+Optional nodes:
+
+* GPIO eXpander
+MFX provides 16 programmable GPIOs, and it is also possible to recover 8
+alternate GPIOs if the main functions are not used (touchscreen controller and
+IDD measurement not enabled).
+
+Required properties:
+- compatible : must be "st,mfx-gpio"
+- interrupt-parent : must be <&mfx>
Not necessary. A parent node with 'interrupt-controller' property is the
interrupt's parent.
I will keep it in mind.
quoted
quoted
+- interrupts = must be <0>
+- gpio-controller: marks the device node as a GPIO controller
+- #gpio-cells: should be <2>, the first cell is the GPIO offset on this GPIO
+ controller, the second cell is the gpio flags in accordance with
+ <dt-bindings/gpio/st-mfx-gpio.h>.
Custom flags? Use standard flags.
DT binding headers should be part of this patch.
Custom flags because MFX GPIOs have several types:
- Output open drain with internal pull-up resistor
- Output open drain without internal pull-up resistor
- Output push pull without internal pull resistor
- Input with internal pull-up resistor
- Input with internal pull-down resistor
- Input floating
- Input analog.
Standard flags don't have pull up or down information. That's why I am
using custom flags, they overloads standard flags.
Hi Amelie,
thanks a lot for your patches!
On Thu, Feb 8, 2018 at 3:27 PM, Amelie Delaunay [off-list ref] wrote:
This series adds support for STMicroelectronics MultiFunction eXpander
(ST MFX), used on some STM32 discovery and evaluation boards.
So I take it that the department creating the STMPE
"ST MPE" or "ST Microelectronics MultiPurpose Expander"
Now created a new set of circuits?
Can we first establish whether this new family is really so
different from STMPE that it really needs a new driver in
MFD and GPIO (and I guess pin control as well)?
I would be annoyed to see later that it is just a few bytes
separating it from the STMPE, in that case I think it is
better to just reuse/improve the good old stmpe driver
and take it from there.
Yours,
Linus Walleij
On Thu, Feb 8, 2018 at 3:27 PM, Amelie Delaunay [off-list ref] wrote:
+Required properties:
+- compatible: must be "st,mfx"
I bet this should be more specific. Tomorrow there will be a new
version of this expander and then we will wish that we used
a more specific compatible for the first one.
Does this chip have any kind of product number on it?
Else I would be tempted to use the compatible "st,mfx-0000"
or something, indicating it is the first of its kind.
+- reg: I2C address of the device
+- interrupts: interrupt triggered by MFX_IRQ_OUT signal
+- interrupt-parent: interrupt controller MFX is connected to
+- interrupt-controller: marks the device as an interrupt controller
+- #interrupt-cells: should be <1>, index of the interrupt within the
+ controller, in accordance with the "one cell" variant of
+ <devicetree/bindings/interrupt-controller/interrupt.txt>
Seems fine.
+Optional nodes:
+
+* GPIO eXpander
+MFX provides 16 programmable GPIOs, and it is also possible to recover 8
+alternate GPIOs if the main functions are not used (touchscreen controller and
+IDD measurement not enabled).
Apparenly Rob thinks this should go elsewhere.
+- gpio-controller: marks the device node as a GPIO controller
+- #gpio-cells: should be <2>, the first cell is the GPIO offset on this GPIO
+ controller, the second cell is the gpio flags in accordance with
+ <dt-bindings/gpio/st-mfx-gpio.h>.
Let's discuss these extra GPIO flag bindings separately.
Yours,
Linus Walleij
On Mon, Feb 19, 2018 at 4:59 PM, Amelie DELAUNAY [off-list ref] wrote:
On 02/19/2018 12:19 AM, Rob Herring wrote:
quoted
On Thu, Feb 08, 2018 at 03:27:32PM +0100, Amelie Delaunay wrote:
quoted
quoted
+- interrupts = must be <0>
+- gpio-controller: marks the device node as a GPIO controller
+- #gpio-cells: should be <2>, the first cell is the GPIO offset on this GPIO
+ controller, the second cell is the gpio flags in accordance with
+ <dt-bindings/gpio/st-mfx-gpio.h>.
Custom flags? Use standard flags.
DT binding headers should be part of this patch.
Custom flags because MFX GPIOs have several types:
- Output open drain with internal pull-up resistor
- Output open drain without internal pull-up resistor
- Output push pull without internal pull resistor
- Input with internal pull-up resistor
- Input with internal pull-down resistor
- Input floating
- Input analog.
Standard flags don't have pull up or down information. That's why I am
using custom flags, they overloads standard flags.
This is because pull up/down and tristate/high z (floating)
also known as PIN_CONFIG_BIAS_HIGH_IMPEDANCE,
is controlled by the pin control subsystem, not GPIO.
If your hardware does even more pin control (like multiplexing)
then I suggest that you create a pin control driver back-end for
this and put the resulting driver in drivers/pinctrl.
Some recent additions of expanders in drivers/pinctrl makes
for great inspiration for this work. See for example:
drivers/pinctrl/pinctrl-sx150x.c
drivers/pinctrl/pinctrl-mcp23s08.c
These are both combined pin control and GPIO drivers that
we moved from drivers/gpio because we concluded that they
do more than just GPIO. The GPIO lines are matches to
pins using the GPIO ranges from the call to
gpiochip_add_pin_range() and using gpiochip_generic_config()
as the gpiochip .set_config() callback.
It has been discussed to expose subsets of pin config to
GPIO. Indeed, we already handle open drain/source and
debounce by simply calling into the pin control back-end
or handling it directly in the GPIO driver using the standard
pin config properties.
I am reluctant about this, I think the split of responsibilities
is pretty nice.
I'd recommend looking at the sx150x pin control driver and
check if you can maybe take this direction with the patch?
Yours,
Linus Walleij
On Thu, Feb 8, 2018 at 3:27 PM, Amelie Delaunay [off-list ref] wrote:
Thanks for working on this complex expander driver.
It is a bit daunting. Sorry if there are lots of comments and
considerations, but it reflects the complexity of the hardware.
Do you need this? Can't you just use regmap and pass
around a struct regmap *map to access registers?
You don't necessarily need to use the default I2C regmap
(like, e.g. drivers/mfd/stw481x.c) but even if a more
complex access pattern is used to read/write registers
I am pretty sure you can use regmap for it.
+int mfx_enable(struct mfx *mfx, unsigned int blocks);
+int mfx_disable(struct mfx *mfx, unsigned int blocks);
This is similar to
extern int stmpe_enable(struct stmpe *stmpe, unsigned int blocks);
extern int stmpe_disable(struct stmpe *stmpe, unsigned int blocks);
So again I am suspicious about duplication of driver code.
It even looks a bit like this driver started as a copy of
the STMPE driver, which is not a good sign. It might be
that it was copied from there because the hardware is
actually very similar.
+/* General */
+#define MFX_REG_CHIP_ID 0x00 /* R */
+#define MFX_REG_FW_VERSION_MSB 0x01 /* R */
+#define MFX_REG_FW_VERSION_LSB 0x02 /* R */
+#define MFX_REG_SYS_CTRL 0x40 /* RW */
The STMPE driver defines enumerated registers in
include/linux/mfd/stmpe.h
then assign each variant using the model specifics in
drivers/mfd/stmpe.h
This doesn't seem super much different.
Even if the old STMPE driver may be a bad fit, is is better
to improve that (e.g. migrate it to use regmap and rewrite the
stmpe-gpio.c driver to use pin control) and use also for this
driver, or write a new driver from scratch like this?
I'm not so sure.
I do know that developers not always like to take out old
hardware and old development boards and start hacking
away before they can get some nice new hardware going
but I am worried that this may be one of those cases where
a serious cleanup of the aging STMPE driver may be a
first necessary step.
I have not read the patch yet. But just for notice:
This output IRQ type needs to be handled as well.
Check the code in
drivers/iio/common/st_sensors/st_sensors_trigger.c
To see how you can detect the properties of an IRQ
to set the right polarity, and handling of open drain
IRQ lines.
Yours,
Linus Walleij
On Thu, Feb 8, 2018 at 3:27 PM, Amelie Delaunay [off-list ref] wrote:
ST Multi-Function eXpander (MFX) can be used as GPIO expander.
It has 16 fast GPIOs and can have 8 extra alternate GPIOs
when other MFX features are not enabled.
Signed-off-by: Amelie Delaunay <redacted>
As mentioned I think this driver needs to:
- Move to drivers/pinctrl/pinctrl-stmfx.c similar to the sx150x controller.
- Pass a regmap around instead of the custom accessor functions.
- Maybe refactor and reuse STMPE infrastructure
Yours,
Linus Walleij
As I think this should not all be handled by GPIO, the joystick on
gpio-keys needs a pin control handle pointing back to pin control
states on the respective pins in the pin control/GPIO driver,
where it can set up these properties.
For the individual lines the pushing and pulling flags should be
removed.
Yours,
Linus Walleij
So I think this node should contain some pin config states
that can be referenced by the drivers to set up push/pull
etc. Probably it can use just standard pin config properties
like sx150x.
Yours,
Linus Walleij
Hi Linus,
Thanks for your review on the whole series.
On 02/22/2018 02:06 PM, Linus Walleij wrote:
Hi Amelie,
thanks a lot for your patches!
On Thu, Feb 8, 2018 at 3:27 PM, Amelie Delaunay [off-list ref] wrote:
quoted
This series adds support for STMicroelectronics MultiFunction eXpander
(ST MFX), used on some STM32 discovery and evaluation boards.
So I take it that the department creating the STMPE
"ST MPE" or "ST Microelectronics MultiPurpose Expander"
Now created a new set of circuits?
ST MFX is based on a STM32L152 with a firmware offering features similar
to the STMPE (GPIO and TS controller) but also IDD measurement to
measure the power consumption of the MCU to which MFX is connected to.
Can we first establish whether this new family is really so
different from STMPE that it really needs a new driver in
MFD and GPIO (and I guess pin control as well)?
ST MFX is different from STMPE as far as the HW is completely different.
IDD is a new feature.
TS management is completely different.
GPIO management looks like but is also rather different.
ST MFX counts a first level of 8 interrupts (acked by writing in the ACK
register), then a second level of 24 interrupts for GPIOs. GPIO IRQ can
be triggered on low level, falling edge, high level, rising edge. GPIO
IRQ have to be acked by writing in the GPI_ACK register, GPIO can be
output open-drain with/without internal pull-up, output push-pull, input
with pull-up/down, input floating or analog.
I would be annoyed to see later that it is just a few bytes
separating it from the STMPE, in that case I think it is
better to just reuse/improve the good old stmpe driver
and take it from there.
Sure, but if it was a new STMPE variant, it would be called STMPE24xx or
something like that (24 GPIOs maximum, 20 if IDD or TS is enabled, 16 if
IDD and TS are enabled).
Regards,
Amelie
On Mon, Feb 19, 2018 at 4:59 PM, Amelie DELAUNAY [off-list ref] wrote:
quoted
On 02/19/2018 12:19 AM, Rob Herring wrote:
quoted
On Thu, Feb 08, 2018 at 03:27:32PM +0100, Amelie Delaunay wrote:
quoted
quoted
quoted
+- interrupts = must be <0>
+- gpio-controller: marks the device node as a GPIO controller
+- #gpio-cells: should be <2>, the first cell is the GPIO offset on this GPIO
+ controller, the second cell is the gpio flags in accordance with
+ <dt-bindings/gpio/st-mfx-gpio.h>.
Custom flags? Use standard flags.
DT binding headers should be part of this patch.
Custom flags because MFX GPIOs have several types:
- Output open drain with internal pull-up resistor
- Output open drain without internal pull-up resistor
- Output push pull without internal pull resistor
- Input with internal pull-up resistor
- Input with internal pull-down resistor
- Input floating
- Input analog.
Standard flags don't have pull up or down information. That's why I am
using custom flags, they overloads standard flags.
This is because pull up/down and tristate/high z (floating)
also known as PIN_CONFIG_BIAS_HIGH_IMPEDANCE,
is controlled by the pin control subsystem, not GPIO.
If your hardware does even more pin control (like multiplexing)
then I suggest that you create a pin control driver back-end for
this and put the resulting driver in drivers/pinctrl.
Some recent additions of expanders in drivers/pinctrl makes
for great inspiration for this work. See for example:
drivers/pinctrl/pinctrl-sx150x.c
drivers/pinctrl/pinctrl-mcp23s08.c
These are both combined pin control and GPIO drivers that
we moved from drivers/gpio because we concluded that they
do more than just GPIO. The GPIO lines are matches to
pins using the GPIO ranges from the call to
gpiochip_add_pin_range() and using gpiochip_generic_config()
as the gpiochip .set_config() callback.
It has been discussed to expose subsets of pin config to
GPIO. Indeed, we already handle open drain/source and
debounce by simply calling into the pin control back-end
or handling it directly in the GPIO driver using the standard
pin config properties.
I am reluctant about this, I think the split of responsibilities
is pretty nice.
I'd recommend looking at the sx150x pin control driver and
check if you can maybe take this direction with the patch?
Thanks for highlighting these examples. I will have a look on these!
Regards,
Amelie
On Thu, Feb 8, 2018 at 3:27 PM, Amelie Delaunay [off-list ref] wrote:
quoted
+Required properties:
+- compatible: must be "st,mfx"
I bet this should be more specific. Tomorrow there will be a new
version of this expander and then we will wish that we used
a more specific compatible for the first one.
Does this chip have any kind of product number on it?
Else I would be tempted to use the compatible "st,mfx-0000"
or something, indicating it is the first of its kind.
This chip has a FW version. So, I agree, I should use a compatible that
reflects this FW version.
quoted
+- reg: I2C address of the device
+- interrupts: interrupt triggered by MFX_IRQ_OUT signal
+- interrupt-parent: interrupt controller MFX is connected to
+- interrupt-controller: marks the device as an interrupt controller
+- #interrupt-cells: should be <1>, index of the interrupt within the
+ controller, in accordance with the "one cell" variant of
+ <devicetree/bindings/interrupt-controller/interrupt.txt>
Seems fine.
quoted
+Optional nodes:
+
+* GPIO eXpander
+MFX provides 16 programmable GPIOs, and it is also possible to recover 8
+alternate GPIOs if the main functions are not used (touchscreen controller and
+IDD measurement not enabled).
Apparenly Rob thinks this should go elsewhere.
It would go with gpio/pinctrl driver, so in dt-bindings/(gpio|pinctrl).
Regards,
Amelie
quoted
+- gpio-controller: marks the device node as a GPIO controller
+- #gpio-cells: should be <2>, the first cell is the GPIO offset on this GPIO
+ controller, the second cell is the gpio flags in accordance with
+ <dt-bindings/gpio/st-mfx-gpio.h>.
Let's discuss these extra GPIO flag bindings separately.
Yours,
Linus Walleij
On Thu, Feb 8, 2018 at 3:27 PM, Amelie Delaunay [off-list ref] wrote:
Thanks for working on this complex expander driver.
It is a bit daunting. Sorry if there are lots of comments and
considerations, but it reflects the complexity of the hardware.
Do you need this? Can't you just use regmap and pass
around a struct regmap *map to access registers?
You don't necessarily need to use the default I2C regmap
(like, e.g. drivers/mfd/stw481x.c) but even if a more
complex access pattern is used to read/write registers
I am pretty sure you can use regmap for it.
Yes, Lee has also raised this point.
quoted
+int mfx_enable(struct mfx *mfx, unsigned int blocks);
+int mfx_disable(struct mfx *mfx, unsigned int blocks);
This is similar to
extern int stmpe_enable(struct stmpe *stmpe, unsigned int blocks);
extern int stmpe_disable(struct stmpe *stmpe, unsigned int blocks);
So again I am suspicious about duplication of driver code.
It even looks a bit like this driver started as a copy of
the STMPE driver, which is not a good sign. It might be
that it was copied from there because the hardware is
actually very similar.
HW is different, FW looks like similar.
quoted
+/* General */
+#define MFX_REG_CHIP_ID 0x00 /* R */
+#define MFX_REG_FW_VERSION_MSB 0x01 /* R */
+#define MFX_REG_FW_VERSION_LSB 0x02 /* R */
+#define MFX_REG_SYS_CTRL 0x40 /* RW */
The STMPE driver defines enumerated registers in
include/linux/mfd/stmpe.h
then assign each variant using the model specifics in
drivers/mfd/stmpe.h
This doesn't seem super much different.
Even if the old STMPE driver may be a bad fit, is is better
to improve that (e.g. migrate it to use regmap and rewrite the
stmpe-gpio.c driver to use pin control) and use also for this
driver, or write a new driver from scratch like this?
I'm not so sure.
I do know that developers not always like to take out old
hardware and old development boards and start hacking
away before they can get some nice new hardware going
but I am worried that this may be one of those cases where
a serious cleanup of the aging STMPE driver may be a
first necessary step.
I guess these blocks works the same as with STMPE,
that you can only use one of them at the time?
What is altgpio?
ALTGPIO enables the use of GPIO[23:16] only if IDD and/or TS is/are
disabled. TS and IDD have priority, if IDD and TS are enabled, ALTGPIO
is forced to 0 by MFX FW.
When IDD is used GPIO[19:16] can be used as GPIO.
When TS is used GPIO[24:20] can be used as GPIO.
I have not read the patch yet. But just for notice:
This output IRQ type needs to be handled as well.
Check the code in
drivers/iio/common/st_sensors/st_sensors_trigger.c
To see how you can detect the properties of an IRQ
to set the right polarity, and handling of open drain
IRQ lines.
Thanks, I will have a look on this driver.
Regards,
Amelie
On Thu, Feb 8, 2018 at 3:27 PM, Amelie Delaunay [off-list ref] wrote:
quoted
ST Multi-Function eXpander (MFX) can be used as GPIO expander.
It has 16 fast GPIOs and can have 8 extra alternate GPIOs
when other MFX features are not enabled.
Signed-off-by: Amelie Delaunay <redacted>
As mentioned I think this driver needs to:
- Move to drivers/pinctrl/pinctrl-stmfx.c similar to the sx150x controller.
- Pass a regmap around instead of the custom accessor functions.
I study this right now!
Thanks,
Amelie
- Maybe refactor and reuse STMPE infrastructure
Yours,
Linus Walleij
So I think this node should contain some pin config states
that can be referenced by the drivers to set up push/pull
etc. Probably it can use just standard pin config properties
like sx150x.
As I think this should not all be handled by GPIO, the joystick on
gpio-keys needs a pin control handle pointing back to pin control
states on the respective pins in the pin control/GPIO driver,
where it can set up these properties.
For the individual lines the pushing and pulling flags should be
removed.
Sure! I keep this in mind, thanks!
Regards,
Amelie
On Thu, Feb 22, 2018 at 4:13 PM, Amelie DELAUNAY [off-list ref] wrote:
ST MFX is different from STMPE as far as the HW is completely different.
IDD is a new feature.
TS management is completely different.
GPIO management looks like but is also rather different.
ST MFX counts a first level of 8 interrupts (acked by writing in the ACK
register), then a second level of 24 interrupts for GPIOs. GPIO IRQ can
be triggered on low level, falling edge, high level, rising edge. GPIO
IRQ have to be acked by writing in the GPI_ACK register, GPIO can be
output open-drain with/without internal pull-up, output push-pull, input
with pull-up/down, input floating or analog.
Okay I am convinced that we need to model this as its own
driver if it is as different as you say.
But let's keep an eye on STMPE's approaches and what it does
right and wrong.
If I rewrote the STMPE driver today, the major changes would be
to use regmap to pass around to subdrivers, and implement a
combined pin control and GPIO driver, so I think those things should
be nice to have for the next iteration.
Yours,
Linus Walleij