| // SPDX-License-Identifier: GPL-2.0 |
| #include <linux/types.h> |
| #include <linux/init.h> |
| #include <linux/delay.h> |
| #include <linux/kernel.h> |
| #include <linux/interrupt.h> |
| #include <linux/spinlock.h> |
| #include <linux/of_irq.h> |
| |
| #include <asm/pmac_feature.h> |
| #include <asm/pmac_pfunc.h> |
| |
| #undef DEBUG |
| #ifdef DEBUG |
| #define DBG(fmt...) printk(fmt) |
| #else |
| #define DBG(fmt...) |
| #endif |
| |
| static irqreturn_t macio_gpio_irq(int irq, void *data) |
| { |
| pmf_do_irq(data); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int macio_do_gpio_irq_enable(struct pmf_function *func) |
| { |
| unsigned int irq = irq_of_parse_and_map(func->node, 0); |
| if (!irq) |
| return -EINVAL; |
| return request_irq(irq, macio_gpio_irq, 0, func->node->name, func); |
| } |
| |
| static int macio_do_gpio_irq_disable(struct pmf_function *func) |
| { |
| unsigned int irq = irq_of_parse_and_map(func->node, 0); |
| if (!irq) |
| return -EINVAL; |
| free_irq(irq, func); |
| return 0; |
| } |
| |
| static int macio_do_gpio_write(PMF_STD_ARGS, u8 value, u8 mask) |
| { |
| u8 __iomem *addr = (u8 __iomem *)func->driver_data; |
| unsigned long flags; |
| u8 tmp; |
| |
| /* Check polarity */ |
| if (args && args->count && !args->u[0].v) |
| value = ~value; |
| |
| /* Toggle the GPIO */ |
| raw_spin_lock_irqsave(&feature_lock, flags); |
| tmp = readb(addr); |
| tmp = (tmp & ~mask) | (value & mask); |
| DBG("Do write 0x%02x to GPIO %pOF (%p)\n", |
| tmp, func->node, addr); |
| writeb(tmp, addr); |
| raw_spin_unlock_irqrestore(&feature_lock, flags); |
| |
| return 0; |
| } |
| |
| static int macio_do_gpio_read(PMF_STD_ARGS, u8 mask, int rshift, u8 xor) |
| { |
| u8 __iomem *addr = (u8 __iomem *)func->driver_data; |
| u32 value; |
| |
| /* Check if we have room for reply */ |
| if (args == NULL || args->count == 0 || args->u[0].p == NULL) |
| return -EINVAL; |
| |
| value = readb(addr); |
| *args->u[0].p = ((value & mask) >> rshift) ^ xor; |
| |
| return 0; |
| } |
| |
| static int macio_do_delay(PMF_STD_ARGS, u32 duration) |
| { |
| /* assume we can sleep ! */ |
| msleep((duration + 999) / 1000); |
| return 0; |
| } |
| |
| static struct pmf_handlers macio_gpio_handlers = { |
| .irq_enable = macio_do_gpio_irq_enable, |
| .irq_disable = macio_do_gpio_irq_disable, |
| .write_gpio = macio_do_gpio_write, |
| .read_gpio = macio_do_gpio_read, |
| .delay = macio_do_delay, |
| }; |
| |
| static void __init macio_gpio_init_one(struct macio_chip *macio) |
| { |
| struct device_node *gparent, *gp; |
| |
| /* |
| * Find the "gpio" parent node |
| */ |
| |
| for_each_child_of_node(macio->of_node, gparent) |
| if (of_node_name_eq(gparent, "gpio")) |
| break; |
| if (gparent == NULL) |
| return; |
| |
| DBG("Installing GPIO functions for macio %pOF\n", |
| macio->of_node); |
| |
| /* |
| * Ok, got one, we dont need anything special to track them down, so |
| * we just create them all |
| */ |
| for_each_child_of_node(gparent, gp) { |
| const u32 *reg = of_get_property(gp, "reg", NULL); |
| unsigned long offset; |
| if (reg == NULL) |
| continue; |
| offset = *reg; |
| /* Deal with old style device-tree. We can safely hard code the |
| * offset for now too even if it's a bit gross ... |
| */ |
| if (offset < 0x50) |
| offset += 0x50; |
| offset += (unsigned long)macio->base; |
| pmf_register_driver(gp, &macio_gpio_handlers, (void *)offset); |
| } |
| |
| DBG("Calling initial GPIO functions for macio %pOF\n", |
| macio->of_node); |
| |
| /* And now we run all the init ones */ |
| for_each_child_of_node(gparent, gp) |
| pmf_do_functions(gp, NULL, 0, PMF_FLAGS_ON_INIT, NULL); |
| |
| /* Note: We do not at this point implement the "at sleep" or "at wake" |
| * functions. I yet to find any for GPIOs anyway |
| */ |
| } |
| |
| static int macio_do_write_reg32(PMF_STD_ARGS, u32 offset, u32 value, u32 mask) |
| { |
| struct macio_chip *macio = func->driver_data; |
| unsigned long flags; |
| |
| raw_spin_lock_irqsave(&feature_lock, flags); |
| MACIO_OUT32(offset, (MACIO_IN32(offset) & ~mask) | (value & mask)); |
| raw_spin_unlock_irqrestore(&feature_lock, flags); |
| return 0; |
| } |
| |
| static int macio_do_read_reg32(PMF_STD_ARGS, u32 offset) |
| { |
| struct macio_chip *macio = func->driver_data; |
| |
| /* Check if we have room for reply */ |
| if (args == NULL || args->count == 0 || args->u[0].p == NULL) |
| return -EINVAL; |
| |
| *args->u[0].p = MACIO_IN32(offset); |
| return 0; |
| } |
| |
| static int macio_do_write_reg8(PMF_STD_ARGS, u32 offset, u8 value, u8 mask) |
| { |
| struct macio_chip *macio = func->driver_data; |
| unsigned long flags; |
| |
| raw_spin_lock_irqsave(&feature_lock, flags); |
| MACIO_OUT8(offset, (MACIO_IN8(offset) & ~mask) | (value & mask)); |
| raw_spin_unlock_irqrestore(&feature_lock, flags); |
| return 0; |
| } |
| |
| static int macio_do_read_reg8(PMF_STD_ARGS, u32 offset) |
| { |
| struct macio_chip *macio = func->driver_data; |
| |
| /* Check if we have room for reply */ |
| if (args == NULL || args->count == 0 || args->u[0].p == NULL) |
| return -EINVAL; |
| |
| *((u8 *)(args->u[0].p)) = MACIO_IN8(offset); |
| return 0; |
| } |
| |
| static int macio_do_read_reg32_msrx(PMF_STD_ARGS, u32 offset, u32 mask, |
| u32 shift, u32 xor) |
| { |
| struct macio_chip *macio = func->driver_data; |
| |
| /* Check if we have room for reply */ |
| if (args == NULL || args->count == 0 || args->u[0].p == NULL) |
| return -EINVAL; |
| |
| *args->u[0].p = ((MACIO_IN32(offset) & mask) >> shift) ^ xor; |
| return 0; |
| } |
| |
| static int macio_do_read_reg8_msrx(PMF_STD_ARGS, u32 offset, u32 mask, |
| u32 shift, u32 xor) |
| { |
| struct macio_chip *macio = func->driver_data; |
| |
| /* Check if we have room for reply */ |
| if (args == NULL || args->count == 0 || args->u[0].p == NULL) |
| return -EINVAL; |
| |
| *((u8 *)(args->u[0].p)) = ((MACIO_IN8(offset) & mask) >> shift) ^ xor; |
| return 0; |
| } |
| |
| static int macio_do_write_reg32_slm(PMF_STD_ARGS, u32 offset, u32 shift, |
| u32 mask) |
| { |
| struct macio_chip *macio = func->driver_data; |
| unsigned long flags; |
| u32 tmp, val; |
| |
| /* Check args */ |
| if (args == NULL || args->count == 0) |
| return -EINVAL; |
| |
| raw_spin_lock_irqsave(&feature_lock, flags); |
| tmp = MACIO_IN32(offset); |
| val = args->u[0].v << shift; |
| tmp = (tmp & ~mask) | (val & mask); |
| MACIO_OUT32(offset, tmp); |
| raw_spin_unlock_irqrestore(&feature_lock, flags); |
| return 0; |
| } |
| |
| static int macio_do_write_reg8_slm(PMF_STD_ARGS, u32 offset, u32 shift, |
| u32 mask) |
| { |
| struct macio_chip *macio = func->driver_data; |
| unsigned long flags; |
| u32 tmp, val; |
| |
| /* Check args */ |
| if (args == NULL || args->count == 0) |
| return -EINVAL; |
| |
| raw_spin_lock_irqsave(&feature_lock, flags); |
| tmp = MACIO_IN8(offset); |
| val = args->u[0].v << shift; |
| tmp = (tmp & ~mask) | (val & mask); |
| MACIO_OUT8(offset, tmp); |
| raw_spin_unlock_irqrestore(&feature_lock, flags); |
| return 0; |
| } |
| |
| static struct pmf_handlers macio_mmio_handlers = { |
| .write_reg32 = macio_do_write_reg32, |
| .read_reg32 = macio_do_read_reg32, |
| .write_reg8 = macio_do_write_reg8, |
| .read_reg8 = macio_do_read_reg8, |
| .read_reg32_msrx = macio_do_read_reg32_msrx, |
| .read_reg8_msrx = macio_do_read_reg8_msrx, |
| .write_reg32_slm = macio_do_write_reg32_slm, |
| .write_reg8_slm = macio_do_write_reg8_slm, |
| .delay = macio_do_delay, |
| }; |
| |
| static void __init macio_mmio_init_one(struct macio_chip *macio) |
| { |
| DBG("Installing MMIO functions for macio %pOF\n", |
| macio->of_node); |
| |
| pmf_register_driver(macio->of_node, &macio_mmio_handlers, macio); |
| } |
| |
| static struct device_node *unin_hwclock; |
| |
| static int unin_do_write_reg32(PMF_STD_ARGS, u32 offset, u32 value, u32 mask) |
| { |
| unsigned long flags; |
| |
| raw_spin_lock_irqsave(&feature_lock, flags); |
| /* This is fairly bogus in darwin, but it should work for our needs |
| * implemeted that way: |
| */ |
| UN_OUT(offset, (UN_IN(offset) & ~mask) | (value & mask)); |
| raw_spin_unlock_irqrestore(&feature_lock, flags); |
| return 0; |
| } |
| |
| |
| static struct pmf_handlers unin_mmio_handlers = { |
| .write_reg32 = unin_do_write_reg32, |
| .delay = macio_do_delay, |
| }; |
| |
| static void __init uninorth_install_pfunc(void) |
| { |
| struct device_node *np; |
| |
| DBG("Installing functions for UniN %pOF\n", |
| uninorth_node); |
| |
| /* |
| * Install handlers for the bridge itself |
| */ |
| pmf_register_driver(uninorth_node, &unin_mmio_handlers, NULL); |
| pmf_do_functions(uninorth_node, NULL, 0, PMF_FLAGS_ON_INIT, NULL); |
| |
| |
| /* |
| * Install handlers for the hwclock child if any |
| */ |
| for (np = NULL; (np = of_get_next_child(uninorth_node, np)) != NULL;) |
| if (of_node_name_eq(np, "hw-clock")) { |
| unin_hwclock = np; |
| break; |
| } |
| if (unin_hwclock) { |
| DBG("Installing functions for UniN clock %pOF\n", |
| unin_hwclock); |
| pmf_register_driver(unin_hwclock, &unin_mmio_handlers, NULL); |
| pmf_do_functions(unin_hwclock, NULL, 0, PMF_FLAGS_ON_INIT, |
| NULL); |
| } |
| } |
| |
| /* We export this as the SMP code might init us early */ |
| int __init pmac_pfunc_base_install(void) |
| { |
| static int pfbase_inited; |
| int i; |
| |
| if (pfbase_inited) |
| return 0; |
| pfbase_inited = 1; |
| |
| if (!machine_is(powermac)) |
| return 0; |
| |
| DBG("Installing base platform functions...\n"); |
| |
| /* |
| * Locate mac-io chips and install handlers |
| */ |
| for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { |
| if (macio_chips[i].of_node) { |
| macio_mmio_init_one(&macio_chips[i]); |
| macio_gpio_init_one(&macio_chips[i]); |
| } |
| } |
| |
| /* |
| * Install handlers for northbridge and direct mapped hwclock |
| * if any. We do not implement the config space access callback |
| * which is only ever used for functions that we do not call in |
| * the current driver (enabling/disabling cells in U2, mostly used |
| * to restore the PCI settings, we do that differently) |
| */ |
| if (uninorth_node && uninorth_base) |
| uninorth_install_pfunc(); |
| |
| DBG("All base functions installed\n"); |
| |
| return 0; |
| } |
| machine_arch_initcall(powermac, pmac_pfunc_base_install); |
| |
| #ifdef CONFIG_PM |
| |
| /* Those can be called by pmac_feature. Ultimately, I should use a sysdev |
| * or a device, but for now, that's good enough until I sort out some |
| * ordering issues. Also, we do not bother with GPIOs, as so far I yet have |
| * to see a case where a GPIO function has the on-suspend or on-resume bit |
| */ |
| void pmac_pfunc_base_suspend(void) |
| { |
| int i; |
| |
| for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { |
| if (macio_chips[i].of_node) |
| pmf_do_functions(macio_chips[i].of_node, NULL, 0, |
| PMF_FLAGS_ON_SLEEP, NULL); |
| } |
| if (uninorth_node) |
| pmf_do_functions(uninorth_node, NULL, 0, |
| PMF_FLAGS_ON_SLEEP, NULL); |
| if (unin_hwclock) |
| pmf_do_functions(unin_hwclock, NULL, 0, |
| PMF_FLAGS_ON_SLEEP, NULL); |
| } |
| |
| void pmac_pfunc_base_resume(void) |
| { |
| int i; |
| |
| if (unin_hwclock) |
| pmf_do_functions(unin_hwclock, NULL, 0, |
| PMF_FLAGS_ON_WAKE, NULL); |
| if (uninorth_node) |
| pmf_do_functions(uninorth_node, NULL, 0, |
| PMF_FLAGS_ON_WAKE, NULL); |
| for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { |
| if (macio_chips[i].of_node) |
| pmf_do_functions(macio_chips[i].of_node, NULL, 0, |
| PMF_FLAGS_ON_WAKE, NULL); |
| } |
| } |
| |
| #endif /* CONFIG_PM */ |