diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-27 21:44:34 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-27 21:44:34 +0200 |
commit | 78c10e556ed904d5bfbd71e9cadd8ce8f25d6982 (patch) | |
tree | f73c802d60e81ff9e9fd2465eab096834d0227cd /drivers | |
parent | Merge branch 'for-4.2' of git://linux-nfs.org/~bfields/linux (diff) | |
parent | MIPS: spinlock: Adjust arch_spin_lock back-off time (diff) | |
download | linux-78c10e556ed904d5bfbd71e9cadd8ce8f25d6982.tar.xz linux-78c10e556ed904d5bfbd71e9cadd8ce8f25d6982.zip |
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
Pull MIPS updates from Ralf Baechle:
- Improvements to the tlb_dump code
- KVM fixes
- Add support for appended DTB
- Minor improvements to the R12000 support
- Minor improvements to the R12000 support
- Various platform improvments for BCM47xx
- The usual pile of minor cleanups
- A number of BPF fixes and improvments
- Some improvments to the support for R3000 and DECstations
- Some improvments to the ATH79 platform support
- A major patchset for the JZ4740 SOC adding support for the CI20 platform
- Add support for the Pistachio SOC
- Minor BMIPS/BCM63xx platform support improvments.
- Avoid "SYNC 0" as memory barrier when unlocking spinlocks
- Add support for the XWR-1750 board.
- Paul's __cpuinit/__cpuinitdata cleanups.
- New Malta CPU board support large memory so enable ZONE_DMA32.
* 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus: (131 commits)
MIPS: spinlock: Adjust arch_spin_lock back-off time
MIPS: asmmacro: Ensure 64-bit FP registers are used with MSA
MIPS: BCM47xx: Simplify handling SPROM revisions
MIPS: Cobalt Don't use module_init in non-modular MTD registration.
MIPS: BCM47xx: Move NVRAM driver to the drivers/firmware/
MIPS: use for_each_sg()
MIPS: BCM47xx: Don't select BCMA_HOST_PCI
MIPS: BCM47xx: Add helper variable for storing NVRAM length
MIPS: IRQ/IP27: Move IRQ allocation API to platform code.
MIPS: Replace smp_mb with release barrier function in unlocks.
MIPS: i8259: DT support
MIPS: Malta: Basic DT plumbing
MIPS: include errno.h for ENODEV in mips-cm.h
MIPS: Define GCR_GIC_STATUS register fields
MIPS: BPF: Introduce BPF ASM helpers
MIPS: BPF: Use BPF register names to describe the ABI
MIPS: BPF: Move register definition to the BPF header
MIPS: net: BPF: Replace RSIZE with SZREG
MIPS: BPF: Free up some callee-saved registers
MIPS: Xtalk: Update xwidget.h with known Xtalk device numbers
...
Diffstat (limited to 'drivers')
26 files changed, 3098 insertions, 8 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 5b6af6a9319f..8732e4c5bf3c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -24,7 +24,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o -obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_MACH_LOONGSON32) += clk-ls1x.o obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o @@ -51,6 +51,7 @@ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ obj-$(CONFIG_ARCH_HIP04) += hisilicon/ obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ obj-$(CONFIG_ARCH_MXC) += imx/ +obj-$(CONFIG_MACH_INGENIC) += ingenic/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ diff --git a/drivers/clk/ingenic/Makefile b/drivers/clk/ingenic/Makefile new file mode 100644 index 000000000000..cd47b0664c2b --- /dev/null +++ b/drivers/clk/ingenic/Makefile @@ -0,0 +1,3 @@ +obj-y += cgu.o +obj-$(CONFIG_MACH_JZ4740) += jz4740-cgu.o +obj-$(CONFIG_MACH_JZ4780) += jz4780-cgu.o diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c new file mode 100644 index 000000000000..b936cdd1a13c --- /dev/null +++ b/drivers/clk/ingenic/cgu.c @@ -0,0 +1,711 @@ +/* + * Ingenic SoC CGU driver + * + * Copyright (c) 2013-2015 Imagination Technologies + * Author: Paul Burton <paul.burton@imgtec.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/bitops.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/delay.h> +#include <linux/math64.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include "cgu.h" + +#define MHZ (1000 * 1000) + +/** + * ingenic_cgu_gate_get() - get the value of clock gate register bit + * @cgu: reference to the CGU whose registers should be read + * @info: info struct describing the gate bit + * + * Retrieves the state of the clock gate bit described by info. The + * caller must hold cgu->lock. + * + * Return: true if the gate bit is set, else false. + */ +static inline bool +ingenic_cgu_gate_get(struct ingenic_cgu *cgu, + const struct ingenic_cgu_gate_info *info) +{ + return readl(cgu->base + info->reg) & BIT(info->bit); +} + +/** + * ingenic_cgu_gate_set() - set the value of clock gate register bit + * @cgu: reference to the CGU whose registers should be modified + * @info: info struct describing the gate bit + * @val: non-zero to gate a clock, otherwise zero + * + * Sets the given gate bit in order to gate or ungate a clock. + * + * The caller must hold cgu->lock. + */ +static inline void +ingenic_cgu_gate_set(struct ingenic_cgu *cgu, + const struct ingenic_cgu_gate_info *info, bool val) +{ + u32 clkgr = readl(cgu->base + info->reg); + + if (val) + clkgr |= BIT(info->bit); + else + clkgr &= ~BIT(info->bit); + + writel(clkgr, cgu->base + info->reg); +} + +/* + * PLL operations + */ + +static unsigned long +ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info; + const struct ingenic_cgu_pll_info *pll_info; + unsigned m, n, od_enc, od; + bool bypass, enable; + unsigned long flags; + u32 ctl; + + clk_info = &cgu->clock_info[ingenic_clk->idx]; + BUG_ON(clk_info->type != CGU_CLK_PLL); + pll_info = &clk_info->pll; + + spin_lock_irqsave(&cgu->lock, flags); + ctl = readl(cgu->base + pll_info->reg); + spin_unlock_irqrestore(&cgu->lock, flags); + + m = (ctl >> pll_info->m_shift) & GENMASK(pll_info->m_bits - 1, 0); + m += pll_info->m_offset; + n = (ctl >> pll_info->n_shift) & GENMASK(pll_info->n_bits - 1, 0); + n += pll_info->n_offset; + od_enc = ctl >> pll_info->od_shift; + od_enc &= GENMASK(pll_info->od_bits - 1, 0); + bypass = !!(ctl & BIT(pll_info->bypass_bit)); + enable = !!(ctl & BIT(pll_info->enable_bit)); + + if (bypass) + return parent_rate; + + if (!enable) + return 0; + + for (od = 0; od < pll_info->od_max; od++) { + if (pll_info->od_encoding[od] == od_enc) + break; + } + BUG_ON(od == pll_info->od_max); + od++; + + return div_u64((u64)parent_rate * m, n * od); +} + +static unsigned long +ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info, + unsigned long rate, unsigned long parent_rate, + unsigned *pm, unsigned *pn, unsigned *pod) +{ + const struct ingenic_cgu_pll_info *pll_info; + unsigned m, n, od; + + pll_info = &clk_info->pll; + od = 1; + + /* + * The frequency after the input divider must be between 10 and 50 MHz. + * The highest divider yields the best resolution. + */ + n = parent_rate / (10 * MHZ); + n = min_t(unsigned, n, 1 << clk_info->pll.n_bits); + n = max_t(unsigned, n, pll_info->n_offset); + + m = (rate / MHZ) * od * n / (parent_rate / MHZ); + m = min_t(unsigned, m, 1 << clk_info->pll.m_bits); + m = max_t(unsigned, m, pll_info->m_offset); + + if (pm) + *pm = m; + if (pn) + *pn = n; + if (pod) + *pod = od; + + return div_u64((u64)parent_rate * m, n * od); +} + +static long +ingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate, + unsigned long *prate) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info; + + clk_info = &cgu->clock_info[ingenic_clk->idx]; + BUG_ON(clk_info->type != CGU_CLK_PLL); + + return ingenic_pll_calc(clk_info, req_rate, *prate, NULL, NULL, NULL); +} + +static int +ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate, + unsigned long parent_rate) +{ + const unsigned timeout = 100; + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info; + const struct ingenic_cgu_pll_info *pll_info; + unsigned long rate, flags; + unsigned m, n, od, i; + u32 ctl; + + clk_info = &cgu->clock_info[ingenic_clk->idx]; + BUG_ON(clk_info->type != CGU_CLK_PLL); + pll_info = &clk_info->pll; + + rate = ingenic_pll_calc(clk_info, req_rate, parent_rate, + &m, &n, &od); + if (rate != req_rate) + pr_info("ingenic-cgu: request '%s' rate %luHz, actual %luHz\n", + clk_info->name, req_rate, rate); + + spin_lock_irqsave(&cgu->lock, flags); + ctl = readl(cgu->base + pll_info->reg); + + ctl &= ~(GENMASK(pll_info->m_bits - 1, 0) << pll_info->m_shift); + ctl |= (m - pll_info->m_offset) << pll_info->m_shift; + + ctl &= ~(GENMASK(pll_info->n_bits - 1, 0) << pll_info->n_shift); + ctl |= (n - pll_info->n_offset) << pll_info->n_shift; + + ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift); + ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift; + + ctl &= ~BIT(pll_info->bypass_bit); + ctl |= BIT(pll_info->enable_bit); + + writel(ctl, cgu->base + pll_info->reg); + + /* wait for the PLL to stabilise */ + for (i = 0; i < timeout; i++) { + ctl = readl(cgu->base + pll_info->reg); + if (ctl & BIT(pll_info->stable_bit)) + break; + mdelay(1); + } + + spin_unlock_irqrestore(&cgu->lock, flags); + + if (i == timeout) + return -EBUSY; + + return 0; +} + +static const struct clk_ops ingenic_pll_ops = { + .recalc_rate = ingenic_pll_recalc_rate, + .round_rate = ingenic_pll_round_rate, + .set_rate = ingenic_pll_set_rate, +}; + +/* + * Operations for all non-PLL clocks + */ + +static u8 ingenic_clk_get_parent(struct clk_hw *hw) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info; + u32 reg; + u8 i, hw_idx, idx = 0; + + clk_info = &cgu->clock_info[ingenic_clk->idx]; + + if (clk_info->type & CGU_CLK_MUX) { + reg = readl(cgu->base + clk_info->mux.reg); + hw_idx = (reg >> clk_info->mux.shift) & + GENMASK(clk_info->mux.bits - 1, 0); + + /* + * Convert the hardware index to the parent index by skipping + * over any -1's in the parents array. + */ + for (i = 0; i < hw_idx; i++) { + if (clk_info->parents[i] != -1) + idx++; + } + } + + return idx; +} + +static int ingenic_clk_set_parent(struct clk_hw *hw, u8 idx) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info; + unsigned long flags; + u8 curr_idx, hw_idx, num_poss; + u32 reg, mask; + + clk_info = &cgu->clock_info[ingenic_clk->idx]; + + if (clk_info->type & CGU_CLK_MUX) { + /* + * Convert the parent index to the hardware index by adding + * 1 for any -1 in the parents array preceding the given + * index. That is, we want the index of idx'th entry in + * clk_info->parents which does not equal -1. + */ + hw_idx = curr_idx = 0; + num_poss = 1 << clk_info->mux.bits; + for (; hw_idx < num_poss; hw_idx++) { + if (clk_info->parents[hw_idx] == -1) + continue; + if (curr_idx == idx) + break; + curr_idx++; + } + + /* idx should always be a valid parent */ + BUG_ON(curr_idx != idx); + + mask = GENMASK(clk_info->mux.bits - 1, 0); + mask <<= clk_info->mux.shift; + + spin_lock_irqsave(&cgu->lock, flags); + + /* write the register */ + reg = readl(cgu->base + clk_info->mux.reg); + reg &= ~mask; + reg |= hw_idx << clk_info->mux.shift; + writel(reg, cgu->base + clk_info->mux.reg); + + spin_unlock_irqrestore(&cgu->lock, flags); + return 0; + } + + return idx ? -EINVAL : 0; +} + +static unsigned long +ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info; + unsigned long rate = parent_rate; + u32 div_reg, div; + + clk_info = &cgu->clock_info[ingenic_clk->idx]; + + if (clk_info->type & CGU_CLK_DIV) { + div_reg = readl(cgu->base + clk_info->div.reg); + div = (div_reg >> clk_info->div.shift) & + GENMASK(clk_info->div.bits - 1, 0); + div += 1; + + rate /= div; + } + + return rate; +} + +static unsigned +ingenic_clk_calc_div(const struct ingenic_cgu_clk_info *clk_info, + unsigned long parent_rate, unsigned long req_rate) +{ + unsigned div; + + /* calculate the divide */ + div = DIV_ROUND_UP(parent_rate, req_rate); + + /* and impose hardware constraints */ + div = min_t(unsigned, div, 1 << clk_info->div.bits); + div = max_t(unsigned, div, 1); + + return div; +} + +static long +ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate, + unsigned long *parent_rate) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info; + long rate = *parent_rate; + + clk_info = &cgu->clock_info[ingenic_clk->idx]; + + if (clk_info->type & CGU_CLK_DIV) + rate /= ingenic_clk_calc_div(clk_info, *parent_rate, req_rate); + else if (clk_info->type & CGU_CLK_FIXDIV) + rate /= clk_info->fixdiv.div; + + return rate; +} + +static int +ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate, + unsigned long parent_rate) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info; + const unsigned timeout = 100; + unsigned long rate, flags; + unsigned div, i; + u32 reg, mask; + int ret = 0; + + clk_info = &cgu->clock_info[ingenic_clk->idx]; + + if (clk_info->type & CGU_CLK_DIV) { + div = ingenic_clk_calc_div(clk_info, parent_rate, req_rate); + rate = parent_rate / div; + + if (rate != req_rate) + return -EINVAL; + + spin_lock_irqsave(&cgu->lock, flags); + reg = readl(cgu->base + clk_info->div.reg); + + /* update the divide */ + mask = GENMASK(clk_info->div.bits - 1, 0); + reg &= ~(mask << clk_info->div.shift); + reg |= (div - 1) << clk_info->div.shift; + + /* clear the stop bit */ + if (clk_info->div.stop_bit != -1) + reg &= ~BIT(clk_info->div.stop_bit); + + /* set the change enable bit */ + if (clk_info->div.ce_bit != -1) + reg |= BIT(clk_info->div.ce_bit); + + /* update the hardware */ + writel(reg, cgu->base + clk_info->div.reg); + + /* wait for the change to take effect */ + if (clk_info->div.busy_bit != -1) { + for (i = 0; i < timeout; i++) { + reg = readl(cgu->base + clk_info->div.reg); + if (!(reg & BIT(clk_info->div.busy_bit))) + break; + mdelay(1); + } + if (i == timeout) + ret = -EBUSY; + } + + spin_unlock_irqrestore(&cgu->lock, flags); + return ret; + } + + return -EINVAL; +} + +static int ingenic_clk_enable(struct clk_hw *hw) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info; + unsigned long flags; + + clk_info = &cgu->clock_info[ingenic_clk->idx]; + + if (clk_info->type & CGU_CLK_GATE) { + /* ungate the clock */ + spin_lock_irqsave(&cgu->lock, flags); + ingenic_cgu_gate_set(cgu, &clk_info->gate, false); + spin_unlock_irqrestore(&cgu->lock, flags); + } + + return 0; +} + +static void ingenic_clk_disable(struct clk_hw *hw) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info; + unsigned long flags; + + clk_info = &cgu->clock_info[ingenic_clk->idx]; + + if (clk_info->type & CGU_CLK_GATE) { + /* gate the clock */ + spin_lock_irqsave(&cgu->lock, flags); + ingenic_cgu_gate_set(cgu, &clk_info->gate, true); + spin_unlock_irqrestore(&cgu->lock, flags); + } +} + +static int ingenic_clk_is_enabled(struct clk_hw *hw) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info; + unsigned long flags; + int enabled = 1; + + clk_info = &cgu->clock_info[ingenic_clk->idx]; + + if (clk_info->type & CGU_CLK_GATE) { + spin_lock_irqsave(&cgu->lock, flags); + enabled = !ingenic_cgu_gate_get(cgu, &clk_info->gate); + spin_unlock_irqrestore(&cgu->lock, flags); + } + + return enabled; +} + +static const struct clk_ops ingenic_clk_ops = { + .get_parent = ingenic_clk_get_parent, + .set_parent = ingenic_clk_set_parent, + + .recalc_rate = ingenic_clk_recalc_rate, + .round_rate = ingenic_clk_round_rate, + .set_rate = ingenic_clk_set_rate, + + .enable = ingenic_clk_enable, + .disable = ingenic_clk_disable, + .is_enabled = ingenic_clk_is_enabled, +}; + +/* + * Setup functions. + */ + +static int ingenic_register_clock(struct ingenic_cgu *cgu, unsigned idx) +{ + const struct ingenic_cgu_clk_info *clk_info = &cgu->clock_info[idx]; + struct clk_init_data clk_init; + struct ingenic_clk *ingenic_clk = NULL; + struct clk *clk, *parent; + const char *parent_names[4]; + unsigned caps, i, num_possible; + int err = -EINVAL; + + BUILD_BUG_ON(ARRAY_SIZE(clk_info->parents) > ARRAY_SIZE(parent_names)); + + if (clk_info->type == CGU_CLK_EXT) { + clk = of_clk_get_by_name(cgu->np, clk_info->name); + if (IS_ERR(clk)) { + pr_err("%s: no external clock '%s' provided\n", + __func__, clk_info->name); + err = -ENODEV; + goto out; + } + err = clk_register_clkdev(clk, clk_info->name, NULL); + if (err) { + clk_put(clk); + goto out; + } + cgu->clocks.clks[idx] = clk; + return 0; + } + + if (!clk_info->type) { + pr_err("%s: no clock type specified for '%s'\n", __func__, + clk_info->name); + goto out; + } + + ingenic_clk = kzalloc(sizeof(*ingenic_clk), GFP_KERNEL); + if (!ingenic_clk) { + err = -ENOMEM; + goto out; + } + + ingenic_clk->hw.init = &clk_init; + ingenic_clk->cgu = cgu; + ingenic_clk->idx = idx; + + clk_init.name = clk_info->name; + clk_init.flags = 0; + clk_init.parent_names = parent_names; + + caps = clk_info->type; + + if (caps & (CGU_CLK_MUX | CGU_CLK_CUSTOM)) { + clk_init.num_parents = 0; + + if (caps & CGU_CLK_MUX) + num_possible = 1 << clk_info->mux.bits; + else + num_possible = ARRAY_SIZE(clk_info->parents); + + for (i = 0; i < num_possible; i++) { + if (clk_info->parents[i] == -1) + continue; + + parent = cgu->clocks.clks[clk_info->parents[i]]; + parent_names[clk_init.num_parents] = + __clk_get_name(parent); + clk_init.num_parents++; + } + + BUG_ON(!clk_init.num_parents); + BUG_ON(clk_init.num_parents > ARRAY_SIZE(parent_names)); + } else { + BUG_ON(clk_info->parents[0] == -1); + clk_init.num_parents = 1; + parent = cgu->clocks.clks[clk_info->parents[0]]; + parent_names[0] = __clk_get_name(parent); + } + + if (caps & CGU_CLK_CUSTOM) { + clk_init.ops = clk_info->custom.clk_ops; + + caps &= ~CGU_CLK_CUSTOM; + + if (caps) { + pr_err("%s: custom clock may not be combined with type 0x%x\n", + __func__, caps); + goto out; + } + } else if (caps & CGU_CLK_PLL) { + clk_init.ops = &ingenic_pll_ops; + + caps &= ~CGU_CLK_PLL; + + if (caps) { + pr_err("%s: PLL may not be combined with type 0x%x\n", + __func__, caps); + goto out; + } + } else { + clk_init.ops = &ingenic_clk_ops; + } + + /* nothing to do for gates or fixed dividers */ + caps &= ~(CGU_CLK_GATE | CGU_CLK_FIXDIV); + + if (caps & CGU_CLK_MUX) { + if (!(caps & CGU_CLK_MUX_GLITCHFREE)) + clk_init.flags |= CLK_SET_PARENT_GATE; + + caps &= ~(CGU_CLK_MUX | CGU_CLK_MUX_GLITCHFREE); + } + + if (caps & CGU_CLK_DIV) { + caps &= ~CGU_CLK_DIV; + } else { + /* pass rate changes to the parent clock */ + clk_init.flags |= CLK_SET_RATE_PARENT; + } + + if (caps) { + pr_err("%s: unknown clock type 0x%x\n", __func__, caps); + goto out; + } + + clk = clk_register(NULL, &ingenic_clk->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock '%s'\n", __func__, + clk_info->name); + err = PTR_ERR(clk); + goto out; + } + + err = clk_register_clkdev(clk, clk_info->name, NULL); + if (err) + goto out; + + cgu->clocks.clks[idx] = clk; +out: + if (err) + kfree(ingenic_clk); + return err; +} + +struct ingenic_cgu * +ingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info, + unsigned num_clocks, struct device_node *np) +{ + struct ingenic_cgu *cgu; + + cgu = kzalloc(sizeof(*cgu), GFP_KERNEL); + if (!cgu) + goto err_out; + + cgu->base = of_iomap(np, 0); + if (!cgu->base) { + pr_err("%s: failed to map CGU registers\n", __func__); + goto err_out_free; + } + + cgu->np = np; + cgu->clock_info = clock_info; + cgu->clocks.clk_num = num_clocks; + + spin_lock_init(&cgu->lock); + + return cgu; + +err_out_free: + kfree(cgu); +err_out: + return NULL; +} + +int ingenic_cgu_register_clocks(struct ingenic_cgu *cgu) +{ + unsigned i; + int err; + + cgu->clocks.clks = kcalloc(cgu->clocks.clk_num, sizeof(struct clk *), + GFP_KERNEL); + if (!cgu->clocks.clks) { + err = -ENOMEM; + goto err_out; + } + + for (i = 0; i < cgu->clocks.clk_num; i++) { + err = ingenic_register_clock(cgu, i); + if (err) + goto err_out_unregister; + } + + err = of_clk_add_provider(cgu->np, of_clk_src_onecell_get, + &cgu->clocks); + if (err) + goto err_out_unregister; + + return 0; + +err_out_unregister: + for (i = 0; i < cgu->clocks.clk_num; i++) { + if (!cgu->clocks.clks[i]) + continue; + if (cgu->clock_info[i].type & CGU_CLK_EXT) + clk_put(cgu->clocks.clks[i]); + else + clk_unregister(cgu->clocks.clks[i]); + } + kfree(cgu->clocks.clks); +err_out: + return err; +} diff --git a/drivers/clk/ingenic/cgu.h b/drivers/clk/ingenic/cgu.h new file mode 100644 index 000000000000..99347e2b97e8 --- /dev/null +++ b/drivers/clk/ingenic/cgu.h @@ -0,0 +1,223 @@ +/* + * Ingenic SoC CGU driver + * + * Copyright (c) 2013-2015 Imagination Technologies + * Author: Paul Burton <paul.burton@imgtec.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DRIVERS_CLK_INGENIC_CGU_H__ +#define __DRIVERS_CLK_INGENIC_CGU_H__ + +#include <linux/bitops.h> +#include <linux/of.h> +#include <linux/spinlock.h> + +/** + * struct ingenic_cgu_pll_info - information about a PLL + * @reg: the offset of the PLL's control register within the CGU + * @m_shift: the number of bits to shift the multiplier value by (ie. the + * index of the lowest bit of the multiplier value in the PLL's + * control register) + * @m_bits: the size of the multiplier field in bits + * @m_offset: the multiplier value which encodes to 0 in the PLL's control + * register + * @n_shift: the number of bits to shift the divider value by (ie. the + * index of the lowest bit of the divider value in the PLL's + * control register) + * @n_bits: the size of the divider field in bits + * @n_offset: the divider value which encodes to 0 in the PLL's control + * register + * @od_shift: the number of bits to shift the post-VCO divider value by (ie. + * the index of the lowest bit of the post-VCO divider value in + * the PLL's control register) + * @od_bits: the size of the post-VCO divider field in bits + * @od_max: the maximum post-VCO divider value + * @od_encoding: a pointer to an array mapping post-VCO divider values to + * their encoded values in the PLL control register, or -1 for + * unsupported values + * @bypass_bit: the index of the bypass bit in the PLL control register + * @enable_bit: the index of the enable bit in the PLL control register + * @stable_bit: the index of the stable bit in the PLL control register + */ +struct ingenic_cgu_pll_info { + unsigned reg; + const s8 *od_encoding; + u8 m_shift, m_bits, m_offset; + u8 n_shift, n_bits, n_offset; + u8 od_shift, od_bits, od_max; + u8 bypass_bit; + u8 enable_bit; + u8 stable_bit; +}; + +/** + * struct ingenic_cgu_mux_info - information about a clock mux + * @reg: offset of the mux control register within the CGU + * @shift: number of bits to shift the mux value by (ie. the index of + * the lowest bit of the mux value within its control register) + * @bits: the size of the mux value in bits + */ +struct ingenic_cgu_mux_info { + unsigned reg; + u8 shift; + u8 bits; +}; + +/** + * struct ingenic_cgu_div_info - information about a divider + * @reg: offset of the divider control register within the CGU + * @shift: number of bits to shift the divide value by (ie. the index of + * the lowest bit of the divide value within its control register) + * @bits: the size of the divide value in bits + * @ce_bit: the index of the change enable bit within reg, or -1 if there + * isn't one + * @busy_bit: the index of the busy bit within reg, or -1 if there isn't one + * @stop_bit: the index of the stop bit within reg, or -1 if there isn't one + */ +struct ingenic_cgu_div_info { + unsigned reg; + u8 shift; + u8 bits; + s8 ce_bit; + s8 busy_bit; + s8 stop_bit; +}; + +/** + * struct ingenic_cgu_fixdiv_info - information about a fixed divider + * @div: the divider applied to the parent clock + */ +struct ingenic_cgu_fixdiv_info { + unsigned div; +}; + +/** + * struct ingenic_cgu_gate_info - information about a clock gate + * @reg: offset of the gate control register within the CGU + * @bit: offset of the bit in the register that controls the gate + */ +struct ingenic_cgu_gate_info { + unsigned reg; + u8 bit; +}; + +/** + * struct ingenic_cgu_custom_info - information about a custom (SoC) clock + * @clk_ops: custom clock operation callbacks + */ +struct ingenic_cgu_custom_info { + struct clk_ops *clk_ops; +}; + +/** + * struct ingenic_cgu_clk_info - information about a clock + * @name: name of the clock + * @type: a bitmask formed from CGU_CLK_* values + * @parents: an array of the indices of potential parents of this clock + * within the clock_info array of the CGU, or -1 in entries + * which correspond to no valid parent + * @pll: information valid if type includes CGU_CLK_PLL + * @gate: information valid if type includes CGU_CLK_GATE + * @mux: information valid if type includes CGU_CLK_MUX + * @div: information valid if type includes CGU_CLK_DIV + * @fixdiv: information valid if type includes CGU_CLK_FIXDIV + * @custom: information valid if type includes CGU_CLK_CUSTOM + */ +struct ingenic_cgu_clk_info { + const char *name; + + enum { + CGU_CLK_NONE = 0, + CGU_CLK_EXT = BIT(0), + CGU_CLK_PLL = BIT(1), + CGU_CLK_GATE = BIT(2), + CGU_CLK_MUX = BIT(3), + CGU_CLK_MUX_GLITCHFREE = BIT(4), + CGU_CLK_DIV = BIT(5), + CGU_CLK_FIXDIV = BIT(6), + CGU_CLK_CUSTOM = BIT(7), + } type; + + int parents[4]; + + union { + struct ingenic_cgu_pll_info pll; + + struct { + struct ingenic_cgu_gate_info gate; + struct ingenic_cgu_mux_info mux; + struct ingenic_cgu_div_info div; + struct ingenic_cgu_fixdiv_info fixdiv; + }; + + struct ingenic_cgu_custom_info custom; + }; +}; + +/** + * struct ingenic_cgu - data about the CGU + * @np: the device tree node that caused the CGU to be probed + * @base: the ioremap'ed base address of the CGU registers + * @clock_info: an array containing information about implemented clocks + * @clocks: used to provide clocks to DT, allows lookup of struct clk* + * @lock: lock to be held whilst manipulating CGU registers + */ +struct ingenic_cgu { + struct device_node *np; + void __iomem *base; + + const struct ingenic_cgu_clk_info *clock_info; + struct clk_onecell_data clocks; + + spinlock_t lock; +}; + +/** + * struct ingenic_clk - private data for a clock + * @hw: see Documentation/clk.txt + * @cgu: a pointer to the CGU data + * @idx: the index of this clock in cgu->clock_info + */ +struct ingenic_clk { + struct clk_hw hw; + struct ingenic_cgu *cgu; + unsigned idx; +}; + +#define to_ingenic_clk(_hw) container_of(_hw, struct ingenic_clk, hw) + +/** + * ingenic_cgu_new() - create a new CGU instance + * @clock_info: an array of clock information structures describing the clocks + * which are implemented by the CGU + * @num_clocks: the number of entries in clock_info + * @np: the device tree node which causes this CGU to be probed + * + * Return: a pointer to the CGU instance if initialisation is successful, + * otherwise NULL. + */ +struct ingenic_cgu * +ingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info, + unsigned num_clocks, struct device_node *np); + +/** + * ingenic_cgu_register_clocks() - Registers the clocks + * @cgu: pointer to cgu data + * + * Register the clocks described by the CGU with the common clock framework. + * + * Return: 0 on success or -errno if unsuccesful. + */ +int ingenic_cgu_register_clocks(struct ingenic_cgu *cgu); + +#endif /* __DRIVERS_CLK_INGENIC_CGU_H__ */ diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c new file mode 100644 index 000000000000..305a26c2a800 --- /dev/null +++ b/drivers/clk/ingenic/jz4740-cgu.c @@ -0,0 +1,303 @@ +/* + * Ingenic JZ4740 SoC CGU driver + * + * Copyright (c) 2015 Imagination Technologies + * Author: Paul Burton <paul.burton@imgtec.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <dt-bindings/clock/jz4740-cgu.h> +#include <asm/mach-jz4740/clock.h> +#include "cgu.h" + +/* CGU register offsets */ +#define CGU_REG_CPCCR 0x00 +#define CGU_REG_LCR 0x04 +#define CGU_REG_CPPCR 0x10 +#define CGU_REG_CLKGR 0x20 +#define CGU_REG_SCR 0x24 +#define CGU_REG_I2SCDR 0x60 +#define CGU_REG_LPCDR 0x64 +#define CGU_REG_MSCCDR 0x68 +#define CGU_REG_UHCCDR 0x6c +#define CGU_REG_SSICDR 0x74 + +/* bits within a PLL control register */ +#define PLLCTL_M_SHIFT 23 +#define PLLCTL_M_MASK (0x1ff << PLLCTL_M_SHIFT) +#define PLLCTL_N_SHIFT 18 +#define PLLCTL_N_MASK (0x1f << PLLCTL_N_SHIFT) +#define PLLCTL_OD_SHIFT 16 +#define PLLCTL_OD_MASK (0x3 << PLLCTL_OD_SHIFT) +#define PLLCTL_STABLE (1 << 10) +#define PLLCTL_BYPASS (1 << 9) +#define PLLCTL_ENABLE (1 << 8) + +/* bits within the LCR register */ +#define LCR_SLEEP (1 << 0) + +/* bits within the CLKGR register */ +#define CLKGR_UDC (1 << 11) + +static struct ingenic_cgu *cgu; + +static const s8 pll_od_encoding[4] = { + 0x0, 0x1, -1, 0x3, +}; + +static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { + + /* External clocks */ + + [JZ4740_CLK_EXT] = { "ext", CGU_CLK_EXT }, + [JZ4740_CLK_RTC] = { "rtc", CGU_CLK_EXT }, + + [JZ4740_CLK_PLL] = { + "pll", CGU_CLK_PLL, + .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, + .pll = { + .reg = CGU_REG_CPPCR, + .m_shift = 23, + .m_bits = 9, + .m_offset = 2, + .n_shift = 18, + .n_bits = 5, + .n_offset = 2, + .od_shift = 16, + .od_bits = 2, + .od_max = 4, + .od_encoding = pll_od_encoding, + .stable_bit = 10, + .bypass_bit = 9, + .enable_bit = 8, + }, + }, + + /* Muxes & dividers */ + + [JZ4740_CLK_PLL_HALF] = { + "pll half", CGU_CLK_DIV, + .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 21, 1, -1, -1, -1 }, + }, + + [JZ4740_CLK_CCLK] = { + "cclk", CGU_CLK_DIV, + .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 0, 4, 22, -1, -1 }, + }, + + [JZ4740_CLK_HCLK] = { + "hclk", CGU_CLK_DIV, + .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 4, 4, 22, -1, -1 }, + }, + + [JZ4740_CLK_PCLK] = { + "pclk", CGU_CLK_DIV, + .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 8, 4, 22, -1, -1 }, + }, + + [JZ4740_CLK_MCLK] = { + "mclk", CGU_CLK_DIV, + .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 12, 4, 22, -1, -1 }, + }, + + [JZ4740_CLK_LCD] = { + "lcd", CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 16, 5, 22, -1, -1 }, + .gate = { CGU_REG_CLKGR, 10 }, + }, + + [JZ4740_CLK_LCD_PCLK] = { + "lcd_pclk", CGU_CLK_DIV, + .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, + .div = { CGU_REG_LPCDR, 0, 11, -1, -1, -1 }, + }, + + [JZ4740_CLK_I2S] = { + "i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, + .mux = { CGU_REG_CPCCR, 31, 1 }, + .div = { CGU_REG_I2SCDR, 0, 8, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 6 }, + }, + + [JZ4740_CLK_SPI] = { + "spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL, -1, -1 }, + .mux = { CGU_REG_SSICDR, 31, 1 }, + .div = { CGU_REG_SSICDR, 0, 4, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 4 }, + }, + + [JZ4740_CLK_MMC] = { + "mmc", CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, + .div = { CGU_REG_MSCCDR, 0, 5, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 7 }, + }, + + [JZ4740_CLK_UHC] = { + "uhc", CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, + .div = { CGU_REG_UHCCDR, 0, 4, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 14 }, + }, + + [JZ4740_CLK_UDC] = { + "udc", CGU_CLK_MUX | CGU_CLK_DIV, + .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, + .mux = { CGU_REG_CPCCR, 29, 1 }, + .div = { CGU_REG_CPCCR, 23, 6, -1, -1, -1 }, + .gate = { CGU_REG_SCR, 6 }, + }, + + /* Gate-only clocks */ + + [JZ4740_CLK_UART0] = { + "uart0", CGU_CLK_GATE, + .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 0 }, + }, + + [JZ4740_CLK_UART1] = { + "uart1", CGU_CLK_GATE, + .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 15 }, + }, + + [JZ4740_CLK_DMA] = { + "dma", CGU_CLK_GATE, + .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 12 }, + }, + + [JZ4740_CLK_IPU] = { + "ipu", CGU_CLK_GATE, + .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 13 }, + }, + + [JZ4740_CLK_ADC] = { + "adc", CGU_CLK_GATE, + .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 8 }, + }, + + [JZ4740_CLK_I2C] = { + "i2c", CGU_CLK_GATE, + .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 3 }, + }, + + [JZ4740_CLK_AIC] = { + "aic", CGU_CLK_GATE, + .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 5 }, + }, +}; + +static void __init jz4740_cgu_init(struct device_node *np) +{ + int retval; + + cgu = ingenic_cgu_new(jz4740_cgu_clocks, + ARRAY_SIZE(jz4740_cgu_clocks), np); + if (!cgu) { + pr_err("%s: failed to initialise CGU\n", __func__); + return; + } + + retval = ingenic_cgu_register_clocks(cgu); + if (retval) + pr_err("%s: failed to register CGU Clocks\n", __func__); +} +CLK_OF_DECLARE(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init); + +void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode) +{ + uint32_t lcr = readl(cgu->base + CGU_REG_LCR); + + switch (mode) { + case JZ4740_WAIT_MODE_IDLE: + lcr &= ~LCR_SLEEP; + break; + + case JZ4740_WAIT_MODE_SLEEP: + lcr |= LCR_SLEEP; + break; + } + + writel(lcr, cgu->base + CGU_REG_LCR); +} + +void jz4740_clock_udc_disable_auto_suspend(void) +{ + uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); + + clkgr &= ~CLKGR_UDC; + writel(clkgr, cgu->base + CGU_REG_CLKGR); +} +EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend); + +void jz4740_clock_udc_enable_auto_suspend(void) +{ + uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); + + clkgr |= CLKGR_UDC; + writel(clkgr, cgu->base + CGU_REG_CLKGR); +} +EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend); + +#define JZ_CLOCK_GATE_UART0 BIT(0) +#define JZ_CLOCK_GATE_TCU BIT(1) +#define JZ_CLOCK_GATE_DMAC BIT(12) + +void jz4740_clock_suspend(void) +{ + uint32_t clkgr, cppcr; + + clkgr = readl(cgu->base + CGU_REG_CLKGR); + clkgr |= JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0; + writel(clkgr, cgu->base + CGU_REG_CLKGR); + + cppcr = readl(cgu->base + CGU_REG_CPPCR); + cppcr &= ~BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); + writel(cppcr, cgu->base + CGU_REG_CPPCR); +} + +void jz4740_clock_resume(void) +{ + uint32_t clkgr, cppcr, stable; + + cppcr = readl(cgu->base + CGU_REG_CPPCR); + cppcr |= BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); + writel(cppcr, cgu->base + CGU_REG_CPPCR); + + stable = BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.stable_bit); + do { + cppcr = readl(cgu->base + CGU_REG_CPPCR); + } while (!(cppcr & stable)); + + clkgr = readl(cgu->base + CGU_REG_CLKGR); + clkgr &= ~JZ_CLOCK_GATE_TCU; + clkgr &= ~JZ_CLOCK_GATE_DMAC; + clkgr &= ~JZ_CLOCK_GATE_UART0; + writel(clkgr, cgu->base + CGU_REG_CLKGR); +} diff --git a/drivers/clk/ingenic/jz4780-cgu.c b/drivers/clk/ingenic/jz4780-cgu.c new file mode 100644 index 000000000000..431f962300b6 --- /dev/null +++ b/drivers/clk/ingenic/jz4780-cgu.c @@ -0,0 +1,733 @@ +/* + * Ingenic JZ4780 SoC CGU driver + * + * Copyright (c) 2013-2015 Imagination Technologies + * Author: Paul Burton <paul.burton@imgtec.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <dt-bindings/clock/jz4780-cgu.h> +#include "cgu.h" + +/* CGU register offsets */ +#define CGU_REG_CLOCKCONTROL 0x00 +#define CGU_REG_PLLCONTROL 0x0c +#define CGU_REG_APLL 0x10 +#define CGU_REG_MPLL 0x14 +#define CGU_REG_EPLL 0x18 +#define CGU_REG_VPLL 0x1c +#define CGU_REG_CLKGR0 0x20 +#define CGU_REG_OPCR 0x24 +#define CGU_REG_CLKGR1 0x28 +#define CGU_REG_DDRCDR 0x2c +#define CGU_REG_VPUCDR 0x30 +#define CGU_REG_USBPCR 0x3c +#define CGU_REG_USBRDT 0x40 +#define CGU_REG_USBVBFIL 0x44 +#define CGU_REG_USBPCR1 0x48 +#define CGU_REG_LP0CDR 0x54 +#define CGU_REG_I2SCDR 0x60 +#define CGU_REG_LP1CDR 0x64 +#define CGU_REG_MSC0CDR 0x68 +#define CGU_REG_UHCCDR 0x6c +#define CGU_REG_SSICDR 0x74 +#define CGU_REG_CIMCDR 0x7c +#define CGU_REG_PCMCDR 0x84 +#define CGU_REG_GPUCDR 0x88 +#define CGU_REG_HDMICDR 0x8c +#define CGU_REG_MSC1CDR 0xa4 +#define CGU_REG_MSC2CDR 0xa8 +#define CGU_REG_BCHCDR 0xac +#define CGU_REG_CLOCKSTATUS 0xd4 + +/* bits within the OPCR register */ +#define OPCR_SPENDN0 (1 << 7) +#define OPCR_SPENDN1 (1 << 6) + +/* bits within the USBPCR register */ +#define USBPCR_USB_MODE BIT(31) +#define USBPCR_IDPULLUP_MASK (0x3 << 28) +#define USBPCR_COMMONONN BIT(25) +#define USBPCR_VBUSVLDEXT BIT(24) +#define USBPCR_VBUSVLDEXTSEL BIT(23) +#define USBPCR_POR BIT(22) +#define USBPCR_OTG_DISABLE BIT(20) +#define USBPCR_COMPDISTUNE_MASK (0x7 << 17) +#define USBPCR_OTGTUNE_MASK (0x7 << 14) +#define USBPCR_SQRXTUNE_MASK (0x7 << 11) +#define USBPCR_TXFSLSTUNE_MASK (0xf << 7) +#define USBPCR_TXPREEMPHTUNE BIT(6) +#define USBPCR_TXHSXVTUNE_MASK (0x3 << 4) +#define USBPCR_TXVREFTUNE_MASK 0xf + +/* bits within the USBPCR1 register */ +#define USBPCR1_REFCLKSEL_SHIFT 26 +#define USBPCR1_REFCLKSEL_MASK (0x3 << USBPCR1_REFCLKSEL_SHIFT) +#define USBPCR1_REFCLKSEL_CORE (0x2 << USBPCR1_REFCLKSEL_SHIFT) +#define USBPCR1_REFCLKDIV_SHIFT 24 +#define USBPCR1_REFCLKDIV_MASK (0x3 << USBPCR1_REFCLKDIV_SHIFT) +#define USBPCR1_REFCLKDIV_19_2 (0x3 << USBPCR1_REFCLKDIV_SHIFT) +#define USBPCR1_REFCLKDIV_48 (0x2 << USBPCR1_REFCLKDIV_SHIFT) +#define USBPCR1_REFCLKDIV_24 (0x1 << USBPCR1_REFCLKDIV_SHIFT) +#define USBPCR1_REFCLKDIV_12 (0x0 << USBPCR1_REFCLKDIV_SHIFT) +#define USBPCR1_USB_SEL BIT(28) +#define USBPCR1_WORD_IF0 BIT(19) +#define USBPCR1_WORD_IF1 BIT(18) + +/* bits within the USBRDT register */ +#define USBRDT_VBFIL_LD_EN BIT(25) +#define USBRDT_USBRDT_MASK 0x7fffff + +/* bits within the USBVBFIL register */ +#define USBVBFIL_IDDIGFIL_SHIFT 16 +#define USBVBFIL_IDDIGFIL_MASK (0xffff << USBVBFIL_IDDIGFIL_SHIFT) +#define USBVBFIL_USBVBFIL_MASK (0xffff) + +static struct ingenic_cgu *cgu; + +static u8 jz4780_otg_phy_get_parent(struct clk_hw *hw) +{ + /* we only use CLKCORE, revisit if that ever changes */ + return 0; +} + +static int jz4780_otg_phy_set_parent(struct clk_hw *hw, u8 idx) +{ + unsigned long flags; + u32 usbpcr1; + + if (idx > 0) + return -EINVAL; + + spin_lock_irqsave(&cgu->lock, flags); + + usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1); + usbpcr1 &= ~USBPCR1_REFCLKSEL_MASK; + /* we only use CLKCORE */ + usbpcr1 |= USBPCR1_REFCLKSEL_CORE; + writel(usbpcr1, cgu->base + CGU_REG_USBPCR1); + + spin_unlock_irqrestore(&cgu->lock, flags); + return 0; +} + +static unsigned long jz4780_otg_phy_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 usbpcr1; + unsigned refclk_div; + + usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1); + refclk_div = usbpcr1 & USBPCR1_REFCLKDIV_MASK; + + switch (refclk_div) { + case USBPCR1_REFCLKDIV_12: + return 12000000; + + case USBPCR1_REFCLKDIV_24: + return 24000000; + + case USBPCR1_REFCLKDIV_48: + return 48000000; + + case USBPCR1_REFCLKDIV_19_2: + return 19200000; + } + + BUG(); + return parent_rate; +} + +static long jz4780_otg_phy_round_rate(struct clk_hw *hw, unsigned long req_rate, + unsigned long *parent_rate) +{ + if (req_rate < 15600000) + return 12000000; + + if (req_rate < 21600000) + return 19200000; + + if (req_rate < 36000000) + return 24000000; + + return 48000000; +} + +static int jz4780_otg_phy_set_rate(struct clk_hw *hw, unsigned long req_rate, + unsigned long parent_rate) +{ + unsigned long flags; + u32 usbpcr1, div_bits; + + switch (req_rate) { + case 12000000: + div_bits = USBPCR1_REFCLKDIV_12; + break; + + case 19200000: + div_bits = USBPCR1_REFCLKDIV_19_2; + break; + + case 24000000: + div_bits = USBPCR1_REFCLKDIV_24; + break; + + case 48000000: + div_bits = USBPCR1_REFCLKDIV_48; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&cgu->lock, flags); + + usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1); + usbpcr1 &= ~USBPCR1_REFCLKDIV_MASK; + usbpcr1 |= div_bits; + writel(usbpcr1, cgu->base + CGU_REG_USBPCR1); + + spin_unlock_irqrestore(&cgu->lock, flags); + return 0; +} + +static struct clk_ops jz4780_otg_phy_ops = { + .get_parent = jz4780_otg_phy_get_parent, + .set_parent = jz4780_otg_phy_set_parent, + + .recalc_rate = jz4780_otg_phy_recalc_rate, + .round_rate = jz4780_otg_phy_round_rate, + .set_rate = jz4780_otg_phy_set_rate, +}; + +static const s8 pll_od_encoding[16] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, +}; + +static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { + + /* External clocks */ + + [JZ4780_CLK_EXCLK] = { "ext", CGU_CLK_EXT }, + [JZ4780_CLK_RTCLK] = { "rtc", CGU_CLK_EXT }, + + /* PLLs */ + +#define DEF_PLL(name) { \ + .reg = CGU_REG_ ## name, \ + .m_shift = 19, \ + .m_bits = 13, \ + .m_offset = 1, \ + .n_shift = 13, \ + .n_bits = 6, \ + .n_offset = 1, \ + .od_shift = 9, \ + .od_bits = 4, \ + .od_max = 16, \ + .od_encoding = pll_od_encoding, \ + .stable_bit = 6, \ + .bypass_bit = 1, \ + .enable_bit = 0, \ +} + + [JZ4780_CLK_APLL] = { + "apll", CGU_CLK_PLL, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .pll = DEF_PLL(APLL), + }, + + [JZ4780_CLK_MPLL] = { + "mpll", CGU_CLK_PLL, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .pll = DEF_PLL(MPLL), + }, + + [JZ4780_CLK_EPLL] = { + "epll", CGU_CLK_PLL, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .pll = DEF_PLL(EPLL), + }, + + [JZ4780_CLK_VPLL] = { + "vpll", CGU_CLK_PLL, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .pll = DEF_PLL(VPLL), + }, + +#undef DEF_PLL + + /* Custom (SoC-specific) OTG PHY */ + + [JZ4780_CLK_OTGPHY] = { + "otg_phy", CGU_CLK_CUSTOM, + .parents = { -1, -1, JZ4780_CLK_EXCLK, -1 }, + .custom = { &jz4780_otg_phy_ops }, + }, + + /* Muxes & dividers */ + + [JZ4780_CLK_SCLKA] = { + "sclk_a", CGU_CLK_MUX, + .parents = { -1, JZ4780_CLK_APLL, JZ4780_CLK_EXCLK, + JZ4780_CLK_RTCLK }, + .mux = { CGU_REG_CLOCKCONTROL, 30, 2 }, + }, + + [JZ4780_CLK_CPUMUX] = { + "cpumux", CGU_CLK_MUX, + .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, + JZ4780_CLK_EPLL }, + .mux = { CGU_REG_CLOCKCONTROL, 28, 2 }, + }, + + [JZ4780_CLK_CPU] = { + "cpu", CGU_CLK_DIV, + .parents = { JZ4780_CLK_CPUMUX, -1, -1, -1 }, + .div = { CGU_REG_CLOCKCONTROL, 0, 4, 22, -1, -1 }, + }, + + [JZ4780_CLK_L2CACHE] = { + "l2cache", CGU_CLK_DIV, + .parents = { JZ4780_CLK_CPUMUX, -1, -1, -1 }, + .div = { CGU_REG_CLOCKCONTROL, 4, 4, -1, -1, -1 }, + }, + + [JZ4780_CLK_AHB0] = { + "ahb0", CGU_CLK_MUX | CGU_CLK_DIV, + .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, + JZ4780_CLK_EPLL }, + .mux = { CGU_REG_CLOCKCONTROL, 26, 2 }, + .div = { CGU_REG_CLOCKCONTROL, 8, 4, 21, -1, -1 }, + }, + + [JZ4780_CLK_AHB2PMUX] = { + "ahb2_apb_mux", CGU_CLK_MUX, + .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, + JZ4780_CLK_RTCLK }, + .mux = { CGU_REG_CLOCKCONTROL, 24, 2 }, + }, + + [JZ4780_CLK_AHB2] = { + "ahb2", CGU_CLK_DIV, + .parents = { JZ4780_CLK_AHB2PMUX, -1, -1, -1 }, + .div = { CGU_REG_CLOCKCONTROL, 12, 4, 20, -1, -1 }, + }, + + [JZ4780_CLK_PCLK] = { + "pclk", CGU_CLK_DIV, + .parents = { JZ4780_CLK_AHB2PMUX, -1, -1, -1 }, + .div = { CGU_REG_CLOCKCONTROL, 16, 4, 20, -1, -1 }, + }, + + [JZ4780_CLK_DDR] = { + "ddr", CGU_CLK_MUX | CGU_CLK_DIV, + .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1 }, + .mux = { CGU_REG_DDRCDR, 30, 2 }, + .div = { CGU_REG_DDRCDR, 0, 4, 29, 28, 27 }, + }, + + [JZ4780_CLK_VPU] = { + "vpu", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, + JZ4780_CLK_EPLL, -1 }, + .mux = { CGU_REG_VPUCDR, 30, 2 }, + .div = { CGU_REG_VPUCDR, 0, 4, 29, 28, 27 }, + .gate = { CGU_REG_CLKGR1, 2 }, + }, + + [JZ4780_CLK_I2SPLL] = { + "i2s_pll", CGU_CLK_MUX | CGU_CLK_DIV, + .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_EPLL, -1, -1 }, + .mux = { CGU_REG_I2SCDR, 30, 1 }, + .div = { CGU_REG_I2SCDR, 0, 8, 29, 28, 27 }, + }, + + [JZ4780_CLK_I2S] = { + "i2s", CGU_CLK_MUX, + .parents = { JZ4780_CLK_EXCLK, JZ4780_CLK_I2SPLL, -1, -1 }, + .mux = { CGU_REG_I2SCDR, 31, 1 }, + }, + + [JZ4780_CLK_LCD0PIXCLK] = { + "lcd0pixclk", CGU_CLK_MUX | CGU_CLK_DIV, + .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, + JZ4780_CLK_VPLL, -1 }, + .mux = { CGU_REG_LP0CDR, 30, 2 }, + .div = { CGU_REG_LP0CDR, 0, 8, 28, 27, 26 }, + }, + + [JZ4780_CLK_LCD1PIXCLK] = { + "lcd1pixclk", CGU_CLK_MUX | CGU_CLK_DIV, + .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, + JZ4780_CLK_VPLL, -1 }, + .mux = { CGU_REG_LP1CDR, 30, 2 }, + .div = { CGU_REG_LP1CDR, 0, 8, 28, 27, 26 }, + }, + + [JZ4780_CLK_MSCMUX] = { + "msc_mux", CGU_CLK_MUX, + .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1 }, + .mux = { CGU_REG_MSC0CDR, 30, 2 }, + }, + + [JZ4780_CLK_MSC0] = { + "msc0", CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 }, + .div = { CGU_REG_MSC0CDR, 0, 8, 29, 28, 27 }, + .gate = { CGU_REG_CLKGR0, 3 }, + }, + + [JZ4780_CLK_MSC1] = { + "msc1", CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 }, + .div = { CGU_REG_MSC1CDR, 0, 8, 29, 28, 27 }, + .gate = { CGU_REG_CLKGR0, 11 }, + }, + + [JZ4780_CLK_MSC2] = { + "msc2", CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 }, + .div = { CGU_REG_MSC2CDR, 0, 8, 29, 28, 27 }, + .gate = { CGU_REG_CLKGR0, 12 }, + }, + + [JZ4780_CLK_UHC] = { + "uhc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, + JZ4780_CLK_EPLL, JZ4780_CLK_OTGPHY }, + .mux = { CGU_REG_UHCCDR, 30, 2 }, + .div = { CGU_REG_UHCCDR, 0, 8, 29, 28, 27 }, + .gate = { CGU_REG_CLKGR0, 24 }, + }, + + [JZ4780_CLK_SSIPLL] = { + "ssi_pll", CGU_CLK_MUX | CGU_CLK_DIV, + .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1, -1 }, + .mux = { CGU_REG_SSICDR, 30, 1 }, + .div = { CGU_REG_SSICDR, 0, 8, 29, 28, 27 }, + }, + + [JZ4780_CLK_SSI] = { + "ssi", CGU_CLK_MUX, + .parents = { JZ4780_CLK_EXCLK, JZ4780_CLK_SSIPLL, -1, -1 }, + .mux = { CGU_REG_SSICDR, 31, 1 }, + }, + + [JZ4780_CLK_CIMMCLK] = { + "cim_mclk", CGU_CLK_MUX | CGU_CLK_DIV, + .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1, -1 }, + .mux = { CGU_REG_CIMCDR, 31, 1 }, + .div = { CGU_REG_CIMCDR, 0, 8, 30, 29, 28 }, + }, + + [JZ4780_CLK_PCMPLL] = { + "pcm_pll", CGU_CLK_MUX | CGU_CLK_DIV, + .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, + JZ4780_CLK_EPLL, JZ4780_CLK_VPLL }, + .mux = { CGU_REG_PCMCDR, 29, 2 }, + .div = { CGU_REG_PCMCDR, 0, 8, 28, 27, 26 }, + }, + + [JZ4780_CLK_PCM] = { + "pcm", CGU_CLK_MUX | CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, JZ4780_CLK_PCMPLL, -1, -1 }, + .mux = { CGU_REG_PCMCDR, 31, 1 }, + .gate = { CGU_REG_CLKGR1, 3 }, + }, + + [JZ4780_CLK_GPU] = { + "gpu", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, + JZ4780_CLK_EPLL }, + .mux = { CGU_REG_GPUCDR, 30, 2 }, + .div = { CGU_REG_GPUCDR, 0, 4, 29, 28, 27 }, + .gate = { CGU_REG_CLKGR1, 4 }, + }, + + [JZ4780_CLK_HDMI] = { + "hdmi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, + JZ4780_CLK_VPLL, -1 }, + .mux = { CGU_REG_HDMICDR, 30, 2 }, + .div = { CGU_REG_HDMICDR, 0, 8, 29, 28, 26 }, + .gate = { CGU_REG_CLKGR1, 9 }, + }, + + [JZ4780_CLK_BCH] = { + "bch", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, + JZ4780_CLK_EPLL }, + .mux = { CGU_REG_BCHCDR, 30, 2 }, + .div = { CGU_REG_BCHCDR, 0, 4, 29, 28, 27 }, + .gate = { CGU_REG_CLKGR0, 1 }, + }, + + /* Gate-only clocks */ + + [JZ4780_CLK_NEMC] = { + "nemc", CGU_CLK_GATE, + .parents = { JZ4780_CLK_AHB2, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 0 }, + }, + + [JZ4780_CLK_OTG0] = { + "otg0", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 2 }, + }, + + [JZ4780_CLK_SSI0] = { + "ssi0", CGU_CLK_GATE, + .parents = { JZ4780_CLK_SSI, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 4 }, + }, + + [JZ4780_CLK_SMB0] = { + "smb0", CGU_CLK_GATE, + .parents = { JZ4780_CLK_PCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 5 }, + }, + + [JZ4780_CLK_SMB1] = { + "smb1", CGU_CLK_GATE, + .parents = { JZ4780_CLK_PCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 6 }, + }, + + [JZ4780_CLK_SCC] = { + "scc", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 7 }, + }, + + [JZ4780_CLK_AIC] = { + "aic", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 8 }, + }, + + [JZ4780_CLK_TSSI0] = { + "tssi0", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 9 }, + }, + + [JZ4780_CLK_OWI] = { + "owi", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 10 }, + }, + + [JZ4780_CLK_KBC] = { + "kbc", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 13 }, + }, + + [JZ4780_CLK_SADC] = { + "sadc", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 14 }, + }, + + [JZ4780_CLK_UART0] = { + "uart0", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 15 }, + }, + + [JZ4780_CLK_UART1] = { + "uart1", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 16 }, + }, + + [JZ4780_CLK_UART2] = { + "uart2", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 17 }, + }, + + [JZ4780_CLK_UART3] = { + "uart3", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 18 }, + }, + + [JZ4780_CLK_SSI1] = { + "ssi1", CGU_CLK_GATE, + .parents = { JZ4780_CLK_SSI, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 19 }, + }, + + [JZ4780_CLK_SSI2] = { + "ssi2", CGU_CLK_GATE, + .parents = { JZ4780_CLK_SSI, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 20 }, + }, + + [JZ4780_CLK_PDMA] = { + "pdma", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 21 }, + }, + + [JZ4780_CLK_GPS] = { + "gps", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 22 }, + }, + + [JZ4780_CLK_MAC] = { + "mac", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 23 }, + }, + + [JZ4780_CLK_SMB2] = { + "smb2", CGU_CLK_GATE, + .parents = { JZ4780_CLK_PCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 24 }, + }, + + [JZ4780_CLK_CIM] = { + "cim", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 26 }, + }, + + [JZ4780_CLK_LCD] = { + "lcd", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 28 }, + }, + + [JZ4780_CLK_TVE] = { + "tve", CGU_CLK_GATE, + .parents = { JZ4780_CLK_LCD, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 27 }, + }, + + [JZ4780_CLK_IPU] = { + "ipu", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 29 }, + }, + + [JZ4780_CLK_DDR0] = { + "ddr0", CGU_CLK_GATE, + .parents = { JZ4780_CLK_DDR, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 30 }, + }, + + [JZ4780_CLK_DDR1] = { + "ddr1", CGU_CLK_GATE, + .parents = { JZ4780_CLK_DDR, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR0, 31 }, + }, + + [JZ4780_CLK_SMB3] = { + "smb3", CGU_CLK_GATE, + .parents = { JZ4780_CLK_PCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 0 }, + }, + + [JZ4780_CLK_TSSI1] = { + "tssi1", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 1 }, + }, + + [JZ4780_CLK_COMPRESS] = { + "compress", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 5 }, + }, + + [JZ4780_CLK_AIC1] = { + "aic1", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 6 }, + }, + + [JZ4780_CLK_GPVLC] = { + "gpvlc", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 7 }, + }, + + [JZ4780_CLK_OTG1] = { + "otg1", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 8 }, + }, + + [JZ4780_CLK_UART4] = { + "uart4", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 10 }, + }, + + [JZ4780_CLK_AHBMON] = { + "ahb_mon", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 11 }, + }, + + [JZ4780_CLK_SMB4] = { + "smb4", CGU_CLK_GATE, + .parents = { JZ4780_CLK_PCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 12 }, + }, + + [JZ4780_CLK_DES] = { + "des", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 13 }, + }, + + [JZ4780_CLK_X2D] = { + "x2d", CGU_CLK_GATE, + .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 14 }, + }, + + [JZ4780_CLK_CORE1] = { + "core1", CGU_CLK_GATE, + .parents = { JZ4780_CLK_CPU, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR1, 15 }, + }, + +}; + +static void __init jz4780_cgu_init(struct device_node *np) +{ + int retval; + + cgu = ingenic_cgu_new(jz4780_cgu_clocks, + ARRAY_SIZE(jz4780_cgu_clocks), np); + if (!cgu) { + pr_err("%s: failed to initialise CGU\n", __func__); + return; + } + + retval = ingenic_cgu_register_clocks(cgu); + if (retval) { + pr_err("%s: failed to register CGU Clocks\n", __func__); + return; + } +} +CLK_OF_DECLARE(jz4780_cgu, "ingenic,jz4780-cgu", jz4780_cgu_init); diff --git a/drivers/cpufreq/ls1x-cpufreq.c b/drivers/cpufreq/ls1x-cpufreq.c index f0913eee2f50..262581b3318d 100644 --- a/drivers/cpufreq/ls1x-cpufreq.c +++ b/drivers/cpufreq/ls1x-cpufreq.c @@ -17,8 +17,8 @@ #include <linux/platform_device.h> #include <linux/slab.h> -#include <asm/mach-loongson1/cpufreq.h> -#include <asm/mach-loongson1/loongson1.h> +#include <cpufreq.h> +#include <loongson1.h> static struct { struct device *dev; diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 6517132e5d8b..99c69a3205c4 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -136,6 +136,7 @@ config QCOM_SCM bool depends on ARM || ARM64 +source "drivers/firmware/broadcom/Kconfig" source "drivers/firmware/google/Kconfig" source "drivers/firmware/efi/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 3001f1ae1062..4a4b897f9314 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_QCOM_SCM) += qcom_scm.o obj-$(CONFIG_QCOM_SCM) += qcom_scm-32.o CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) +obj-y += broadcom/ obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_UEFI_CPER) += efi/ diff --git a/drivers/firmware/broadcom/Kconfig b/drivers/firmware/broadcom/Kconfig new file mode 100644 index 000000000000..6bed119930dd --- /dev/null +++ b/drivers/firmware/broadcom/Kconfig @@ -0,0 +1,11 @@ +config BCM47XX_NVRAM + bool "Broadcom NVRAM driver" + depends on BCM47XX || ARCH_BCM_5301X + help + Broadcom home routers contain flash partition called "nvram" with all + important hardware configuration as well as some minor user setup. + NVRAM partition contains a text-like data representing name=value + pairs. + This driver provides an easy way to get value of requested parameter. + It simply reads content of NVRAM and parses it. It doesn't control any + hardware part itself. diff --git a/drivers/firmware/broadcom/Makefile b/drivers/firmware/broadcom/Makefile new file mode 100644 index 000000000000..d0e683583cd6 --- /dev/null +++ b/drivers/firmware/broadcom/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_BCM47XX_NVRAM) += bcm47xx_nvram.o diff --git a/drivers/firmware/broadcom/bcm47xx_nvram.c b/drivers/firmware/broadcom/bcm47xx_nvram.c new file mode 100644 index 000000000000..87add3fdce52 --- /dev/null +++ b/drivers/firmware/broadcom/bcm47xx_nvram.c @@ -0,0 +1,248 @@ +/* + * BCM947xx nvram variable access + * + * Copyright (C) 2005 Broadcom Corporation + * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/io.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/mtd/mtd.h> +#include <linux/bcm47xx_nvram.h> + +#define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */ +#define NVRAM_SPACE 0x10000 +#define NVRAM_MAX_GPIO_ENTRIES 32 +#define NVRAM_MAX_GPIO_VALUE_LEN 30 + +#define FLASH_MIN 0x00020000 /* Minimum flash size */ + +struct nvram_header { + u32 magic; + u32 len; + u32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */ + u32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */ + u32 config_ncdl; /* ncdl values for memc */ +}; + +static char nvram_buf[NVRAM_SPACE]; +static size_t nvram_len; +static const u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000}; + +static u32 find_nvram_size(void __iomem *end) +{ + struct nvram_header __iomem *header; + int i; + + for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { + header = (struct nvram_header *)(end - nvram_sizes[i]); + if (header->magic == NVRAM_MAGIC) + return nvram_sizes[i]; + } + + return 0; +} + +/* Probe for NVRAM header */ +static int nvram_find_and_copy(void __iomem *iobase, u32 lim) +{ + struct nvram_header __iomem *header; + int i; + u32 off; + u32 *src, *dst; + u32 size; + + if (nvram_len) { + pr_warn("nvram already initialized\n"); + return -EEXIST; + } + + /* TODO: when nvram is on nand flash check for bad blocks first. */ + off = FLASH_MIN; + while (off <= lim) { + /* Windowed flash access */ + size = find_nvram_size(iobase + off); + if (size) { + header = (struct nvram_header *)(iobase + off - size); + goto found; + } + off <<= 1; + } + + /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ + header = (struct nvram_header *)(iobase + 4096); + if (header->magic == NVRAM_MAGIC) { + size = NVRAM_SPACE; + goto found; + } + + header = (struct nvram_header *)(iobase + 1024); + if (header->magic == NVRAM_MAGIC) { + size = NVRAM_SPACE; + goto found; + } + + pr_err("no nvram found\n"); + return -ENXIO; + +found: + src = (u32 *)header; + dst = (u32 *)nvram_buf; + for (i = 0; i < sizeof(struct nvram_header); i += 4) + *dst++ = __raw_readl(src++); + header = (struct nvram_header *)nvram_buf; + nvram_len = header->len; + if (nvram_len > size) { + pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n"); + nvram_len = size; + } + if (nvram_len >= NVRAM_SPACE) { + pr_err("nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n", + header->len, NVRAM_SPACE - 1); + nvram_len = NVRAM_SPACE - 1; + } + /* proceed reading data after header */ + for (; i < nvram_len; i += 4) + *dst++ = readl(src++); + nvram_buf[NVRAM_SPACE - 1] = '\0'; + + return 0; +} + +/* + * On bcm47xx we need access to the NVRAM very early, so we can't use mtd + * subsystem to access flash. We can't even use platform device / driver to + * store memory offset. + * To handle this we provide following symbol. It's supposed to be called as + * soon as we get info about flash device, before any NVRAM entry is needed. + */ +int bcm47xx_nvram_init_from_mem(u32 base, u32 lim) +{ + void __iomem *iobase; + int err; + + iobase = ioremap_nocache(base, lim); + if (!iobase) + return -ENOMEM; + + err = nvram_find_and_copy(iobase, lim); + + iounmap(iobase); + + return err; +} + +static int nvram_init(void) +{ +#ifdef CONFIG_MTD + struct mtd_info *mtd; + struct nvram_header header; + size_t bytes_read; + int err; + + mtd = get_mtd_device_nm("nvram"); + if (IS_ERR(mtd)) + return -ENODEV; + + err = mtd_read(mtd, 0, sizeof(header), &bytes_read, (uint8_t *)&header); + if (!err && header.magic == NVRAM_MAGIC && + header.len > sizeof(header)) { + nvram_len = header.len; + if (nvram_len >= NVRAM_SPACE) { + pr_err("nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n", + header.len, NVRAM_SPACE); + nvram_len = NVRAM_SPACE - 1; + } + + err = mtd_read(mtd, 0, nvram_len, &nvram_len, + (u8 *)nvram_buf); + return err; + } +#endif + + return -ENXIO; +} + +int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len) +{ + char *var, *value, *end, *eq; + int err; + + if (!name) + return -EINVAL; + + if (!nvram_len) { + err = nvram_init(); + if (err) + return err; + } + + /* Look for name=value and return value */ + var = &nvram_buf[sizeof(struct nvram_header)]; + end = nvram_buf + sizeof(nvram_buf); + while (var < end && *var) { + eq = strchr(var, '='); + if (!eq) + break; + value = eq + 1; + if (eq - var == strlen(name) && + strncmp(var, name, eq - var) == 0) + return snprintf(val, val_len, "%s", value); + var = value + strlen(value) + 1; + } + return -ENOENT; +} +EXPORT_SYMBOL(bcm47xx_nvram_getenv); + +int bcm47xx_nvram_gpio_pin(const char *name) +{ + int i, err; + char nvram_var[] = "gpioXX"; + char buf[NVRAM_MAX_GPIO_VALUE_LEN]; + + /* TODO: Optimize it to don't call getenv so many times */ + for (i = 0; i < NVRAM_MAX_GPIO_ENTRIES; i++) { + err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i); + if (err <= 0) + continue; + err = bcm47xx_nvram_getenv(nvram_var, buf, sizeof(buf)); + if (err <= 0) + continue; + if (!strcmp(name, buf)) + return i; + } + return -ENOENT; +} +EXPORT_SYMBOL(bcm47xx_nvram_gpio_pin); + +char *bcm47xx_nvram_get_contents(size_t *nvram_size) +{ + int err; + char *nvram; + + if (!nvram_len) { + err = nvram_init(); + if (err) + return NULL; + } + + *nvram_size = nvram_len - sizeof(struct nvram_header); + nvram = vmalloc(*nvram_size); + if (!nvram) + return NULL; + memcpy(nvram, &nvram_buf[sizeof(struct nvram_header)], *nvram_size); + + return nvram; +} +EXPORT_SYMBOL(bcm47xx_nvram_get_contents); + +MODULE_LICENSE("GPLv2"); diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 8a7d7807b596..120d81543e53 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -86,6 +86,11 @@ config IMGPDC_IRQ select GENERIC_IRQ_CHIP select IRQ_DOMAIN +config IRQ_MIPS_CPU + bool + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + config CLPS711X_IRQCHIP bool depends on ARCH_CLPS711X @@ -160,10 +165,15 @@ config MIPS_GIC bool select MIPS_CM +config INGENIC_IRQ + bool + depends on MACH_INGENIC + default y + config RENESAS_H8300H_INTC bool select IRQ_DOMAIN config RENESAS_H8S_INTC bool - select IRQ_DOMAIN
\ No newline at end of file + select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 5c9adf1f554d..b8d4e9691890 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_ARM_VIC) += irq-vic.o obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o obj-$(CONFIG_ATMEL_AIC5_IRQ) += irq-atmel-aic-common.o irq-atmel-aic5.o obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o +obj-$(CONFIG_IRQ_MIPS_CPU) += irq-mips-cpu.o obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o @@ -50,3 +51,4 @@ obj-$(CONFIG_ARCH_DIGICOLOR) += irq-digicolor.o obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o +obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o diff --git a/drivers/irqchip/irq-ingenic.c b/drivers/irqchip/irq-ingenic.c new file mode 100644 index 000000000000..005de3f932ae --- /dev/null +++ b/drivers/irqchip/irq-ingenic.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * JZ4740 platform IRQ support + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/irqchip/ingenic.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/timex.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#include <asm/io.h> +#include <asm/mach-jz4740/irq.h> + +#include "irqchip.h" + +struct ingenic_intc_data { + void __iomem *base; + unsigned num_chips; +}; + +#define JZ_REG_INTC_STATUS 0x00 +#define JZ_REG_INTC_MASK 0x04 +#define JZ_REG_INTC_SET_MASK 0x08 +#define JZ_REG_INTC_CLEAR_MASK 0x0c +#define JZ_REG_INTC_PENDING 0x10 +#define CHIP_SIZE 0x20 + +static irqreturn_t intc_cascade(int irq, void *data) +{ + struct ingenic_intc_data *intc = irq_get_handler_data(irq); + uint32_t irq_reg; + unsigned i; + + for (i = 0; i < intc->num_chips; i++) { + irq_reg = readl(intc->base + (i * CHIP_SIZE) + + JZ_REG_INTC_PENDING); + if (!irq_reg) + continue; + + generic_handle_irq(__fls(irq_reg) + (i * 32) + JZ4740_IRQ_BASE); + } + + return IRQ_HANDLED; +} + +static void intc_irq_set_mask(struct irq_chip_generic *gc, uint32_t mask) +{ + struct irq_chip_regs *regs = &gc->chip_types->regs; + + writel(mask, gc->reg_base + regs->enable); + writel(~mask, gc->reg_base + regs->disable); +} + +void ingenic_intc_irq_suspend(struct irq_data *data) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + intc_irq_set_mask(gc, gc->wake_active); +} + +void ingenic_intc_irq_resume(struct irq_data *data) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + intc_irq_set_mask(gc, gc->mask_cache); +} + +static struct irqaction intc_cascade_action = { + .handler = intc_cascade, + .name = "SoC intc cascade interrupt", +}; + +static int __init ingenic_intc_of_init(struct device_node *node, + unsigned num_chips) +{ + struct ingenic_intc_data *intc; + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + struct irq_domain *domain; + int parent_irq, err = 0; + unsigned i; + + intc = kzalloc(sizeof(*intc), GFP_KERNEL); + if (!intc) { + err = -ENOMEM; + goto out_err; + } + + parent_irq = irq_of_parse_and_map(node, 0); + if (!parent_irq) { + err = -EINVAL; + goto out_free; + } + + err = irq_set_handler_data(parent_irq, intc); + if (err) + goto out_unmap_irq; + + intc->num_chips = num_chips; + intc->base = of_iomap(node, 0); + if (!intc->base) { + err = -ENODEV; + goto out_unmap_irq; + } + + for (i = 0; i < num_chips; i++) { + /* Mask all irqs */ + writel(0xffffffff, intc->base + (i * CHIP_SIZE) + + JZ_REG_INTC_SET_MASK); + + gc = irq_alloc_generic_chip("INTC", 1, + JZ4740_IRQ_BASE + (i * 32), + intc->base + (i * CHIP_SIZE), + handle_level_irq); + + gc->wake_enabled = IRQ_MSK(32); + + ct = gc->chip_types; + ct->regs.enable = JZ_REG_INTC_CLEAR_MASK; + ct->regs.disable = JZ_REG_INTC_SET_MASK; + ct->chip.irq_unmask = irq_gc_unmask_enable_reg; + ct->chip.irq_mask = irq_gc_mask_disable_reg; + ct->chip.irq_mask_ack = irq_gc_mask_disable_reg; + ct->chip.irq_set_wake = irq_gc_set_wake; + ct->chip.irq_suspend = ingenic_intc_irq_suspend; + ct->chip.irq_resume = ingenic_intc_irq_resume; + + irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0, + IRQ_NOPROBE | IRQ_LEVEL); + } + + domain = irq_domain_add_legacy(node, num_chips * 32, JZ4740_IRQ_BASE, 0, + &irq_domain_simple_ops, NULL); + if (!domain) + pr_warn("unable to register IRQ domain\n"); + + setup_irq(parent_irq, &intc_cascade_action); + return 0; + +out_unmap_irq: + irq_dispose_mapping(parent_irq); +out_free: + kfree(intc); +out_err: + return err; +} + +static int __init intc_1chip_of_init(struct device_node *node, + struct device_node *parent) +{ + return ingenic_intc_of_init(node, 1); +} +IRQCHIP_DECLARE(jz4740_intc, "ingenic,jz4740-intc", intc_1chip_of_init); + +static int __init intc_2chip_of_init(struct device_node *node, + struct device_node *parent) +{ + return ingenic_intc_of_init(node, 2); +} +IRQCHIP_DECLARE(jz4770_intc, "ingenic,jz4770-intc", intc_2chip_of_init); +IRQCHIP_DECLARE(jz4775_intc, "ingenic,jz4775-intc", intc_2chip_of_init); +IRQCHIP_DECLARE(jz4780_intc, "ingenic,jz4780-intc", intc_2chip_of_init); diff --git a/drivers/irqchip/irq-mips-cpu.c b/drivers/irqchip/irq-mips-cpu.c new file mode 100644 index 000000000000..a43c41988009 --- /dev/null +++ b/drivers/irqchip/irq-mips-cpu.c @@ -0,0 +1,172 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * Copyright (C) 2001 Ralf Baechle + * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved. + * Author: Maciej W. Rozycki <macro@mips.com> + * + * This file define the irq handler for MIPS CPU interrupts. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * Almost all MIPS CPUs define 8 interrupt sources. They are typically + * level triggered (i.e., cannot be cleared from CPU; must be cleared from + * device). The first two are software interrupts which we don't really + * use or support. The last one is usually the CPU timer interrupt if + * counter register is present or, for CPUs with an external FPU, by + * convention it's the FPU exception interrupt. + * + * Don't even think about using this on SMP. You have been warned. + * + * This file exports one global function: + * void mips_cpu_irq_init(void); + */ +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> + +#include <asm/irq_cpu.h> +#include <asm/mipsregs.h> +#include <asm/mipsmtregs.h> +#include <asm/setup.h> + +#include "irqchip.h" + +static inline void unmask_mips_irq(struct irq_data *d) +{ + set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE)); + irq_enable_hazard(); +} + +static inline void mask_mips_irq(struct irq_data *d) +{ + clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE)); + irq_disable_hazard(); +} + +static struct irq_chip mips_cpu_irq_controller = { + .name = "MIPS", + .irq_ack = mask_mips_irq, + .irq_mask = mask_mips_irq, + .irq_mask_ack = mask_mips_irq, + .irq_unmask = unmask_mips_irq, + .irq_eoi = unmask_mips_irq, + .irq_disable = mask_mips_irq, + .irq_enable = unmask_mips_irq, +}; + +/* + * Basically the same as above but taking care of all the MT stuff + */ + +static unsigned int mips_mt_cpu_irq_startup(struct irq_data *d) +{ + unsigned int vpflags = dvpe(); + + clear_c0_cause(0x100 << (d->irq - MIPS_CPU_IRQ_BASE)); + evpe(vpflags); + unmask_mips_irq(d); + return 0; +} + +/* + * While we ack the interrupt interrupts are disabled and thus we don't need + * to deal with concurrency issues. Same for mips_cpu_irq_end. + */ +static void mips_mt_cpu_irq_ack(struct irq_data *d) +{ + unsigned int vpflags = dvpe(); + clear_c0_cause(0x100 << (d->irq - MIPS_CPU_IRQ_BASE)); + evpe(vpflags); + mask_mips_irq(d); +} + +static struct irq_chip mips_mt_cpu_irq_controller = { + .name = "MIPS", + .irq_startup = mips_mt_cpu_irq_startup, + .irq_ack = mips_mt_cpu_irq_ack, + .irq_mask = mask_mips_irq, + .irq_mask_ack = mips_mt_cpu_irq_ack, + .irq_unmask = unmask_mips_irq, + .irq_eoi = unmask_mips_irq, + .irq_disable = mask_mips_irq, + .irq_enable = unmask_mips_irq, +}; + +asmlinkage void __weak plat_irq_dispatch(void) +{ + unsigned long pending = read_c0_cause() & read_c0_status() & ST0_IM; + int irq; + + if (!pending) { + spurious_interrupt(); + return; + } + + pending >>= CAUSEB_IP; + while (pending) { + irq = fls(pending) - 1; + do_IRQ(MIPS_CPU_IRQ_BASE + irq); + pending &= ~BIT(irq); + } +} + +static int mips_cpu_intc_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + static struct irq_chip *chip; + + if (hw < 2 && cpu_has_mipsmt) { + /* Software interrupts are used for MT/CMT IPI */ + chip = &mips_mt_cpu_irq_controller; + } else { + chip = &mips_cpu_irq_controller; + } + + if (cpu_has_vint) + set_vi_handler(hw, plat_irq_dispatch); + + irq_set_chip_and_handler(irq, chip, handle_percpu_irq); + + return 0; +} + +static const struct irq_domain_ops mips_cpu_intc_irq_domain_ops = { + .map = mips_cpu_intc_map, + .xlate = irq_domain_xlate_onecell, +}; + +static void __init __mips_cpu_irq_init(struct device_node *of_node) +{ + struct irq_domain *domain; + + /* Mask interrupts. */ + clear_c0_status(ST0_IM); + clear_c0_cause(CAUSEF_IP); + + domain = irq_domain_add_legacy(of_node, 8, MIPS_CPU_IRQ_BASE, 0, + &mips_cpu_intc_irq_domain_ops, NULL); + if (!domain) + panic("Failed to add irqdomain for MIPS CPU"); +} + +void __init mips_cpu_irq_init(void) +{ + __mips_cpu_irq_init(NULL); +} + +int __init mips_cpu_irq_of_init(struct device_node *of_node, + struct device_node *parent) +{ + __mips_cpu_irq_init(of_node); + return 0; +} +IRQCHIP_DECLARE(cpu_intc, "mti,cpu-interrupt-controller", mips_cpu_irq_of_init); diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 487d057431cc..c0e6ede3e27d 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -267,6 +267,13 @@ config PHY_EXYNOS5_USBDRD This driver provides PHY interface for USB 3.0 DRD controller present on Exynos5 SoC series. +config PHY_PISTACHIO_USB + tristate "IMG Pistachio USB2.0 PHY driver" + depends on MACH_PISTACHIO + select GENERIC_PHY + help + Enable this to support the USB2.0 PHY on the IMG Pistachio SoC. + config PHY_QCOM_APQ8064_SATA tristate "Qualcomm APQ8064 SATA SerDes/PHY driver" depends on ARCH_QCOM diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 42f58e95aff0..f344e1b2e825 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -44,3 +44,4 @@ obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o +obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o diff --git a/drivers/phy/phy-pistachio-usb.c b/drivers/phy/phy-pistachio-usb.c new file mode 100644 index 000000000000..c6db35e6bb63 --- /dev/null +++ b/drivers/phy/phy-pistachio-usb.c @@ -0,0 +1,206 @@ +/* + * IMG Pistachio USB PHY driver + * + * Copyright (C) 2015 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/phy/phy-pistachio-usb.h> + +#define USB_PHY_CONTROL1 0x04 +#define USB_PHY_CONTROL1_FSEL_SHIFT 2 +#define USB_PHY_CONTROL1_FSEL_MASK 0x7 + +#define USB_PHY_STRAP_CONTROL 0x10 +#define USB_PHY_STRAP_CONTROL_REFCLK_SHIFT 4 +#define USB_PHY_STRAP_CONTROL_REFCLK_MASK 0x3 + +#define USB_PHY_STATUS 0x14 +#define USB_PHY_STATUS_RX_PHY_CLK BIT(9) +#define USB_PHY_STATUS_RX_UTMI_CLK BIT(8) +#define USB_PHY_STATUS_VBUS_FAULT BIT(7) + +struct pistachio_usb_phy { + struct device *dev; + struct regmap *cr_top; + struct clk *phy_clk; + unsigned int refclk; +}; + +static const unsigned long fsel_rate_map[] = { + 9600000, + 10000000, + 12000000, + 19200000, + 20000000, + 24000000, + 0, + 50000000, +}; + +static int pistachio_usb_phy_power_on(struct phy *phy) +{ + struct pistachio_usb_phy *p_phy = phy_get_drvdata(phy); + unsigned long timeout, rate; + unsigned int i; + int ret; + + ret = clk_prepare_enable(p_phy->phy_clk); + if (ret < 0) { + dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret); + return ret; + } + + regmap_update_bits(p_phy->cr_top, USB_PHY_STRAP_CONTROL, + USB_PHY_STRAP_CONTROL_REFCLK_MASK << + USB_PHY_STRAP_CONTROL_REFCLK_SHIFT, + p_phy->refclk << USB_PHY_STRAP_CONTROL_REFCLK_SHIFT); + + rate = clk_get_rate(p_phy->phy_clk); + if (p_phy->refclk == REFCLK_XO_CRYSTAL && rate != 12000000) { + dev_err(p_phy->dev, "Unsupported rate for XO crystal: %ld\n", + rate); + ret = -EINVAL; + goto disable_clk; + } + + for (i = 0; i < ARRAY_SIZE(fsel_rate_map); i++) { + if (rate == fsel_rate_map[i]) + break; + } + if (i == ARRAY_SIZE(fsel_rate_map)) { + dev_err(p_phy->dev, "Unsupported clock rate: %lu\n", rate); + ret = -EINVAL; + goto disable_clk; + } + + regmap_update_bits(p_phy->cr_top, USB_PHY_CONTROL1, + USB_PHY_CONTROL1_FSEL_MASK << + USB_PHY_CONTROL1_FSEL_SHIFT, + i << USB_PHY_CONTROL1_FSEL_SHIFT); + + timeout = jiffies + msecs_to_jiffies(200); + while (time_before(jiffies, timeout)) { + unsigned int val; + + regmap_read(p_phy->cr_top, USB_PHY_STATUS, &val); + if (val & USB_PHY_STATUS_VBUS_FAULT) { + dev_err(p_phy->dev, "VBUS fault detected\n"); + ret = -EIO; + goto disable_clk; + } + if ((val & USB_PHY_STATUS_RX_PHY_CLK) && + (val & USB_PHY_STATUS_RX_UTMI_CLK)) + return 0; + usleep_range(1000, 1500); + } + + dev_err(p_phy->dev, "Timed out waiting for PHY to power on\n"); + ret = -ETIMEDOUT; + +disable_clk: + clk_disable_unprepare(p_phy->phy_clk); + return ret; +} + +static int pistachio_usb_phy_power_off(struct phy *phy) +{ + struct pistachio_usb_phy *p_phy = phy_get_drvdata(phy); + + clk_disable_unprepare(p_phy->phy_clk); + + return 0; +} + +static const struct phy_ops pistachio_usb_phy_ops = { + .power_on = pistachio_usb_phy_power_on, + .power_off = pistachio_usb_phy_power_off, + .owner = THIS_MODULE, +}; + +static int pistachio_usb_phy_probe(struct platform_device *pdev) +{ + struct pistachio_usb_phy *p_phy; + struct phy_provider *provider; + struct phy *phy; + int ret; + + p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL); + if (!p_phy) + return -ENOMEM; + p_phy->dev = &pdev->dev; + platform_set_drvdata(pdev, p_phy); + + p_phy->cr_top = syscon_regmap_lookup_by_phandle(p_phy->dev->of_node, + "img,cr-top"); + if (IS_ERR(p_phy->cr_top)) { + dev_err(p_phy->dev, "Failed to get CR_TOP registers: %ld\n", + PTR_ERR(p_phy->cr_top)); + return PTR_ERR(p_phy->cr_top); + } + + p_phy->phy_clk = devm_clk_get(p_phy->dev, "usb_phy"); + if (IS_ERR(p_phy->phy_clk)) { + dev_err(p_phy->dev, "Failed to get usb_phy clock: %ld\n", + PTR_ERR(p_phy->phy_clk)); + return PTR_ERR(p_phy->phy_clk); + } + + ret = of_property_read_u32(p_phy->dev->of_node, "img,refclk", + &p_phy->refclk); + if (ret < 0) { + dev_err(p_phy->dev, "No reference clock selector specified\n"); + return ret; + } + + phy = devm_phy_create(p_phy->dev, NULL, &pistachio_usb_phy_ops); + if (IS_ERR(phy)) { + dev_err(p_phy->dev, "Failed to create PHY: %ld\n", + PTR_ERR(phy)); + return PTR_ERR(phy); + } + phy_set_drvdata(phy, p_phy); + + provider = devm_of_phy_provider_register(p_phy->dev, + of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(p_phy->dev, "Failed to register PHY provider: %ld\n", + PTR_ERR(provider)); + return PTR_ERR(provider); + } + + return 0; +} + +static const struct of_device_id pistachio_usb_phy_of_match[] = { + { .compatible = "img,pistachio-usb-phy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, pistachio_usb_phy_of_match); + +static struct platform_driver pistachio_usb_phy_driver = { + .probe = pistachio_usb_phy_probe, + .driver = { + .name = "pistachio-usb-phy", + .of_match_table = pistachio_usb_phy_of_match, + }, +}; +module_platform_driver(pistachio_usb_phy_driver); + +MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>"); +MODULE_DESCRIPTION("IMG Pistachio USB2.0 PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index db2fe4ab4b4a..83b4b89b9d5a 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1503,7 +1503,7 @@ config RTC_DRV_PUV3 config RTC_DRV_LOONGSON1 tristate "loongson1 RTC support" - depends on MACH_LOONGSON1 + depends on MACH_LOONGSON32 help This is a driver for the loongson1 on-chip Counter0 (Time-Of-Year counter) to be used as a RTC. diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c index 8445e564094a..22a9ec4f2b83 100644 --- a/drivers/rtc/rtc-ls1x.c +++ b/drivers/rtc/rtc-ls1x.c @@ -17,7 +17,7 @@ #include <linux/delay.h> #include <linux/types.h> #include <linux/io.h> -#include <asm/mach-loongson1/loongson1.h> +#include <loongson1.h> #define LS1X_RTC_REG_OFFSET (LS1X_RTC_BASE + 0x20) #define LS1X_RTC_REGS(x) \ diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c new file mode 100644 index 000000000000..21bf81fe794f --- /dev/null +++ b/drivers/tty/serial/8250/8250_ingenic.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2010 Lars-Peter Clausen <lars@metafoo.de> + * Copyright (C) 2015 Imagination Technologies + * + * Ingenic SoC UART support + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/io.h> +#include <linux/libfdt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/platform_device.h> +#include <linux/serial_8250.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> + +struct ingenic_uart_data { + struct clk *clk_module; + struct clk *clk_baud; + int line; +}; + +#define UART_FCR_UME BIT(4) + +static struct earlycon_device *early_device; + +static uint8_t __init early_in(struct uart_port *port, int offset) +{ + return readl(port->membase + (offset << 2)); +} + +static void __init early_out(struct uart_port *port, int offset, uint8_t value) +{ + writel(value, port->membase + (offset << 2)); +} + +static void __init ingenic_early_console_putc(struct uart_port *port, int c) +{ + uint8_t lsr; + + do { + lsr = early_in(port, UART_LSR); + } while ((lsr & UART_LSR_TEMT) == 0); + + early_out(port, UART_TX, c); +} + +static void __init ingenic_early_console_write(struct console *console, + const char *s, unsigned int count) +{ + uart_console_write(&early_device->port, s, count, + ingenic_early_console_putc); +} + +static void __init ingenic_early_console_setup_clock(struct earlycon_device *dev) +{ + void *fdt = initial_boot_params; + const __be32 *prop; + int offset; + + offset = fdt_path_offset(fdt, "/ext"); + if (offset < 0) + return; + + prop = fdt_getprop(fdt, offset, "clock-frequency", NULL); + if (!prop) + return; + + dev->port.uartclk = be32_to_cpup(prop); +} + +static int __init ingenic_early_console_setup(struct earlycon_device *dev, + const char *opt) +{ + struct uart_port *port = &dev->port; + unsigned int baud, divisor; + + if (!dev->port.membase) + return -ENODEV; + + ingenic_early_console_setup_clock(dev); + + baud = dev->baud ?: 115200; + divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud); + + early_out(port, UART_IER, 0); + early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); + early_out(port, UART_DLL, 0); + early_out(port, UART_DLM, 0); + early_out(port, UART_LCR, UART_LCR_WLEN8); + early_out(port, UART_FCR, UART_FCR_UME | UART_FCR_CLEAR_XMIT | + UART_FCR_CLEAR_RCVR | UART_FCR_ENABLE_FIFO); + early_out(port, UART_MCR, UART_MCR_RTS | UART_MCR_DTR); + + early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); + early_out(port, UART_DLL, divisor & 0xff); + early_out(port, UART_DLM, (divisor >> 8) & 0xff); + early_out(port, UART_LCR, UART_LCR_WLEN8); + + early_device = dev; + dev->con->write = ingenic_early_console_write; + + return 0; +} + +EARLYCON_DECLARE(jz4740_uart, ingenic_early_console_setup); +OF_EARLYCON_DECLARE(jz4740_uart, "ingenic,jz4740-uart", + ingenic_early_console_setup); + +EARLYCON_DECLARE(jz4775_uart, ingenic_early_console_setup); +OF_EARLYCON_DECLARE(jz4775_uart, "ingenic,jz4775-uart", + ingenic_early_console_setup); + +EARLYCON_DECLARE(jz4780_uart, ingenic_early_console_setup); +OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart", + ingenic_early_console_setup); + +static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) +{ + switch (offset) { + case UART_FCR: + /* UART module enable */ + value |= UART_FCR_UME; + break; + + case UART_IER: + value |= (value & 0x4) << 2; + break; + + default: + break; + } + + writeb(value, p->membase + (offset << p->regshift)); +} + +static int ingenic_uart_probe(struct platform_device *pdev) +{ + struct uart_8250_port uart = {}; + struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + struct ingenic_uart_data *data; + int err, line; + + if (!regs || !irq) { + dev_err(&pdev->dev, "no registers/irq defined\n"); + return -EINVAL; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&uart.port.lock); + uart.port.type = PORT_16550; + uart.port.flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE; + uart.port.iotype = UPIO_MEM; + uart.port.mapbase = regs->start; + uart.port.regshift = 2; + uart.port.serial_out = ingenic_uart_serial_out; + uart.port.irq = irq->start; + uart.port.dev = &pdev->dev; + + /* Check for a fixed line number */ + line = of_alias_get_id(pdev->dev.of_node, "serial"); + if (line >= 0) + uart.port.line = line; + + uart.port.membase = devm_ioremap(&pdev->dev, regs->start, + resource_size(regs)); + if (!uart.port.membase) + return -ENOMEM; + + data->clk_module = devm_clk_get(&pdev->dev, "module"); + if (IS_ERR(data->clk_module)) { + err = PTR_ERR(data->clk_module); + if (err != -EPROBE_DEFER) + dev_err(&pdev->dev, + "unable to get module clock: %d\n", err); + return err; + } + + data->clk_baud = devm_clk_get(&pdev->dev, "baud"); + if (IS_ERR(data->clk_baud)) { + err = PTR_ERR(data->clk_baud); + if (err != -EPROBE_DEFER) + dev_err(&pdev->dev, + "unable to get baud clock: %d\n", err); + return err; + } + + err = clk_prepare_enable(data->clk_module); + if (err) { + dev_err(&pdev->dev, "could not enable module clock: %d\n", err); + goto out; + } + + err = clk_prepare_enable(data->clk_baud); + if (err) { + dev_err(&pdev->dev, "could not enable baud clock: %d\n", err); + goto out_disable_moduleclk; + } + uart.port.uartclk = clk_get_rate(data->clk_baud); + + data->line = serial8250_register_8250_port(&uart); + if (data->line < 0) { + err = data->line; + goto out_disable_baudclk; + } + + platform_set_drvdata(pdev, data); + return 0; + +out_disable_baudclk: + clk_disable_unprepare(data->clk_baud); +out_disable_moduleclk: + clk_disable_unprepare(data->clk_module); +out: + return err; +} + +static int ingenic_uart_remove(struct platform_device *pdev) +{ + struct ingenic_uart_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); + clk_disable_unprepare(data->clk_module); + clk_disable_unprepare(data->clk_baud); + return 0; +} + +static const struct of_device_id of_match[] = { + { .compatible = "ingenic,jz4740-uart" }, + { .compatible = "ingenic,jz4775-uart" }, + { .compatible = "ingenic,jz4780-uart" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, of_match); + +static struct platform_driver ingenic_uart_platform_driver = { + .driver = { + .name = "ingenic-uart", + .owner = THIS_MODULE, + .of_match_table = of_match, + }, + .probe = ingenic_uart_probe, + .remove = ingenic_uart_remove, +}; + +module_platform_driver(ingenic_uart_platform_driver); + +MODULE_AUTHOR("Paul Burton"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Ingenic SoC UART driver"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index a74a8e4717d4..e1de1181b322 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -357,3 +357,12 @@ config SERIAL_8250_UNIPHIER help If you have a UniPhier based board and want to use the on-chip serial ports, say Y to this option. If unsure, say N. + +config SERIAL_8250_INGENIC + bool "Support for Ingenic SoC serial ports" + depends on SERIAL_8250_CONSOLE && OF_FLATTREE + select LIBFDT + select SERIAL_EARLYCON + help + If you have a system using an Ingenic SoC and wish to make use of + its UARTs, say Y to this option. If unsure, say N. diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 6fa22ffad63d..706295913c34 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -25,3 +25,6 @@ obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o +obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o + +CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 1c4791033b72..2847108cc8dd 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -462,6 +462,7 @@ static struct sysrq_key_op *sysrq_key_table[36] = { /* v: May be registered for frame buffer console restore */ NULL, /* v */ &sysrq_showstate_blocked_op, /* w */ + /* x: May be registered on mips for TLB dump */ /* x: May be registered on ppc/powerpc for xmon */ /* x: May be registered on sparc64 for global PMU dump */ NULL, /* x */ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 547cee83400b..8afc3c1efdab 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -295,7 +295,7 @@ config USB_OCTEON_EHCI bool "Octeon on-chip EHCI support (DEPRECATED)" depends on CAVIUM_OCTEON_SOC default n - select USB_EHCI_BIG_ENDIAN_MMIO + select USB_EHCI_BIG_ENDIAN_MMIO if CPU_BIG_ENDIAN select USB_EHCI_HCD_PLATFORM help This option is deprecated now and the driver was removed, use @@ -568,7 +568,7 @@ config USB_OCTEON_OHCI bool "Octeon on-chip OHCI support (DEPRECATED)" depends on CAVIUM_OCTEON_SOC default USB_OCTEON_EHCI - select USB_OHCI_BIG_ENDIAN_MMIO + select USB_OHCI_BIG_ENDIAN_MMIO if CPU_BIG_ENDIAN select USB_OHCI_LITTLE_ENDIAN select USB_OHCI_HCD_PLATFORM help |