怎么简单熟悉下一个新平台的设备树

gpio概述

gpio节点可能有多个,他们的中断号都是位于同一个中断控制器,比如gic中断控制器;还有个细节就是simple-bus的字节点也会在系统初始化时生成platform_device,以匹配驱动

aon {
        compatible = "simple-bus";

        #address-cells = <2>;
        #size-cells = <2>;
        ranges;

        eic_debounce: gpio@631d0000 {
                compatible = "sprd,orca-eic-debounce", "sprd,sharkl5-eic-debounce";
                reg = <0 0x631d0000 0 0x80>,
                        <0 0x631e0000 0 0x80>,
                        <0 0x631f0000 0 0x80>,
                        <0 0x63200000 0 0x80>;
                gpio-controller;
                #gpio-cells = <2>;
                interrupt-controller;
                #interrupt-cells = <2>;
                interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
        };

        ap_gpio: gpio@632d0000 {
                compatible = "sprd,orca-gpio", "sprd,sharkl5-gpio";
                reg = <0 0x632d0000 0 0x10000>;
                gpio-controller;
                #gpio-cells = <2>;
                interrupt-controller;
                #interrupt-cells = <2>;
                interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
        };
}

gpio中断处理

gpio控制器节点对应的驱动,再注册这个中断号对应的中断处理函数,来在中断触发时解析出是那个那个gpio,然后再调用该gpio对应的irq的中断处理函数 ;gpiochip_add_irqchip对每个gpio做了irq映射,详见我们的《这篇

static void sprd_gpio_irq_handler(struct irq_desc *desc)
{
        struct gpio_chip *chip = irq_desc_get_handler_data(desc);
        struct irq_chip *ic = irq_desc_get_chip(desc);
        struct sprd_gpio *sprd_gpio = gpiochip_get_data(chip);
        u32 bank, n, girq;

        chained_irq_enter(ic, desc);

        for (bank = 0; bank * SPRD_GPIO_BANK_NR < chip->ngpio; bank++) {
                void __iomem *base = sprd_gpio_bank_base(sprd_gpio, bank);
                unsigned long reg = readl_relaxed(base + SPRD_GPIO_MIS) &
                        SPRD_GPIO_BANK_MASK;

                for_each_set_bit(n, &reg, SPRD_GPIO_BANK_NR) {
                        girq = irq_find_mapping(chip->irq.domain,
                                                bank * SPRD_GPIO_BANK_NR + n);

                        generic_handle_irq(girq);
                }

        }
        chained_irq_exit(ic, desc);
}

使用示例

其他节点就会来引用gpio控制器,及其对应的gpio编号

gpio-keys {
                compatible = "gpio-keys";

                key-factoryrst {
                        label = "Factory Rst Key";
                        linux,code = <KEY_F11>;
                        gpios = <&eic_debounce 2 GPIO_ACTIVE_LOW>;
                        debounce-interval = <2>;
                        wakeup-source;
                };

                key-poweroff {
                        label = "Power Off Key";
                        linux,code = <KEY_F12>;
                        gpios = <&ap_gpio 1 GPIO_ACTIVE_LOW>;
                        debounce-interval = <2>;
                        wakeup-source;
                };
 }

pinctrl差异对比

rockchip

某平台gpio功能的request,方向等都是调用的注册pinctrl里的函数,详见《这篇》,gpio作为pinctrl的子节点

pinctrl: pinctrl {
                compatible = "rockchip,rk3399-pinctrl";
                rockchip,grf = <&grf>;
                rockchip,pmu = <&pmugrf>;
                #address-cells = <2>;
                #size-cells = <2>;
                ranges;

                gpio0: gpio0@ff720000 {
                        compatible = "rockchip,gpio-bank";
                        reg = <0x0 0xff720000 0x0 0x100>;
                        clocks = <&pmucru PCLK_GPIO0_PMU>;
                        interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH 0>;

                        gpio-controller;
                        #gpio-cells = <0x2>;

                        interrupt-controller;
                        #interrupt-cells = <0x2>;
                };

                gpio1: gpio1@ff730000 {
                        compatible = "rockchip,gpio-bank";
                        reg = <0x0 0xff730000 0x0 0x100>;
                        clocks = <&pmucru PCLK_GPIO1_PMU>;
                        interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH 0>;

                        gpio-controller;
                        #gpio-cells = <0x2>;

                        interrupt-controller;
                        #interrupt-cells = <0x2>;
                }
 }

sprd

新平台:pinctrl控制器只管复用和电气属性的设置

pin_controller: pinctrl@63450000 {
                            compatible = "sprd,orca-pinctrl";
                            reg = <0 0x63450000 0 0x10000>;

                            vbc_iis0_0: iis-matrix-cfg-inf0@7 {
                                    pins = "ORCA_IIS_INF0_SYS_SEL";
                                    sprd,control = <0x7>;
                            };

                            vbc_iis0_1: iis-matrix-cfg-inf1@7 {
                                    pins = "ORCA_IIS_INF1_SYS_SEL";
                                    sprd,control = <0x7>;
                            };

                            agcp_iis0_0: iis-matrix-cfg-inf0@3 {
                                    pins = "ORCA_IIS_INF0_SYS_SEL";
                                    sprd,control = <0x3>;
                            };

                            agcp_iis0_1: iis-matrix-cfg-inf1@3 {
                                    pins = "ORCA_IIS_INF1_SYS_SEL";
                                    sprd,control = <0x3>;
                            };

                            agcp_iis1_0: iis-matrix-cfg-inf0@4 {
                                    pins = "ORCA_IIS_INF0_SYS_SEL";
                                    sprd,control = <0x4>;
                            };

                            agcp_iis1_1: iis-matrix-cfg-inf1@4 {
                                    pins = "ORCA_IIS_INF1_SYS_SEL";
                                    sprd,control = <0x4>;
                            };

                    };

自己承担gpio的方向,高低电平的设置

ap_gpio: gpio@632d0000 {
                                compatible = "sprd,orca-gpio", "sprd,sharkl5-gpio";
                                reg = <0 0x632d0000 0 0x10000>;
                                gpio-controller;
                                #gpio-cells = <2>;
                                interrupt-controller;
                                #interrupt-cells = <2>;
                                interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
                        };

static int sprd_gpio_probe(struct platform_device *pdev)
{
        struct gpio_irq_chip *irq;
        struct sprd_gpio *sprd_gpio;
        struct resource *res;
        int ret;

        sprd_gpio = devm_kzalloc(&pdev->dev, sizeof(*sprd_gpio), GFP_KERNEL);
        if (!sprd_gpio)
                return -ENOMEM;

        sprd_gpio->irq = platform_get_irq(pdev, 0);
        if (sprd_gpio->irq < 0) {
                dev_err(&pdev->dev, "Failed to get GPIO interrupt.\n");
                return sprd_gpio->irq;
        }

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        sprd_gpio->base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(sprd_gpio->base))
                return PTR_ERR(sprd_gpio->base);

        spin_lock_init(&sprd_gpio->lock);

        sprd_gpio->chip.label = dev_name(&pdev->dev);
        sprd_gpio->chip.ngpio = SPRD_GPIO_NR;
        sprd_gpio->chip.base = -1;
        sprd_gpio->chip.parent = &pdev->dev;
        sprd_gpio->chip.of_node = pdev->dev.of_node;
        sprd_gpio->chip.request = sprd_gpio_request;
        sprd_gpio->chip.free = sprd_gpio_free;
        sprd_gpio->chip.get = sprd_gpio_get;
        sprd_gpio->chip.set = sprd_gpio_set;
        sprd_gpio->chip.direction_input = sprd_gpio_direction_input;
        sprd_gpio->chip.direction_output = sprd_gpio_direction_output;

        irq = &sprd_gpio->chip.irq;
        irq->chip = &sprd_gpio_irqchip;
        irq->handler = handle_bad_irq;
        irq->default_type = IRQ_TYPE_NONE;
        irq->parent_handler = sprd_gpio_irq_handler;
        irq->parent_handler_data = sprd_gpio;
        irq->num_parents = 1;
        irq->parents = &sprd_gpio->irq;

        ret = devm_gpiochip_add_data(&pdev->dev, &sprd_gpio->chip, sprd_gpio);
        if (ret < 0) {
                dev_err(&pdev->dev, "Could not register gpiochip %d\n", ret);
                return ret;
        }

        platform_set_drvdata(pdev, sprd_gpio);
        return 0;
}

static int sprd_gpio_request(struct gpio_chip *chip, unsigned int offset)
{
        sprd_gpio_update(chip, offset, SPRD_GPIO_DMSK, 1);
        return 0;
}
static int sprd_gpio_direction_input(struct gpio_chip *chip,
                                     unsigned int offset)
{
        sprd_gpio_update(chip, offset, SPRD_GPIO_DIR, 0);
        sprd_gpio_update(chip, offset, SPRD_GPIO_INEN, 1);
        return 0;
}

static int sprd_gpio_direction_output(struct gpio_chip *chip,
                                      unsigned int offset, int value)
{
        sprd_gpio_update(chip, offset, SPRD_GPIO_DIR, 1);
        sprd_gpio_update(chip, offset, SPRD_GPIO_INEN, 0);
        sprd_gpio_update(chip, offset, SPRD_GPIO_DATA, value);
        return 0;
}

static int sprd_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
        return sprd_gpio_read(chip, offset, SPRD_GPIO_DATA);
}

static void sprd_gpio_set(struct gpio_chip *chip, unsigned int offset,
                          int value)
{
        sprd_gpio_update(chip, offset, SPRD_GPIO_DATA, value);
}

中断跟时钟差异对比

rockchip

中断都是传递给gic控制器,并带有pinctrl复用功能属性,时钟来自apb总线的时钟分频

spi0: spi@ff1c0000 {
                compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi";
                reg = <0x0 0xff1c0000 0x0 0x1000>;
                clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
                clock-names = "spiclk", "apb_pclk";
                interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH 0>;
                pinctrl-names = "default";
                pinctrl-0 = <&spi0_clk &spi0_tx &spi0_rx &spi0_cs0>;
                #address-cells = <1>;
                #size-cells = <0>;
                status = "disabled";
        };

devm_clk_get获取时钟,clk_prepare_enable使能时钟; pinctrl_lookup_state查找并获取状态,并在后续通过pinctrl_select_state选择pin的复用功能

static int rockchip_spi_probe(struct platform_device *pdev)
{
        int ret = 0;
        struct rockchip_spi *rs;
        struct spi_master *master;
        struct resource *mem;
        u32 rsd_nsecs;

        master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi));
        if (!master)
                return -ENOMEM;

        platform_set_drvdata(pdev, master);

        rs = spi_master_get_devdata(master);

        /* Get basic io resource and map it */
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        rs->regs = devm_ioremap_resource(&pdev->dev, mem);
        if (IS_ERR(rs->regs)) {
                ret =  PTR_ERR(rs->regs);
                goto err_ioremap_resource;
        }

        rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk");
        if (IS_ERR(rs->apb_pclk)) {
                dev_err(&pdev->dev, "Failed to get apb_pclk\n");
                ret = PTR_ERR(rs->apb_pclk);
                goto err_ioremap_resource;
        }

        rs->spiclk = devm_clk_get(&pdev->dev, "spiclk");
        if (IS_ERR(rs->spiclk)) {
                dev_err(&pdev->dev, "Failed to get spi_pclk\n");
                ret = PTR_ERR(rs->spiclk);
                goto err_ioremap_resource;
        }

        ret = clk_prepare_enable(rs->apb_pclk);
        if (ret) {
                dev_err(&pdev->dev, "Failed to enable apb_pclk\n");
                goto err_ioremap_resource;
        }

        ret = clk_prepare_enable(rs->spiclk);
        if (ret) {
                dev_err(&pdev->dev, "Failed to enable spi_clk\n");
                goto err_spiclk_enable;
        }

        spi_enable_chip(rs, 0);

        rs->type = SSI_MOTO_SPI;
        rs->master = master;
        rs->dev = &pdev->dev;
        rs->max_freq = clk_get_rate(rs->spiclk);

        if (!of_property_read_u32(pdev->dev.of_node, "rx-sample-delay-ns",
                                  &rsd_nsecs))
                rs->rsd_nsecs = rsd_nsecs;

        rs->fifo_len = get_fifo_len(rs);
        if (!rs->fifo_len) {
                dev_err(&pdev->dev, "Failed to get fifo length\n");
                ret = -EINVAL;
                goto err_get_fifo_len;
        }

        spin_lock_init(&rs->lock);

        pm_runtime_set_active(&pdev->dev);
        pm_runtime_enable(&pdev->dev);

        master->auto_runtime_pm = true;
        master->bus_num = pdev->id;
        master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
        master->num_chipselect = 2;
        master->dev.of_node = pdev->dev.of_node;
        master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8);

        master->set_cs = rockchip_spi_set_cs;
        master->prepare_message = rockchip_spi_prepare_message;
        master->unprepare_message = rockchip_spi_unprepare_message;
        master->transfer_one = rockchip_spi_transfer_one;
        master->handle_err = rockchip_spi_handle_err;

        rs->dma_tx.ch = dma_request_slave_channel(rs->dev, "tx");
        if (IS_ERR_OR_NULL(rs->dma_tx.ch)) {
                /* Check tx to see if we need defer probing driver */
                if (PTR_ERR(rs->dma_tx.ch) == -EPROBE_DEFER) {
                        ret = -EPROBE_DEFER;
                        goto err_get_fifo_len;
                }
                dev_warn(rs->dev, "Failed to request TX DMA channel\n");
        }
        rs->dma_rx.ch = dma_request_slave_channel(rs->dev, "rx");
        if (!rs->dma_rx.ch) {
                if (rs->dma_tx.ch) {
                        dma_release_channel(rs->dma_tx.ch);
                        rs->dma_tx.ch = NULL;
                }
                dev_warn(rs->dev, "Failed to request RX DMA channel\n");
        }

        if (rs->dma_tx.ch && rs->dma_rx.ch) {
                rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR);
                rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR);
                rs->dma_tx.direction = DMA_MEM_TO_DEV;
                rs->dma_rx.direction = DMA_DEV_TO_MEM;

                master->can_dma = rockchip_spi_can_dma;
                master->dma_tx = rs->dma_tx.ch;
                master->dma_rx = rs->dma_rx.ch;
        }

        rs->high_speed_state = pinctrl_lookup_state(rs->dev->pins->p,
                                                     "high_speed");
        if (IS_ERR_OR_NULL(rs->high_speed_state)) {
                dev_warn(&pdev->dev, "no high_speed pinctrl state\n");
                rs->high_speed_state = NULL;
        }

        ret = devm_spi_register_master(&pdev->dev, master);
        if (ret) {
                dev_err(&pdev->dev, "Failed to register master\n");
                goto err_register_master;
        }

        return 0;

err_register_master:
        pm_runtime_disable(&pdev->dev);
        if (rs->dma_tx.ch)
                dma_release_channel(rs->dma_tx.ch);
        if (rs->dma_rx.ch)
                dma_release_channel(rs->dma_rx.ch);
err_get_fifo_len:
        clk_disable_unprepare(rs->spiclk);
err_spiclk_enable:
        clk_disable_unprepare(rs->apb_pclk);
err_ioremap_resource:
        spi_master_put(master);

        return ret;
}

sprd

新平台的控制器节点没有pinctrl属性,但是时钟属性还是一样的存在,并指明时钟来源

spi0: spi@24700000{
                compatible = "sprd,sc9860-spi", "sprd,orca-spi";
                reg = <0 0x24700000 0 0x1000>;
                clock-names = "enable", "spi", "source";
                clocks = <&apapb_gate CLK_AP_APB_SPI0_EB>,
                <&aon_clk CLK_AP_SPI0>, <&g3_pll CLK_TWPLL_192M>;
                interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
                #address-cells = <1>;
                #size-cells = <0>;
                status = "disabled";
        };

devm_clk_get获取时钟,clk_prepare_enable使能时钟;无需使用pinctrl_select_state复用pin的状态,默认行为就是spi,那么我们可以猜测要么硬件上电默认就是spi功能;要么使能了对应时钟,会自动耦合成spi功能

static int sprd_spi_clk_init(struct platform_device *pdev, struct sprd_spi *ss)
{
        struct clk *clk_spi, *clk_parent;

        clk_spi = devm_clk_get(&pdev->dev, "spi");
        if (IS_ERR(clk_spi)) {
                dev_warn(&pdev->dev, "can't get the spi clock\n");
                clk_spi = NULL;
        }

        clk_parent = devm_clk_get(&pdev->dev, "source");
        if (IS_ERR(clk_parent)) {
                dev_warn(&pdev->dev, "can't get the source clock\n");
                clk_parent = NULL;
        }

        ss->clk = devm_clk_get(&pdev->dev, "enable");
        if (IS_ERR(ss->clk)) {
                dev_err(&pdev->dev, "can't get the enable clock\n");
                return PTR_ERR(ss->clk);
        }

        if (!clk_set_parent(clk_spi, clk_parent))
                ss->src_clk = clk_get_rate(clk_spi);
        else
                ss->src_clk = SPRD_SPI_DEFAULT_SOURCE;

        return 0;
}

static int sprd_spi_probe(struct platform_device *pdev)
{
        struct spi_controller *sctlr;
        struct resource *res;
        struct sprd_spi *ss;
        int ret;

        pdev->id = of_alias_get_id(pdev->dev.of_node, "spi");
        sctlr = spi_alloc_master(&pdev->dev, sizeof(*ss));
        if (!sctlr)
                return -ENOMEM;

        ss = spi_controller_get_devdata(sctlr);
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        ss->base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(ss->base)) {
                ret = PTR_ERR(ss->base);
                goto free_controller;
        }

        ss->dev = &pdev->dev;
        sctlr->dev.of_node = pdev->dev.of_node;
        sctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_3WIRE | SPI_TX_DUAL;
        sctlr->bus_num = pdev->id;
        sctlr->set_cs = sprd_spi_chipselect;
        sctlr->transfer_one = sprd_spi_transfer_one;
        sctlr->auto_runtime_pm = true;
        sctlr->max_speed_hz = min_t(u32, ss->src_clk >> 1,
                                    SPRD_SPI_MAX_SPEED_HZ);

        platform_set_drvdata(pdev, sctlr);
        ret = sprd_spi_clk_init(pdev, ss);
        if (ret)
                goto free_controller;

        ret = clk_prepare_enable(ss->clk);
        if (ret)
                goto free_controller;

        ret = pm_runtime_set_active(&pdev->dev);
        if (ret < 0)
                goto disable_clk;

        pm_runtime_set_autosuspend_delay(&pdev->dev,
                                         SPRD_SPI_AUTOSUSPEND_DELAY);
        pm_runtime_use_autosuspend(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
        ret = pm_runtime_get_sync(&pdev->dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to resume SPI controller\n");
                goto err_rpm_put;
        }

        ret = devm_spi_register_controller(&pdev->dev, sctlr);
        if (ret)
                goto err_rpm_put;

        pm_runtime_mark_last_busy(&pdev->dev);
        pm_runtime_put_autosuspend(&pdev->dev);

        return 0;

err_rpm_put:
        pm_runtime_put_noidle(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
disable_clk:
        clk_disable_unprepare(ss->clk);
free_controller:
        spi_controller_put(sctlr);

        return ret;
}

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值