| // 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); | |
| of_node_put(gparent); | |
| /* 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_each_child_of_node(uninorth_node, np) | |
| 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 */ |