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 39 struct spacemit_gpio; 40 41 struct 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 50 struct spacemit_gpio { 51 struct device *dev; 52 struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS]; 53 }; 54 55 static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb) 56 { 57 return (u32)(gb - gb->sg->sgb); 58 } 59 60 static 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 81 static 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 88 static 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 103 static 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 119 static 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 143 static 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 150 static 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 161 static 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 172 static 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 251 static 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 290 static const struct of_device_id spacemit_gpio_dt_ids[] = { 291 { .compatible = "spacemit,k1-gpio" }, 292 { /* sentinel */ } 293 }; 294 MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids); 295 296 static 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 }; 303 module_platform_driver(spacemit_gpio_driver); 304 305 MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>"); 306 MODULE_DESCRIPTION("GPIO driver for SpacemiT K1 SoC"); 307 MODULE_LICENSE("GPL"); 308