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 #define SPACEMIT_NR_BANKS 4 19 #define SPACEMIT_NR_GPIOS_PER_BANK 32 20 21 #define to_spacemit_gpio_bank(x) container_of((x), struct spacemit_gpio_bank, gc) 22 #define to_spacemit_gpio_regs(gb) ((gb)->sg->data->offsets) 23 24 enum spacemit_gpio_registers { 25 SPACEMIT_GPLR, /* port level - R */ 26 SPACEMIT_GPDR, /* port direction - R/W */ 27 SPACEMIT_GPSR, /* port set - W */ 28 SPACEMIT_GPCR, /* port clear - W */ 29 SPACEMIT_GRER, /* port rising edge R/W */ 30 SPACEMIT_GFER, /* port falling edge R/W */ 31 SPACEMIT_GEDR, /* edge detect status - R/W1C */ 32 SPACEMIT_GSDR, /* (set) direction - W */ 33 SPACEMIT_GCDR, /* (clear) direction - W */ 34 SPACEMIT_GSRER, /* (set) rising edge detect enable - W */ 35 SPACEMIT_GCRER, /* (clear) rising edge detect enable - W */ 36 SPACEMIT_GSFER, /* (set) falling edge detect enable - W */ 37 SPACEMIT_GCFER, /* (clear) falling edge detect enable - W */ 38 SPACEMIT_GAPMASK, /* interrupt mask , 0 disable, 1 enable - R/W */ 39 SPACEMIT_GCPMASK, /* interrupt mask for K3 */ 40 }; 41 42 struct spacemit_gpio; 43 44 struct spacemit_gpio_data { 45 const unsigned int *offsets; 46 u32 bank_offsets[SPACEMIT_NR_BANKS]; 47 }; 48 49 struct spacemit_gpio_bank { 50 struct gpio_generic_chip chip; 51 struct spacemit_gpio *sg; 52 void __iomem *base; 53 u32 irq_mask; 54 u32 irq_rising_edge; 55 u32 irq_falling_edge; 56 }; 57 58 struct spacemit_gpio { 59 struct device *dev; 60 const struct spacemit_gpio_data *data; 61 struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS]; 62 }; 63 64 static u32 spacemit_gpio_read(struct spacemit_gpio_bank *gb, 65 enum spacemit_gpio_registers reg) 66 { 67 return readl(gb->base + to_spacemit_gpio_regs(gb)[reg]); 68 } 69 70 static void spacemit_gpio_write(struct spacemit_gpio_bank *gb, 71 enum spacemit_gpio_registers reg, u32 val) 72 { 73 writel(val, gb->base + to_spacemit_gpio_regs(gb)[reg]); 74 } 75 76 static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb) 77 { 78 return (u32)(gb - gb->sg->sgb); 79 } 80 81 static irqreturn_t spacemit_gpio_irq_handler(int irq, void *dev_id) 82 { 83 struct spacemit_gpio_bank *gb = dev_id; 84 unsigned long pending; 85 u32 n, gedr; 86 87 gedr = spacemit_gpio_read(gb, SPACEMIT_GEDR); 88 if (!gedr) 89 return IRQ_NONE; 90 spacemit_gpio_write(gb, SPACEMIT_GEDR, gedr); 91 92 pending = gedr & gb->irq_mask; 93 if (!pending) 94 return IRQ_NONE; 95 96 for_each_set_bit(n, &pending, BITS_PER_LONG) 97 handle_nested_irq(irq_find_mapping(gb->chip.gc.irq.domain, n)); 98 99 return IRQ_HANDLED; 100 } 101 102 static void spacemit_gpio_irq_ack(struct irq_data *d) 103 { 104 struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); 105 106 spacemit_gpio_write(gb, SPACEMIT_GEDR, BIT(irqd_to_hwirq(d))); 107 } 108 109 static void spacemit_gpio_irq_mask(struct irq_data *d) 110 { 111 struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); 112 u32 bit = BIT(irqd_to_hwirq(d)); 113 114 gb->irq_mask &= ~bit; 115 spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask); 116 117 if (bit & gb->irq_rising_edge) 118 spacemit_gpio_write(gb, SPACEMIT_GCRER, bit); 119 120 if (bit & gb->irq_falling_edge) 121 spacemit_gpio_write(gb, SPACEMIT_GCFER, bit); 122 } 123 124 static void spacemit_gpio_irq_unmask(struct irq_data *d) 125 { 126 struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); 127 u32 bit = BIT(irqd_to_hwirq(d)); 128 129 gb->irq_mask |= bit; 130 131 if (bit & gb->irq_rising_edge) 132 spacemit_gpio_write(gb, SPACEMIT_GSRER, bit); 133 134 if (bit & gb->irq_falling_edge) 135 spacemit_gpio_write(gb, SPACEMIT_GSFER, bit); 136 137 spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask); 138 } 139 140 static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type) 141 { 142 struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); 143 u32 bit = BIT(irqd_to_hwirq(d)); 144 145 if (type & IRQ_TYPE_EDGE_RISING) { 146 gb->irq_rising_edge |= bit; 147 spacemit_gpio_write(gb, SPACEMIT_GSRER, bit); 148 } else { 149 gb->irq_rising_edge &= ~bit; 150 spacemit_gpio_write(gb, SPACEMIT_GCRER, bit); 151 } 152 153 if (type & IRQ_TYPE_EDGE_FALLING) { 154 gb->irq_falling_edge |= bit; 155 spacemit_gpio_write(gb, SPACEMIT_GSFER, bit); 156 } else { 157 gb->irq_falling_edge &= ~bit; 158 spacemit_gpio_write(gb, SPACEMIT_GCFER, bit); 159 } 160 161 return 0; 162 } 163 164 static void spacemit_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p) 165 { 166 struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(data); 167 168 seq_printf(p, "%s-%d", dev_name(gb->chip.gc.parent), spacemit_gpio_bank_index(gb)); 169 } 170 171 static struct irq_chip spacemit_gpio_chip = { 172 .name = "k1-gpio-irqchip", 173 .irq_ack = spacemit_gpio_irq_ack, 174 .irq_mask = spacemit_gpio_irq_mask, 175 .irq_unmask = spacemit_gpio_irq_unmask, 176 .irq_set_type = spacemit_gpio_irq_set_type, 177 .irq_print_chip = spacemit_gpio_irq_print_chip, 178 .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE, 179 GPIOCHIP_IRQ_RESOURCE_HELPERS, 180 }; 181 182 static bool spacemit_of_node_instance_match(struct gpio_chip *gc, unsigned int i) 183 { 184 struct spacemit_gpio_bank *gb = gpiochip_get_data(gc); 185 struct spacemit_gpio *sg = gb->sg; 186 187 if (i >= SPACEMIT_NR_BANKS) 188 return false; 189 190 return (gc == &sg->sgb[i].chip.gc); 191 } 192 193 static int spacemit_gpio_add_bank(struct spacemit_gpio *sg, 194 void __iomem *regs, 195 int index, int irq) 196 { 197 struct spacemit_gpio_bank *gb = &sg->sgb[index]; 198 struct gpio_generic_chip_config config; 199 struct gpio_chip *gc = &gb->chip.gc; 200 struct device *dev = sg->dev; 201 struct gpio_irq_chip *girq; 202 void __iomem *dat, *set, *clr, *dirout; 203 int ret; 204 205 gb->base = regs + sg->data->bank_offsets[index]; 206 gb->sg = sg; 207 208 dat = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPLR]; 209 set = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPSR]; 210 clr = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPCR]; 211 dirout = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPDR]; 212 213 config = (struct gpio_generic_chip_config) { 214 .dev = dev, 215 .sz = 4, 216 .dat = dat, 217 .set = set, 218 .clr = clr, 219 .dirout = dirout, 220 .flags = GPIO_GENERIC_UNREADABLE_REG_SET, 221 }; 222 223 /* This registers 32 GPIO lines per bank */ 224 ret = gpio_generic_chip_init(&gb->chip, &config); 225 if (ret) 226 return dev_err_probe(dev, ret, "failed to init gpio chip\n"); 227 228 gc->label = dev_name(dev); 229 gc->request = gpiochip_generic_request; 230 gc->free = gpiochip_generic_free; 231 gc->ngpio = SPACEMIT_NR_GPIOS_PER_BANK; 232 gc->base = -1; 233 gc->of_gpio_n_cells = 3; 234 gc->of_node_instance_match = spacemit_of_node_instance_match; 235 236 girq = &gc->irq; 237 girq->threaded = true; 238 girq->handler = handle_simple_irq; 239 240 gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip); 241 242 /* Disable Interrupt */ 243 spacemit_gpio_write(gb, SPACEMIT_GAPMASK, 0); 244 /* Disable Edge Detection Settings */ 245 spacemit_gpio_write(gb, SPACEMIT_GRER, 0x0); 246 spacemit_gpio_write(gb, SPACEMIT_GFER, 0x0); 247 /* Clear Interrupt */ 248 spacemit_gpio_write(gb, SPACEMIT_GCRER, 0xffffffff); 249 spacemit_gpio_write(gb, SPACEMIT_GCFER, 0xffffffff); 250 251 ret = devm_request_threaded_irq(dev, irq, NULL, 252 spacemit_gpio_irq_handler, 253 IRQF_ONESHOT | IRQF_SHARED, 254 gb->chip.gc.label, gb); 255 if (ret < 0) 256 return dev_err_probe(dev, ret, "failed to register IRQ\n"); 257 258 ret = devm_gpiochip_add_data(dev, gc, gb); 259 if (ret) 260 return ret; 261 262 /* Distuingish IRQ domain, for selecting threecells mode */ 263 irq_domain_update_bus_token(girq->domain, DOMAIN_BUS_WIRED); 264 265 return 0; 266 } 267 268 static int spacemit_gpio_probe(struct platform_device *pdev) 269 { 270 struct device *dev = &pdev->dev; 271 struct spacemit_gpio *sg; 272 struct clk *core_clk, *bus_clk; 273 void __iomem *regs; 274 int i, irq, ret; 275 276 sg = devm_kzalloc(dev, sizeof(*sg), GFP_KERNEL); 277 if (!sg) 278 return -ENOMEM; 279 280 sg->data = of_device_get_match_data(dev); 281 if (!sg->data) 282 return dev_err_probe(dev, -EINVAL, "No available compatible data."); 283 284 regs = devm_platform_ioremap_resource(pdev, 0); 285 if (IS_ERR(regs)) 286 return PTR_ERR(regs); 287 288 irq = platform_get_irq(pdev, 0); 289 if (irq < 0) 290 return irq; 291 292 sg->dev = dev; 293 294 core_clk = devm_clk_get_enabled(dev, "core"); 295 if (IS_ERR(core_clk)) 296 return dev_err_probe(dev, PTR_ERR(core_clk), "failed to get clock\n"); 297 298 bus_clk = devm_clk_get_enabled(dev, "bus"); 299 if (IS_ERR(bus_clk)) 300 return dev_err_probe(dev, PTR_ERR(bus_clk), "failed to get bus clock\n"); 301 302 for (i = 0; i < SPACEMIT_NR_BANKS; i++) { 303 ret = spacemit_gpio_add_bank(sg, regs, i, irq); 304 if (ret) 305 return ret; 306 } 307 308 return 0; 309 } 310 311 static const unsigned int spacemit_gpio_k1_offsets[] = { 312 [SPACEMIT_GPLR] = 0x00, 313 [SPACEMIT_GPDR] = 0x0c, 314 [SPACEMIT_GPSR] = 0x18, 315 [SPACEMIT_GPCR] = 0x24, 316 [SPACEMIT_GRER] = 0x30, 317 [SPACEMIT_GFER] = 0x3c, 318 [SPACEMIT_GEDR] = 0x48, 319 [SPACEMIT_GSDR] = 0x54, 320 [SPACEMIT_GCDR] = 0x60, 321 [SPACEMIT_GSRER] = 0x6c, 322 [SPACEMIT_GCRER] = 0x78, 323 [SPACEMIT_GSFER] = 0x84, 324 [SPACEMIT_GCFER] = 0x90, 325 [SPACEMIT_GAPMASK] = 0x9c, 326 [SPACEMIT_GCPMASK] = 0xA8, 327 }; 328 329 static const unsigned int spacemit_gpio_k3_offsets[] = { 330 [SPACEMIT_GPLR] = 0x0, 331 [SPACEMIT_GPDR] = 0x4, 332 [SPACEMIT_GPSR] = 0x8, 333 [SPACEMIT_GPCR] = 0xc, 334 [SPACEMIT_GRER] = 0x10, 335 [SPACEMIT_GFER] = 0x14, 336 [SPACEMIT_GEDR] = 0x18, 337 [SPACEMIT_GSDR] = 0x1c, 338 [SPACEMIT_GCDR] = 0x20, 339 [SPACEMIT_GSRER] = 0x24, 340 [SPACEMIT_GCRER] = 0x28, 341 [SPACEMIT_GSFER] = 0x2c, 342 [SPACEMIT_GCFER] = 0x30, 343 [SPACEMIT_GAPMASK] = 0x34, 344 [SPACEMIT_GCPMASK] = 0x38, 345 }; 346 347 static const struct spacemit_gpio_data k1_gpio_data = { 348 .offsets = spacemit_gpio_k1_offsets, 349 .bank_offsets = { 0x0, 0x4, 0x8, 0x100 }, 350 }; 351 352 static const struct spacemit_gpio_data k3_gpio_data = { 353 .offsets = spacemit_gpio_k3_offsets, 354 .bank_offsets = { 0x0, 0x40, 0x80, 0x100 }, 355 }; 356 357 static const struct of_device_id spacemit_gpio_dt_ids[] = { 358 { .compatible = "spacemit,k1-gpio", .data = &k1_gpio_data }, 359 { .compatible = "spacemit,k3-gpio", .data = &k3_gpio_data }, 360 { /* sentinel */ } 361 }; 362 MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids); 363 364 static struct platform_driver spacemit_gpio_driver = { 365 .probe = spacemit_gpio_probe, 366 .driver = { 367 .name = "spacemit-gpio", 368 .of_match_table = spacemit_gpio_dt_ids, 369 }, 370 }; 371 module_platform_driver(spacemit_gpio_driver); 372 373 MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>"); 374 MODULE_DESCRIPTION("GPIO driver for SpacemiT K1/K3 SoC"); 375 MODULE_LICENSE("GPL"); 376