From 913df4453f85f1fe79b35ecf3c9a0c0b707d22a2 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 13 Oct 2009 12:35:30 +0900 Subject: sh: maple: PHYSADDR() -> virt_to_phys() conversion. Maple's abuse of PHYSADDR() likewise can be converted to virt_to_phys() for its cases, although in practice this really wants explicit remapping. Signed-off-by: Paul Mundt --- drivers/sh/maple/maple.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/sh') diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c index 93c20e135ee1..4e8f57d4131f 100644 --- a/drivers/sh/maple/maple.c +++ b/drivers/sh/maple/maple.c @@ -106,7 +106,7 @@ static void maple_dma_reset(void) * max delay is 11 */ ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(0xFFFF), MAPLE_SPEED); - ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR); + ctrl_outl(virt_to_phys(maple_sendbuf), MAPLE_DMAADDR); ctrl_outl(1, MAPLE_ENABLE); } @@ -258,7 +258,7 @@ static void maple_build_block(struct mapleq *mq) maple_lastptr = maple_sendptr; *maple_sendptr++ = (port << 16) | len | 0x80000000; - *maple_sendptr++ = PHYSADDR(mq->recvbuf->buf); + *maple_sendptr++ = virt_to_phys(mq->recvbuf->buf); *maple_sendptr++ = mq->command | (to << 8) | (from << 16) | (len << 24); while (len-- > 0) -- cgit v1.2.2 From 9b798d50df3a98d22a6cbae565d9f4f630d161a6 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 27 Oct 2009 11:36:43 +0900 Subject: sh: intc: Make ack_regs generally available. Currently this is ifdef'ed under SH-3 and SH-4A, but there are other CPUs that will need this as well. Given the size of the existing data structures, this doesn't cause any additional cacheline utilization for the existing users, so has no direct impact on the data structures. Signed-off-by: Paul Mundt --- drivers/sh/intc.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/sh') diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 559b5fe9dc0f..94e6e46ff82c 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -70,9 +70,7 @@ static LIST_HEAD(intc_list); #endif static unsigned int intc_prio_level[NR_IRQS]; /* for now */ -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) static unsigned long ack_handle[NR_IRQS]; -#endif static inline struct intc_desc_int *get_intc_desc(unsigned int irq) { @@ -250,7 +248,6 @@ static int intc_set_wake(unsigned int irq, unsigned int on) return 0; /* allow wakeup, but setup hardware in intc_suspend() */ } -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) static void intc_mask_ack(unsigned int irq) { struct intc_desc_int *d = get_intc_desc(irq); @@ -282,7 +279,6 @@ static void intc_mask_ack(unsigned int irq) } } } -#endif static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, unsigned int nr_hp, @@ -501,7 +497,6 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc, return 0; } -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) static unsigned int __init intc_ack_data(struct intc_desc *desc, struct intc_desc_int *d, intc_enum enum_id) @@ -533,7 +528,6 @@ static unsigned int __init intc_ack_data(struct intc_desc *desc, return 0; } -#endif static unsigned int __init intc_sense_data(struct intc_desc *desc, struct intc_desc_int *d, @@ -641,10 +635,8 @@ static void __init intc_register_irq(struct intc_desc *desc, /* irq should be disabled by default */ d->chip.mask(irq); -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) if (desc->ack_regs) ack_handle[irq] = intc_ack_data(desc, d, enum_id); -#endif } static unsigned int __init save_reg(struct intc_desc_int *d, @@ -681,10 +673,8 @@ void __init register_intc_controller(struct intc_desc *desc) d->nr_reg = desc->mask_regs ? desc->nr_mask_regs * 2 : 0; d->nr_reg += desc->prio_regs ? desc->nr_prio_regs * 2 : 0; d->nr_reg += desc->sense_regs ? desc->nr_sense_regs : 0; - -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0; -#endif + d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); #ifdef CONFIG_SMP d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); @@ -727,14 +717,12 @@ void __init register_intc_controller(struct intc_desc *desc) d->chip.set_type = intc_set_sense; d->chip.set_wake = intc_set_wake; -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) if (desc->ack_regs) { for (i = 0; i < desc->nr_ack_regs; i++) k += save_reg(d, k, desc->ack_regs[i].set_reg, 0); d->chip.mask_ack = intc_mask_ack; } -#endif BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ -- cgit v1.2.2 From 1ce7b039b5029ab698f9d64c0ad603794bc31ae7 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 2 Nov 2009 10:30:26 +0900 Subject: sh: intc: dynamic IRQ support. This adds support for dynamic IRQ allocation/deallocation for all parts using the SH-style vectored IRQs. While this is not inherently INTC-specific, the INTC code is the main tie-in for vectored IRQ registration, and is the only place that a full view of the utilized vector map is possible. The implementation is fairly straightforward, implementing a flat IRQ map where each registered vector is reserved, allowing us to scan for holes and dynamically wire up IRQs lazily later on in the boot stage. This piggybacks on top of sparseirq in order to make the best use of the available vector space. Dynamic IRQs can be used for any number of things, ranging from MSI in the SH-X3 PCIe case down to demux vectors for board FPGAs and system controllers that presently allocate an arbitrary range. In the latter case, this also allows those platforms to use sparseirq without blowing up, which brings us one step closer to enabling sparseirq as the default for all platform and CPU combinations. Signed-off-by: Paul Mundt --- drivers/sh/intc.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) (limited to 'drivers/sh') diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 94e6e46ff82c..4789df43c0f9 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -2,6 +2,7 @@ * Shared interrupt handling code for IPR and INTC2 types of IRQs. * * Copyright (C) 2007, 2008 Magnus Damm + * Copyright (C) 2009 Paul Mundt * * Based on intc2.c and ipr.c * @@ -24,6 +25,7 @@ #include #include #include +#include #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ @@ -59,6 +61,20 @@ struct intc_desc_int { static LIST_HEAD(intc_list); +/* + * The intc_irq_map provides a global map of bound IRQ vectors for a + * given platform. Allocation of IRQs are either static through the CPU + * vector map, or dynamic in the case of board mux vectors or MSI. + * + * As this is a central point for all IRQ controllers on the system, + * each of the available sources are mapped out here. This combined with + * sparseirq makes it quite trivial to keep the vector map tightly packed + * when dynamically creating IRQs, as well as tying in to otherwise + * unused irq_desc positions in the sparse array. + */ +static DECLARE_BITMAP(intc_irq_map, NR_IRQS); +static DEFINE_SPINLOCK(vector_lock); + #ifdef CONFIG_SMP #define IS_SMP(x) x.smp #define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c)) @@ -566,6 +582,11 @@ static void __init intc_register_irq(struct intc_desc *desc, struct intc_handle_int *hp; unsigned int data[2], primary; + /* + * Register the IRQ position with the global IRQ map + */ + set_bit(irq, intc_irq_map); + /* Prefer single interrupt source bitmap over other combinations: * 1. bitmap, single interrupt source * 2. priority, single interrupt source @@ -844,5 +865,66 @@ static int __init register_intc_sysdevs(void) return error; } - device_initcall(register_intc_sysdevs); + +/* + * Dynamic IRQ allocation and deallocation + */ +static unsigned int create_irq_on_node(unsigned int irq_want, int node) +{ + unsigned int irq = 0, new; + unsigned long flags; + struct irq_desc *desc; + + spin_lock_irqsave(&vector_lock, flags); + + /* + * First try the wanted IRQ, then scan. + */ + if (test_and_set_bit(irq_want, intc_irq_map)) { + new = find_first_zero_bit(intc_irq_map, nr_irqs); + if (unlikely(new == nr_irqs)) + goto out_unlock; + + desc = irq_to_desc_alloc_node(new, node); + if (unlikely(!desc)) { + pr_info("can't get irq_desc for %d\n", new); + goto out_unlock; + } + + desc = move_irq_desc(desc, node); + __set_bit(new, intc_irq_map); + irq = new; + } + +out_unlock: + spin_unlock_irqrestore(&vector_lock, flags); + + if (irq > 0) + dynamic_irq_init(irq); + + return irq; +} + +int create_irq(void) +{ + int nid = cpu_to_node(smp_processor_id()); + int irq; + + irq = create_irq_on_node(NR_IRQS_LEGACY, nid); + if (irq == 0) + irq = -1; + + return irq; +} + +void destroy_irq(unsigned int irq) +{ + unsigned long flags; + + dynamic_irq_cleanup(irq); + + spin_lock_irqsave(&vector_lock, flags); + __clear_bit(irq, intc_irq_map); + spin_unlock_irqrestore(&vector_lock, flags); +} -- cgit v1.2.2 From 45b9deaf14e74543371aa8faea69c14e27b038c6 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 2 Nov 2009 15:43:20 +0900 Subject: sh: intc: Handle legacy IRQ reservation in vector map. Different CPUs will have different starting vectors, with varying amounts of reserved or unusable vector space prior to the first slot. This introduces a legacy vector reservation system that inserts itself in between the CPU vector map registration and the platform specific IRQ setup. This works fine in practice as the only new vectors that boards need to establish on their own should be dynamically allocated rather than arbitrarily assigned. As a plus, this also makes all of the converted platforms sparseirq ready. Signed-off-by: Paul Mundt --- drivers/sh/intc.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/sh') diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 4789df43c0f9..a7e5c2e9986c 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -928,3 +928,28 @@ void destroy_irq(unsigned int irq) __clear_bit(irq, intc_irq_map); spin_unlock_irqrestore(&vector_lock, flags); } + +int reserve_irq_vector(unsigned int irq) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&vector_lock, flags); + if (test_and_set_bit(irq, intc_irq_map)) + ret = -EBUSY; + spin_unlock_irqrestore(&vector_lock, flags); + + return ret; +} + +void reserve_irq_legacy(void) +{ + unsigned long flags; + int i, j; + + spin_lock_irqsave(&vector_lock, flags); + j = find_first_bit(intc_irq_map, nr_irqs); + for (i = 0; i < j; i++) + __set_bit(i, intc_irq_map); + spin_unlock_irqrestore(&vector_lock, flags); +} -- cgit v1.2.2 From fae4339919c741f89f7e293b8c646207e1df28e1 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 27 Nov 2009 07:38:01 +0000 Subject: sh: Break out SuperH PFC code This file breaks out the SuperH PFC code from arch/sh/kernel/gpio.c + arch/sh/include/asm/gpio.h to drivers/sh/pfc.c + include/linux/sh_pfc.h. Similar to the INTC stuff. The non-SuperH specific file location makes it possible to share the code between multiple architectures. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/sh/Makefile | 1 + drivers/sh/pfc.c | 584 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 585 insertions(+) create mode 100644 drivers/sh/pfc.c (limited to 'drivers/sh') diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 6a025cefe6dc..4956bf1f2134 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -3,4 +3,5 @@ # obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_MAPLE) += maple/ +obj-$(CONFIG_GENERIC_GPIO) += pfc.o obj-y += intc.o diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c new file mode 100644 index 000000000000..d22e5af699f9 --- /dev/null +++ b/drivers/sh/pfc.c @@ -0,0 +1,584 @@ +/* + * Pinmuxed GPIO support for SuperH. + * + * Copyright (C) 2008 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) +{ + if (enum_id < r->begin) + return 0; + + if (enum_id > r->end) + return 0; + + return 1; +} + +static unsigned long gpio_read_raw_reg(unsigned long reg, + unsigned long reg_width) +{ + switch (reg_width) { + case 8: + return ctrl_inb(reg); + case 16: + return ctrl_inw(reg); + case 32: + return ctrl_inl(reg); + } + + BUG(); + return 0; +} + +static void gpio_write_raw_reg(unsigned long reg, + unsigned long reg_width, + unsigned long data) +{ + switch (reg_width) { + case 8: + ctrl_outb(data, reg); + return; + case 16: + ctrl_outw(data, reg); + return; + case 32: + ctrl_outl(data, reg); + return; + } + + BUG(); +} + +static void gpio_write_bit(struct pinmux_data_reg *dr, + unsigned long in_pos, unsigned long value) +{ + unsigned long pos; + + pos = dr->reg_width - (in_pos + 1); + +#ifdef DEBUG + pr_info("write_bit addr = %lx, value = %ld, pos = %ld, " + "r_width = %ld\n", + dr->reg, !!value, pos, dr->reg_width); +#endif + + if (value) + set_bit(pos, &dr->reg_shadow); + else + clear_bit(pos, &dr->reg_shadow); + + gpio_write_raw_reg(dr->reg, dr->reg_width, dr->reg_shadow); +} + +static int gpio_read_reg(unsigned long reg, unsigned long reg_width, + unsigned long field_width, unsigned long in_pos) +{ + unsigned long data, mask, pos; + + data = 0; + mask = (1 << field_width) - 1; + pos = reg_width - ((in_pos + 1) * field_width); + +#ifdef DEBUG + pr_info("read_reg: addr = %lx, pos = %ld, " + "r_width = %ld, f_width = %ld\n", + reg, pos, reg_width, field_width); +#endif + + data = gpio_read_raw_reg(reg, reg_width); + return (data >> pos) & mask; +} + +static void gpio_write_reg(unsigned long reg, unsigned long reg_width, + unsigned long field_width, unsigned long in_pos, + unsigned long value) +{ + unsigned long mask, pos; + + mask = (1 << field_width) - 1; + pos = reg_width - ((in_pos + 1) * field_width); + +#ifdef DEBUG + pr_info("write_reg addr = %lx, value = %ld, pos = %ld, " + "r_width = %ld, f_width = %ld\n", + reg, value, pos, reg_width, field_width); +#endif + + mask = ~(mask << pos); + value = value << pos; + + switch (reg_width) { + case 8: + ctrl_outb((ctrl_inb(reg) & mask) | value, reg); + break; + case 16: + ctrl_outw((ctrl_inw(reg) & mask) | value, reg); + break; + case 32: + ctrl_outl((ctrl_inl(reg) & mask) | value, reg); + break; + } +} + +static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) +{ + struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + struct pinmux_data_reg *data_reg; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + return -1; + + k = 0; + while (1) { + data_reg = gpioc->data_regs + k; + + if (!data_reg->reg_width) + break; + + for (n = 0; n < data_reg->reg_width; n++) { + if (data_reg->enum_ids[n] == gpiop->enum_id) { + gpiop->flags &= ~PINMUX_FLAG_DREG; + gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT); + gpiop->flags &= ~PINMUX_FLAG_DBIT; + gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT); + return 0; + } + } + k++; + } + + BUG(); + + return -1; +} + +static void setup_data_regs(struct pinmux_info *gpioc) +{ + struct pinmux_data_reg *drp; + int k; + + for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++) + setup_data_reg(gpioc, k); + + k = 0; + while (1) { + drp = gpioc->data_regs + k; + + if (!drp->reg_width) + break; + + drp->reg_shadow = gpio_read_raw_reg(drp->reg, drp->reg_width); + k++; + } +} + +static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, + struct pinmux_data_reg **drp, int *bitp) +{ + struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + return -1; + + k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; + n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; + *drp = gpioc->data_regs + k; + *bitp = n; + return 0; +} + +static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, + struct pinmux_cfg_reg **crp, int *indexp, + unsigned long **cntp) +{ + struct pinmux_cfg_reg *config_reg; + unsigned long r_width, f_width; + int k, n; + + k = 0; + while (1) { + config_reg = gpioc->cfg_regs + k; + + r_width = config_reg->reg_width; + f_width = config_reg->field_width; + + if (!r_width) + break; + for (n = 0; n < (r_width / f_width) * 1 << f_width; n++) { + if (config_reg->enum_ids[n] == enum_id) { + *crp = config_reg; + *indexp = n; + *cntp = &config_reg->cnt[n / (1 << f_width)]; + return 0; + } + } + k++; + } + + return -1; +} + +static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, + int pos, pinmux_enum_t *enum_idp) +{ + pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id; + pinmux_enum_t *data = gpioc->gpio_data; + int k; + + if (!enum_in_range(enum_id, &gpioc->data)) { + if (!enum_in_range(enum_id, &gpioc->mark)) { + pr_err("non data/mark enum_id for gpio %d\n", gpio); + return -1; + } + } + + if (pos) { + *enum_idp = data[pos + 1]; + return pos + 1; + } + + for (k = 0; k < gpioc->gpio_data_size; k++) { + if (data[k] == enum_id) { + *enum_idp = data[k + 1]; + return k + 1; + } + } + + pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); + return -1; +} + +static void write_config_reg(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + int index) +{ + unsigned long ncomb, pos, value; + + ncomb = 1 << crp->field_width; + pos = index / ncomb; + value = index % ncomb; + + gpio_write_reg(crp->reg, crp->reg_width, crp->field_width, pos, value); +} + +static int check_config_reg(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + int index) +{ + unsigned long ncomb, pos, value; + + ncomb = 1 << crp->field_width; + pos = index / ncomb; + value = index % ncomb; + + if (gpio_read_reg(crp->reg, crp->reg_width, + crp->field_width, pos) == value) + return 0; + + return -1; +} + +enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; + +static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, + int pinmux_type, int cfg_mode) +{ + struct pinmux_cfg_reg *cr = NULL; + pinmux_enum_t enum_id; + struct pinmux_range *range; + int in_range, pos, index; + unsigned long *cntp; + + switch (pinmux_type) { + + case PINMUX_TYPE_FUNCTION: + range = NULL; + break; + + case PINMUX_TYPE_OUTPUT: + range = &gpioc->output; + break; + + case PINMUX_TYPE_INPUT: + range = &gpioc->input; + break; + + case PINMUX_TYPE_INPUT_PULLUP: + range = &gpioc->input_pu; + break; + + case PINMUX_TYPE_INPUT_PULLDOWN: + range = &gpioc->input_pd; + break; + + default: + goto out_err; + } + + pos = 0; + enum_id = 0; + index = 0; + while (1) { + pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id); + if (pos <= 0) + goto out_err; + + if (!enum_id) + break; + + in_range = enum_in_range(enum_id, &gpioc->function); + if (!in_range && range) { + in_range = enum_in_range(enum_id, range); + + if (in_range && enum_id == range->force) + continue; + } + + if (!in_range) + continue; + + if (get_config_reg(gpioc, enum_id, &cr, &index, &cntp) != 0) + goto out_err; + + switch (cfg_mode) { + case GPIO_CFG_DRYRUN: + if (!*cntp || !check_config_reg(gpioc, cr, index)) + continue; + break; + + case GPIO_CFG_REQ: + write_config_reg(gpioc, cr, index); + *cntp = *cntp + 1; + break; + + case GPIO_CFG_FREE: + *cntp = *cntp - 1; + break; + } + } + + return 0; + out_err: + return -1; +} + +static DEFINE_SPINLOCK(gpio_lock); + +static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip) +{ + return container_of(chip, struct pinmux_info, chip); +} + +static int sh_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + struct pinmux_data_reg *dummy; + unsigned long flags; + int i, ret, pinmux_type; + + ret = -EINVAL; + + if (!gpioc) + goto err_out; + + spin_lock_irqsave(&gpio_lock, flags); + + if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) + goto err_unlock; + + /* setup pin function here if no data is associated with pin */ + + if (get_data_reg(gpioc, offset, &dummy, &i) != 0) + pinmux_type = PINMUX_TYPE_FUNCTION; + else + pinmux_type = PINMUX_TYPE_GPIO; + + if (pinmux_type == PINMUX_TYPE_FUNCTION) { + if (pinmux_config_gpio(gpioc, offset, + pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_unlock; + + if (pinmux_config_gpio(gpioc, offset, + pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + } + + gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[offset].flags |= pinmux_type; + + ret = 0; + err_unlock: + spin_unlock_irqrestore(&gpio_lock, flags); + err_out: + return ret; +} + +static void sh_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + unsigned long flags; + int pinmux_type; + + if (!gpioc) + return; + + spin_lock_irqsave(&gpio_lock, flags); + + pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE; + pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE); + gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE; + + spin_unlock_irqrestore(&gpio_lock, flags); +} + +static int pinmux_direction(struct pinmux_info *gpioc, + unsigned gpio, int new_pinmux_type) +{ + int pinmux_type; + int ret = -EINVAL; + + if (!gpioc) + goto err_out; + + pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE; + + switch (pinmux_type) { + case PINMUX_TYPE_GPIO: + break; + case PINMUX_TYPE_OUTPUT: + case PINMUX_TYPE_INPUT: + case PINMUX_TYPE_INPUT_PULLUP: + case PINMUX_TYPE_INPUT_PULLDOWN: + pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE); + break; + default: + goto err_out; + } + + if (pinmux_config_gpio(gpioc, gpio, + new_pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_out; + + if (pinmux_config_gpio(gpioc, gpio, + new_pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + + gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[gpio].flags |= new_pinmux_type; + + ret = 0; + err_out: + return ret; +} + +static int sh_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + unsigned long flags; + int ret; + + spin_lock_irqsave(&gpio_lock, flags); + ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT); + spin_unlock_irqrestore(&gpio_lock, flags); + + return ret; +} + +static void sh_gpio_set_value(struct pinmux_info *gpioc, + unsigned gpio, int value) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) + BUG(); + else + gpio_write_bit(dr, bit, value); +} + +static int sh_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct pinmux_info *gpioc = chip_to_pinmux(chip); + unsigned long flags; + int ret; + + sh_gpio_set_value(gpioc, offset, value); + spin_lock_irqsave(&gpio_lock, flags); + ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT); + spin_unlock_irqrestore(&gpio_lock, flags); + + return ret; +} + +static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) { + BUG(); + return 0; + } + + return gpio_read_reg(dr->reg, dr->reg_width, 1, bit); +} + +static int sh_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + return sh_gpio_get_value(chip_to_pinmux(chip), offset); +} + +static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + sh_gpio_set_value(chip_to_pinmux(chip), offset, value); +} + +int register_pinmux(struct pinmux_info *pip) +{ + struct gpio_chip *chip = &pip->chip; + + pr_info("sh pinmux: %s handling gpio %d -> %d\n", + pip->name, pip->first_gpio, pip->last_gpio); + + setup_data_regs(pip); + + chip->request = sh_gpio_request; + chip->free = sh_gpio_free; + chip->direction_input = sh_gpio_direction_input; + chip->get = sh_gpio_get; + chip->direction_output = sh_gpio_direction_output; + chip->set = sh_gpio_set; + + WARN_ON(pip->first_gpio != 0); /* needs testing */ + + chip->label = pip->name; + chip->owner = THIS_MODULE; + chip->base = pip->first_gpio; + chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1; + + return gpiochip_add(chip); +} -- cgit v1.2.2 From 9cdae914b2d08febca6e6e0440817d60da115ba5 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 30 Nov 2009 12:10:41 +0900 Subject: sh: pfc: Convert from ctrl_xxx() to __raw_xxx() I/O routines. Now that the PFC code is exposed for other architectures, use the common __raw_xxx() routines instead of the ctrl_xxx() ones. This will be needed for ARM-based SH-Mobiles amongst others. Signed-off-by: Paul Mundt --- drivers/sh/pfc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/sh') diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index d22e5af699f9..448ba232c408 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -35,11 +35,11 @@ static unsigned long gpio_read_raw_reg(unsigned long reg, { switch (reg_width) { case 8: - return ctrl_inb(reg); + return __raw_readb(reg); case 16: - return ctrl_inw(reg); + return __raw_readw(reg); case 32: - return ctrl_inl(reg); + return __raw_readl(reg); } BUG(); @@ -52,13 +52,13 @@ static void gpio_write_raw_reg(unsigned long reg, { switch (reg_width) { case 8: - ctrl_outb(data, reg); + __raw_writeb(data, reg); return; case 16: - ctrl_outw(data, reg); + __raw_writew(data, reg); return; case 32: - ctrl_outl(data, reg); + __raw_writel(data, reg); return; } @@ -125,13 +125,13 @@ static void gpio_write_reg(unsigned long reg, unsigned long reg_width, switch (reg_width) { case 8: - ctrl_outb((ctrl_inb(reg) & mask) | value, reg); + __raw_writeb((__raw_readb(reg) & mask) | value, reg); break; case 16: - ctrl_outw((ctrl_inw(reg) & mask) | value, reg); + __raw_writew((__raw_readw(reg) & mask) | value, reg); break; case 32: - ctrl_outl((ctrl_inl(reg) & mask) | value, reg); + __raw_writel((__raw_readl(reg) & mask) | value, reg); break; } } -- cgit v1.2.2 From fd2cb0ce74e07babaf8c7bf96ef03c25d194e463 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 30 Nov 2009 12:15:04 +0900 Subject: sh: pfc: pr_info() -> pr_debug() cleanups. For some reason this was using pr_info() nested under an ifdef DEBUG. While this is appealing in that it circumvents the effort necessary to change ones loglevel, it's not terribly practical. So, convert it over to pr_debug(). Signed-off-by: Paul Mundt --- drivers/sh/pfc.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers/sh') diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index 448ba232c408..841ed5030c8f 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -7,7 +7,6 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ - #include #include #include @@ -72,11 +71,9 @@ static void gpio_write_bit(struct pinmux_data_reg *dr, pos = dr->reg_width - (in_pos + 1); -#ifdef DEBUG - pr_info("write_bit addr = %lx, value = %ld, pos = %ld, " - "r_width = %ld\n", - dr->reg, !!value, pos, dr->reg_width); -#endif + pr_debug("write_bit addr = %lx, value = %ld, pos = %ld, " + "r_width = %ld\n", + dr->reg, !!value, pos, dr->reg_width); if (value) set_bit(pos, &dr->reg_shadow); @@ -95,11 +92,9 @@ static int gpio_read_reg(unsigned long reg, unsigned long reg_width, mask = (1 << field_width) - 1; pos = reg_width - ((in_pos + 1) * field_width); -#ifdef DEBUG - pr_info("read_reg: addr = %lx, pos = %ld, " - "r_width = %ld, f_width = %ld\n", - reg, pos, reg_width, field_width); -#endif + pr_debug("read_reg: addr = %lx, pos = %ld, " + "r_width = %ld, f_width = %ld\n", + reg, pos, reg_width, field_width); data = gpio_read_raw_reg(reg, reg_width); return (data >> pos) & mask; @@ -114,11 +109,9 @@ static void gpio_write_reg(unsigned long reg, unsigned long reg_width, mask = (1 << field_width) - 1; pos = reg_width - ((in_pos + 1) * field_width); -#ifdef DEBUG - pr_info("write_reg addr = %lx, value = %ld, pos = %ld, " - "r_width = %ld, f_width = %ld\n", - reg, value, pos, reg_width, field_width); -#endif + pr_debug("write_reg addr = %lx, value = %ld, pos = %ld, " + "r_width = %ld, f_width = %ld\n", + reg, value, pos, reg_width, field_width); mask = ~(mask << pos); value = value << pos; -- cgit v1.2.2