FILE
gpio-spacemit-k1.c
File preview with syntax highlighting.
linux-6.19.14/drivers/gpio/gpio-spacemit-k1.c
1// SPDX-License-Identifier: GPL-2.0 OR MIT 2/* 3 * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd 4 * Copyright (C) 2025 Yixun Lan <dlan@gentoo.org> 5 */ 6 7#include <linux/clk.h> 8#include <linux/gpio/driver.h> 9#include <linux/gpio/generic.h> 10#include <linux/init.h> 11#include <linux/interrupt.h> 12#include <linux/io.h> 13#include <linux/irq.h> 14#include <linux/module.h> 15#include <linux/platform_device.h> 16#include <linux/seq_file.h> 17 18/* register offset */ 19#define SPACEMIT_GPLR 0x00 /* port level - R */ 20#define SPACEMIT_GPDR 0x0c /* port direction - R/W */ 21#define SPACEMIT_GPSR 0x18 /* port set - W */ 22#define SPACEMIT_GPCR 0x24 /* port clear - W */ 23#define SPACEMIT_GRER 0x30 /* port rising edge R/W */ 24#define SPACEMIT_GFER 0x3c /* port falling edge R/W */ 25#define SPACEMIT_GEDR 0x48 /* edge detect status - R/W1C */ 26#define SPACEMIT_GSDR 0x54 /* (set) direction - W */ 27#define SPACEMIT_GCDR 0x60 /* (clear) direction - W */ 28#define SPACEMIT_GSRER 0x6c /* (set) rising edge detect enable - W */ 29#define SPACEMIT_GCRER 0x78 /* (clear) rising edge detect enable - W */ 30#define SPACEMIT_GSFER 0x84 /* (set) falling edge detect enable - W */ 31#define SPACEMIT_GCFER 0x90 /* (clear) falling edge detect enable - W */ 32#define SPACEMIT_GAPMASK 0x9c /* interrupt mask , 0 disable, 1 enable - R/W */ 33 34#define SPACEMIT_NR_BANKS 4 35#define SPACEMIT_NR_GPIOS_PER_BANK 32 36 37#define to_spacemit_gpio_bank(x) container_of((x), struct spacemit_gpio_bank, gc) 38 39struct spacemit_gpio; 40 41struct spacemit_gpio_bank { 42 struct gpio_generic_chip chip; 43 struct spacemit_gpio *sg; 44 void __iomem *base; 45 u32 irq_mask; 46 u32 irq_rising_edge; 47 u32 irq_falling_edge; 48}; 49 50struct spacemit_gpio { 51 struct device *dev; 52 struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS]; 53}; 54 55static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb) 56{ 57 return (u32)(gb - gb->sg->sgb); 58} 59 60static irqreturn_t spacemit_gpio_irq_handler(int irq, void *dev_id) 61{ 62 struct spacemit_gpio_bank *gb = dev_id; 63 unsigned long pending; 64 u32 n, gedr; 65 66 gedr = readl(gb->base + SPACEMIT_GEDR); 67 if (!gedr) 68 return IRQ_NONE; 69 writel(gedr, gb->base + SPACEMIT_GEDR); 70 71 pending = gedr & gb->irq_mask; 72 if (!pending) 73 return IRQ_NONE; 74 75 for_each_set_bit(n, &pending, BITS_PER_LONG) 76 handle_nested_irq(irq_find_mapping(gb->chip.gc.irq.domain, n)); 77 78 return IRQ_HANDLED; 79} 80 81static void spacemit_gpio_irq_ack(struct irq_data *d) 82{ 83 struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); 84 85 writel(BIT(irqd_to_hwirq(d)), gb->base + SPACEMIT_GEDR); 86} 87 88static void spacemit_gpio_irq_mask(struct irq_data *d) 89{ 90 struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); 91 u32 bit = BIT(irqd_to_hwirq(d)); 92 93 gb->irq_mask &= ~bit; 94 writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); 95 96 if (bit & gb->irq_rising_edge) 97 writel(bit, gb->base + SPACEMIT_GCRER); 98 99 if (bit & gb->irq_falling_edge) 100 writel(bit, gb->base + SPACEMIT_GCFER); 101} 102 103static void spacemit_gpio_irq_unmask(struct irq_data *d) 104{ 105 struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); 106 u32 bit = BIT(irqd_to_hwirq(d)); 107 108 gb->irq_mask |= bit; 109 110 if (bit & gb->irq_rising_edge) 111 writel(bit, gb->base + SPACEMIT_GSRER); 112 113 if (bit & gb->irq_falling_edge) 114 writel(bit, gb->base + SPACEMIT_GSFER); 115 116 writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); 117} 118 119static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type) 120{ 121 struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); 122 u32 bit = BIT(irqd_to_hwirq(d)); 123 124 if (type & IRQ_TYPE_EDGE_RISING) { 125 gb->irq_rising_edge |= bit; 126 writel(bit, gb->base + SPACEMIT_GSRER); 127 } else { 128 gb->irq_rising_edge &= ~bit; 129 writel(bit, gb->base + SPACEMIT_GCRER); 130 } 131 132 if (type & IRQ_TYPE_EDGE_FALLING) { 133 gb->irq_falling_edge |= bit; 134 writel(bit, gb->base + SPACEMIT_GSFER); 135 } else { 136 gb->irq_falling_edge &= ~bit; 137 writel(bit, gb->base + SPACEMIT_GCFER); 138 } 139 140 return 0; 141} 142 143static void spacemit_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p) 144{ 145 struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(data); 146 147 seq_printf(p, "%s-%d", dev_name(gb->chip.gc.parent), spacemit_gpio_bank_index(gb)); 148} 149 150static struct irq_chip spacemit_gpio_chip = { 151 .name = "k1-gpio-irqchip", 152 .irq_ack = spacemit_gpio_irq_ack, 153 .irq_mask = spacemit_gpio_irq_mask, 154 .irq_unmask = spacemit_gpio_irq_unmask, 155 .irq_set_type = spacemit_gpio_irq_set_type, 156 .irq_print_chip = spacemit_gpio_irq_print_chip, 157 .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE, 158 GPIOCHIP_IRQ_RESOURCE_HELPERS, 159}; 160 161static bool spacemit_of_node_instance_match(struct gpio_chip *gc, unsigned int i) 162{ 163 struct spacemit_gpio_bank *gb = gpiochip_get_data(gc); 164 struct spacemit_gpio *sg = gb->sg; 165 166 if (i >= SPACEMIT_NR_BANKS) 167 return false; 168 169 return (gc == &sg->sgb[i].chip.gc); 170} 171 172static int spacemit_gpio_add_bank(struct spacemit_gpio *sg, 173 void __iomem *regs, 174 int index, int irq) 175{ 176 struct spacemit_gpio_bank *gb = &sg->sgb[index]; 177 struct gpio_generic_chip_config config; 178 struct gpio_chip *gc = &gb->chip.gc; 179 struct device *dev = sg->dev; 180 struct gpio_irq_chip *girq; 181 void __iomem *dat, *set, *clr, *dirin, *dirout; 182 int ret, bank_base[] = { 0x0, 0x4, 0x8, 0x100 }; 183 184 gb->base = regs + bank_base[index]; 185 186 dat = gb->base + SPACEMIT_GPLR; 187 set = gb->base + SPACEMIT_GPSR; 188 clr = gb->base + SPACEMIT_GPCR; 189 dirin = gb->base + SPACEMIT_GCDR; 190 dirout = gb->base + SPACEMIT_GSDR; 191 192 config = (struct gpio_generic_chip_config) { 193 .dev = dev, 194 .sz = 4, 195 .dat = dat, 196 .set = set, 197 .clr = clr, 198 .dirout = dirout, 199 .dirin = dirin, 200 .flags = GPIO_GENERIC_UNREADABLE_REG_SET | 201 GPIO_GENERIC_UNREADABLE_REG_DIR, 202 }; 203 204 /* This registers 32 GPIO lines per bank */ 205 ret = gpio_generic_chip_init(&gb->chip, &config); 206 if (ret) 207 return dev_err_probe(dev, ret, "failed to init gpio chip\n"); 208 209 gb->sg = sg; 210 211 gc->label = dev_name(dev); 212 gc->request = gpiochip_generic_request; 213 gc->free = gpiochip_generic_free; 214 gc->ngpio = SPACEMIT_NR_GPIOS_PER_BANK; 215 gc->base = -1; 216 gc->of_gpio_n_cells = 3; 217 gc->of_node_instance_match = spacemit_of_node_instance_match; 218 219 girq = &gc->irq; 220 girq->threaded = true; 221 girq->handler = handle_simple_irq; 222 223 gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip); 224 225 /* Disable Interrupt */ 226 writel(0, gb->base + SPACEMIT_GAPMASK); 227 /* Disable Edge Detection Settings */ 228 writel(0x0, gb->base + SPACEMIT_GRER); 229 writel(0x0, gb->base + SPACEMIT_GFER); 230 /* Clear Interrupt */ 231 writel(0xffffffff, gb->base + SPACEMIT_GCRER); 232 writel(0xffffffff, gb->base + SPACEMIT_GCFER); 233 234 ret = devm_request_threaded_irq(dev, irq, NULL, 235 spacemit_gpio_irq_handler, 236 IRQF_ONESHOT | IRQF_SHARED, 237 gb->chip.gc.label, gb); 238 if (ret < 0) 239 return dev_err_probe(dev, ret, "failed to register IRQ\n"); 240 241 ret = devm_gpiochip_add_data(dev, gc, gb); 242 if (ret) 243 return ret; 244 245 /* Distuingish IRQ domain, for selecting threecells mode */ 246 irq_domain_update_bus_token(girq->domain, DOMAIN_BUS_WIRED); 247 248 return 0; 249} 250 251static int spacemit_gpio_probe(struct platform_device *pdev) 252{ 253 struct device *dev = &pdev->dev; 254 struct spacemit_gpio *sg; 255 struct clk *core_clk, *bus_clk; 256 void __iomem *regs; 257 int i, irq, ret; 258 259 sg = devm_kzalloc(dev, sizeof(*sg), GFP_KERNEL); 260 if (!sg) 261 return -ENOMEM; 262 263 regs = devm_platform_ioremap_resource(pdev, 0); 264 if (IS_ERR(regs)) 265 return PTR_ERR(regs); 266 267 irq = platform_get_irq(pdev, 0); 268 if (irq < 0) 269 return irq; 270 271 sg->dev = dev; 272 273 core_clk = devm_clk_get_enabled(dev, "core"); 274 if (IS_ERR(core_clk)) 275 return dev_err_probe(dev, PTR_ERR(core_clk), "failed to get clock\n"); 276 277 bus_clk = devm_clk_get_enabled(dev, "bus"); 278 if (IS_ERR(bus_clk)) 279 return dev_err_probe(dev, PTR_ERR(bus_clk), "failed to get bus clock\n"); 280 281 for (i = 0; i < SPACEMIT_NR_BANKS; i++) { 282 ret = spacemit_gpio_add_bank(sg, regs, i, irq); 283 if (ret) 284 return ret; 285 } 286 287 return 0; 288} 289 290static const struct of_device_id spacemit_gpio_dt_ids[] = { 291 { .compatible = "spacemit,k1-gpio" }, 292 { /* sentinel */ } 293}; 294MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids); 295 296static struct platform_driver spacemit_gpio_driver = { 297 .probe = spacemit_gpio_probe, 298 .driver = { 299 .name = "k1-gpio", 300 .of_match_table = spacemit_gpio_dt_ids, 301 }, 302}; 303module_platform_driver(spacemit_gpio_driver); 304 305MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>"); 306MODULE_DESCRIPTION("GPIO driver for SpacemiT K1 SoC"); 307MODULE_LICENSE("GPL");
~/
Read-only file preview.