mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:04:51 +01:00
gpio: spacemit: Add GPIO support for K3 SoC
SpacemiT K3 SoC has changed gpio register layout while comparing with previous generation, the register offset and bank offset need to be adjusted, introduce a compatible data to extend the driver to support this. Signed-off-by: Yixun Lan <dlan@gentoo.org> Link: https://lore.kernel.org/r/20260106-02-k3-gpio-v3-2-4800c214810b@gentoo.org Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
This commit is contained in:
parent
48033e4c67
commit
da64eb5159
1 changed files with 117 additions and 46 deletions
|
|
@ -15,29 +15,37 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
/* register offset */
|
||||
#define SPACEMIT_GPLR 0x00 /* port level - R */
|
||||
#define SPACEMIT_GPDR 0x0c /* port direction - R/W */
|
||||
#define SPACEMIT_GPSR 0x18 /* port set - W */
|
||||
#define SPACEMIT_GPCR 0x24 /* port clear - W */
|
||||
#define SPACEMIT_GRER 0x30 /* port rising edge R/W */
|
||||
#define SPACEMIT_GFER 0x3c /* port falling edge R/W */
|
||||
#define SPACEMIT_GEDR 0x48 /* edge detect status - R/W1C */
|
||||
#define SPACEMIT_GSDR 0x54 /* (set) direction - W */
|
||||
#define SPACEMIT_GCDR 0x60 /* (clear) direction - W */
|
||||
#define SPACEMIT_GSRER 0x6c /* (set) rising edge detect enable - W */
|
||||
#define SPACEMIT_GCRER 0x78 /* (clear) rising edge detect enable - W */
|
||||
#define SPACEMIT_GSFER 0x84 /* (set) falling edge detect enable - W */
|
||||
#define SPACEMIT_GCFER 0x90 /* (clear) falling edge detect enable - W */
|
||||
#define SPACEMIT_GAPMASK 0x9c /* interrupt mask , 0 disable, 1 enable - R/W */
|
||||
|
||||
#define SPACEMIT_NR_BANKS 4
|
||||
#define SPACEMIT_NR_GPIOS_PER_BANK 32
|
||||
|
||||
#define to_spacemit_gpio_bank(x) container_of((x), struct spacemit_gpio_bank, gc)
|
||||
#define to_spacemit_gpio_regs(gb) ((gb)->sg->data->offsets)
|
||||
|
||||
enum spacemit_gpio_registers {
|
||||
SPACEMIT_GPLR, /* port level - R */
|
||||
SPACEMIT_GPDR, /* port direction - R/W */
|
||||
SPACEMIT_GPSR, /* port set - W */
|
||||
SPACEMIT_GPCR, /* port clear - W */
|
||||
SPACEMIT_GRER, /* port rising edge R/W */
|
||||
SPACEMIT_GFER, /* port falling edge R/W */
|
||||
SPACEMIT_GEDR, /* edge detect status - R/W1C */
|
||||
SPACEMIT_GSDR, /* (set) direction - W */
|
||||
SPACEMIT_GCDR, /* (clear) direction - W */
|
||||
SPACEMIT_GSRER, /* (set) rising edge detect enable - W */
|
||||
SPACEMIT_GCRER, /* (clear) rising edge detect enable - W */
|
||||
SPACEMIT_GSFER, /* (set) falling edge detect enable - W */
|
||||
SPACEMIT_GCFER, /* (clear) falling edge detect enable - W */
|
||||
SPACEMIT_GAPMASK, /* interrupt mask , 0 disable, 1 enable - R/W */
|
||||
SPACEMIT_GCPMASK, /* interrupt mask for K3 */
|
||||
};
|
||||
|
||||
struct spacemit_gpio;
|
||||
|
||||
struct spacemit_gpio_data {
|
||||
const unsigned int *offsets;
|
||||
u32 bank_offsets[SPACEMIT_NR_BANKS];
|
||||
};
|
||||
|
||||
struct spacemit_gpio_bank {
|
||||
struct gpio_generic_chip chip;
|
||||
struct spacemit_gpio *sg;
|
||||
|
|
@ -49,9 +57,22 @@ struct spacemit_gpio_bank {
|
|||
|
||||
struct spacemit_gpio {
|
||||
struct device *dev;
|
||||
const struct spacemit_gpio_data *data;
|
||||
struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS];
|
||||
};
|
||||
|
||||
static u32 spacemit_gpio_read(struct spacemit_gpio_bank *gb,
|
||||
enum spacemit_gpio_registers reg)
|
||||
{
|
||||
return readl(gb->base + to_spacemit_gpio_regs(gb)[reg]);
|
||||
}
|
||||
|
||||
static void spacemit_gpio_write(struct spacemit_gpio_bank *gb,
|
||||
enum spacemit_gpio_registers reg, u32 val)
|
||||
{
|
||||
writel(val, gb->base + to_spacemit_gpio_regs(gb)[reg]);
|
||||
}
|
||||
|
||||
static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb)
|
||||
{
|
||||
return (u32)(gb - gb->sg->sgb);
|
||||
|
|
@ -63,10 +84,10 @@ static irqreturn_t spacemit_gpio_irq_handler(int irq, void *dev_id)
|
|||
unsigned long pending;
|
||||
u32 n, gedr;
|
||||
|
||||
gedr = readl(gb->base + SPACEMIT_GEDR);
|
||||
gedr = spacemit_gpio_read(gb, SPACEMIT_GEDR);
|
||||
if (!gedr)
|
||||
return IRQ_NONE;
|
||||
writel(gedr, gb->base + SPACEMIT_GEDR);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GEDR, gedr);
|
||||
|
||||
pending = gedr & gb->irq_mask;
|
||||
if (!pending)
|
||||
|
|
@ -82,7 +103,7 @@ static void spacemit_gpio_irq_ack(struct irq_data *d)
|
|||
{
|
||||
struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d);
|
||||
|
||||
writel(BIT(irqd_to_hwirq(d)), gb->base + SPACEMIT_GEDR);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GEDR, BIT(irqd_to_hwirq(d)));
|
||||
}
|
||||
|
||||
static void spacemit_gpio_irq_mask(struct irq_data *d)
|
||||
|
|
@ -91,13 +112,13 @@ static void spacemit_gpio_irq_mask(struct irq_data *d)
|
|||
u32 bit = BIT(irqd_to_hwirq(d));
|
||||
|
||||
gb->irq_mask &= ~bit;
|
||||
writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask);
|
||||
|
||||
if (bit & gb->irq_rising_edge)
|
||||
writel(bit, gb->base + SPACEMIT_GCRER);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GCRER, bit);
|
||||
|
||||
if (bit & gb->irq_falling_edge)
|
||||
writel(bit, gb->base + SPACEMIT_GCFER);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GCFER, bit);
|
||||
}
|
||||
|
||||
static void spacemit_gpio_irq_unmask(struct irq_data *d)
|
||||
|
|
@ -108,12 +129,12 @@ static void spacemit_gpio_irq_unmask(struct irq_data *d)
|
|||
gb->irq_mask |= bit;
|
||||
|
||||
if (bit & gb->irq_rising_edge)
|
||||
writel(bit, gb->base + SPACEMIT_GSRER);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GSRER, bit);
|
||||
|
||||
if (bit & gb->irq_falling_edge)
|
||||
writel(bit, gb->base + SPACEMIT_GSFER);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GSFER, bit);
|
||||
|
||||
writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask);
|
||||
}
|
||||
|
||||
static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
|
|
@ -123,18 +144,18 @@ static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
|||
|
||||
if (type & IRQ_TYPE_EDGE_RISING) {
|
||||
gb->irq_rising_edge |= bit;
|
||||
writel(bit, gb->base + SPACEMIT_GSRER);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GSRER, bit);
|
||||
} else {
|
||||
gb->irq_rising_edge &= ~bit;
|
||||
writel(bit, gb->base + SPACEMIT_GCRER);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GCRER, bit);
|
||||
}
|
||||
|
||||
if (type & IRQ_TYPE_EDGE_FALLING) {
|
||||
gb->irq_falling_edge |= bit;
|
||||
writel(bit, gb->base + SPACEMIT_GSFER);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GSFER, bit);
|
||||
} else {
|
||||
gb->irq_falling_edge &= ~bit;
|
||||
writel(bit, gb->base + SPACEMIT_GCFER);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GCFER, bit);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -179,15 +200,16 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
|
|||
struct device *dev = sg->dev;
|
||||
struct gpio_irq_chip *girq;
|
||||
void __iomem *dat, *set, *clr, *dirin, *dirout;
|
||||
int ret, bank_base[] = { 0x0, 0x4, 0x8, 0x100 };
|
||||
int ret;
|
||||
|
||||
gb->base = regs + bank_base[index];
|
||||
gb->base = regs + sg->data->bank_offsets[index];
|
||||
gb->sg = sg;
|
||||
|
||||
dat = gb->base + SPACEMIT_GPLR;
|
||||
set = gb->base + SPACEMIT_GPSR;
|
||||
clr = gb->base + SPACEMIT_GPCR;
|
||||
dirin = gb->base + SPACEMIT_GCDR;
|
||||
dirout = gb->base + SPACEMIT_GSDR;
|
||||
dat = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPLR];
|
||||
set = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPSR];
|
||||
clr = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPCR];
|
||||
dirin = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GCDR];
|
||||
dirout = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GSDR];
|
||||
|
||||
config = (struct gpio_generic_chip_config) {
|
||||
.dev = dev,
|
||||
|
|
@ -206,8 +228,6 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
|
|||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to init gpio chip\n");
|
||||
|
||||
gb->sg = sg;
|
||||
|
||||
gc->label = dev_name(dev);
|
||||
gc->request = gpiochip_generic_request;
|
||||
gc->free = gpiochip_generic_free;
|
||||
|
|
@ -223,13 +243,13 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
|
|||
gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip);
|
||||
|
||||
/* Disable Interrupt */
|
||||
writel(0, gb->base + SPACEMIT_GAPMASK);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GAPMASK, 0);
|
||||
/* Disable Edge Detection Settings */
|
||||
writel(0x0, gb->base + SPACEMIT_GRER);
|
||||
writel(0x0, gb->base + SPACEMIT_GFER);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GRER, 0x0);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GFER, 0x0);
|
||||
/* Clear Interrupt */
|
||||
writel(0xffffffff, gb->base + SPACEMIT_GCRER);
|
||||
writel(0xffffffff, gb->base + SPACEMIT_GCFER);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GCRER, 0xffffffff);
|
||||
spacemit_gpio_write(gb, SPACEMIT_GCFER, 0xffffffff);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
spacemit_gpio_irq_handler,
|
||||
|
|
@ -260,6 +280,10 @@ static int spacemit_gpio_probe(struct platform_device *pdev)
|
|||
if (!sg)
|
||||
return -ENOMEM;
|
||||
|
||||
sg->data = of_device_get_match_data(dev);
|
||||
if (!sg->data)
|
||||
return dev_err_probe(dev, -EINVAL, "No available compatible data.");
|
||||
|
||||
regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
|
@ -287,8 +311,55 @@ static int spacemit_gpio_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const unsigned int spacemit_gpio_k1_offsets[] = {
|
||||
[SPACEMIT_GPLR] = 0x00,
|
||||
[SPACEMIT_GPDR] = 0x0c,
|
||||
[SPACEMIT_GPSR] = 0x18,
|
||||
[SPACEMIT_GPCR] = 0x24,
|
||||
[SPACEMIT_GRER] = 0x30,
|
||||
[SPACEMIT_GFER] = 0x3c,
|
||||
[SPACEMIT_GEDR] = 0x48,
|
||||
[SPACEMIT_GSDR] = 0x54,
|
||||
[SPACEMIT_GCDR] = 0x60,
|
||||
[SPACEMIT_GSRER] = 0x6c,
|
||||
[SPACEMIT_GCRER] = 0x78,
|
||||
[SPACEMIT_GSFER] = 0x84,
|
||||
[SPACEMIT_GCFER] = 0x90,
|
||||
[SPACEMIT_GAPMASK] = 0x9c,
|
||||
[SPACEMIT_GCPMASK] = 0xA8,
|
||||
};
|
||||
|
||||
static const unsigned int spacemit_gpio_k3_offsets[] = {
|
||||
[SPACEMIT_GPLR] = 0x0,
|
||||
[SPACEMIT_GPDR] = 0x4,
|
||||
[SPACEMIT_GPSR] = 0x8,
|
||||
[SPACEMIT_GPCR] = 0xc,
|
||||
[SPACEMIT_GRER] = 0x10,
|
||||
[SPACEMIT_GFER] = 0x14,
|
||||
[SPACEMIT_GEDR] = 0x18,
|
||||
[SPACEMIT_GSDR] = 0x1c,
|
||||
[SPACEMIT_GCDR] = 0x20,
|
||||
[SPACEMIT_GSRER] = 0x24,
|
||||
[SPACEMIT_GCRER] = 0x28,
|
||||
[SPACEMIT_GSFER] = 0x2c,
|
||||
[SPACEMIT_GCFER] = 0x30,
|
||||
[SPACEMIT_GAPMASK] = 0x34,
|
||||
[SPACEMIT_GCPMASK] = 0x38,
|
||||
};
|
||||
|
||||
static const struct spacemit_gpio_data k1_gpio_data = {
|
||||
.offsets = spacemit_gpio_k1_offsets,
|
||||
.bank_offsets = { 0x0, 0x4, 0x8, 0x100 },
|
||||
};
|
||||
|
||||
static const struct spacemit_gpio_data k3_gpio_data = {
|
||||
.offsets = spacemit_gpio_k3_offsets,
|
||||
.bank_offsets = { 0x0, 0x40, 0x80, 0x100 },
|
||||
};
|
||||
|
||||
static const struct of_device_id spacemit_gpio_dt_ids[] = {
|
||||
{ .compatible = "spacemit,k1-gpio" },
|
||||
{ .compatible = "spacemit,k1-gpio", .data = &k1_gpio_data },
|
||||
{ .compatible = "spacemit,k3-gpio", .data = &k3_gpio_data },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids);
|
||||
|
|
@ -296,12 +367,12 @@ MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids);
|
|||
static struct platform_driver spacemit_gpio_driver = {
|
||||
.probe = spacemit_gpio_probe,
|
||||
.driver = {
|
||||
.name = "k1-gpio",
|
||||
.name = "spacemit-gpio",
|
||||
.of_match_table = spacemit_gpio_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(spacemit_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>");
|
||||
MODULE_DESCRIPTION("GPIO driver for SpacemiT K1 SoC");
|
||||
MODULE_DESCRIPTION("GPIO driver for SpacemiT K1/K3 SoC");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue