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, ®, 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;
}