supporting multi function devices in the linux kernel a
play

Supporting multi-function devices in the Linux kernel: a tour of the - PowerPoint PPT Presentation

Embedded Linux Conference Europe 2015 Supporting multi-function devices in the Linux kernel: a tour of the mfd, regmap and syscon APIs Alexandre Belloni alexandre.belloni@free-electrons.com http://free-electrons.com 1/30 free electrons free


  1. Embedded Linux Conference Europe 2015 Supporting multi-function devices in the Linux kernel: a tour of the mfd, regmap and syscon APIs Alexandre Belloni alexandre.belloni@free-electrons.com http://free-electrons.com 1/30 free electrons free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  2. Alexandre Belloni subsystem http://free-electrons.com ARM (Berlin) processors ARM processors 2/30 ▶ Embedded Linux engineer at free electrons ▶ Embedded Linux expertise ▶ Development , consulting and training ▶ Strong open-source focus ▶ Open-source contributor ▶ Maintainer for the Linux kernel RTC ▶ Co-Maintainer of kernel support for Atmel free electrons ▶ Contributing to kernel support for Marvell Embedded Linux Experts free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  3. What is a multi-function device ? USB interface controller http://free-electrons.com specialized i2c_client or spi_device ) external peripherals are represented by only one struct device (or the backlight controller, status LED controller, GPIOs, ON key, ADC driver, ON key functionality 3/30 ▶ An external peripheral or a hardware block exposing more than a single ▶ Examples: ▶ PMICs ▶ da9063: regulators, led controller, watchdog, rtc, temperature sensor, vibration motor ▶ max77843: regulators, charger, fuel gauge, haptic feedback, LED controller, micro ▶ wm831x: regulator, clocks, rtc, watchdog, touch controller, temperature sensor, ▶ some even include a codec ▶ atmel-hlcdc: display controller and backlight pwm ▶ Diolan DLN2: USB to I2C, SPI and GPIO controllers ▶ Realtek PCI-E card reader: SD/MMC and memory stick reader ▶ The main issue is to register those in difgerent kernel subsystems. In particular the free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  4. MFD subsystem and handle IRQs subsystems. http://free-electrons.com 4/30 ▶ The MFD subsystem has been created to handle those devices ▶ Allows to register the same device in multiple subsystems ▶ The MFD driver has to multiplex access on the bus (mainly takes care of locking) ▶ May handle clocks ▶ May also need to confjgure the IP ▶ May do variant or functions detection ▶ Other benefjt: allows driver reuse, multiple MFD can reuse drivers from other free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  5. MFD API const struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base, struct irq_domain *irq_domain); mfd_cell_disable but they are seldom used. http://free-electrons.com 5/30 ▶ Defjned in include/linux/mfd/core.h ▶ Implemented in drivers/mfd/mfd-core.c ▶ int mfd_add_devices(struct device *parent, int id, ▶ extern void mfd_remove_devices(struct device *parent); ▶ Also mfd_add_hotplug_devices , mfd_clone_cell , mfd_cell_enable , free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  6. struct mfd_cell struct mfd_cell { http://free-electrons.com }; [...] *resources; const struct resource num_resources; int */ * For accessing hardware you should use resources from the platform dev * These resources can be specified relative to the parent device. /* [...] *of_compatible; const char */ * See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details * Device Tree compatible string /* pdata_size; size_t *platform_data; void /* platform data passed to the sub devices drivers */ [...] id; int *name; const char 6/30 free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  7. Example: tps6507x - registration subsys_initcall(tps6507x_i2c_init); }; static int __init tps6507x_i2c_init(void) { return i2c_add_driver(&tps6507x_i2c_driver); } /* init early so consumer devices can complete system boot */ static void __exit tps6507x_i2c_exit(void) static const struct i2c_device_id tps6507x_i2c_id[] = { { i2c_del_driver(&tps6507x_i2c_driver); } module_exit(tps6507x_i2c_exit); initcall(tps6507x_i2c_init); to register early enough http://free-electrons.com .id_table = tps6507x_i2c_id, .remove = tps6507x_i2c_remove, .probe = tps6507x_i2c_probe, {}, { } }; MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id); #ifdef CONFIG_OF static const struct of_device_id tps6507x_of_match[] = { }, 7/30 }; MODULE_DEVICE_TABLE(of, tps6507x_of_match); #endif static struct i2c_driver tps6507x_i2c_driver = { .driver = { .of_match_table = of_match_ptr(tps6507x_of_match), { "tps6507x", 0 }, {.compatible = "ti,tps6507x", }, ▶ registers as a simple i2c device .name = "tps6507x", ▶ only oddity subsys_ free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  8. Example: tps6507x - probing { http://free-electrons.com } return mfd_add_devices(tps6507x->dev, -1, tps6507x_devs, tps6507x->write_dev = tps6507x_i2c_write_device; tps6507x->read_dev = tps6507x_i2c_read_device; tps6507x->i2c_client = i2c; tps6507x->dev = &i2c->dev; i2c_set_clientdata(i2c, tps6507x); return -ENOMEM; if (tps6507x == NULL) GFP_KERNEL); static const struct mfd_cell tps6507x_devs[] = { struct tps6507x_dev *tps6507x; const struct i2c_device_id *id) }; { }, { static int tps6507x_i2c_probe(struct i2c_client *i2c, }, drivers/regulator/tps6507x-regulator.c drivers/input/touchscreen/tps6507x-ts.c 8/30 ▶ tps6507x-pmic in .name = "tps6507x-pmic", ▶ tps6507x-ts in .name = "tps6507x-ts", tps6507x = devm_kzalloc(&i2c->dev, sizeof(struct tps6507x_dev), ARRAY_SIZE(tps6507x_devs), NULL, 0, NULL); free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  9. Example: tps6507x - struct tps6507x_dev void *src); http://free-electrons.com drivers. }; struct tps6507x_dev { [...] int (*write_dev)(struct tps6507x_dev *tps6507x, char reg, int size, void *dest); int (*read_dev)(struct tps6507x_dev *tps6507x, char reg, int size, struct i2c_client *i2c_client; struct device *dev; 9/30 ▶ Defjned in include/linux/mfd/tps6507x.h ▶ Allows to pass the i2c_client and the accessors. ▶ tps6507x.h also contains the register defjnitions that can be used in the function free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  10. Example: tps6507x - function drivers static int tps6507x_ts_probe(struct platform_device *pdev) { struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); [...] }; static int tps6507x_pmic_probe(struct platform_device *pdev) { struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); [...] }; http://free-electrons.com 10/30 ▶ Easy to get the struct tps6507x_dev by using dev.parent free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  11. Example: da9063 - registering [...] .name = DA9063_DRVNAME_RTC, .num_resources = ARRAY_SIZE(da9063_rtc_resources), .resources = da9063_rtc_resources, .of_compatible }, }; [...] like it was done using platform_data named for easy retrieval .of_compatible , the function has to be a child of the MFD (see bindings) http://free-electrons.com static struct resource da9063_rtc_resources[] = { { static const struct mfd_cell da9063_devs[] = { }, { .name .start = DA9063_IRQ_ALARM, .end = DA9063_IRQ_ALARM, .flags = IORESOURCE_IRQ, }; 11/30 { .name .start = DA9063_IRQ_TICK, .end = DA9063_IRQ_TICK, .flags = IORESOURCE_IRQ, } = "ALARM", ▶ resources are defjned = "TICK", ▶ in that case, they are ▶ when using = "dlg,da9063-rtc", free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  12. Example: da9063 - drivers/rtc/rtc-da9063.c static int da9063_rtc_probe(struct platform_device *pdev) http://free-electrons.com standalone chip. platform_get_irq , platform_get_irq_byname to retrieve the resources }; [...] } return ret; irq_alarm, ret); if (ret) { "ALARM", rtc); IRQF_TRIGGER_LOW | IRQF_ONESHOT, da9063_alarm_event, [...] { 12/30 irq_alarm = platform_get_irq_byname(pdev, "ALARM"); ret = devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL, dev_err(&pdev->dev, "Failed to request ALARM IRQ %d: %d\n", ▶ Use platform_get_resource , platform_get_resource_byname , ▶ Doesn’t even need dev.parent , the same driver could be used for an MFD and a free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  13. Example: da9063 - DT bindings pmic0: da9063@58 { reg = <0x58>; interrupt-parent = <&gpio6>; interrupts = <11 IRQ_TYPE_LEVEL_LOW>; interrupt-controller; rtc { }; [...] }; http://free-electrons.com 13/30 compatible = "dlg,da9063" compatible = "dlg,da9063-rtc"; free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

  14. MFD: multiplexing register access register sets is to use regmap . pass it down to the children http://free-electrons.com 14/30 ▶ A common way of multiplexing access to ▶ Create the regmap from the MFD driver and free electrons - Embedded Linux, kernel, drivers and Android - Development, consulting, training and support.

Recommend


More recommend