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

Configure Feed

Select the types of activity you want to include in your feed.

gpio: loongson-64bit: Add support for Loongson-2K0300 SoC

This controller's input and output logic is similar to previous
generations of SoCs. Additionally, it's capable of interrupt masking,
and could be configured to detect levels and edges, and is supplied with
a distinct reset signal.

The interrupt functionality is implemented through an irqchip, whose
operations are written with previous generation SoCs in mind and could
be reused. Since all Loongson SoCs with similar interrupt capability
(LS2K1500, LS2K2000) support byte-control mode, these operations are for
byte-control mode only for simplicity.

Signed-off-by: Yao Zi <ziyao@disroot.org>
Reviewed-by: Huacai Chen <chenhuacai@loongson.cn>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20250904013438.2405-3-ziyao@disroot.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

authored by

Yao Zi and committed by
Bartosz Golaszewski
03c146cb 084d01a1

+183 -7
+1
drivers/gpio/Kconfig
··· 436 436 depends on LOONGARCH || COMPILE_TEST 437 437 depends on OF_GPIO 438 438 select GPIO_GENERIC 439 + select GPIOLIB_IRQCHIP 439 440 help 440 441 Say yes here to support the GPIO functionality of a number of 441 442 Loongson series of chips. The Loongson GPIO controller supports
+182 -7
drivers/gpio/gpio-loongson-64bit.c
··· 7 7 8 8 #include <linux/kernel.h> 9 9 #include <linux/init.h> 10 + #include <linux/irq.h> 11 + #include <linux/irqdesc.h> 10 12 #include <linux/module.h> 11 13 #include <linux/spinlock.h> 12 14 #include <linux/err.h> ··· 16 14 #include <linux/gpio/generic.h> 17 15 #include <linux/platform_device.h> 18 16 #include <linux/bitops.h> 17 + #include <linux/reset.h> 19 18 #include <asm/types.h> 20 19 21 20 enum loongson_gpio_mode { ··· 31 28 unsigned int out_offset; 32 29 unsigned int in_offset; 33 30 unsigned int inten_offset; 31 + unsigned int intpol_offset; 32 + unsigned int intedge_offset; 33 + unsigned int intclr_offset; 34 + unsigned int intsts_offset; 35 + unsigned int intdual_offset; 36 + unsigned int intr_num; 37 + irq_flow_handler_t irq_handler; 38 + const struct irq_chip *girqchip; 34 39 }; 35 40 36 41 struct loongson_gpio_chip { ··· 148 137 return platform_get_irq(pdev, offset); 149 138 } 150 139 151 - static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgpio, 140 + static void loongson_gpio_irq_ack(struct irq_data *data) 141 + { 142 + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 143 + struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip); 144 + irq_hw_number_t hwirq = irqd_to_hwirq(data); 145 + 146 + writeb(0x1, lgpio->reg_base + lgpio->chip_data->intclr_offset + hwirq); 147 + } 148 + 149 + static void loongson_gpio_irq_mask(struct irq_data *data) 150 + { 151 + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 152 + struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip); 153 + irq_hw_number_t hwirq = irqd_to_hwirq(data); 154 + 155 + writeb(0x0, lgpio->reg_base + lgpio->chip_data->inten_offset + hwirq); 156 + } 157 + 158 + static void loongson_gpio_irq_unmask(struct irq_data *data) 159 + { 160 + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 161 + struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip); 162 + irq_hw_number_t hwirq = irqd_to_hwirq(data); 163 + 164 + writeb(0x1, lgpio->reg_base + lgpio->chip_data->inten_offset + hwirq); 165 + } 166 + 167 + static int loongson_gpio_irq_set_type(struct irq_data *data, unsigned int type) 168 + { 169 + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 170 + struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip); 171 + irq_hw_number_t hwirq = irqd_to_hwirq(data); 172 + u8 pol = 0, edge = 0, dual = 0; 173 + 174 + if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { 175 + edge = 1; 176 + dual = 1; 177 + irq_set_handler_locked(data, handle_edge_irq); 178 + } else { 179 + switch (type) { 180 + case IRQ_TYPE_LEVEL_HIGH: 181 + pol = 1; 182 + fallthrough; 183 + case IRQ_TYPE_LEVEL_LOW: 184 + irq_set_handler_locked(data, handle_level_irq); 185 + break; 186 + 187 + case IRQ_TYPE_EDGE_RISING: 188 + pol = 1; 189 + fallthrough; 190 + case IRQ_TYPE_EDGE_FALLING: 191 + edge = 1; 192 + irq_set_handler_locked(data, handle_edge_irq); 193 + break; 194 + 195 + default: 196 + return -EINVAL; 197 + }; 198 + } 199 + 200 + writeb(pol, lgpio->reg_base + lgpio->chip_data->intpol_offset + hwirq); 201 + writeb(edge, lgpio->reg_base + lgpio->chip_data->intedge_offset + hwirq); 202 + writeb(dual, lgpio->reg_base + lgpio->chip_data->intdual_offset + hwirq); 203 + 204 + return 0; 205 + } 206 + 207 + static void loongson_gpio_ls2k0300_irq_handler(struct irq_desc *desc) 208 + { 209 + struct loongson_gpio_chip *lgpio = irq_desc_get_handler_data(desc); 210 + struct irq_chip *girqchip = irq_desc_get_chip(desc); 211 + int i; 212 + 213 + chained_irq_enter(girqchip, desc); 214 + 215 + for (i = 0; i < lgpio->chip.gc.ngpio; i++) { 216 + /* 217 + * For the GPIO controller of LS2K0300, interrupts status bits 218 + * may be wrongly set even if the corresponding interrupt is 219 + * disabled. Thus interrupt enable bits are checked along with 220 + * status bits to detect interrupts reliably. 221 + */ 222 + if (readb(lgpio->reg_base + lgpio->chip_data->intsts_offset + i) && 223 + readb(lgpio->reg_base + lgpio->chip_data->inten_offset + i)) 224 + generic_handle_domain_irq(lgpio->chip.gc.irq.domain, i); 225 + } 226 + 227 + chained_irq_exit(girqchip, desc); 228 + } 229 + 230 + static const struct irq_chip loongson_gpio_ls2k0300_irqchip = { 231 + .irq_ack = loongson_gpio_irq_ack, 232 + .irq_mask = loongson_gpio_irq_mask, 233 + .irq_unmask = loongson_gpio_irq_unmask, 234 + .irq_set_type = loongson_gpio_irq_set_type, 235 + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE, 236 + GPIOCHIP_IRQ_RESOURCE_HELPERS, 237 + }; 238 + 239 + static int loongson_gpio_init_irqchip(struct platform_device *pdev, 240 + struct loongson_gpio_chip *lgpio) 241 + { 242 + const struct loongson_gpio_chip_data *data = lgpio->chip_data; 243 + struct gpio_chip *chip = &lgpio->chip.gc; 244 + int i; 245 + 246 + chip->irq.default_type = IRQ_TYPE_NONE; 247 + chip->irq.handler = handle_bad_irq; 248 + chip->irq.parent_handler = data->irq_handler; 249 + chip->irq.parent_handler_data = lgpio; 250 + gpio_irq_chip_set_chip(&chip->irq, data->girqchip); 251 + 252 + chip->irq.num_parents = data->intr_num; 253 + chip->irq.parents = devm_kcalloc(&pdev->dev, data->intr_num, 254 + sizeof(*chip->irq.parents), GFP_KERNEL); 255 + if (!chip->parent) 256 + return -ENOMEM; 257 + 258 + for (i = 0; i < data->intr_num; i++) { 259 + chip->irq.parents[i] = platform_get_irq(pdev, i); 260 + if (chip->irq.parents[i] < 0) 261 + return dev_err_probe(&pdev->dev, chip->irq.parents[i], 262 + "failed to get IRQ %d\n", i); 263 + } 264 + 265 + for (i = 0; i < data->intr_num; i++) { 266 + writeb(0x0, lgpio->reg_base + data->inten_offset + i); 267 + writeb(0x1, lgpio->reg_base + data->intclr_offset + i); 268 + } 269 + 270 + return 0; 271 + } 272 + 273 + static int loongson_gpio_init(struct platform_device *pdev, struct loongson_gpio_chip *lgpio, 152 274 void __iomem *reg_base) 153 275 { 154 276 struct gpio_generic_chip_config config; ··· 290 146 lgpio->reg_base = reg_base; 291 147 if (lgpio->chip_data->mode == BIT_CTRL_MODE) { 292 148 config = (typeof(config)){ 293 - .dev = dev, 149 + .dev = &pdev->dev, 294 150 .sz = 8, 295 151 .dat = lgpio->reg_base + lgpio->chip_data->in_offset, 296 152 .set = lgpio->reg_base + lgpio->chip_data->out_offset, ··· 299 155 300 156 ret = gpio_generic_chip_init(&lgpio->chip, &config); 301 157 if (ret) { 302 - dev_err(dev, "unable to init generic GPIO\n"); 158 + dev_err(&pdev->dev, "unable to init generic GPIO\n"); 303 159 return ret; 304 160 } 305 161 } else { ··· 308 164 lgpio->chip.gc.get_direction = loongson_gpio_get_direction; 309 165 lgpio->chip.gc.direction_output = loongson_gpio_direction_output; 310 166 lgpio->chip.gc.set = loongson_gpio_set; 311 - lgpio->chip.gc.parent = dev; 167 + lgpio->chip.gc.parent = &pdev->dev; 312 168 spin_lock_init(&lgpio->lock); 313 169 } 314 170 315 171 lgpio->chip.gc.label = lgpio->chip_data->label; 316 172 lgpio->chip.gc.can_sleep = false; 317 - if (lgpio->chip_data->inten_offset) 173 + if (lgpio->chip_data->girqchip) { 174 + ret = loongson_gpio_init_irqchip(pdev, lgpio); 175 + if (ret) 176 + return dev_err_probe(&pdev->dev, ret, "failed to initialize irqchip\n"); 177 + } else if (lgpio->chip_data->inten_offset) { 318 178 lgpio->chip.gc.to_irq = loongson_gpio_to_irq; 179 + } 319 180 320 - return devm_gpiochip_add_data(dev, &lgpio->chip.gc, lgpio); 181 + return devm_gpiochip_add_data(&pdev->dev, &lgpio->chip.gc, lgpio); 321 182 } 322 183 323 184 static int loongson_gpio_probe(struct platform_device *pdev) ··· 330 181 void __iomem *reg_base; 331 182 struct loongson_gpio_chip *lgpio; 332 183 struct device *dev = &pdev->dev; 184 + struct reset_control *rst; 333 185 334 186 lgpio = devm_kzalloc(dev, sizeof(*lgpio), GFP_KERNEL); 335 187 if (!lgpio) ··· 342 192 if (IS_ERR(reg_base)) 343 193 return PTR_ERR(reg_base); 344 194 345 - return loongson_gpio_init(dev, lgpio, reg_base); 195 + rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL); 196 + if (IS_ERR(rst)) 197 + return dev_err_probe(&pdev->dev, PTR_ERR(rst), "failed to get reset control\n"); 198 + 199 + return loongson_gpio_init(pdev, lgpio, reg_base); 346 200 } 347 201 348 202 static const struct loongson_gpio_chip_data loongson_gpio_ls2k_data = { ··· 356 202 .in_offset = 0x20, 357 203 .out_offset = 0x10, 358 204 .inten_offset = 0x30, 205 + }; 206 + 207 + static const struct loongson_gpio_chip_data loongson_gpio_ls2k0300_data = { 208 + .label = "ls2k0300_gpio", 209 + .mode = BYTE_CTRL_MODE, 210 + .conf_offset = 0x800, 211 + .in_offset = 0xa00, 212 + .out_offset = 0x900, 213 + .inten_offset = 0xb00, 214 + .intpol_offset = 0xc00, 215 + .intedge_offset = 0xd00, 216 + .intclr_offset = 0xe00, 217 + .intsts_offset = 0xf00, 218 + .intdual_offset = 0xf80, 219 + .intr_num = 7, 220 + .irq_handler = loongson_gpio_ls2k0300_irq_handler, 221 + .girqchip = &loongson_gpio_ls2k0300_irqchip, 359 222 }; 360 223 361 224 static const struct loongson_gpio_chip_data loongson_gpio_ls2k0500_data0 = { ··· 470 299 { 471 300 .compatible = "loongson,ls2k-gpio", 472 301 .data = &loongson_gpio_ls2k_data, 302 + }, 303 + { 304 + .compatible = "loongson,ls2k0300-gpio", 305 + .data = &loongson_gpio_ls2k0300_data, 473 306 }, 474 307 { 475 308 .compatible = "loongson,ls2k0500-gpio0",