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->set_config = gpiochip_generic_config; 232 gc->ngpio = SPACEMIT_NR_GPIOS_PER_BANK; 233 gc->base = -1; 234 gc->of_gpio_n_cells = 3; 235 gc->of_node_instance_match = spacemit_of_node_instance_match; 236 237 girq = &gc->irq; 238 girq->threaded = true; 239 girq->handler = handle_simple_irq; 240 241 gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip); 242 243 /* Disable Interrupt */ 244 spacemit_gpio_write(gb, SPACEMIT_GAPMASK, 0); 245 /* Disable Edge Detection Settings */ 246 spacemit_gpio_write(gb, SPACEMIT_GRER, 0x0); 247 spacemit_gpio_write(gb, SPACEMIT_GFER, 0x0); 248 /* Clear Interrupt */ 249 spacemit_gpio_write(gb, SPACEMIT_GCRER, 0xffffffff); 250 spacemit_gpio_write(gb, SPACEMIT_GCFER, 0xffffffff); 251 252 ret = devm_request_threaded_irq(dev, irq, NULL, 253 spacemit_gpio_irq_handler, 254 IRQF_ONESHOT | IRQF_SHARED, 255 gb->chip.gc.label, gb); 256 if (ret < 0) 257 return dev_err_probe(dev, ret, "failed to register IRQ\n"); 258 259 ret = devm_gpiochip_add_data(dev, gc, gb); 260 if (ret) 261 return ret; 262 263 /* Distuingish IRQ domain, for selecting threecells mode */ 264 irq_domain_update_bus_token(girq->domain, DOMAIN_BUS_WIRED); 265 266 return 0; 267 } 268 269 static int spacemit_gpio_probe(struct platform_device *pdev) 270 { 271 struct device *dev = &pdev->dev; 272 struct spacemit_gpio *sg; 273 struct clk *core_clk, *bus_clk; 274 void __iomem *regs; 275 int i, irq, ret; 276 277 sg = devm_kzalloc(dev, sizeof(*sg), GFP_KERNEL); 278 if (!sg) 279 return -ENOMEM; 280 281 sg->data = of_device_get_match_data(dev); 282 if (!sg->data) 283 return dev_err_probe(dev, -EINVAL, "No available compatible data."); 284 285 regs = devm_platform_ioremap_resource(pdev, 0); 286 if (IS_ERR(regs)) 287 return PTR_ERR(regs); 288 289 irq = platform_get_irq(pdev, 0); 290 if (irq < 0) 291 return irq; 292 293 sg->dev = dev; 294 295 core_clk = devm_clk_get_enabled(dev, "core"); 296 if (IS_ERR(core_clk)) 297 return dev_err_probe(dev, PTR_ERR(core_clk), "failed to get clock\n"); 298 299 bus_clk = devm_clk_get_enabled(dev, "bus"); 300 if (IS_ERR(bus_clk)) 301 return dev_err_probe(dev, PTR_ERR(bus_clk), "failed to get bus clock\n"); 302 303 for (i = 0; i < SPACEMIT_NR_BANKS; i++) { 304 ret = spacemit_gpio_add_bank(sg, regs, i, irq); 305 if (ret) 306 return ret; 307 } 308 309 return 0; 310 } 311 312 static const unsigned int spacemit_gpio_k1_offsets[] = { 313 [SPACEMIT_GPLR] = 0x00, 314 [SPACEMIT_GPDR] = 0x0c, 315 [SPACEMIT_GPSR] = 0x18, 316 [SPACEMIT_GPCR] = 0x24, 317 [SPACEMIT_GRER] = 0x30, 318 [SPACEMIT_GFER] = 0x3c, 319 [SPACEMIT_GEDR] = 0x48, 320 [SPACEMIT_GSDR] = 0x54, 321 [SPACEMIT_GCDR] = 0x60, 322 [SPACEMIT_GSRER] = 0x6c, 323 [SPACEMIT_GCRER] = 0x78, 324 [SPACEMIT_GSFER] = 0x84, 325 [SPACEMIT_GCFER] = 0x90, 326 [SPACEMIT_GAPMASK] = 0x9c, 327 [SPACEMIT_GCPMASK] = 0xA8, 328 }; 329 330 static const unsigned int spacemit_gpio_k3_offsets[] = { 331 [SPACEMIT_GPLR] = 0x0, 332 [SPACEMIT_GPDR] = 0x4, 333 [SPACEMIT_GPSR] = 0x8, 334 [SPACEMIT_GPCR] = 0xc, 335 [SPACEMIT_GRER] = 0x10, 336 [SPACEMIT_GFER] = 0x14, 337 [SPACEMIT_GEDR] = 0x18, 338 [SPACEMIT_GSDR] = 0x1c, 339 [SPACEMIT_GCDR] = 0x20, 340 [SPACEMIT_GSRER] = 0x24, 341 [SPACEMIT_GCRER] = 0x28, 342 [SPACEMIT_GSFER] = 0x2c, 343 [SPACEMIT_GCFER] = 0x30, 344 [SPACEMIT_GAPMASK] = 0x34, 345 [SPACEMIT_GCPMASK] = 0x38, 346 }; 347 348 static const struct spacemit_gpio_data k1_gpio_data = { 349 .offsets = spacemit_gpio_k1_offsets, 350 .bank_offsets = { 0x0, 0x4, 0x8, 0x100 }, 351 }; 352 353 static const struct spacemit_gpio_data k3_gpio_data = { 354 .offsets = spacemit_gpio_k3_offsets, 355 .bank_offsets = { 0x0, 0x40, 0x80, 0x100 }, 356 }; 357 358 static const struct of_device_id spacemit_gpio_dt_ids[] = { 359 { .compatible = "spacemit,k1-gpio", .data = &k1_gpio_data }, 360 { .compatible = "spacemit,k3-gpio", .data = &k3_gpio_data }, 361 { /* sentinel */ } 362 }; 363 MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids); 364 365 static struct platform_driver spacemit_gpio_driver = { 366 .probe = spacemit_gpio_probe, 367 .driver = { 368 .name = "spacemit-gpio", 369 .of_match_table = spacemit_gpio_dt_ids, 370 }, 371 }; 372 module_platform_driver(spacemit_gpio_driver); 373 374 MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>"); 375 MODULE_DESCRIPTION("GPIO driver for SpacemiT K1/K3 SoC"); 376 MODULE_LICENSE("GPL"); 377