From 56242a1fc595d158eddefbb4d6d76e82c2535f55 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 21 Nov 2011 21:33:18 -0800 Subject: sh: clkfwk: setup clock parent from current register value Some clocks can select its parent clock by CPG register. But it might have been modified by boot-loader or something. This patch removed fixed initial parent clock, and setup it from their current register settings. It works on div6 reparent clocks for now. Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt --- include/linux/sh_clk.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index a20831cf336a..e834304c0b6a 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -131,10 +131,9 @@ int sh_clk_div4_enable_register(struct clk *clks, int nr, int sh_clk_div4_reparent_register(struct clk *clks, int nr, struct clk_div4_table *table); -#define SH_CLK_DIV6_EXT(_parent, _reg, _flags, _parents, \ +#define SH_CLK_DIV6_EXT(_reg, _flags, _parents, \ _num_parents, _src_shift, _src_width) \ { \ - .parent = _parent, \ .enable_reg = (void __iomem *)_reg, \ .flags = _flags, \ .parent_table = _parents, \ @@ -144,7 +143,11 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, } #define SH_CLK_DIV6(_parent, _reg, _flags) \ - SH_CLK_DIV6_EXT(_parent, _reg, _flags, NULL, 0, 0, 0) +{ \ + .parent = _parent, \ + .enable_reg = (void __iomem *)_reg, \ + .flags = _flags, \ +} int sh_clk_div6_register(struct clk *clks, int nr); int sh_clk_div6_reparent_register(struct clk *clks, int nr); -- cgit v1.2.3 From faf02f8fee5563ea7f950b3f5f08c654aa6c4525 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 2 Dec 2011 17:44:50 +0900 Subject: serial: sh-sci: per-port modem control. The bulk of the ports do not support any sort of modem control, so blindly twiddling the MCE bit doesn't accomplish much. We now require ports to manually specify which line supports modem control signals. While at it, tidy up the RTS/CTSIO handling in SCSPTR parts so it's a bit more obvious what's going on (and without clobbering other configurations in the process). Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.c | 31 ++++++++++++++++++++----------- include/linux/serial_sci.h | 10 ++++++++++ 2 files changed, 30 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 46deaaec836d..fd60d72eac89 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -474,8 +474,15 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag) if (!reg->size) return; - if (!(cflag & CRTSCTS)) - sci_out(port, SCSPTR, 0x0080); /* Set RTS = 1 */ + if ((s->cfg->capabilities & SCIx_HAVE_RTSCTS) && + ((!(cflag & CRTSCTS)))) { + unsigned short status; + + status = sci_in(port, SCSPTR); + status &= ~SCSPTR_CTSIO; + status |= SCSPTR_RTSIO; + sci_out(port, SCSPTR, status); /* Set RTS = 1 */ + } } static int sci_txfill(struct uart_port *port) @@ -1764,16 +1771,18 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_init_pins(port, termios->c_cflag); - reg = sci_getreg(port, SCFCR); - if (reg->size) { - unsigned short ctrl; + if (s->cfg->capabilities & SCIx_HAVE_RTSCTS) { + reg = sci_getreg(port, SCFCR); + if (reg->size) { + unsigned short ctrl; - ctrl = sci_in(port, SCFCR); - if (termios->c_cflag & CRTSCTS) - ctrl |= SCFCR_MCE; - else - ctrl &= ~SCFCR_MCE; - sci_out(port, SCFCR, ctrl); + ctrl = sci_in(port, SCFCR); + if (termios->c_cflag & CRTSCTS) + ctrl |= SCFCR_MCE; + else + ctrl &= ~SCFCR_MCE; + sci_out(port, SCFCR, ctrl); + } } sci_out(port, SCSCR, s->cfg->scscr); diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 369273a52679..15b1bdcaa9f5 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -49,6 +49,10 @@ enum { #define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) +/* SCSPTR, optional */ +#define SCSPTR_RTSIO (1 << 7) +#define SCSPTR_CTSIO (1 << 5) + /* Offsets into the sci_port->irqs array */ enum { SCIx_ERI_IRQ, @@ -108,6 +112,11 @@ struct plat_sci_port_ops { void (*init_pins)(struct uart_port *, unsigned int cflag); }; +/* + * Port-specific capabilities + */ +#define SCIx_HAVE_RTSCTS (1 << 0) + /* * Platform device specific platform_data struct */ @@ -116,6 +125,7 @@ struct plat_sci_port { unsigned int irqs[SCIx_NR_IRQS]; /* ERI, RXI, TXI, BRI */ unsigned int type; /* SCI / SCIF / IRDA */ upf_t flags; /* UPF_* flags */ + unsigned long capabilities; /* Port features/capabilities */ unsigned int scbrr_algo_id; /* SCBRR calculation algo */ unsigned int scscr; /* SCSCR initialization */ -- cgit v1.2.3 From 50f0959ad4f9ac1c5ee208bb820de299a1b3730b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 2 Dec 2011 20:09:48 +0900 Subject: serial: sh-sci: Handle GPIO function requests. This adds initial support for requesting the various GPIO functions necessary for certain ports. This just plugs in dumb request/free logic, but serves as a building block for migrating off of the ->init_pins mess to a wholly gpiolib backed solution (primarily parts with external RTS/CTS pins, but will also allow us to clean up RXD pin testing). Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.c | 71 +++++++++++++++++++++++++++++++++++++++++++-- include/linux/serial_sci.h | 12 ++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 761a800cb483..9e62349b3d9f 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -50,6 +50,7 @@ #include #include #include +#include #ifdef CONFIG_SUPERH #include @@ -73,6 +74,7 @@ struct sci_port { struct clk *fclk; char *irqstr[SCIx_NR_IRQS]; + char *gpiostr[SCIx_NR_FNS]; struct dma_chan *chan_tx; struct dma_chan *chan_rx; @@ -1105,6 +1107,67 @@ static void sci_free_irq(struct sci_port *port) } } +static const char *sci_gpio_names[SCIx_NR_FNS] = { + "sck", "rxd", "txd", "cts", "rts", +}; + +static const char *sci_gpio_str(unsigned int index) +{ + return sci_gpio_names[index]; +} + +static void __devinit sci_init_gpios(struct sci_port *port) +{ + struct uart_port *up = &port->port; + int i; + + if (!port->cfg) + return; + + for (i = 0; i < SCIx_NR_FNS; i++) { + const char *desc; + int ret; + + if (!port->cfg->gpios[i]) + continue; + + desc = sci_gpio_str(i); + + port->gpiostr[i] = kasprintf(GFP_KERNEL, "%s:%s", + dev_name(up->dev), desc); + + /* + * If we've failed the allocation, we can still continue + * on with a NULL string. + */ + if (!port->gpiostr[i]) + dev_notice(up->dev, "%s string allocation failure\n", + desc); + + ret = gpio_request(port->cfg->gpios[i], port->gpiostr[i]); + if (unlikely(ret != 0)) { + dev_notice(up->dev, "failed %s gpio request\n", desc); + + /* + * If we can't get the GPIO for whatever reason, + * no point in keeping the verbose string around. + */ + kfree(port->gpiostr[i]); + } + } +} + +static void sci_free_gpios(struct sci_port *port) +{ + int i; + + for (i = 0; i < SCIx_NR_FNS; i++) + if (port->cfg->gpios[i]) { + gpio_free(port->cfg->gpios[i]); + kfree(port->gpiostr[i]); + } +} + static unsigned int sci_tx_empty(struct uart_port *port) { unsigned short status = sci_in(port, SCxSR); @@ -1962,6 +2025,8 @@ static int __devinit sci_init_single(struct platform_device *dev, struct uart_port *port = &sci_port->port; int ret; + sci_port->cfg = p; + port->ops = &sci_uart_ops; port->iotype = UPIO_MEM; port->line = index; @@ -2007,6 +2072,8 @@ static int __devinit sci_init_single(struct platform_device *dev, port->dev = &dev->dev; + sci_init_gpios(sci_port); + pm_runtime_irq_safe(&dev->dev); pm_runtime_enable(&dev->dev); } @@ -2041,8 +2108,6 @@ static int __devinit sci_init_single(struct platform_device *dev, p->error_mask |= (1 << p->overrun_bit); } - sci_port->cfg = p; - port->mapbase = p->mapbase; port->type = p->type; port->flags = p->flags; @@ -2249,6 +2314,8 @@ static int sci_remove(struct platform_device *dev) cpufreq_unregister_notifier(&port->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); + sci_free_gpios(port); + uart_remove_one_port(&sci_uart_driver, &port->port); clk_put(port->iclk); diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 15b1bdcaa9f5..78779074f6e8 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -64,6 +64,17 @@ enum { SCIx_MUX_IRQ = SCIx_NR_IRQS, /* special case */ }; +/* Offsets into the sci_port->gpios array */ +enum { + SCIx_SCK, + SCIx_RXD, + SCIx_TXD, + SCIx_CTS, + SCIx_RTS, + + SCIx_NR_FNS, +}; + enum { SCIx_PROBE_REGTYPE, @@ -123,6 +134,7 @@ struct plat_sci_port_ops { struct plat_sci_port { unsigned long mapbase; /* resource base */ unsigned int irqs[SCIx_NR_IRQS]; /* ERI, RXI, TXI, BRI */ + unsigned int gpios[SCIx_NR_FNS]; /* SCK, RXD, TXD, CTS, RTS */ unsigned int type; /* SCI / SCIF / IRDA */ upf_t flags; /* UPF_* flags */ unsigned long capabilities; /* Port features/capabilities */ -- cgit v1.2.3 From eda2030a5b60bb818f062adacbcfb6fd2d366fb9 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 8 Dec 2011 22:58:54 +0900 Subject: sh: extend clock struct with mapped_reg member Add a "mapped_reg" member to struct clk and use that to keep the ioremapped register based on enable_reg. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/sh/clk/core.c | 9 +++++++-- include/linux/sh_clk.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c index db257a35e71a..7715de2629c1 100644 --- a/drivers/sh/clk/core.c +++ b/drivers/sh/clk/core.c @@ -355,7 +355,7 @@ static int clk_establish_mapping(struct clk *clk) */ if (!clk->parent) { clk->mapping = &dummy_mapping; - return 0; + goto out; } /* @@ -384,6 +384,9 @@ static int clk_establish_mapping(struct clk *clk) } clk->mapping = mapping; +out: + clk->mapped_reg = clk->mapping->base; + clk->mapped_reg += (phys_addr_t)clk->enable_reg - clk->mapping->phys; return 0; } @@ -402,10 +405,12 @@ static void clk_teardown_mapping(struct clk *clk) /* Nothing to do */ if (mapping == &dummy_mapping) - return; + goto out; kref_put(&mapping->ref, clk_destroy_mapping); clk->mapping = NULL; +out: + clk->mapped_reg = NULL; } int clk_register(struct clk *clk) diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index e834304c0b6a..54341d811685 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -49,6 +49,7 @@ struct clk { void __iomem *enable_reg; unsigned int enable_bit; + void __iomem *mapped_reg; unsigned long arch_flags; void *priv; -- cgit v1.2.3 From b0e10211cba1629e2e534ca9cb3d87cfc7e389ea Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 9 Dec 2011 12:14:27 +0900 Subject: sh: pfc: ioremap() support Add support for non-entity mapped PFC registers through the use of struct resource and ioremap()/iounmap(). The PFC main data structure gets updated with a pointer to a struct resources array that point out all register windows used by the PFC instance. The register definitions are kept as physical addresses but the PFC code will do transparent conversion into virtual addresses whenever register windows are specified using with struct resource. To introduce as little performance penalty as possible the virtual address of each data register is cached in memory. The virtual address of each configuration register is however calculated during run time. This because the configuration is considered slow path so focus is instead put on keeping memory foot print as small as possible. The PFC register access code is in this patch updated from __raw_readN() / __raw_writeN() into ioreadN() / iowriteN(). This patch is needed to support the PFC block in r8a7779. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/sh/pfc.c | 137 ++++++++++++++++++++++++++++++++++++++++--------- include/linux/sh_pfc.h | 11 ++++ 2 files changed, 124 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index e67fe170d8d5..e7d127a9c1c5 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -19,6 +19,75 @@ #include #include #include +#include +#include + +static void pfc_iounmap(struct pinmux_info *pip) +{ + int k; + + for (k = 0; k < pip->num_resources; k++) + if (pip->window[k].virt) + iounmap(pip->window[k].virt); + + kfree(pip->window); + pip->window = NULL; +} + +static int pfc_ioremap(struct pinmux_info *pip) +{ + struct resource *res; + int k; + + if (!pip->num_resources) + return 0; + + pip->window = kzalloc(pip->num_resources * sizeof(*pip->window), + GFP_NOWAIT); + if (!pip->window) + goto err1; + + for (k = 0; k < pip->num_resources; k++) { + res = pip->resource + k; + WARN_ON(resource_type(res) != IORESOURCE_MEM); + pip->window[k].phys = res->start; + pip->window[k].size = resource_size(res); + pip->window[k].virt = ioremap_nocache(res->start, + resource_size(res)); + if (!pip->window[k].virt) + goto err2; + } + + return 0; + +err2: + pfc_iounmap(pip); +err1: + return -1; +} + +static void __iomem *pfc_phys_to_virt(struct pinmux_info *pip, + unsigned long address) +{ + struct pfc_window *window; + int k; + + /* scan through physical windows and convert address */ + for (k = 0; k < pip->num_resources; k++) { + window = pip->window + k; + + if (address < window->phys) + continue; + + if (address >= (window->phys + window->size)) + continue; + + return window->virt + (address - window->phys); + } + + /* no windows defined, register must be 1:1 mapped virt:phys */ + return (void __iomem *)address; +} static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) { @@ -31,35 +100,35 @@ static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) return 1; } -static unsigned long gpio_read_raw_reg(unsigned long reg, +static unsigned long gpio_read_raw_reg(void __iomem *mapped_reg, unsigned long reg_width) { switch (reg_width) { case 8: - return __raw_readb(reg); + return ioread8(mapped_reg); case 16: - return __raw_readw(reg); + return ioread16(mapped_reg); case 32: - return __raw_readl(reg); + return ioread32(mapped_reg); } BUG(); return 0; } -static void gpio_write_raw_reg(unsigned long reg, +static void gpio_write_raw_reg(void __iomem *mapped_reg, unsigned long reg_width, unsigned long data) { switch (reg_width) { case 8: - __raw_writeb(data, reg); + iowrite8(data, mapped_reg); return; case 16: - __raw_writew(data, reg); + iowrite16(data, mapped_reg); return; case 32: - __raw_writel(data, reg); + iowrite32(data, mapped_reg); return; } @@ -82,11 +151,12 @@ static void gpio_write_bit(struct pinmux_data_reg *dr, else clear_bit(pos, &dr->reg_shadow); - gpio_write_raw_reg(dr->reg, dr->reg_width, dr->reg_shadow); + gpio_write_raw_reg(dr->mapped_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) +static int gpio_read_reg(void __iomem *mapped_reg, unsigned long reg_width, + unsigned long field_width, unsigned long in_pos, + unsigned long reg) { unsigned long data, mask, pos; @@ -98,13 +168,13 @@ static int gpio_read_reg(unsigned long reg, unsigned long reg_width, "r_width = %ld, f_width = %ld\n", reg, pos, reg_width, field_width); - data = gpio_read_raw_reg(reg, reg_width); + data = gpio_read_raw_reg(mapped_reg, reg_width); return (data >> pos) & mask; } -static void gpio_write_reg(unsigned long reg, unsigned long reg_width, +static void gpio_write_reg(void __iomem *mapped_reg, unsigned long reg_width, unsigned long field_width, unsigned long in_pos, - unsigned long value) + unsigned long value, unsigned long reg) { unsigned long mask, pos; @@ -120,13 +190,13 @@ static void gpio_write_reg(unsigned long reg, unsigned long reg_width, switch (reg_width) { case 8: - __raw_writeb((__raw_readb(reg) & mask) | value, reg); + iowrite8((ioread8(mapped_reg) & mask) | value, mapped_reg); break; case 16: - __raw_writew((__raw_readw(reg) & mask) | value, reg); + iowrite16((ioread16(mapped_reg) & mask) | value, mapped_reg); break; case 32: - __raw_writel((__raw_readl(reg) & mask) | value, reg); + iowrite32((ioread32(mapped_reg) & mask) | value, mapped_reg); break; } } @@ -147,6 +217,8 @@ static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) if (!data_reg->reg_width) break; + data_reg->mapped_reg = pfc_phys_to_virt(gpioc, data_reg->reg); + for (n = 0; n < data_reg->reg_width; n++) { if (data_reg->enum_ids[n] == gpiop->enum_id) { gpiop->flags &= ~PINMUX_FLAG_DREG; @@ -179,7 +251,8 @@ static void setup_data_regs(struct pinmux_info *gpioc) if (!drp->reg_width) break; - drp->reg_shadow = gpio_read_raw_reg(drp->reg, drp->reg_width); + drp->reg_shadow = gpio_read_raw_reg(drp->mapped_reg, + drp->reg_width); k++; } } @@ -266,12 +339,16 @@ static void write_config_reg(struct pinmux_info *gpioc, int index) { unsigned long ncomb, pos, value; + void __iomem *mapped_reg; ncomb = 1 << crp->field_width; pos = index / ncomb; value = index % ncomb; - gpio_write_reg(crp->reg, crp->reg_width, crp->field_width, pos, value); + mapped_reg = pfc_phys_to_virt(gpioc, crp->reg); + + gpio_write_reg(mapped_reg, crp->reg_width, crp->field_width, + pos, value, crp->reg); } static int check_config_reg(struct pinmux_info *gpioc, @@ -279,13 +356,16 @@ static int check_config_reg(struct pinmux_info *gpioc, int index) { unsigned long ncomb, pos, value; + void __iomem *mapped_reg; 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) + mapped_reg = pfc_phys_to_virt(gpioc, crp->reg); + + if (gpio_read_reg(mapped_reg, crp->reg_width, + crp->field_width, pos, crp->reg) == value) return 0; return -1; @@ -564,7 +644,7 @@ static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) return -EINVAL; - return gpio_read_reg(dr->reg, dr->reg_width, 1, bit); + return gpio_read_reg(dr->mapped_reg, dr->reg_width, 1, bit, dr->reg); } static int sh_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -606,10 +686,15 @@ static int sh_gpio_to_irq(struct gpio_chip *chip, unsigned offset) int register_pinmux(struct pinmux_info *pip) { struct gpio_chip *chip = &pip->chip; + int ret; pr_info("%s handling gpio %d -> %d\n", pip->name, pip->first_gpio, pip->last_gpio); + ret = pfc_ioremap(pip); + if (ret < 0) + return ret; + setup_data_regs(pip); chip->request = sh_gpio_request; @@ -627,12 +712,16 @@ int register_pinmux(struct pinmux_info *pip) chip->base = pip->first_gpio; chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1; - return gpiochip_add(chip); + ret = gpiochip_add(chip); + if (ret < 0) + pfc_iounmap(pip); + + return ret; } int unregister_pinmux(struct pinmux_info *pip) { pr_info("%s deregistering\n", pip->name); - + pfc_iounmap(pip); return gpiochip_remove(&pip->chip); } diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index 8446789216e5..91666a58529d 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -55,6 +55,7 @@ struct pinmux_cfg_reg { struct pinmux_data_reg { unsigned long reg, reg_width, reg_shadow; pinmux_enum_t *enum_ids; + void __iomem *mapped_reg; }; #define PINMUX_DATA_REG(name, r, r_width) \ @@ -75,6 +76,12 @@ struct pinmux_range { pinmux_enum_t force; }; +struct pfc_window { + phys_addr_t phys; + void __iomem *virt; + unsigned long size; +}; + struct pinmux_info { char *name; pinmux_enum_t reserved_id; @@ -98,6 +105,10 @@ struct pinmux_info { struct pinmux_irq *gpio_irq; unsigned int gpio_irq_size; + struct resource *resource; + unsigned int num_resources; + struct pfc_window *window; + struct gpio_chip chip; }; -- cgit v1.2.3