forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
410 lines
10 KiB
410 lines
10 KiB
// 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 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 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 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 */
|
|
|