root/arch/arm/mach-omap1/timer.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP1 Dual-Mode Timers - platform device registration
 *
 * Contains first level initialization routines which internally
 * generates timer device information and registers with linux
 * device model. It also has a low level function to change the timer
 * input clock source.
 *
 * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
 * Tarun Kanti DebBarma <tarun.kanti@ti.com>
 * Thara Gopinath <thara@ti.com>
 */

#include <linux/clk.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/platform_data/dmtimer-omap.h>
#include <linux/soc/ti/omap1-io.h>

#include <clocksource/timer-ti-dm.h>

#include "soc.h"

#define OMAP1610_GPTIMER1_BASE          0xfffb1400
#define OMAP1610_GPTIMER2_BASE          0xfffb1c00
#define OMAP1610_GPTIMER3_BASE          0xfffb2400
#define OMAP1610_GPTIMER4_BASE          0xfffb2c00
#define OMAP1610_GPTIMER5_BASE          0xfffb3400
#define OMAP1610_GPTIMER6_BASE          0xfffb3c00
#define OMAP1610_GPTIMER7_BASE          0xfffb7400
#define OMAP1610_GPTIMER8_BASE          0xfffbd400

#define OMAP1_DM_TIMER_COUNT            8

static int omap1_dm_timer_set_src(struct platform_device *pdev,
                                int source)
{
        int n = (pdev->id - 1) << 1;
        u32 l;

        l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n);
        l |= source << n;
        omap_writel(l, MOD_CONF_CTRL_1);

        return 0;
}

static int __init omap1_dm_timer_init(void)
{
        int i;
        int ret;
        struct dmtimer_platform_data *pdata;
        struct platform_device *pdev;

        if (!cpu_is_omap16xx())
                return 0;

        for (i = 1; i <= OMAP1_DM_TIMER_COUNT; i++) {
                struct resource res[2];
                u32 base, irq;

                switch (i) {
                case 1:
                        base = OMAP1610_GPTIMER1_BASE;
                        irq = INT_1610_GPTIMER1;
                        break;
                case 2:
                        base = OMAP1610_GPTIMER2_BASE;
                        irq = INT_1610_GPTIMER2;
                        break;
                case 3:
                        base = OMAP1610_GPTIMER3_BASE;
                        irq = INT_1610_GPTIMER3;
                        break;
                case 4:
                        base = OMAP1610_GPTIMER4_BASE;
                        irq = INT_1610_GPTIMER4;
                        break;
                case 5:
                        base = OMAP1610_GPTIMER5_BASE;
                        irq = INT_1610_GPTIMER5;
                        break;
                case 6:
                        base = OMAP1610_GPTIMER6_BASE;
                        irq = INT_1610_GPTIMER6;
                        break;
                case 7:
                        base = OMAP1610_GPTIMER7_BASE;
                        irq = INT_1610_GPTIMER7;
                        break;
                case 8:
                        base = OMAP1610_GPTIMER8_BASE;
                        irq = INT_1610_GPTIMER8;
                        break;
                default:
                        /*
                         * not supposed to reach here.
                         * this is to remove warning.
                         */
                        return -EINVAL;
                }

                pdev = platform_device_alloc("omap_timer", i);
                if (!pdev) {
                        pr_err("%s: Failed to device alloc for dmtimer%d\n",
                                __func__, i);
                        return -ENOMEM;
                }

                memset(res, 0, 2 * sizeof(struct resource));
                res[0].start = base;
                res[0].end = base + 0x46;
                res[0].flags = IORESOURCE_MEM;
                res[1].start = irq;
                res[1].end = irq;
                res[1].flags = IORESOURCE_IRQ;
                ret = platform_device_add_resources(pdev, res,
                                ARRAY_SIZE(res));
                if (ret) {
                        dev_err(&pdev->dev, "%s: Failed to add resources.\n",
                                __func__);
                        goto err_free_pdev;
                }

                pdata = kzalloc_obj(*pdata);
                if (!pdata) {
                        ret = -ENOMEM;
                        goto err_free_pdata;
                }

                pdata->set_timer_src = omap1_dm_timer_set_src;
                pdata->timer_capability = OMAP_TIMER_ALWON |
                                OMAP_TIMER_NEEDS_RESET | OMAP_TIMER_HAS_DSP_IRQ;

                ret = platform_device_add_data(pdev, pdata, sizeof(*pdata));
                if (ret) {
                        dev_err(&pdev->dev, "%s: Failed to add platform data.\n",
                                __func__);
                        goto err_free_pdata;
                }

                ret = platform_device_add(pdev);
                if (ret) {
                        dev_err(&pdev->dev, "%s: Failed to add platform device.\n",
                                __func__);
                        goto err_free_pdata;
                }

                dev_dbg(&pdev->dev, " Registered.\n");
        }

        return 0;

err_free_pdata:
        kfree(pdata);

err_free_pdev:
        platform_device_put(pdev);

        return ret;
}
arch_initcall(omap1_dm_timer_init);