Message ID | 20081203192738.GA13035@oksana.dev.rtsoft.ru (mailing list archive) |
---|---|
State | Accepted, archived |
Commit | 1b9e89046c31fd39d08742915b6bd72f6c239608 |
Delegated to: | Kumar Gala |
Headers | show |
On Wed, Dec 03, 2008 at 10:27:38PM +0300, Anton Vorontsov wrote: > With this API we're able to set a QE pin to the GPIO mode or a dedicated > peripheral function. > > The API relies on the fact that QE gpio controllers are registered. If > they aren't, the API won't work (gracefully though). > > There is one caveat though: if anybody occupied the node->data before us, > or overwrote it, then bad things will happen. Luckily this is all in the > platform code that we fully control, so this should never happen. > > I could implement more checks (for example we could create a list of > successfully registered QE controllers, and compare the node->data in the > qe_pin_request()), but this is unneeded if nobody is going to do silly > things behind our back. > > Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> > --- Kumar, can you please merge this patch? It is the last patch that holds the FHCI USB driver. Thanks, > arch/powerpc/include/asm/qe.h | 21 ++++ > arch/powerpc/sysdev/qe_lib/gpio.c | 195 +++++++++++++++++++++++++++++++++++++ > 2 files changed, 216 insertions(+), 0 deletions(-) > > diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h > index edee15d..3227440 100644 > --- a/arch/powerpc/include/asm/qe.h > +++ b/arch/powerpc/include/asm/qe.h > @@ -17,6 +17,8 @@ > #ifdef __KERNEL__ > > #include <linux/spinlock.h> > +#include <linux/errno.h> > +#include <linux/err.h> > #include <asm/cpm.h> > #include <asm/immap_qe.h> > > @@ -112,6 +114,25 @@ extern int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, > int assignment, int has_irq); > extern int par_io_data_set(u8 port, u8 pin, u8 val); > > +/* > + * Pin multiplexing functions. > + */ > +struct qe_pin; > +#ifdef CONFIG_QE_GPIO > +extern struct qe_pin *qe_pin_request(struct device_node *np, int index); > +extern void qe_pin_free(struct qe_pin *qe_pin); > +extern void qe_pin_set_gpio(struct qe_pin *qe_pin); > +extern void qe_pin_set_dedicated(struct qe_pin *pin); > +#else > +static inline struct qe_pin *qe_pin_request(struct device_node *np, int index) > +{ > + return ERR_PTR(-ENOSYS); > +} > +static inline void qe_pin_free(struct qe_pin *qe_pin) {} > +static inline void qe_pin_set_gpio(struct qe_pin *qe_pin) {} > +static inline void qe_pin_set_dedicated(struct qe_pin *pin) {} > +#endif /* CONFIG_QE_GPIO */ > + > /* QE internal API */ > int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input); > enum qe_clock qe_clock_source(const char *source); > diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/arch/powerpc/sysdev/qe_lib/gpio.c > index 8e5a0bc..3485288 100644 > --- a/arch/powerpc/sysdev/qe_lib/gpio.c > +++ b/arch/powerpc/sysdev/qe_lib/gpio.c > @@ -14,6 +14,7 @@ > #include <linux/kernel.h> > #include <linux/init.h> > #include <linux/spinlock.h> > +#include <linux/err.h> > #include <linux/io.h> > #include <linux/of.h> > #include <linux/of_gpio.h> > @@ -24,8 +25,14 @@ struct qe_gpio_chip { > struct of_mm_gpio_chip mm_gc; > spinlock_t lock; > > + unsigned long pin_flags[QE_PIO_PINS]; > +#define QE_PIN_REQUESTED 0 > + > /* shadowed data register to clear/set bits safely */ > u32 cpdata; > + > + /* saved_regs used to restore dedicated functions */ > + struct qe_pio_regs saved_regs; > }; > > static inline struct qe_gpio_chip * > @@ -40,6 +47,12 @@ static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) > struct qe_pio_regs __iomem *regs = mm_gc->regs; > > qe_gc->cpdata = in_be32(®s->cpdata); > + qe_gc->saved_regs.cpdata = qe_gc->cpdata; > + qe_gc->saved_regs.cpdir1 = in_be32(®s->cpdir1); > + qe_gc->saved_regs.cpdir2 = in_be32(®s->cpdir2); > + qe_gc->saved_regs.cppar1 = in_be32(®s->cppar1); > + qe_gc->saved_regs.cppar2 = in_be32(®s->cppar2); > + qe_gc->saved_regs.cpodr = in_be32(®s->cpodr); > } > > static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio) > @@ -103,6 +116,188 @@ static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) > return 0; > } > > +struct qe_pin { > + /* > + * The qe_gpio_chip name is unfortunate, we should change that to > + * something like qe_pio_controller. Someday. > + */ > + struct qe_gpio_chip *controller; > + int num; > +}; > + > +/** > + * qe_pin_request - Request a QE pin > + * @np: device node to get a pin from > + * @index: index of a pin in the device tree > + * Context: non-atomic > + * > + * This function return qe_pin so that you could use it with the rest of > + * the QE Pin Multiplexing API. > + */ > +struct qe_pin *qe_pin_request(struct device_node *np, int index) > +{ > + struct qe_pin *qe_pin; > + struct device_node *gc; > + struct of_gpio_chip *of_gc = NULL; > + struct of_mm_gpio_chip *mm_gc; > + struct qe_gpio_chip *qe_gc; > + int err; > + int size; > + const void *gpio_spec; > + const u32 *gpio_cells; > + unsigned long flags; > + > + qe_pin = kzalloc(sizeof(*qe_pin), GFP_KERNEL); > + if (!qe_pin) { > + pr_debug("%s: can't allocate memory\n", __func__); > + return ERR_PTR(-ENOMEM); > + } > + > + err = of_parse_phandles_with_args(np, "gpios", "#gpio-cells", index, > + &gc, &gpio_spec); > + if (err) { > + pr_debug("%s: can't parse gpios property\n", __func__); > + goto err0; > + } > + > + if (!of_device_is_compatible(gc, "fsl,mpc8323-qe-pario-bank")) { > + pr_debug("%s: tried to get a non-qe pin\n", __func__); > + err = -EINVAL; > + goto err1; > + } > + > + of_gc = gc->data; > + if (!of_gc) { > + pr_debug("%s: gpio controller %s isn't registered\n", > + np->full_name, gc->full_name); > + err = -ENODEV; > + goto err1; > + } > + > + gpio_cells = of_get_property(gc, "#gpio-cells", &size); > + if (!gpio_cells || size != sizeof(*gpio_cells) || > + *gpio_cells != of_gc->gpio_cells) { > + pr_debug("%s: wrong #gpio-cells for %s\n", > + np->full_name, gc->full_name); > + err = -EINVAL; > + goto err1; > + } > + > + err = of_gc->xlate(of_gc, np, gpio_spec, NULL); > + if (err < 0) > + goto err1; > + > + mm_gc = to_of_mm_gpio_chip(&of_gc->gc); > + qe_gc = to_qe_gpio_chip(mm_gc); > + > + spin_lock_irqsave(&qe_gc->lock, flags); > + > + if (test_and_set_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[err]) == 0) { > + qe_pin->controller = qe_gc; > + qe_pin->num = err; > + err = 0; > + } else { > + err = -EBUSY; > + } > + > + spin_unlock_irqrestore(&qe_gc->lock, flags); > + > + if (!err) > + return qe_pin; > +err1: > + of_node_put(gc); > +err0: > + kfree(qe_pin); > + pr_debug("%s failed with status %d\n", __func__, err); > + return ERR_PTR(err); > +} > +EXPORT_SYMBOL(qe_pin_request); > + > +/** > + * qe_pin_free - Free a pin > + * @qe_pin: pointer to the qe_pin structure > + * Context: any > + * > + * This function frees the qe_pin structure and makes a pin available > + * for further qe_pin_request() calls. > + */ > +void qe_pin_free(struct qe_pin *qe_pin) > +{ > + struct qe_gpio_chip *qe_gc = qe_pin->controller; > + unsigned long flags; > + const int pin = qe_pin->num; > + > + spin_lock_irqsave(&qe_gc->lock, flags); > + test_and_clear_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[pin]); > + spin_unlock_irqrestore(&qe_gc->lock, flags); > + > + kfree(qe_pin); > +} > +EXPORT_SYMBOL(qe_pin_free); > + > +/** > + * qe_pin_set_dedicated - Revert a pin to a dedicated peripheral function mode > + * @qe_pin: pointer to the qe_pin structure > + * Context: any > + * > + * This function resets a pin to a dedicated peripheral function that > + * has been set up by the firmware. > + */ > +void qe_pin_set_dedicated(struct qe_pin *qe_pin) > +{ > + struct qe_gpio_chip *qe_gc = qe_pin->controller; > + struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; > + struct qe_pio_regs *sregs = &qe_gc->saved_regs; > + int pin = qe_pin->num; > + u32 mask1 = 1 << (QE_PIO_PINS - (pin + 1)); > + u32 mask2 = 0x3 << (QE_PIO_PINS - (pin % (QE_PIO_PINS / 2) + 1) * 2); > + bool second_reg = pin > (QE_PIO_PINS / 2) - 1; > + unsigned long flags; > + > + spin_lock_irqsave(&qe_gc->lock, flags); > + > + if (second_reg) { > + clrsetbits_be32(®s->cpdir2, mask2, sregs->cpdir2 & mask2); > + clrsetbits_be32(®s->cppar2, mask2, sregs->cppar2 & mask2); > + } else { > + clrsetbits_be32(®s->cpdir1, mask2, sregs->cpdir1 & mask2); > + clrsetbits_be32(®s->cppar1, mask2, sregs->cppar1 & mask2); > + } > + > + if (sregs->cpdata & mask1) > + qe_gc->cpdata |= mask1; > + else > + qe_gc->cpdata &= ~mask1; > + > + out_be32(®s->cpdata, qe_gc->cpdata); > + clrsetbits_be32(®s->cpodr, mask1, sregs->cpodr & mask1); > + > + spin_unlock_irqrestore(&qe_gc->lock, flags); > +} > +EXPORT_SYMBOL(qe_pin_set_dedicated); > + > +/** > + * qe_pin_set_gpio - Set a pin to the GPIO mode > + * @qe_pin: pointer to the qe_pin structure > + * Context: any > + * > + * This function sets a pin to the GPIO mode. > + */ > +void qe_pin_set_gpio(struct qe_pin *qe_pin) > +{ > + struct qe_gpio_chip *qe_gc = qe_pin->controller; > + struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; > + unsigned long flags; > + > + spin_lock_irqsave(&qe_gc->lock, flags); > + > + /* Let's make it input by default, GPIO API is able to change that. */ > + __par_io_config_pin(regs, qe_pin->num, QE_PIO_DIR_IN, 0, 0, 0); > + > + spin_unlock_irqrestore(&qe_gc->lock, flags); > +} > +EXPORT_SYMBOL(qe_pin_set_gpio); > + > static int __init qe_add_gpiochips(void) > { > struct device_node *np; > -- > 1.5.6.5 > > _______________________________________________ > Linuxppc-dev mailing list > Linuxppc-dev@ozlabs.org > https://ozlabs.org/mailman/listinfo/linuxppc-dev
On Dec 17, 2008, at 9:41 AM, Anton Vorontsov wrote: > On Wed, Dec 03, 2008 at 10:27:38PM +0300, Anton Vorontsov wrote: >> With this API we're able to set a QE pin to the GPIO mode or a >> dedicated >> peripheral function. >> >> The API relies on the fact that QE gpio controllers are registered. >> If >> they aren't, the API won't work (gracefully though). >> >> There is one caveat though: if anybody occupied the node->data >> before us, >> or overwrote it, then bad things will happen. Luckily this is all >> in the >> platform code that we fully control, so this should never happen. >> >> I could implement more checks (for example we could create a list of >> successfully registered QE controllers, and compare the node->data >> in the >> qe_pin_request()), but this is unneeded if nobody is going to do >> silly >> things behind our back. >> >> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> >> --- > > Kumar, can you please merge this patch? It is the last patch that > holds the FHCI USB driver. > > Thanks, Will do. Can I get you to add kdoc comments for the API functions: +extern struct qe_pin *qe_pin_request(struct device_node *np, int index); +extern void qe_pin_free(struct qe_pin *qe_pin); +extern void qe_pin_set_gpio(struct qe_pin *qe_pin); +extern void qe_pin_set_dedicated(struct qe_pin *pin); - k
On Dec 3, 2008, at 1:27 PM, Anton Vorontsov wrote: > With this API we're able to set a QE pin to the GPIO mode or a > dedicated > peripheral function. > > The API relies on the fact that QE gpio controllers are registered. If > they aren't, the API won't work (gracefully though). > > There is one caveat though: if anybody occupied the node->data > before us, > or overwrote it, then bad things will happen. Luckily this is all in > the > platform code that we fully control, so this should never happen. > > I could implement more checks (for example we could create a list of > successfully registered QE controllers, and compare the node->data > in the > qe_pin_request()), but this is unneeded if nobody is going to do silly > things behind our back. > > Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> > --- > arch/powerpc/include/asm/qe.h | 21 ++++ > arch/powerpc/sysdev/qe_lib/gpio.c | 195 ++++++++++++++++++++++++++++ > +++++++++ > 2 files changed, 216 insertions(+), 0 deletions(-) applied to next - k
On Wed, Dec 17, 2008 at 10:46:17AM -0600, Kumar Gala wrote: > > On Dec 17, 2008, at 9:41 AM, Anton Vorontsov wrote: > >> On Wed, Dec 03, 2008 at 10:27:38PM +0300, Anton Vorontsov wrote: >>> With this API we're able to set a QE pin to the GPIO mode or a >>> dedicated >>> peripheral function. >>> >>> The API relies on the fact that QE gpio controllers are registered. >>> If >>> they aren't, the API won't work (gracefully though). >>> >>> There is one caveat though: if anybody occupied the node->data >>> before us, >>> or overwrote it, then bad things will happen. Luckily this is all in >>> the >>> platform code that we fully control, so this should never happen. >>> >>> I could implement more checks (for example we could create a list of >>> successfully registered QE controllers, and compare the node->data >>> in the >>> qe_pin_request()), but this is unneeded if nobody is going to do >>> silly >>> things behind our back. >>> >>> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> >>> --- >> >> Kumar, can you please merge this patch? It is the last patch that >> holds the FHCI USB driver. >> >> Thanks, > > Will do. Can I get you to add kdoc comments for the API functions: The functions already have kdoc comments (see qe_lib/gpio.c changes). > +extern struct qe_pin *qe_pin_request(struct device_node *np, int > index); > +extern void qe_pin_free(struct qe_pin *qe_pin); > +extern void qe_pin_set_gpio(struct qe_pin *qe_pin); > +extern void qe_pin_set_dedicated(struct qe_pin *pin);
>>>> >>> >>> Kumar, can you please merge this patch? It is the last patch that >>> holds the FHCI USB driver. >>> >>> Thanks, >> >> Will do. Can I get you to add kdoc comments for the API functions: > > The functions already have kdoc comments (see qe_lib/gpio.c changes). Yeah, I noticed that.. sorry about the noise. - k
diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h index edee15d..3227440 100644 --- a/arch/powerpc/include/asm/qe.h +++ b/arch/powerpc/include/asm/qe.h @@ -17,6 +17,8 @@ #ifdef __KERNEL__ #include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/err.h> #include <asm/cpm.h> #include <asm/immap_qe.h> @@ -112,6 +114,25 @@ extern int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, int assignment, int has_irq); extern int par_io_data_set(u8 port, u8 pin, u8 val); +/* + * Pin multiplexing functions. + */ +struct qe_pin; +#ifdef CONFIG_QE_GPIO +extern struct qe_pin *qe_pin_request(struct device_node *np, int index); +extern void qe_pin_free(struct qe_pin *qe_pin); +extern void qe_pin_set_gpio(struct qe_pin *qe_pin); +extern void qe_pin_set_dedicated(struct qe_pin *pin); +#else +static inline struct qe_pin *qe_pin_request(struct device_node *np, int index) +{ + return ERR_PTR(-ENOSYS); +} +static inline void qe_pin_free(struct qe_pin *qe_pin) {} +static inline void qe_pin_set_gpio(struct qe_pin *qe_pin) {} +static inline void qe_pin_set_dedicated(struct qe_pin *pin) {} +#endif /* CONFIG_QE_GPIO */ + /* QE internal API */ int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input); enum qe_clock qe_clock_source(const char *source); diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/arch/powerpc/sysdev/qe_lib/gpio.c index 8e5a0bc..3485288 100644 --- a/arch/powerpc/sysdev/qe_lib/gpio.c +++ b/arch/powerpc/sysdev/qe_lib/gpio.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/spinlock.h> +#include <linux/err.h> #include <linux/io.h> #include <linux/of.h> #include <linux/of_gpio.h> @@ -24,8 +25,14 @@ struct qe_gpio_chip { struct of_mm_gpio_chip mm_gc; spinlock_t lock; + unsigned long pin_flags[QE_PIO_PINS]; +#define QE_PIN_REQUESTED 0 + /* shadowed data register to clear/set bits safely */ u32 cpdata; + + /* saved_regs used to restore dedicated functions */ + struct qe_pio_regs saved_regs; }; static inline struct qe_gpio_chip * @@ -40,6 +47,12 @@ static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) struct qe_pio_regs __iomem *regs = mm_gc->regs; qe_gc->cpdata = in_be32(®s->cpdata); + qe_gc->saved_regs.cpdata = qe_gc->cpdata; + qe_gc->saved_regs.cpdir1 = in_be32(®s->cpdir1); + qe_gc->saved_regs.cpdir2 = in_be32(®s->cpdir2); + qe_gc->saved_regs.cppar1 = in_be32(®s->cppar1); + qe_gc->saved_regs.cppar2 = in_be32(®s->cppar2); + qe_gc->saved_regs.cpodr = in_be32(®s->cpodr); } static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio) @@ -103,6 +116,188 @@ static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) return 0; } +struct qe_pin { + /* + * The qe_gpio_chip name is unfortunate, we should change that to + * something like qe_pio_controller. Someday. + */ + struct qe_gpio_chip *controller; + int num; +}; + +/** + * qe_pin_request - Request a QE pin + * @np: device node to get a pin from + * @index: index of a pin in the device tree + * Context: non-atomic + * + * This function return qe_pin so that you could use it with the rest of + * the QE Pin Multiplexing API. + */ +struct qe_pin *qe_pin_request(struct device_node *np, int index) +{ + struct qe_pin *qe_pin; + struct device_node *gc; + struct of_gpio_chip *of_gc = NULL; + struct of_mm_gpio_chip *mm_gc; + struct qe_gpio_chip *qe_gc; + int err; + int size; + const void *gpio_spec; + const u32 *gpio_cells; + unsigned long flags; + + qe_pin = kzalloc(sizeof(*qe_pin), GFP_KERNEL); + if (!qe_pin) { + pr_debug("%s: can't allocate memory\n", __func__); + return ERR_PTR(-ENOMEM); + } + + err = of_parse_phandles_with_args(np, "gpios", "#gpio-cells", index, + &gc, &gpio_spec); + if (err) { + pr_debug("%s: can't parse gpios property\n", __func__); + goto err0; + } + + if (!of_device_is_compatible(gc, "fsl,mpc8323-qe-pario-bank")) { + pr_debug("%s: tried to get a non-qe pin\n", __func__); + err = -EINVAL; + goto err1; + } + + of_gc = gc->data; + if (!of_gc) { + pr_debug("%s: gpio controller %s isn't registered\n", + np->full_name, gc->full_name); + err = -ENODEV; + goto err1; + } + + gpio_cells = of_get_property(gc, "#gpio-cells", &size); + if (!gpio_cells || size != sizeof(*gpio_cells) || + *gpio_cells != of_gc->gpio_cells) { + pr_debug("%s: wrong #gpio-cells for %s\n", + np->full_name, gc->full_name); + err = -EINVAL; + goto err1; + } + + err = of_gc->xlate(of_gc, np, gpio_spec, NULL); + if (err < 0) + goto err1; + + mm_gc = to_of_mm_gpio_chip(&of_gc->gc); + qe_gc = to_qe_gpio_chip(mm_gc); + + spin_lock_irqsave(&qe_gc->lock, flags); + + if (test_and_set_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[err]) == 0) { + qe_pin->controller = qe_gc; + qe_pin->num = err; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irqrestore(&qe_gc->lock, flags); + + if (!err) + return qe_pin; +err1: + of_node_put(gc); +err0: + kfree(qe_pin); + pr_debug("%s failed with status %d\n", __func__, err); + return ERR_PTR(err); +} +EXPORT_SYMBOL(qe_pin_request); + +/** + * qe_pin_free - Free a pin + * @qe_pin: pointer to the qe_pin structure + * Context: any + * + * This function frees the qe_pin structure and makes a pin available + * for further qe_pin_request() calls. + */ +void qe_pin_free(struct qe_pin *qe_pin) +{ + struct qe_gpio_chip *qe_gc = qe_pin->controller; + unsigned long flags; + const int pin = qe_pin->num; + + spin_lock_irqsave(&qe_gc->lock, flags); + test_and_clear_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[pin]); + spin_unlock_irqrestore(&qe_gc->lock, flags); + + kfree(qe_pin); +} +EXPORT_SYMBOL(qe_pin_free); + +/** + * qe_pin_set_dedicated - Revert a pin to a dedicated peripheral function mode + * @qe_pin: pointer to the qe_pin structure + * Context: any + * + * This function resets a pin to a dedicated peripheral function that + * has been set up by the firmware. + */ +void qe_pin_set_dedicated(struct qe_pin *qe_pin) +{ + struct qe_gpio_chip *qe_gc = qe_pin->controller; + struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; + struct qe_pio_regs *sregs = &qe_gc->saved_regs; + int pin = qe_pin->num; + u32 mask1 = 1 << (QE_PIO_PINS - (pin + 1)); + u32 mask2 = 0x3 << (QE_PIO_PINS - (pin % (QE_PIO_PINS / 2) + 1) * 2); + bool second_reg = pin > (QE_PIO_PINS / 2) - 1; + unsigned long flags; + + spin_lock_irqsave(&qe_gc->lock, flags); + + if (second_reg) { + clrsetbits_be32(®s->cpdir2, mask2, sregs->cpdir2 & mask2); + clrsetbits_be32(®s->cppar2, mask2, sregs->cppar2 & mask2); + } else { + clrsetbits_be32(®s->cpdir1, mask2, sregs->cpdir1 & mask2); + clrsetbits_be32(®s->cppar1, mask2, sregs->cppar1 & mask2); + } + + if (sregs->cpdata & mask1) + qe_gc->cpdata |= mask1; + else + qe_gc->cpdata &= ~mask1; + + out_be32(®s->cpdata, qe_gc->cpdata); + clrsetbits_be32(®s->cpodr, mask1, sregs->cpodr & mask1); + + spin_unlock_irqrestore(&qe_gc->lock, flags); +} +EXPORT_SYMBOL(qe_pin_set_dedicated); + +/** + * qe_pin_set_gpio - Set a pin to the GPIO mode + * @qe_pin: pointer to the qe_pin structure + * Context: any + * + * This function sets a pin to the GPIO mode. + */ +void qe_pin_set_gpio(struct qe_pin *qe_pin) +{ + struct qe_gpio_chip *qe_gc = qe_pin->controller; + struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; + unsigned long flags; + + spin_lock_irqsave(&qe_gc->lock, flags); + + /* Let's make it input by default, GPIO API is able to change that. */ + __par_io_config_pin(regs, qe_pin->num, QE_PIO_DIR_IN, 0, 0, 0); + + spin_unlock_irqrestore(&qe_gc->lock, flags); +} +EXPORT_SYMBOL(qe_pin_set_gpio); + static int __init qe_add_gpiochips(void) { struct device_node *np;
With this API we're able to set a QE pin to the GPIO mode or a dedicated peripheral function. The API relies on the fact that QE gpio controllers are registered. If they aren't, the API won't work (gracefully though). There is one caveat though: if anybody occupied the node->data before us, or overwrote it, then bad things will happen. Luckily this is all in the platform code that we fully control, so this should never happen. I could implement more checks (for example we could create a list of successfully registered QE controllers, and compare the node->data in the qe_pin_request()), but this is unneeded if nobody is going to do silly things behind our back. Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> --- arch/powerpc/include/asm/qe.h | 21 ++++ arch/powerpc/sysdev/qe_lib/gpio.c | 195 +++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+), 0 deletions(-)