Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio: spacemit: Add GPIO support for K3 SoC

SpacemiT K3 SoC has changed gpio register layout while comparing
with previous generation, the register offset and bank offset
need to be adjusted, introduce a compatible data to extend the
driver to support this.

Signed-off-by: Yixun Lan <dlan@gentoo.org>
Link: https://lore.kernel.org/r/20260106-02-k3-gpio-v3-2-4800c214810b@gentoo.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

authored by

Yixun Lan and committed by
Bartosz Golaszewski
da64eb51 48033e4c

+117 -46
+117 -46
drivers/gpio/gpio-spacemit-k1.c
··· 15 15 #include <linux/platform_device.h> 16 16 #include <linux/seq_file.h> 17 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 18 #define SPACEMIT_NR_BANKS 4 35 19 #define SPACEMIT_NR_GPIOS_PER_BANK 32 36 20 37 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 + }; 38 41 39 42 struct spacemit_gpio; 43 + 44 + struct spacemit_gpio_data { 45 + const unsigned int *offsets; 46 + u32 bank_offsets[SPACEMIT_NR_BANKS]; 47 + }; 40 48 41 49 struct spacemit_gpio_bank { 42 50 struct gpio_generic_chip chip; ··· 57 49 58 50 struct spacemit_gpio { 59 51 struct device *dev; 52 + const struct spacemit_gpio_data *data; 60 53 struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS]; 61 54 }; 55 + 56 + static u32 spacemit_gpio_read(struct spacemit_gpio_bank *gb, 57 + enum spacemit_gpio_registers reg) 58 + { 59 + return readl(gb->base + to_spacemit_gpio_regs(gb)[reg]); 60 + } 61 + 62 + static void spacemit_gpio_write(struct spacemit_gpio_bank *gb, 63 + enum spacemit_gpio_registers reg, u32 val) 64 + { 65 + writel(val, gb->base + to_spacemit_gpio_regs(gb)[reg]); 66 + } 62 67 63 68 static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb) 64 69 { ··· 84 63 unsigned long pending; 85 64 u32 n, gedr; 86 65 87 - gedr = readl(gb->base + SPACEMIT_GEDR); 66 + gedr = spacemit_gpio_read(gb, SPACEMIT_GEDR); 88 67 if (!gedr) 89 68 return IRQ_NONE; 90 - writel(gedr, gb->base + SPACEMIT_GEDR); 69 + spacemit_gpio_write(gb, SPACEMIT_GEDR, gedr); 91 70 92 71 pending = gedr & gb->irq_mask; 93 72 if (!pending) ··· 103 82 { 104 83 struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); 105 84 106 - writel(BIT(irqd_to_hwirq(d)), gb->base + SPACEMIT_GEDR); 85 + spacemit_gpio_write(gb, SPACEMIT_GEDR, BIT(irqd_to_hwirq(d))); 107 86 } 108 87 109 88 static void spacemit_gpio_irq_mask(struct irq_data *d) ··· 112 91 u32 bit = BIT(irqd_to_hwirq(d)); 113 92 114 93 gb->irq_mask &= ~bit; 115 - writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); 94 + spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask); 116 95 117 96 if (bit & gb->irq_rising_edge) 118 - writel(bit, gb->base + SPACEMIT_GCRER); 97 + spacemit_gpio_write(gb, SPACEMIT_GCRER, bit); 119 98 120 99 if (bit & gb->irq_falling_edge) 121 - writel(bit, gb->base + SPACEMIT_GCFER); 100 + spacemit_gpio_write(gb, SPACEMIT_GCFER, bit); 122 101 } 123 102 124 103 static void spacemit_gpio_irq_unmask(struct irq_data *d) ··· 129 108 gb->irq_mask |= bit; 130 109 131 110 if (bit & gb->irq_rising_edge) 132 - writel(bit, gb->base + SPACEMIT_GSRER); 111 + spacemit_gpio_write(gb, SPACEMIT_GSRER, bit); 133 112 134 113 if (bit & gb->irq_falling_edge) 135 - writel(bit, gb->base + SPACEMIT_GSFER); 114 + spacemit_gpio_write(gb, SPACEMIT_GSFER, bit); 136 115 137 - writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); 116 + spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask); 138 117 } 139 118 140 119 static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type) ··· 144 123 145 124 if (type & IRQ_TYPE_EDGE_RISING) { 146 125 gb->irq_rising_edge |= bit; 147 - writel(bit, gb->base + SPACEMIT_GSRER); 126 + spacemit_gpio_write(gb, SPACEMIT_GSRER, bit); 148 127 } else { 149 128 gb->irq_rising_edge &= ~bit; 150 - writel(bit, gb->base + SPACEMIT_GCRER); 129 + spacemit_gpio_write(gb, SPACEMIT_GCRER, bit); 151 130 } 152 131 153 132 if (type & IRQ_TYPE_EDGE_FALLING) { 154 133 gb->irq_falling_edge |= bit; 155 - writel(bit, gb->base + SPACEMIT_GSFER); 134 + spacemit_gpio_write(gb, SPACEMIT_GSFER, bit); 156 135 } else { 157 136 gb->irq_falling_edge &= ~bit; 158 - writel(bit, gb->base + SPACEMIT_GCFER); 137 + spacemit_gpio_write(gb, SPACEMIT_GCFER, bit); 159 138 } 160 139 161 140 return 0; ··· 200 179 struct device *dev = sg->dev; 201 180 struct gpio_irq_chip *girq; 202 181 void __iomem *dat, *set, *clr, *dirin, *dirout; 203 - int ret, bank_base[] = { 0x0, 0x4, 0x8, 0x100 }; 182 + int ret; 204 183 205 - gb->base = regs + bank_base[index]; 184 + gb->base = regs + sg->data->bank_offsets[index]; 185 + gb->sg = sg; 206 186 207 - dat = gb->base + SPACEMIT_GPLR; 208 - set = gb->base + SPACEMIT_GPSR; 209 - clr = gb->base + SPACEMIT_GPCR; 210 - dirin = gb->base + SPACEMIT_GCDR; 211 - dirout = gb->base + SPACEMIT_GSDR; 187 + dat = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPLR]; 188 + set = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPSR]; 189 + clr = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPCR]; 190 + dirin = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GCDR]; 191 + dirout = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GSDR]; 212 192 213 193 config = (struct gpio_generic_chip_config) { 214 194 .dev = dev, ··· 228 206 if (ret) 229 207 return dev_err_probe(dev, ret, "failed to init gpio chip\n"); 230 208 231 - gb->sg = sg; 232 - 233 209 gc->label = dev_name(dev); 234 210 gc->request = gpiochip_generic_request; 235 211 gc->free = gpiochip_generic_free; ··· 243 223 gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip); 244 224 245 225 /* Disable Interrupt */ 246 - writel(0, gb->base + SPACEMIT_GAPMASK); 226 + spacemit_gpio_write(gb, SPACEMIT_GAPMASK, 0); 247 227 /* Disable Edge Detection Settings */ 248 - writel(0x0, gb->base + SPACEMIT_GRER); 249 - writel(0x0, gb->base + SPACEMIT_GFER); 228 + spacemit_gpio_write(gb, SPACEMIT_GRER, 0x0); 229 + spacemit_gpio_write(gb, SPACEMIT_GFER, 0x0); 250 230 /* Clear Interrupt */ 251 - writel(0xffffffff, gb->base + SPACEMIT_GCRER); 252 - writel(0xffffffff, gb->base + SPACEMIT_GCFER); 231 + spacemit_gpio_write(gb, SPACEMIT_GCRER, 0xffffffff); 232 + spacemit_gpio_write(gb, SPACEMIT_GCFER, 0xffffffff); 253 233 254 234 ret = devm_request_threaded_irq(dev, irq, NULL, 255 235 spacemit_gpio_irq_handler, ··· 280 260 if (!sg) 281 261 return -ENOMEM; 282 262 263 + sg->data = of_device_get_match_data(dev); 264 + if (!sg->data) 265 + return dev_err_probe(dev, -EINVAL, "No available compatible data."); 266 + 283 267 regs = devm_platform_ioremap_resource(pdev, 0); 284 268 if (IS_ERR(regs)) 285 269 return PTR_ERR(regs); ··· 311 287 return 0; 312 288 } 313 289 290 + static const unsigned int spacemit_gpio_k1_offsets[] = { 291 + [SPACEMIT_GPLR] = 0x00, 292 + [SPACEMIT_GPDR] = 0x0c, 293 + [SPACEMIT_GPSR] = 0x18, 294 + [SPACEMIT_GPCR] = 0x24, 295 + [SPACEMIT_GRER] = 0x30, 296 + [SPACEMIT_GFER] = 0x3c, 297 + [SPACEMIT_GEDR] = 0x48, 298 + [SPACEMIT_GSDR] = 0x54, 299 + [SPACEMIT_GCDR] = 0x60, 300 + [SPACEMIT_GSRER] = 0x6c, 301 + [SPACEMIT_GCRER] = 0x78, 302 + [SPACEMIT_GSFER] = 0x84, 303 + [SPACEMIT_GCFER] = 0x90, 304 + [SPACEMIT_GAPMASK] = 0x9c, 305 + [SPACEMIT_GCPMASK] = 0xA8, 306 + }; 307 + 308 + static const unsigned int spacemit_gpio_k3_offsets[] = { 309 + [SPACEMIT_GPLR] = 0x0, 310 + [SPACEMIT_GPDR] = 0x4, 311 + [SPACEMIT_GPSR] = 0x8, 312 + [SPACEMIT_GPCR] = 0xc, 313 + [SPACEMIT_GRER] = 0x10, 314 + [SPACEMIT_GFER] = 0x14, 315 + [SPACEMIT_GEDR] = 0x18, 316 + [SPACEMIT_GSDR] = 0x1c, 317 + [SPACEMIT_GCDR] = 0x20, 318 + [SPACEMIT_GSRER] = 0x24, 319 + [SPACEMIT_GCRER] = 0x28, 320 + [SPACEMIT_GSFER] = 0x2c, 321 + [SPACEMIT_GCFER] = 0x30, 322 + [SPACEMIT_GAPMASK] = 0x34, 323 + [SPACEMIT_GCPMASK] = 0x38, 324 + }; 325 + 326 + static const struct spacemit_gpio_data k1_gpio_data = { 327 + .offsets = spacemit_gpio_k1_offsets, 328 + .bank_offsets = { 0x0, 0x4, 0x8, 0x100 }, 329 + }; 330 + 331 + static const struct spacemit_gpio_data k3_gpio_data = { 332 + .offsets = spacemit_gpio_k3_offsets, 333 + .bank_offsets = { 0x0, 0x40, 0x80, 0x100 }, 334 + }; 335 + 314 336 static const struct of_device_id spacemit_gpio_dt_ids[] = { 315 - { .compatible = "spacemit,k1-gpio" }, 337 + { .compatible = "spacemit,k1-gpio", .data = &k1_gpio_data }, 338 + { .compatible = "spacemit,k3-gpio", .data = &k3_gpio_data }, 316 339 { /* sentinel */ } 317 340 }; 318 341 MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids); ··· 367 296 static struct platform_driver spacemit_gpio_driver = { 368 297 .probe = spacemit_gpio_probe, 369 298 .driver = { 370 - .name = "k1-gpio", 299 + .name = "spacemit-gpio", 371 300 .of_match_table = spacemit_gpio_dt_ids, 372 301 }, 373 302 }; 374 303 module_platform_driver(spacemit_gpio_driver); 375 304 376 305 MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>"); 377 - MODULE_DESCRIPTION("GPIO driver for SpacemiT K1 SoC"); 306 + MODULE_DESCRIPTION("GPIO driver for SpacemiT K1/K3 SoC"); 378 307 MODULE_LICENSE("GPL");