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