\documentclass[aspectratio=169,obeyspaces,spaces,hyphens]{beamer}
\usepackage{minted}
\usepackage{newunicodechar}

\mode<presentation>
\usetheme{Bootlin}

\newcommand{\codehack}[1]{{\usebeamercolor[fg]{code} {\tt #1}}}

\DeclareTextFontCommand{\textfallback}{\fallbackfont}
\newunicodechar{✂}{\textfallback{✂}}
\newunicodechar{✁}{\textfallback{✁}}
\newunicodechar{✃}{\textfallback{✃}}

\title{Supporting multi-function devices in the Linux kernel: a tour of the mfd, regmap and syscon APIs}
\authors{Alexandre Belloni}
\institute{Bootlin}

\usepackage{hyperref}

\begin{document}

\begin{frame}{Embedded Linux Conference Europe 2015}
  \fontsize{19}{19}\selectfont
  Supporting multi-function devices in the Linux kernel: a tour of the mfd, regmap and syscon APIs\\
  \vspace{2cm}
  \Large
  Alexandre Belloni\\
  Bootlin\\
  \normalsize
  {\em alexandre.belloni@bootlin.com}
  \vfill
\end{frame}

\begin{frame}{Alexandre Belloni}
  \begin{columns}
    \column{0.6\textwidth}
    \begin{itemize}
      \item Embedded Linux engineer at Bootlin
        \begin{itemize}
          \item Embedded Linux {\bf expertise}
          \item {\bf Development}, consulting and training
          \item Strong open-source focus
        \end{itemize}
      \item Open-source contributor
        \begin{itemize}
          \item Maintainer for the Linux kernel {\bf RTC subsystem}
          \item Co-Maintainer of {\bf kernel support for Atmel ARM
            processors}
          \item Contributing to {\bf kernel support for Marvell
            ARM (Berlin) processors}
        \end{itemize}
    \end{itemize}
    \column{0.4\textwidth}
    \includegraphics[width=\textwidth]{common/logo-square.pdf}\\
  \end{columns}
\end{frame}

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

\begin{frame}{MFD subsystem}
  \begin{itemize}
    \item The MFD subsystem has been created to handle those devices
    \item Allows to register the same device in multiple subsystems
    \item The MFD driver has to multiplex access on the bus (mainly
      takes care of locking) and handle IRQs
    \item May handle clocks
    \item May also need to configure the IP
    \item May do variant or functions detection
    \item Other benefit: allows driver reuse, multiple MFD can reuse
      drivers from other subsystems.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{MFD API}
  \begin{itemize}
    \item Defined in \code{include/linux/mfd/core.h}
    \item Implemented in \code{drivers/mfd/mfd-core.c}
    \item
      \begin{block}{}
        \begin{minted}{c}
int mfd_add_devices(struct device *parent, int id,
                    const struct mfd_cell *cells, int n_devs,
                    struct resource *mem_base,
                    int irq_base, struct irq_domain *irq_domain);
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
extern void mfd_remove_devices(struct device *parent);
        \end{minted}
      \end{block}
    \item Also \code{mfd_add_hotplug_devices}, \code{mfd_clone_cell}, \code{mfd_cell_enable},
      \code{mfd_cell_disable} but they are seldom used.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{\code{struct mfd_cell}}
  \begin{block}{}
    \fontsize{9}{9}\selectfont
    \begin{minted}{c}
struct mfd_cell {
        const char                *name;
        int                        id;
[...]
        /* platform data passed to the sub devices drivers */
        void                        *platform_data;
        size_t                        pdata_size;
        /*
         * Device Tree compatible string
         * See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details
         */
        const char                *of_compatible;
[...]
        /*
         * These resources can be specified relative to the parent device.
         * For accessing hardware you should use resources from the platform dev
         */
        int                        num_resources;
        const struct resource        *resources;
[...]
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Example: tps6507x - registration}
  \begin{columns}
    \column{0.5\textwidth}
    \begin{block}{}
    \fontsize{7}{8}\selectfont
      \begin{minted}{c}
static const struct i2c_device_id tps6507x_i2c_id[] = {
        { "tps6507x", 0 },
        { }
};
MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);

#ifdef CONFIG_OF
static const struct of_device_id tps6507x_of_match[] = {
        {.compatible = "ti,tps6507x", },
        {},
};
MODULE_DEVICE_TABLE(of, tps6507x_of_match);
#endif

static struct i2c_driver tps6507x_i2c_driver = {
        .driver = {
                   .name = "tps6507x",
                   .of_match_table = 
                           of_match_ptr(tps6507x_of_match),
        },
        .probe = tps6507x_i2c_probe,
        .remove = tps6507x_i2c_remove,
        .id_table = tps6507x_i2c_id,
};
      \end{minted}
    \end{block}
    \column{0.5\textwidth}
    \begin{block}{}
    \fontsize{7}{8}\selectfont
      \begin{minted}{c}
static int __init tps6507x_i2c_init(void)
{
        return i2c_add_driver(&tps6507x_i2c_driver);
}
/* init early so consumer devices can complete system boot */
subsys_initcall(tps6507x_i2c_init);

static void __exit tps6507x_i2c_exit(void)
{
        i2c_del_driver(&tps6507x_i2c_driver);
}
module_exit(tps6507x_i2c_exit);
      \end{minted}
    \end{block}
    \vfill
    \begin{itemize}
      \item registers as a simple i2c device
      \item only oddity \code{subsys_initcall(tps6507x_i2c_init);} to
        register early enough
    \end{itemize}
  \end{columns}
\end{frame}

\begin{frame}[fragile]{Example: tps6507x - probing}
  \begin{columns}
    \column{0.5\textwidth}
  \begin{block}{}
  \fontsize{8}{5}\selectfont
    \begin{minted}{c}
static const struct mfd_cell tps6507x_devs[] = {
        {
                .name = "tps6507x-pmic",
        },
        {
                .name = "tps6507x-ts",
        },
};
    \end{minted}
  \end{block}
    \column{0.5\textwidth}
    \fontsize{8}{8}\selectfont
    \begin{itemize}
      \item \code{tps6507x-pmic} in \code{drivers/regulator/tps6507x-regulator.c}
      \item \code{tps6507x-ts} in \code{drivers/input/touchscreen/tps6507x-ts.c}
    \end{itemize}
  \end{columns}
  \begin{block}{}
  \fontsize{8}{5}\selectfont
    \begin{minted}{c}
static int tps6507x_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
{
        struct tps6507x_dev *tps6507x;

        tps6507x = devm_kzalloc(&i2c->dev, sizeof(struct tps6507x_dev),
                                GFP_KERNEL);
        if (tps6507x == NULL)
                return -ENOMEM;

        i2c_set_clientdata(i2c, tps6507x);
        tps6507x->dev = &i2c->dev;
        tps6507x->i2c_client = i2c;
        tps6507x->read_dev = tps6507x_i2c_read_device;
        tps6507x->write_dev = tps6507x_i2c_write_device;

        return mfd_add_devices(tps6507x->dev, -1, tps6507x_devs,
                               ARRAY_SIZE(tps6507x_devs), NULL, 0, NULL);
}
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Example: tps6507x - \code{struct tps6507x_dev}}
  \begin{block}{}
    \begin{minted}{c}
struct tps6507x_dev {
        struct device *dev;
        struct i2c_client *i2c_client;
        int (*read_dev)(struct tps6507x_dev *tps6507x, char reg, int size,
                        void *dest);
        int (*write_dev)(struct tps6507x_dev *tps6507x, char reg, int size,
                         void *src);
[...]
};
    \end{minted}
  \end{block}
  \begin{itemize}
    \item Defined in \code{include/linux/mfd/tps6507x.h}
    \item Allows to pass the \code{i2c_client} and the accessors.
    \item \code{tps6507x.h} also contains the register definitions
      that can be used in the function drivers.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Example: tps6507x - function drivers}
  \begin{block}{}
  \fontsize{10}{10}\selectfont
    \begin{minted}{c}
static int tps6507x_ts_probe(struct platform_device *pdev)
{
        struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
[...]
};
    \end{minted}
  \end{block}
  \begin{block}{}
  \fontsize{10}{10}\selectfont
    \begin{minted}{c}
static int tps6507x_pmic_probe(struct platform_device *pdev)
{
        struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
[...]
};
    \end{minted}
  \end{block}
  \begin{itemize}
    \item Easy to get the \code{struct tps6507x_dev} by using \code{dev.parent}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Example: da9063 - registering}
  \begin{columns}
    \column{0.72\textwidth}
  \begin{block}{}
  \fontsize{8}{5}\selectfont
    \begin{minted}{c}
static struct resource da9063_rtc_resources[] = {
        {
                .name        = "ALARM",
                .start        = DA9063_IRQ_ALARM,
                .end        = DA9063_IRQ_ALARM,
                .flags        = IORESOURCE_IRQ,
        },
        {
                .name        = "TICK",
                .start        = DA9063_IRQ_TICK,
                .end        = DA9063_IRQ_TICK,
                .flags        = IORESOURCE_IRQ,
        }
};

static const struct mfd_cell da9063_devs[] = {
[...]
        {
                .name                = DA9063_DRVNAME_RTC,
                .num_resources        = ARRAY_SIZE(da9063_rtc_resources),
                .resources        = da9063_rtc_resources,
                .of_compatible        = "dlg,da9063-rtc",
        },
[...]
};
    \end{minted}
  \end{block}
    \column{0.28\textwidth}
    \fontsize{9}{9}\selectfont
    \begin{itemize}
      \item resources are defined like it was done using \code{platform_data}
      \item in that case, they are named for easy retrieval
      \item when using \code{.of_compatible}, the function has to be a
        child of the MFD (see bindings)
    \end{itemize}
    \vfill
  \end{columns}
\end{frame}

\begin{frame}[fragile]{Example: da9063 - \code{drivers/rtc/rtc-da9063.c}}
  \begin{block}{}
  \fontsize{10}{9}\selectfont
    \begin{minted}{c}
static int da9063_rtc_probe(struct platform_device *pdev)
{
[...]
        irq_alarm = platform_get_irq_byname(pdev, "ALARM");
        ret = devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL,
                                        da9063_alarm_event,
                                        IRQF_TRIGGER_LOW | IRQF_ONESHOT,
                                        "ALARM", rtc);
        if (ret) {
                dev_err(&pdev->dev, "Failed to request ALARM IRQ %d: %d\n",
                        irq_alarm, ret);
                return ret;
        }
[...]
};
\end{minted}
  \end{block}
  \begin{itemize}
    \item Use \code{platform_get_resource},
      \code{platform_get_resource_byname}, \code{platform_get_irq},
      \code{platform_get_irq_byname} to retrieve the resources
    \item Doesn't even need \code{dev.parent}, the same driver could be
      used for an MFD and a standalone chip.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Example: da9063 - DT bindings}
  \begin{block}{}
  \fontsize{10}{10}\selectfont
    \begin{minted}{c}
pmic0: da9063@58 {
        compatible = "dlg,da9063"
        reg = <0x58>;
        interrupt-parent = <&gpio6>;
        interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
        interrupt-controller;

        rtc {
                compatible = "dlg,da9063-rtc";
        };
[...]
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}{MFD: multiplexing register access}
  \begin{columns}
    \column{0.6\textwidth}
    \begin{itemize}
      \item A common way of multiplexing access to register sets is to
        use \code{regmap}.
      \item Create the \code{regmap} from the MFD driver and pass it
        down to the children
    \end{itemize}
    \column{0.4\textwidth}
    \includegraphics[width=\textwidth]{locking.pdf}\\
  \end{columns}
\end{frame}

\begin{frame}[fragile]{regmap}
  \begin{itemize}
    \item has its roots in ASoC (ALSA)
    \item can use I2C, SPI and MMIO (also SPMI)
    \item actually abstracts the underlying bus
    \item can handle locking when necessary
    \item can cache registers
    \item can handle endianness conversion
    \item can handle IRQ chips and IRQs
    \item can check register ranges
    \item handles read only, write only, volatile, precious registers
    \item handles register pages
    \item API is defined in \code{include/linux/regmap.h}
    \item implemented in \code{drivers/base/regmap/}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{regmap: creation}
  \begin{itemize}
    \item 
      \begin{block}{}
        \begin{minted}{c}
#define regmap_init(dev, bus, bus_context, config)                        \
        __regmap_lockdep_wrapper(__regmap_init, #config,                  \
                                dev, bus, bus_context, config)
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
#define regmap_init_i2c(i2c, config)                                        \
        __regmap_lockdep_wrapper(__regmap_init_i2c, #config,                \
                                i2c, config)
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
#define regmap_init_spi(dev, config)                                        \
        __regmap_lockdep_wrapper(__regmap_init_spi, #config,                \
                                dev, config)
        \end{minted}
      \end{block}
  \end{itemize}
  \begin{itemize}
    \item Also \code{devm_} versions
    \item and \code{_clk} versions
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{regmap: access}
  \begin{itemize}
    \item 
      \begin{block}{}
        \begin{minted}{c}
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
int regmap_update_bits(struct regmap *map, unsigned int reg,
                       unsigned int mask, unsigned int val);
        \end{minted}
      \end{block}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{regmap: cache management}
  \begin{itemize}
    \item
      \begin{block}{}
        \begin{minted}{c}
int regcache_sync(struct regmap *map);
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
int regcache_sync_region(struct regmap *map, unsigned int min,
                         unsigned int max);
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
int regcache_drop_region(struct regmap *map, unsigned int min,
                         unsigned int max);
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
void regcache_cache_only(struct regmap *map, bool enable);
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
void regcache_cache_bypass(struct regmap *map, bool enable);
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
void regcache_mark_dirty(struct regmap *map);
        \end{minted}
      \end{block}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Example: atmel-hlcdc}
  \begin{columns}
    \column{0.3\textwidth}
  \begin{block}{include/linux/mfd/atmel-hlcdc.h}
  \fontsize{8}{8}\selectfont
    \begin{minted}{c}
struct atmel_hlcdc {
        struct regmap *regmap;
        struct clk *periph_clk;
        struct clk *sys_clk;
        struct clk *slow_clk;
        int irq;
};
    \end{minted}
  \end{block}
    \column{0.7\textwidth}
  \begin{block}{driver/mfd/atmel-hlcdc.c}
  \fontsize{8}{7}\selectfont
    \begin{minted}{c}
static const struct regmap_config atmel_hlcdc_regmap_config = {
        .reg_bits = 32,
        .val_bits = 32,
        .reg_stride = 4,
        .max_register = ATMEL_HLCDC_REG_MAX,
        .reg_write = regmap_atmel_hlcdc_reg_write,
        .reg_read = regmap_atmel_hlcdc_reg_read,
        .fast_io = true,
};

static int atmel_hlcdc_probe(struct platform_device *pdev)
{
        struct atmel_hlcdc_regmap *hregmap;
        struct device *dev = &pdev->dev;
        struct atmel_hlcdc *hlcdc;
        struct resource *res;
[...]
        hlcdc->regmap = devm_regmap_init(dev, NULL, hregmap,
                                         &atmel_hlcdc_regmap_config);
        if (IS_ERR(hlcdc->regmap))
                return PTR_ERR(hlcdc->regmap);

        dev_set_drvdata(dev, hlcdc);
[...]
}
    \end{minted}
  \end{block}
  \end{columns}
\end{frame}

\begin{frame}[fragile]{Example: pwm-atmel-hlcdc}
  \begin{block}{}
  \fontsize{8}{7}\selectfont
    \begin{minted}{c}
static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
{
        const struct of_device_id *match;
        struct device *dev = &pdev->dev;
        struct atmel_hlcdc_pwm *chip;
        struct atmel_hlcdc *hlcdc;
        int ret;

        hlcdc = dev_get_drvdata(dev->parent);
[...]
        chip->hlcdc = hlcdc;
[...]
}
    \end{minted}
  \end{block}
  \begin{block}{}
  \fontsize{8}{7}\selectfont
    \begin{minted}{c}
static int atmel_hlcdc_pwm_set_polarity(struct pwm_chip *c,
                                        struct pwm_device *pwm,
                                        enum pwm_polarity polarity)
{
        struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c);
        struct atmel_hlcdc *hlcdc = chip->hlcdc;
        u32 cfg = 0;

        if (polarity == PWM_POLARITY_NORMAL)
                cfg = ATMEL_HLCDC_PWMPOL;

        return regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
                                  ATMEL_HLCDC_PWMPOL, cfg);
}
    \end{minted}
  \end{block}
\end{frame}
\begin{frame}[fragile]{Example: atmel-flexcom}
  \begin{itemize}
    \item Sometimes an MFD only supports one simultaneous function.
    \item The MFD driver only configures the function.
      \begin{block}{}
      \fontsize{10}{10}\selectfont
        \begin{minted}{c}
static int atmel_flexcom_probe(struct platform_device *pdev)
{
        struct device_node *np = pdev->dev.of_node;
[...]
        err = of_property_read_u32(np, "atmel,flexcom-mode", &opmode);
        if (err)
                return err;

        if (opmode < ATMEL_FLEXCOM_MODE_USART ||
            opmode > ATMEL_FLEXCOM_MODE_TWI)
                return -EINVAL;
[...]
        writel(FLEX_MR_OPMODE(opmode), base + FLEX_MR);
[...]
        return of_platform_populate(np, NULL, NULL, &pdev->dev);
}
        \end{minted}
      \end{block}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Example: atmel-flexcom - DT bindings}
  \begin{block}{}
  \fontsize{9}{9}\selectfont
    \begin{minted}{c}
flexcom@f8034000 {
        compatible = "atmel,sama5d2-flexcom";
        reg = <0xf8034000 0x200>;
        clocks = <&flx0_clk>;
        #address-cells = <1>;
        #size-cells = <1>;
        ranges = <0x0 0xf8034000 0x800>;
        atmel,flexcom-mode = <2>;

        spi@400 {
                compatible = "atmel,at91rm9200-spi";
                reg = <0x400 0x200>;
                interrupts = <19 IRQ_TYPE_LEVEL_HIGH 7>;
                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_flx0_default>;
[...]
                };
        };
};
    \end{minted}
  \end{block}
  \begin{itemize}
    \item The SPI driver from 2007 is reused and has not been modified
      to handle the MFD specifics.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{syscon}
  \begin{itemize}
    \item Sometimes, a set of registers is used to configure
      miscellaneous features from otherwise well separated IPs
    \item Automatically creates a \code{regmap} when accessed
    \item Defined in \code{include/linux/mfd/syscon.h}
    \item Implemented in \code{drivers/mfd/syscon.c}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{syscon: API}
  \begin{itemize}
    \item 
      \begin{block}{}
        \begin{minted}{c}
extern struct regmap *syscon_node_to_regmap(struct device_node *np);
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s);
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
extern struct regmap *syscon_regmap_lookup_by_pdevname(const char *s);
        \end{minted}
      \end{block}
    \item 
      \begin{block}{}
        \begin{minted}{c}
extern struct regmap *syscon_regmap_lookup_by_phandle(
                                        struct device_node *np,
                                        const char *property);
        \end{minted}
      \end{block}
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Example: pinctrl-dove.c}
  \begin{block}{}
  \fontsize{7}{6}\selectfont
    \begin{minted}{c}
static int dove_pinctrl_probe(struct platform_device *pdev)
{
        struct resource *res, *mpp_res;
        struct resource fb_res;
        const struct of_device_id *match =
                of_match_device(dove_pinctrl_of_match, &pdev->dev);
        pdev->dev.platform_data = (void *)match->data;
[...]
        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
        if (!res) {
                dev_warn(&pdev->dev, "falling back to hardcoded MPP4 resource\n");
                adjust_resource(&fb_res,
                        (mpp_res->start & INT_REGS_MASK) + MPP4_REGS_OFFS, 0x4);
                res = &fb_res;
        }

        mpp4_base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(mpp4_base))
                return PTR_ERR(mpp4_base);

        res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
        if (!res) {
                dev_warn(&pdev->dev, "falling back to hardcoded PMU resource\n");
                adjust_resource(&fb_res,
                        (mpp_res->start & INT_REGS_MASK) + PMU_REGS_OFFS, 0x8);
                res = &fb_res;
        }

        pmu_base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(pmu_base))
                return PTR_ERR(pmu_base);

        gconfmap = syscon_regmap_lookup_by_compatible("marvell,dove-global-config");
[...]
}
    \end{minted}
  \end{block}
\end{frame}


\begin{frame}[fragile]{\code{simple-mfd}}
  \begin{itemize}
    \item Simple DT binding
    \item Documented in \code{Documentation/devicetree/bindings/mfd/mfd.txt}
    \item Implemented in \code{drivers/of/platform.c}
    \item It is actually an alias to \code{simple-bus}
    \item Used in conjunction with \code{syscon} to create the
      \code{regmap}, it allows to avoid writing an MFD driver.
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Example: system-timer}
  \begin{block}{arch/arm/boot/dts/at91rm9200.dtsi}
  \fontsize{9}{9}\selectfont
    \begin{minted}{c}
        st: timer@fffffd00 {
                compatible = "atmel,at91rm9200-st", "syscon", "simple-mfd";
                reg = <0xfffffd00 0x100>;
                interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
                clocks = <&slow_xtal>;

                watchdog {
                        compatible = "atmel,at91rm9200-wdt";
                };
        };
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Example: system-timer}
  \begin{block}{drivers/clocksource/timer-atmel-st.c}
  \fontsize{9}{9}\selectfont
    \begin{minted}{c}
static struct regmap *regmap_st;
[...]
static void __init atmel_st_timer_init(struct device_node *node)
{
        unsigned int val;
        int irq, ret;

        regmap_st = syscon_node_to_regmap(node);
        if (IS_ERR(regmap_st))
                panic(pr_fmt("Unable to get regmap\n"));

        /* Disable all timer interrupts, and clear any pending ones */
        regmap_write(regmap_st, AT91_ST_IDR,
                AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS);
        regmap_read(regmap_st, AT91_ST_SR, &val);
[...]
}
CLOCKSOURCE_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st",
                       atmel_st_timer_init);
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Example: system-timer}
  \begin{block}{drivers/watchdog/at91rm9200\_wdt.c}
  \fontsize{9}{9}\selectfont
    \begin{minted}{c}
static struct regmap *regmap_st;
[...]
static int at91wdt_probe(struct platform_device *pdev)
{
        struct device *dev = &pdev->dev;
        struct device *parent;
[...]
        parent = dev->parent;
        if (!parent) {
                dev_err(dev, "no parent\n");
                return -ENODEV;
        }

        regmap_st = syscon_node_to_regmap(parent->of_node);
        if (IS_ERR(regmap_st))
                return -ENODEV;
[...]
}
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}
  \begin{center}
    \LARGE
    Questions?\\
    \vspace{2.5cm}
    \small
    Alexandre Belloni\\
    \code{alexandre.belloni@bootlin.com}\\
    \vspace{0.5cm}
    Slides under CC-BY-SA 3.0\\
    \scriptsize
    \url{http://bootlin.com/pub/conferences/2015/elce/belloni-mfd-regmap-syscon/}
  \end{center}
\end{frame}

\end{document}
