diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/hisilicon/clk-hi6220.c | 73 | ||||
-rw-r--r-- | drivers/clk/hisilicon/clkgate-separated.c | 1 | ||||
-rw-r--r-- | drivers/mailbox/Kconfig | 8 | ||||
-rw-r--r-- | drivers/mailbox/Makefile | 2 | ||||
-rw-r--r-- | drivers/mailbox/hi6220-mailbox.c | 371 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 10 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/hi655x-pmic.c | 176 | ||||
-rw-r--r-- | drivers/misc/Kconfig | 8 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/hi6220-sysconfig.c | 72 | ||||
-rw-r--r-- | drivers/mmc/host/dw_mmc-k3.c | 13 | ||||
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 41 | ||||
-rw-r--r-- | drivers/regulator/Kconfig | 15 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 2 | ||||
-rw-r--r-- | drivers/regulator/hi6220-mtcmos.c | 269 | ||||
-rw-r--r-- | drivers/regulator/hi655x-regulator.c | 226 | ||||
-rw-r--r-- | drivers/reset/Kconfig | 1 | ||||
-rw-r--r-- | drivers/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/reset/hisilicon/Kconfig | 5 | ||||
-rw-r--r-- | drivers/reset/hisilicon/Makefile | 1 | ||||
-rw-r--r-- | drivers/reset/hisilicon/hi6220_reset.c | 164 |
22 files changed, 1447 insertions, 14 deletions
diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index 4563343b6420..fe5ec1d0a11b 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -11,9 +11,12 @@ */ #include <linux/kernel.h> +#include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/clkdev.h> +#include <linux/delay.h> #include <linux/io.h> +#include <linux/kernel.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> @@ -34,8 +37,8 @@ static struct hisi_fixed_rate_clock hi6220_fixed_rate_clks[] __initdata = { { HI6220_PLL_BBP, "bbppll0", NULL, CLK_IS_ROOT, 245760000, }, { HI6220_PLL_GPU, "gpupll", NULL, CLK_IS_ROOT, 1000000000,}, { HI6220_PLL1_DDR, "ddrpll1", NULL, CLK_IS_ROOT, 1066000000,}, - { HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1200000000,}, - { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1190494208,}, + { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1190494208,}, { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1200000000,}, { HI6220_PLL_MEDIA, "media_pll", NULL, CLK_IS_ROOT, 1440000000,}, { HI6220_PLL_DDR, "ddrpll0", NULL, CLK_IS_ROOT, 1600000000,}, @@ -68,24 +71,75 @@ static struct hisi_gate_clock hi6220_separated_gate_clks_ao[] __initdata = { { HI6220_TIMER7_PCLK, "timer7_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 22, 0, }, { HI6220_TIMER8_PCLK, "timer8_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 23, 0, }, { HI6220_UART0_PCLK, "uart0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 24, 0, }, + { HI6220_RTC0_PCLK, "rtc0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 25, 0, }, }; +#define SOC_PERI_SCTRL_BASE_ADDR 0xF7030000 /* peri ctrl base addr */ +#define SC_PERIPH_CTRL14 0x02C +#define SC_PERIPH_STAT1 0x094 +#define SOC_PMCTRL_BASE_ADDR 0xF7032000 /* pm ctrl base addr*/ +#define SC_PM_DDRPLL_STAT 0x18 +#define SC_PM_SYSPLL_STAT 0x28 +#define SC_PM_MEDPLL_STAT 0x38 + +static struct hisi_clock_data *clk_data_ao; + static void __init hi6220_clk_ao_init(struct device_node *np) { - struct hisi_clock_data *clk_data_ao; + void __iomem *peri_base, *pm_base; + unsigned int freq_u, freq_l, freq, pll_stat; + int i; clk_data_ao = hisi_clk_init(np, HI6220_AO_NR_CLKS); if (!clk_data_ao) return; + peri_base = ioremap(SOC_PERI_SCTRL_BASE_ADDR, 0x1000); + pm_base = ioremap(SOC_PMCTRL_BASE_ADDR, 0x1000); + /* SYSPLL is set by bootloader. Read it */ + /* check syspll enablement status */ + pll_stat = readl(pm_base + SC_PM_SYSPLL_STAT); + pr_info("SYSPLL: syspll PM status: 0x%x\n", pll_stat); + /* 0x2101 means to calculate clk_sys_pll */ + writew(0x2101, peri_base + SC_PERIPH_CTRL14); + mdelay(1); + /* read back the calculated value */ + freq_l = readw(peri_base + SC_PERIPH_STAT1); + freq_u = readw(peri_base + SC_PERIPH_STAT1 + 2); + mdelay(1); + freq = freq_u << 16 | freq_l; + pr_info("SYSPLL: syspll is read: l: 0x%04X, u: 0x%04X\n", + freq_l, freq_u); + pr_info("SYSPLL: syspll is read: 0x%X, %d\n", freq, freq); + if (freq == 0x00020000 || freq == 0) { + pr_info("SYSPLL: ERROR: read returns misterious value.\n"); + freq = 1200000000; + } + + /* mask off freq */ + freq -= (freq % 100000); + pr_info("SYSPLL: set syspll medpll: %d\n", freq); + + for (i = 0; i < ARRAY_SIZE(hi6220_fixed_rate_clks); i++) { + if (hi6220_fixed_rate_clks[i].id == HI6220_PLL_SYS || + hi6220_fixed_rate_clks[i].id == HI6220_PLL_SYS_MEDIA) { + hi6220_fixed_rate_clks[i].fixed_rate = freq; + pr_info("SYSPLL: modified fix_rate[%d], id=%d, f=%d\n", + i, hi6220_fixed_rate_clks[i].id, freq); + } + } + hisi_clk_register_fixed_rate(hi6220_fixed_rate_clks, - ARRAY_SIZE(hi6220_fixed_rate_clks), clk_data_ao); + ARRAY_SIZE(hi6220_fixed_rate_clks), + clk_data_ao); hisi_clk_register_fixed_factor(hi6220_fixed_factor_clks, - ARRAY_SIZE(hi6220_fixed_factor_clks), clk_data_ao); + ARRAY_SIZE(hi6220_fixed_factor_clks), + clk_data_ao); hisi_clk_register_gate_sep(hi6220_separated_gate_clks_ao, - ARRAY_SIZE(hi6220_separated_gate_clks_ao), clk_data_ao); + ARRAY_SIZE(hi6220_separated_gate_clks_ao), + clk_data_ao); } CLK_OF_DECLARE(hi6220_clk_ao, "hisilicon,hi6220-aoctrl", hi6220_clk_ao_init); @@ -192,6 +246,13 @@ static void __init hi6220_clk_sys_init(struct device_node *np) hi6220_clk_register_divider(hi6220_div_clks_sys, ARRAY_SIZE(hi6220_div_clks_sys), clk_data); + + if (!clk_data_ao) + return; + + /* enable high speed clock on UART1 mux */ + clk_set_parent(clk_data->clk_data.clks[HI6220_UART1_SRC], + clk_data_ao->clk_data.clks[HI6220_150M]); } CLK_OF_DECLARE(hi6220_clk_sys, "hisilicon,hi6220-sysctrl", hi6220_clk_sys_init); diff --git a/drivers/clk/hisilicon/clkgate-separated.c b/drivers/clk/hisilicon/clkgate-separated.c index a47812f56a17..7908bc3c9ec7 100644 --- a/drivers/clk/hisilicon/clkgate-separated.c +++ b/drivers/clk/hisilicon/clkgate-separated.c @@ -120,6 +120,7 @@ struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name, sclk->bit_idx = bit_idx; sclk->flags = clk_gate_flags; sclk->hw.init = &init; + sclk->lock = lock; clk = clk_register(dev, &sclk->hw); if (IS_ERR(clk)) diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 546d05f4358a..d55d656c8d5a 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -78,6 +78,14 @@ config STI_MBOX Mailbox implementation for STMicroelectonics family chips with hardware for interprocessor communication. +config HI6220_MBOX + tristate "Hi6220 Mailbox" + depends on ARCH_HISI + help + An implementation of the hi6220 mailbox. It is used to send message + between application processors and MCU. Say Y here if you want to build + the Hi6220 mailbox controller driver. + config MAILBOX_TEST tristate "Mailbox Test Client" depends on OF diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 92435ef11f26..565adda57e52 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -17,3 +17,5 @@ obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o obj-$(CONFIG_BCM2835_MBOX) += bcm2835-mailbox.o obj-$(CONFIG_STI_MBOX) += mailbox-sti.o + +obj-$(CONFIG_HI6220_MBOX) += hi6220-mailbox.o diff --git a/drivers/mailbox/hi6220-mailbox.c b/drivers/mailbox/hi6220-mailbox.c new file mode 100644 index 000000000000..c0e19d574a57 --- /dev/null +++ b/drivers/mailbox/hi6220-mailbox.c @@ -0,0 +1,371 @@ +/* + * Hisilicon's Hi6220 mailbox driver + * + * Copyright (c) 2015 Hisilicon Limited. + * Copyright (c) 2015 Linaro Limited. + * + * Author: Leo Yan <leo.yan@linaro.org> + * + * 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, version 2 of the License. + * + * 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/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kfifo.h> +#include <linux/mailbox_controller.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define MBOX_CHAN_MAX 32 +#define MBOX_CHAN_NUM 2 + +#define MBOX_RX 0x0 +#define MBOX_TX 0x1 + +/* Mailbox message length: 8 words */ +#define MBOX_MSG_LEN 8 + +/* Mailbox Registers */ +#define MBOX_OFF(m) (0x40 * (m)) +#define MBOX_MODE_REG(m) (MBOX_OFF(m) + 0x0) +#define MBOX_DATA_REG(m) (MBOX_OFF(m) + 0x4) + +#define MBOX_STATE_MASK (0xF << 4) +#define MBOX_STATE_IDLE (0x1 << 4) +#define MBOX_STATE_TX (0x2 << 4) +#define MBOX_STATE_RX (0x4 << 4) +#define MBOX_STATE_ACK (0x8 << 4) +#define MBOX_ACK_CONFIG_MASK (0x1 << 0) +#define MBOX_ACK_AUTOMATIC (0x1 << 0) +#define MBOX_ACK_IRQ (0x0 << 0) + +/* IPC registers */ +#define ACK_INT_RAW_REG(i) ((i) + 0x400) +#define ACK_INT_MSK_REG(i) ((i) + 0x404) +#define ACK_INT_STAT_REG(i) ((i) + 0x408) +#define ACK_INT_CLR_REG(i) ((i) + 0x40c) +#define ACK_INT_ENA_REG(i) ((i) + 0x500) +#define ACK_INT_DIS_REG(i) ((i) + 0x504) +#define DST_INT_RAW_REG(i) ((i) + 0x420) + + +struct hi6220_mbox_chan { + + /* + * Description for channel's hardware info: + * - direction: tx or rx + * - dst irq: peer core's irq number + * - ack irq: local irq number + * - slot number + */ + unsigned int dir, dst_irq, ack_irq; + unsigned int slot; + + struct hi6220_mbox *parent; +}; + +struct hi6220_mbox { + struct device *dev; + + int irq; + + /* flag of enabling tx's irq mode */ + bool tx_irq_mode; + + /* region for ipc event */ + void __iomem *ipc; + + /* region for mailbox */ + void __iomem *base; + + unsigned int chan_num; + struct hi6220_mbox_chan *mchan; + + void *irq_map_chan[MBOX_CHAN_MAX]; + struct mbox_chan *chan; + struct mbox_controller controller; +}; + +static void mbox_set_state(struct hi6220_mbox *mbox, + unsigned int slot, u32 val) +{ + u32 status; + + status = readl(mbox->base + MBOX_MODE_REG(slot)); + status = (status & ~MBOX_STATE_MASK) | val; + writel(status, mbox->base + MBOX_MODE_REG(slot)); +} + +static void mbox_set_mode(struct hi6220_mbox *mbox, + unsigned int slot, u32 val) +{ + u32 mode; + + mode = readl(mbox->base + MBOX_MODE_REG(slot)); + mode = (mode & ~MBOX_ACK_CONFIG_MASK) | val; + writel(mode, mbox->base + MBOX_MODE_REG(slot)); +} + +static bool hi6220_mbox_last_tx_done(struct mbox_chan *chan) +{ + struct hi6220_mbox_chan *mchan = chan->con_priv; + struct hi6220_mbox *mbox = mchan->parent; + u32 state; + + /* Only set idle state for polling mode */ + BUG_ON(mbox->tx_irq_mode); + + state = readl(mbox->base + MBOX_MODE_REG(mchan->slot)); + return ((state & MBOX_STATE_MASK) == MBOX_STATE_IDLE); +} + +static int hi6220_mbox_send_data(struct mbox_chan *chan, void *msg) +{ + struct hi6220_mbox_chan *mchan = chan->con_priv; + struct hi6220_mbox *mbox = mchan->parent; + unsigned int slot = mchan->slot; + u32 *buf = msg; + int i; + + mbox_set_state(mbox, slot, MBOX_STATE_TX); + + if (mbox->tx_irq_mode) + mbox_set_mode(mbox, slot, MBOX_ACK_IRQ); + else + mbox_set_mode(mbox, slot, MBOX_ACK_AUTOMATIC); + + for (i = 0; i < MBOX_MSG_LEN; i++) + writel(buf[i], mbox->base + MBOX_DATA_REG(slot) + i * 4); + + /* trigger remote request */ + writel(BIT(mchan->dst_irq), DST_INT_RAW_REG(mbox->ipc)); + return 0; +} + +static irqreturn_t hi6220_mbox_interrupt(int irq, void *p) +{ + struct hi6220_mbox *mbox = p; + struct hi6220_mbox_chan *mchan; + struct mbox_chan *chan; + unsigned int state, intr_bit, i; + u32 msg[MBOX_MSG_LEN]; + + state = readl(ACK_INT_STAT_REG(mbox->ipc)); + if (!state) { + dev_warn(mbox->dev, "%s: spurious interrupt\n", + __func__); + return IRQ_HANDLED; + } + + while (state) { + intr_bit = __ffs(state); + state &= (state - 1); + + chan = mbox->irq_map_chan[intr_bit]; + if (!chan) { + dev_warn(mbox->dev, "%s: unexpected irq vector %d\n", + __func__, intr_bit); + continue; + } + + mchan = chan->con_priv; + if (mchan->dir == MBOX_TX) + mbox_chan_txdone(chan, 0); + else { + for (i = 0; i < MBOX_MSG_LEN; i++) + msg[i] = readl(mbox->base + + MBOX_DATA_REG(mchan->slot) + i * 4); + + mbox_chan_received_data(chan, (void *)msg); + } + + /* clear IRQ source */ + writel(BIT(mchan->ack_irq), ACK_INT_CLR_REG(mbox->ipc)); + mbox_set_state(mbox, mchan->slot, MBOX_STATE_IDLE); + } + + return IRQ_HANDLED; +} + +static int hi6220_mbox_startup(struct mbox_chan *chan) +{ + struct hi6220_mbox_chan *mchan = chan->con_priv; + struct hi6220_mbox *mbox = mchan->parent; + + mbox->irq_map_chan[mchan->ack_irq] = (void *)chan; + + /* enable interrupt */ + writel(BIT(mchan->ack_irq), ACK_INT_ENA_REG(mbox->ipc)); + return 0; +} + +static void hi6220_mbox_shutdown(struct mbox_chan *chan) +{ + struct hi6220_mbox_chan *mchan = chan->con_priv; + struct hi6220_mbox *mbox = mchan->parent; + + /* disable interrupt */ + writel(BIT(mchan->ack_irq), ACK_INT_DIS_REG(mbox->ipc)); + mbox->irq_map_chan[mchan->ack_irq] = NULL; +} + +static struct mbox_chan_ops hi6220_mbox_chan_ops = { + .send_data = hi6220_mbox_send_data, + .startup = hi6220_mbox_startup, + .shutdown = hi6220_mbox_shutdown, + .last_tx_done = hi6220_mbox_last_tx_done, +}; + +static void hi6220_mbox_init_hw(struct hi6220_mbox *mbox) +{ + struct hi6220_mbox_chan init_data[MBOX_CHAN_NUM] = { + { MBOX_RX, 1, 10 }, + { MBOX_TX, 0, 11 }, + }; + struct hi6220_mbox_chan *mchan = mbox->mchan; + int i; + + for (i = 0; i < MBOX_CHAN_NUM; i++) { + memcpy(&mchan[i], &init_data[i], sizeof(*mchan)); + mchan[i].slot = i; + mchan[i].parent = mbox; + } + + /* mask and clear all interrupt vectors */ + writel(0x0, ACK_INT_MSK_REG(mbox->ipc)); + writel(~0x0, ACK_INT_CLR_REG(mbox->ipc)); + + /* use interrupt for tx's ack */ + mbox->tx_irq_mode = true; +} + +static const struct of_device_id hi6220_mbox_of_match[] = { + { .compatible = "hisilicon,hi6220-mbox", }, + {}, +}; +MODULE_DEVICE_TABLE(of, hi6220_mbox_of_match); + +static int hi6220_mbox_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hi6220_mbox *mbox; + struct resource *res; + int i, err; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + mbox->dev = dev; + mbox->chan_num = MBOX_CHAN_NUM; + mbox->mchan = devm_kzalloc(dev, + mbox->chan_num * sizeof(*mbox->mchan), GFP_KERNEL); + if (!mbox->mchan) + return -ENOMEM; + + mbox->chan = devm_kzalloc(dev, + mbox->chan_num * sizeof(*mbox->chan), GFP_KERNEL); + if (!mbox->chan) + return -ENOMEM; + + mbox->irq = platform_get_irq(pdev, 0); + if (mbox->irq < 0) + return mbox->irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mbox->ipc = devm_ioremap_resource(dev, res); + if (IS_ERR(mbox->ipc)) { + dev_err(dev, "ioremap ipc failed\n"); + return PTR_ERR(mbox->ipc); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + mbox->base = devm_ioremap_resource(dev, res); + if (IS_ERR(mbox->base)) { + dev_err(dev, "ioremap buffer failed\n"); + return PTR_ERR(mbox->base); + } + + err = devm_request_irq(dev, mbox->irq, hi6220_mbox_interrupt, 0, + dev_name(dev), mbox); + if (err) { + dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", + err); + return -ENODEV; + } + + /* init hardware parameters */ + hi6220_mbox_init_hw(mbox); + + for (i = 0; i < mbox->chan_num; i++) { + mbox->chan[i].con_priv = &mbox->mchan[i]; + mbox->irq_map_chan[i] = NULL; + } + + mbox->controller.dev = dev; + mbox->controller.chans = &mbox->chan[0]; + mbox->controller.num_chans = mbox->chan_num; + mbox->controller.ops = &hi6220_mbox_chan_ops; + + if (mbox->tx_irq_mode) + mbox->controller.txdone_irq = true; + else { + mbox->controller.txdone_poll = true; + mbox->controller.txpoll_period = 5; + } + + err = mbox_controller_register(&mbox->controller); + if (err) { + dev_err(dev, "Failed to register mailbox %d\n", err); + return err; + } + + platform_set_drvdata(pdev, mbox); + dev_info(dev, "Mailbox enabled\n"); + return 0; +} + +static int hi6220_mbox_remove(struct platform_device *pdev) +{ + struct hi6220_mbox *mbox = platform_get_drvdata(pdev); + + mbox_controller_unregister(&mbox->controller); + return 0; +} + +static struct platform_driver hi6220_mbox_driver = { + .driver = { + .name = "hi6220-mbox", + .owner = THIS_MODULE, + .of_match_table = hi6220_mbox_of_match, + }, + .probe = hi6220_mbox_probe, + .remove = hi6220_mbox_remove, +}; + +static int __init hi6220_mbox_init(void) +{ + return platform_driver_register(&hi6220_mbox_driver); +} +core_initcall(hi6220_mbox_init); + +static void __exit hi6220_mbox_exit(void) +{ + platform_driver_unregister(&hi6220_mbox_driver); +} +module_exit(hi6220_mbox_exit); + +MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>"); +MODULE_DESCRIPTION("Hi6220 mailbox driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7398262a2fab..0226aacb2f0c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -284,6 +284,16 @@ config MFD_HI6421_PMIC menus in order to enable them. We communicate with the Hi6421 via memory-mapped I/O. +config MFD_HI655X_PMIC + tristate "HiSilicon Hi655X series PMU/Codec IC" + depends on ARCH_HISI || COMPILE_TEST + depends on OF + select MFD_CORE + select REGMAP_MMIO + select REGMAP_IRQ + help + Select this option to enable Hisilicon hi655x series pmic driver. + config HTC_EGPIO bool "HTC EGPIO support" depends on GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index a8b76b81b467..6a7b0e1fe6ba 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -186,6 +186,7 @@ obj-$(CONFIG_MFD_STW481X) += stw481x.o obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o +obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o obj-$(CONFIG_MFD_DLN2) += dln2.o obj-$(CONFIG_MFD_RT5033) += rt5033.o obj-$(CONFIG_MFD_SKY81452) += sky81452.o diff --git a/drivers/mfd/hi655x-pmic.c b/drivers/mfd/hi655x-pmic.c new file mode 100644 index 000000000000..20607af75927 --- /dev/null +++ b/drivers/mfd/hi655x-pmic.c @@ -0,0 +1,176 @@ +/* + * Device driver for MFD hi655x PMIC + * + * Copyright (c) 2016 Hisilicon. + * + * Authors: + * Chen Feng <puck.chen@hisilicon.com> + * Fei Wang <w.f@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/gpio.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/mfd/core.h> +#include <linux/mfd/hi655x-pmic.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +static const struct mfd_cell hi655x_pmic_devs[] = { + { .name = "hi655x-regulator", }, +}; + +static const struct regmap_irq hi655x_irqs[] = { + { .reg_offset = 0, .mask = OTMP_D1R_INT }, + { .reg_offset = 0, .mask = VSYS_2P5_R_INT }, + { .reg_offset = 0, .mask = VSYS_UV_D3R_INT }, + { .reg_offset = 0, .mask = VSYS_6P0_D200UR_INT }, + { .reg_offset = 0, .mask = PWRON_D4SR_INT }, + { .reg_offset = 0, .mask = PWRON_D20F_INT }, + { .reg_offset = 0, .mask = PWRON_D20R_INT }, + { .reg_offset = 0, .mask = RESERVE_INT }, +}; + +static struct of_device_id of_hi655x_pmic_child_match_tbl[] = { + { .compatible = "hisilicon,hi6552-regulator-pmic", }, + { .compatible = "hisilicon,hi6552-powerkey", }, + { .compatible = "hisilicon,hi6552-usbvbus", }, + { .compatible = "hisilicon,hi6552-coul", }, + { .compatible = "hisilicon,hi6552-pmu-rtc", }, + { .compatible = "hisilicon,hi6552-pmic-mntn", }, + { /* end */ } +}; + +static const struct regmap_irq_chip hi655x_irq_chip = { + .name = "hi655x-pmic", + .irqs = hi655x_irqs, + .num_regs = 1, + .num_irqs = ARRAY_SIZE(hi655x_irqs), + .status_base = HI655X_IRQ_STAT_BASE, + .ack_base = HI655X_IRQ_STAT_BASE, + .mask_base = HI655X_IRQ_MASK_BASE, +}; + +static struct regmap_config hi655x_regmap_config = { + .reg_bits = 32, + .reg_stride = HI655X_STRIDE, + .val_bits = 8, + .max_register = HI655X_BUS_ADDR(0xFFF), +}; + +static void hi655x_local_irq_clear(struct regmap *map) +{ + int i; + + regmap_write(map, HI655X_ANA_IRQM_BASE, HI655X_IRQ_CLR); + for (i = 0; i < HI655X_IRQ_ARRAY; i++) { + regmap_write(map, HI655X_IRQ_STAT_BASE + i * HI655X_STRIDE, + HI655X_IRQ_CLR); + } +} + +static int hi655x_pmic_probe(struct platform_device *pdev) +{ + int ret; + struct hi655x_pmic *pmic; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void __iomem *base; + + pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + pmic->dev = dev; + + pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!pmic->res) + return -ENOENT; + + base = devm_ioremap_resource(dev, pmic->res); + if (!base) + return -ENOMEM; + + pmic->regmap = devm_regmap_init_mmio_clk(dev, NULL, base, + &hi655x_regmap_config); + + regmap_read(pmic->regmap, HI655X_BUS_ADDR(HI655X_VER_REG), &pmic->ver); + if ((pmic->ver < PMU_VER_START) || (pmic->ver > PMU_VER_END)) { + dev_warn(dev, "PMU version %d unsupported\n", pmic->ver); + return -EINVAL; + } + + hi655x_local_irq_clear(pmic->regmap); + + pmic->gpio = of_get_named_gpio(np, "pmic-gpios", 0); + if (!gpio_is_valid(pmic->gpio)) { + dev_err(dev, "Failed to get the pmic-gpios\n"); + return -ENODEV; + } + + ret = devm_gpio_request_one(dev, pmic->gpio, GPIOF_IN, + "hi655x_pmic_irq"); + if (ret < 0) { + dev_err(dev, "Failed to request gpio %d ret = %d\n", + pmic->gpio, ret); + return ret; + } + + ret = regmap_add_irq_chip(pmic->regmap, gpio_to_irq(pmic->gpio), + IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND, 0, + &hi655x_irq_chip, &pmic->irq_data); + if (ret) { + dev_err(dev, "Failed to obtain 'hi655x_pmic_irq' %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, pmic); + + /* populate sub nodes */ + of_platform_populate(np, of_hi655x_pmic_child_match_tbl, NULL, dev); + + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, hi655x_pmic_devs, + ARRAY_SIZE(hi655x_pmic_devs), NULL, 0, NULL); + if (ret) { + dev_err(dev, "Failed to register device %d\n", ret); + regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data); + return ret; + } + + return 0; +} + +static int hi655x_pmic_remove(struct platform_device *pdev) +{ + struct hi655x_pmic *pmic = platform_get_drvdata(pdev); + + regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data); + mfd_remove_devices(&pdev->dev); + return 0; +} + +static const struct of_device_id hi655x_pmic_match[] = { + { .compatible = "hisilicon,hi655x-pmic", }, + {}, +}; + +static struct platform_driver hi655x_pmic_driver = { + .driver = { + .name = "hi655x-pmic", + .of_match_table = of_match_ptr(hi655x_pmic_match), + }, + .probe = hi655x_pmic_probe, + .remove = hi655x_pmic_remove, +}; +module_platform_driver(hi655x_pmic_driver); + +MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>"); +MODULE_DESCRIPTION("Hisilicon hi655x PMIC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 4bf7d50b1bc7..c4bbd7280e9d 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -466,6 +466,14 @@ config BMP085_SPI To compile this driver as a module, choose M here: the module will be called bmp085-spi. +config HI6220_SYSCFG + bool "Hisilicon HI6220 System Configuration driver" + depends on ARCH_HISI + default y + help + Hisilicon HI6220 uses some registers to configure some chip hosts to + work or not, e.g. disable the UART hosts reset and let's them work. + config PCH_PHUB tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB" select GENERIC_NET_UTILS diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 537d7f3b78da..b84697a9010b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o obj-$(CONFIG_BMP085) += bmp085.o obj-$(CONFIG_BMP085_I2C) += bmp085-i2c.o obj-$(CONFIG_BMP085_SPI) += bmp085-spi.o +obj-$(CONFIG_HI6220_SYSCFG) += hi6220-sysconfig.o obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o obj-$(CONFIG_ICS932S401) += ics932s401.o obj-$(CONFIG_LKDTM) += lkdtm.o diff --git a/drivers/misc/hi6220-sysconfig.c b/drivers/misc/hi6220-sysconfig.c new file mode 100644 index 000000000000..9b9d5d8bff19 --- /dev/null +++ b/drivers/misc/hi6220-sysconfig.c @@ -0,0 +1,72 @@ +/* + * For Hisilicon Hi6220 SoC, the reset of some hosts (e.g. UART) should be disabled + * before using them, this driver will handle the host chip reset disable. + * + * Copyright (C) 2015 Hisilicon Ltd. + * Author: Bintian Wang <bintian.wang@huawei.com> + * + */ + +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#define reset_offset 0x334 +#define pclk_offset 0x230 +#define PMUSSI_REG_EX(pmu_base, reg_addr) (((reg_addr) << 2) + (char *)pmu_base) + +static int __init hi6220_sysconf(void) +{ + static void __iomem *base = NULL; + struct device_node *node; + static void __iomem *base1 = NULL; + struct device_node *node1; + unsigned char ret; + + node = of_find_compatible_node(NULL, NULL, "hisilicon,hi6220-sysctrl"); + if (!node) + return -ENOENT; + + base = of_iomap(node, 0); + if (base == NULL) { + printk(KERN_ERR "hi6220: sysctrl reg iomap failed!\n"); + return -ENOMEM; + } + + /*Disable UART1 reset and set pclk*/ + writel(BIT(5), base + reset_offset); + writel(BIT(5), base + pclk_offset); + + /*Disable UART2 reset and set pclk*/ + writel(BIT(6), base + reset_offset); + writel(BIT(6), base + pclk_offset); + + /*Disable UART3 reset and set pclk*/ + writel(BIT(7), base + reset_offset); + writel(BIT(7), base + pclk_offset); + + /*Disable UART4 reset and set pclk*/ + writel(BIT(8), base + reset_offset); + writel(BIT(8), base + pclk_offset); + + iounmap(base); + + node1 = of_find_compatible_node(NULL, NULL, "hisilicon,hi655x-pmic"); + if (!node1) + return -ENOENT; + + base1 = of_iomap(node1, 0); + if (base1 == NULL) { + printk(KERN_ERR "hi6220: pmic reg iomap failed!\n"); + return -ENOMEM; + } + + /*enable clk for BT/WIFI*/ + ret = *(volatile unsigned char*)PMUSSI_REG_EX(base1, 0x1c); + ret |= 0x40; + *(volatile unsigned char*)PMUSSI_REG_EX(base1, 0x1c) = ret; + + iounmap(base1); + return 0; +} +postcore_initcall(hi6220_sysconf); diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 63c2e2ed1288..6aa6d0d63f7f 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -32,6 +32,12 @@ struct k3_priv { struct regmap *reg; }; +static unsigned long dw_mci_hi6220_caps[] = { + MMC_CAP_CMD23, + MMC_CAP_CMD23, + 0 +}; + static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios) { int ret; @@ -125,10 +131,17 @@ static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios) host->bus_hz = clk_get_rate(host->biu_clk); } +static void dw_mci_hi6220_prepare_command(struct dw_mci *host, u32 *cmdr) +{ + *cmdr |= SDMMC_CMD_USE_HOLD_REG; +} + static const struct dw_mci_drv_data hi6220_data = { + .caps = dw_mci_hi6220_caps, .switch_voltage = dw_mci_hi6220_switch_voltage, .set_ios = dw_mci_hi6220_set_ios, .parse_dt = dw_mci_hi6220_parse_dt, + .prepare_command = dw_mci_hi6220_prepare_command, }; static const struct of_device_id dw_mci_k3_match[] = { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 581f5d0271f4..184c7575ebd7 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -250,7 +250,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr |= SDMMC_CMD_PRV_DAT_WAIT; if (cmd->opcode == SD_SWITCH_VOLTAGE) { - u32 clk_en_a; + /*u32 clk_en_a;*/ /* Special bit makes CMD11 not die */ cmdr |= SDMMC_CMD_VOLT_SWITCH; @@ -270,11 +270,11 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) * ever called with a non-zero clock. That shouldn't happen * until the voltage change is all done. */ - clk_en_a = mci_readl(host, CLKENA); - clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id); - mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | - SDMMC_CMD_PRV_DAT_WAIT, 0); + /*clk_en_a = mci_readl(host, CLKENA);*/ + /*clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id);*/ + /*mci_writel(host, CLKENA, clk_en_a);*/ + /*mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |*/ + /*SDMMC_CMD_PRV_DAT_WAIT, 0);*/ } if (cmd->flags & MMC_RSP_PRESENT) { @@ -1126,7 +1126,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; - if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags)) + if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags) && (slot->mmc->index != 1)) clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; mci_writel(host, CLKENA, clk_en_a); @@ -1300,6 +1300,8 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) regs |= ((0x1 << slot->id) << 16); else regs &= ~((0x1 << slot->id) << 16); + if (mmc->index == 1) + regs |= (0x1 << slot->id); mci_writel(slot->host, UHS_REG, regs); slot->host->timing = ios->timing; @@ -1542,6 +1544,8 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) if (drv_data && drv_data->execute_tuning) err = drv_data->execute_tuning(slot, opcode); + else + err = 0; return err; } @@ -2881,6 +2885,15 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) if (!pdata) return ERR_PTR(-ENOMEM); + /* find reset controller when exist */ + pdata->rstc = devm_reset_control_get_optional(dev, NULL); + if (IS_ERR(pdata->rstc)) { + if (PTR_ERR(pdata->rstc) == -EPROBE_DEFER) + return ERR_PTR(-EPROBE_DEFER); + else + pdata->rstc = NULL; + } + /* find out number of slots supported */ if (of_property_read_u32(dev->of_node, "num-slots", &pdata->num_slots)) { @@ -2961,7 +2974,9 @@ int dw_mci_probe(struct dw_mci *host) if (!host->pdata) { host->pdata = dw_mci_parse_dt(host); - if (IS_ERR(host->pdata)) { + if (PTR_ERR(host->pdata) == -EPROBE_DEFER) + return -EPROBE_DEFER; + else if (IS_ERR(host->pdata)) { dev_err(host->dev, "platform data not available\n"); return -EINVAL; } @@ -3030,6 +3045,9 @@ int dw_mci_probe(struct dw_mci *host) } } + if (host->pdata->rstc != NULL) + reset_control_deassert(host->pdata->rstc); + setup_timer(&host->cmd11_timer, dw_mci_cmd11_timer, (unsigned long)host); @@ -3176,6 +3194,9 @@ err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); + if (host->pdata->rstc != NULL) + reset_control_assert(host->pdata->rstc); + err_clk_ciu: if (!IS_ERR(host->ciu_clk)) clk_disable_unprepare(host->ciu_clk); @@ -3208,11 +3229,15 @@ void dw_mci_remove(struct dw_mci *host) if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); + if (host->pdata->rstc != NULL) + reset_control_assert(host->pdata->rstc); + if (!IS_ERR(host->ciu_clk)) clk_disable_unprepare(host->ciu_clk); if (!IS_ERR(host->biu_clk)) clk_disable_unprepare(host->biu_clk); + } EXPORT_SYMBOL(dw_mci_remove); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 00676208080e..10c46e236e8b 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -261,6 +261,21 @@ config REGULATOR_HI6421 21 general purpose LDOs, 3 dedicated LDOs, and 5 BUCKs. All of them come with support to either ECO (idle) or sleep mode. +config REGULATOR_HI6220_MTCMOS + bool "Hisilicon Hi6220 mtcmos support" + depends on ARCH_HISI + help + This driver provides support for the mtcmos regulators of Hi6220 Soc. + + +config REGULATOR_HI655X + tristate "Hisilicon HI655X PMIC regulators support" + depends on ARCH_HISI || COMPILE_TEST + depends on MFD_HI655X_PMIC && OF + help + This driver provides support for the voltage regulators of the + Hisilicon Hi655x PMIC device. + config REGULATOR_ISL9305 tristate "Intersil ISL9305 regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 0f8174913c17..7345d43551ee 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -34,6 +34,8 @@ obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o +obj-$(CONFIG_REGULATOR_HI6220_MTCMOS) += hi6220-mtcmos.o +obj-$(CONFIG_REGULATOR_HI655X) += hi655x-regulator.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_ISL9305) += isl9305.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o diff --git a/drivers/regulator/hi6220-mtcmos.c b/drivers/regulator/hi6220-mtcmos.c new file mode 100644 index 000000000000..492be7adfaa2 --- /dev/null +++ b/drivers/regulator/hi6220-mtcmos.c @@ -0,0 +1,269 @@ +/* + * Device driver for MTCMOS DRIVER in HI6220 SOC + * + * Copyright (c) 2011 Hisilicon Co. Ltd + * + */ +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/delay.h> +#include <linux/time.h> + +enum { + HI6220_MTCMOS1, + HI6220_MTCMOS2, + HI6220_RG_MAX, +}; + +struct hi6220_mtcmos_ctrl_regs { + unsigned int enable_reg; + unsigned int disable_reg; + unsigned int status_reg; +}; + +struct hi6220_mtcmos_ctrl_data { + int shift; + unsigned int mask; +}; + +struct hi6220_mtcmos_info { + struct regulator_desc rdesc; + struct hi6220_mtcmos_ctrl_regs ctrl_regs; + struct hi6220_mtcmos_ctrl_data ctrl_data; +}; + +struct hi6220_mtcmos { + struct regulator_dev *rdev[HI6220_RG_MAX]; + void __iomem *sc_on_regs; + int mtcmos_steady_time; + spinlock_t mtcmos_spin_lock; +}; + +static int hi6220_mtcmos_is_on(struct hi6220_mtcmos *mtcmos, + unsigned int regs, unsigned int mask, int shift) +{ + unsigned int ret; + unsigned long mtcmos_spin_flag = 0; + + spin_lock_irqsave(&mtcmos->mtcmos_spin_lock, mtcmos_spin_flag); + ret = readl(mtcmos->sc_on_regs + regs); + spin_unlock_irqrestore(&mtcmos->mtcmos_spin_lock, mtcmos_spin_flag); + + ret &= (mask << shift); + return !!ret; +} + +int hi6220_mtcmos_on(struct hi6220_mtcmos *mtcmos, + unsigned int regs, unsigned int mask, int shift) +{ + unsigned long mtcmos_spin_flag = 0; + + spin_lock_irqsave(&mtcmos->mtcmos_spin_lock, mtcmos_spin_flag); + writel(mask << shift, mtcmos->sc_on_regs + regs); + udelay(mtcmos->mtcmos_steady_time); + spin_unlock_irqrestore(&mtcmos->mtcmos_spin_lock, mtcmos_spin_flag); + + return 0; +} + +int hi6220_mtcmos_off(struct hi6220_mtcmos *mtcmos, + unsigned int regs, unsigned int mask, int shift) +{ + unsigned long mtcmos_spin_flag = 0; + + spin_lock_irqsave(&mtcmos->mtcmos_spin_lock, mtcmos_spin_flag); + writel(mask << shift, mtcmos->sc_on_regs + regs); + udelay(mtcmos->mtcmos_steady_time); + spin_unlock_irqrestore(&mtcmos->mtcmos_spin_lock, + mtcmos_spin_flag); + + return 0; +} + +static int hi6220_regulator_mtcmos_is_enabled(struct regulator_dev *rdev) +{ + int ret; + struct hi6220_mtcmos_info *sreg = rdev_get_drvdata(rdev); + struct platform_device *pdev = + container_of(rdev->dev.parent, struct platform_device, dev); + struct hi6220_mtcmos *mtcmos = platform_get_drvdata(pdev); + struct hi6220_mtcmos_ctrl_regs *ctrl_regs = &(sreg->ctrl_regs); + struct hi6220_mtcmos_ctrl_data *ctrl_data = &(sreg->ctrl_data); + + ret = hi6220_mtcmos_is_on(mtcmos, ctrl_regs->status_reg, + ctrl_data->mask, ctrl_data->shift); + return ret; +} + +static int hi6220_regulator_mtcmos_enabled(struct regulator_dev *rdev) +{ + int ret; + struct hi6220_mtcmos_info *sreg = rdev_get_drvdata(rdev); + struct platform_device *pdev = + container_of(rdev->dev.parent, struct platform_device, dev); + struct hi6220_mtcmos *mtcmos = platform_get_drvdata(pdev); + struct hi6220_mtcmos_ctrl_regs *ctrl_regs = &(sreg->ctrl_regs); + struct hi6220_mtcmos_ctrl_data *ctrl_data = &(sreg->ctrl_data); + + ret = hi6220_mtcmos_on(mtcmos, ctrl_regs->enable_reg, + ctrl_data->mask, ctrl_data->shift); + if (0 == hi6220_mtcmos_is_on(mtcmos, ctrl_regs->status_reg, + ctrl_data->mask, ctrl_data->shift)) { + return -1; + } + return ret; +} + +static int hi6220_regulator_mtcmos_disabled(struct regulator_dev *rdev) +{ + int ret; + struct hi6220_mtcmos_info *sreg = rdev_get_drvdata(rdev); + struct platform_device *pdev = + container_of(rdev->dev.parent, struct platform_device, dev); + struct hi6220_mtcmos *mtcmos = platform_get_drvdata(pdev); + struct hi6220_mtcmos_ctrl_regs *ctrl_regs = &(sreg->ctrl_regs); + struct hi6220_mtcmos_ctrl_data *ctrl_data = &(sreg->ctrl_data); + + ret = hi6220_mtcmos_off(mtcmos, ctrl_regs->disable_reg, + ctrl_data->mask, ctrl_data->shift); + + return ret; +} + +static struct regulator_ops hi6220_mtcmos_mtcmos_rops = { + .is_enabled = hi6220_regulator_mtcmos_is_enabled, + .enable = hi6220_regulator_mtcmos_enabled, + .disable = hi6220_regulator_mtcmos_disabled, +}; + +#define HI6220_MTCMOS(vreg) \ +{ \ + .rdesc = { \ + .name = #vreg, \ + .ops = &hi6220_mtcmos_mtcmos_rops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ +} + +static struct hi6220_mtcmos_info hi6220_mtcmos_info[] = { + HI6220_MTCMOS(MTCMOS1), + HI6220_MTCMOS(MTCMOS2), +}; + +static struct of_regulator_match hi6220_mtcmos_matches[] = { + { .name = "mtcmos1", + .driver_data = &hi6220_mtcmos_info[HI6220_MTCMOS1], }, + { .name = "mtcmos2", + .driver_data = &hi6220_mtcmos_info[HI6220_MTCMOS2], }, +}; + +static int hi6220_mtcmos_probe(struct platform_device *pdev) +{ + int ret; + struct hi6220_mtcmos *mtcmos; + const __be32 *sc_on_regs = NULL; + void __iomem *regs; + struct device *dev; + struct device_node *np, *child; + int count, i; + struct regulator_config config = { }; + struct regulator_init_data *init_data; + struct hi6220_mtcmos_info *sreg; + + dev = &pdev->dev; + np = dev->of_node; + mtcmos = devm_kzalloc(dev, + sizeof(struct hi6220_mtcmos), GFP_KERNEL); + if (!mtcmos) { + dev_err(dev, "cannot allocate hi6220_mtcmos device info\n"); + return -ENOMEM; + } + + spin_lock_init((spinlock_t *)&mtcmos->mtcmos_spin_lock); + sc_on_regs = of_get_property(np, "hisilicon,mtcmos-sc-on-base", NULL); + if (sc_on_regs) { + regs = ioremap(be32_to_cpu(*sc_on_regs), 0x1000); + mtcmos->sc_on_regs = regs; + } + ret = of_property_read_u32(np, "hisilicon,mtcmos-steady-us", + &mtcmos->mtcmos_steady_time); + + count = of_regulator_match(&pdev->dev, np, + hi6220_mtcmos_matches, + ARRAY_SIZE(hi6220_mtcmos_matches)); + + for (i = 0; i < HI6220_RG_MAX; i++) { + init_data = hi6220_mtcmos_matches[i].init_data; + if (!init_data) + continue; + sreg = hi6220_mtcmos_matches[i].driver_data; + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = sreg; + config.of_node = hi6220_mtcmos_matches[i].of_node; + child = config.of_node; + + ret = of_property_read_u32_array(child, "hisilicon,ctrl-regs", + (unsigned int *)(&sreg->ctrl_regs), 0x3); + ret = of_property_read_u32_array(child, "hisilicon,ctrl-data", + (unsigned int *)(&sreg->ctrl_data), 0x2); + + mtcmos->rdev[i] = regulator_register(&sreg->rdesc, &config); + if (IS_ERR(mtcmos->rdev[i])) { + ret = PTR_ERR(mtcmos->rdev[i]); + dev_err(&pdev->dev, "failed to register mtcmos %s\n", + sreg->rdesc.name); + while (--i >= 0) + regulator_unregister(mtcmos->rdev[i]); + + return ret; + } + } + + platform_set_drvdata(pdev, mtcmos); + + return 0; +} + +static struct of_device_id of_hi6220_mtcmos_match_tbl[] = { + { .compatible = "hisilicon,hi6220-mtcmos-driver", }, + {} +}; + +static struct platform_driver mtcmos_driver = { + .driver = { + .name = "hisi_hi6220_mtcmos", + .owner = THIS_MODULE, + .of_match_table = of_hi6220_mtcmos_match_tbl, + }, + .probe = hi6220_mtcmos_probe, +}; + +static int __init hi6220_mtcmos_init(void) +{ + return platform_driver_register(&mtcmos_driver); +} + +static void __exit hi6220_mtcmos_exit(void) +{ + platform_driver_unregister(&mtcmos_driver); +} + +fs_initcall(hi6220_mtcmos_init); +module_exit(hi6220_mtcmos_exit); + +MODULE_AUTHOR("Baixing Quan<quanbaixing@huawei.com>"); +MODULE_DESCRIPTION("HI6220 MTCMOS interface driver"); +MODULE_LICENSE("GPL V2"); diff --git a/drivers/regulator/hi655x-regulator.c b/drivers/regulator/hi655x-regulator.c new file mode 100644 index 000000000000..9074355791a2 --- /dev/null +++ b/drivers/regulator/hi655x-regulator.c @@ -0,0 +1,226 @@ +/* + * Device driver for regulators in hi655x IC + * + * Copyright (c) 2016 Hisilicon. + * + * Authors: + * Chen Feng <puck.chen@hisilicon.com> + * Fei Wang <w.f@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/mfd/hi655x-pmic.h> + +struct hi655x_regulator { + unsigned int disable_reg; + unsigned int status_reg; + unsigned int ctrl_regs; + unsigned int ctrl_mask; + struct regulator_desc rdesc; +}; + +static const unsigned int ldo7_voltages[] = { + 1800000, 1850000, 2850000, 2900000, + 3000000, 3100000, 3200000, 3300000, +}; + +static const unsigned int ldo19_voltages[] = { + 1800000, 1850000, 1900000, 1750000, + 2800000, 2850000, 2900000, 3000000, +}; + +static const unsigned int ldo22_voltages[] = { + 900000, 1000000, 1050000, 1100000, + 1150000, 1175000, 1185000, 1200000, +}; + +enum hi655x_regulator_id { + HI655X_LDO0, + HI655X_LDO1, + HI655X_LDO2, + HI655X_LDO3, + HI655X_LDO4, + HI655X_LDO5, + HI655X_LDO6, + HI655X_LDO7, + HI655X_LDO8, + HI655X_LDO9, + HI655X_LDO10, + HI655X_LDO11, + HI655X_LDO12, + HI655X_LDO13, + HI655X_LDO14, + HI655X_LDO15, + HI655X_LDO16, + HI655X_LDO17, + HI655X_LDO18, + HI655X_LDO19, + HI655X_LDO20, + HI655X_LDO21, + HI655X_LDO22, +}; + +static int hi655x_is_enabled(struct regulator_dev *rdev) +{ + unsigned int value = 0; + + struct hi655x_regulator *regulator = rdev_get_drvdata(rdev); + + regmap_read(rdev->regmap, regulator->status_reg, &value); + return (value & BIT(regulator->ctrl_mask)); +} + +static int hi655x_disable(struct regulator_dev *rdev) +{ + int ret = 0; + + struct hi655x_regulator *regulator = rdev_get_drvdata(rdev); + + ret = regmap_write(rdev->regmap, regulator->disable_reg, + BIT(regulator->ctrl_mask)); + return ret; +} + +static struct regulator_ops hi655x_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = hi655x_disable, + .is_enabled = hi655x_is_enabled, + .list_voltage = regulator_list_voltage_table, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static struct regulator_ops hi655x_ldo_linear_ops = { + .enable = regulator_enable_regmap, + .disable = hi655x_disable, + .is_enabled = hi655x_is_enabled, + .list_voltage = regulator_list_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +#define HI655X_LDO(_ID, vreg, vmask, ereg, dreg, \ + sreg, cmask, vtable) { \ + .rdesc = { \ + .name = #_ID, \ + .of_match = of_match_ptr(#_ID), \ + .ops = &hi655x_regulator_ops, \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .id = HI655X_##_ID, \ + .owner = THIS_MODULE, \ + .n_voltages = ARRAY_SIZE(vtable), \ + .volt_table = vtable, \ + .vsel_reg = HI655X_BUS_ADDR(vreg), \ + .vsel_mask = vmask, \ + .enable_reg = HI655X_BUS_ADDR(ereg), \ + .enable_mask = BIT(cmask), \ + }, \ + .disable_reg = HI655X_BUS_ADDR(dreg), \ + .status_reg = HI655X_BUS_ADDR(sreg), \ + .ctrl_mask = cmask, \ +} + +#define HI655X_LDO_LINEAR(_ID, vreg, vmask, ereg, dreg, \ + sreg, cmask, minv, nvolt, vstep) { \ + .rdesc = { \ + .name = #_ID, \ + .of_match = of_match_ptr(#_ID), \ + .ops = &hi655x_ldo_linear_ops, \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .id = HI655X_##_ID, \ + .owner = THIS_MODULE, \ + .min_uV = minv, \ + .n_voltages = nvolt, \ + .uV_step = vstep, \ + .vsel_reg = HI655X_BUS_ADDR(vreg), \ + .vsel_mask = vmask, \ + .enable_reg = HI655X_BUS_ADDR(ereg), \ + .enable_mask = BIT(cmask), \ + }, \ + .disable_reg = HI655X_BUS_ADDR(dreg), \ + .status_reg = HI655X_BUS_ADDR(sreg), \ + .ctrl_mask = cmask, \ +} + +static struct hi655x_regulator regulators[] = { + HI655X_LDO_LINEAR(LDO2, 0x72, 0x07, 0x29, 0x2a, 0x2b, 0x01, + 2500000, 8, 100000), + HI655X_LDO(LDO7, 0x78, 0x07, 0x29, 0x2a, 0x2b, 0x06, ldo7_voltages), + HI655X_LDO(LDO10, 0x78, 0x07, 0x29, 0x2a, 0x2b, 0x01, ldo7_voltages), + HI655X_LDO_LINEAR(LDO13, 0x7e, 0x07, 0x2c, 0x2d, 0x2e, 0x04, + 1600000, 8, 50000), + HI655X_LDO_LINEAR(LDO14, 0x7f, 0x07, 0x2c, 0x2d, 0x2e, 0x05, + 2500000, 8, 100000), + HI655X_LDO_LINEAR(LDO15, 0x80, 0x07, 0x2c, 0x2d, 0x2e, 0x06, + 1600000, 8, 50000), + HI655X_LDO_LINEAR(LDO17, 0x82, 0x07, 0x2f, 0x30, 0x31, 0x00, + 2500000, 8, 100000), + HI655X_LDO(LDO19, 0x84, 0x07, 0x2f, 0x30, 0x31, 0x02, ldo19_voltages), + HI655X_LDO_LINEAR(LDO21, 0x86, 0x07, 0x2f, 0x30, 0x31, 0x04, + 1650000, 8, 50000), + HI655X_LDO(LDO22, 0x87, 0x07, 0x2f, 0x30, 0x31, 0x05, ldo22_voltages), +}; + +static int hi655x_regulator_probe(struct platform_device *pdev) +{ + unsigned int i; + struct hi655x_regulator *regulator; + struct hi655x_pmic *pmic; + struct regulator_config config = { }; + struct regulator_dev *rdev; + + pmic = dev_get_drvdata(pdev->dev.parent); + if (!pmic) { + dev_err(&pdev->dev, "no pmic in the regulator parent node\n"); + return -ENODEV; + } + + regulator = devm_kzalloc(&pdev->dev, sizeof(*regulator), GFP_KERNEL); + if (!regulator) + return -ENOMEM; + + platform_set_drvdata(pdev, regulator); + + config.dev = pdev->dev.parent; + config.regmap = pmic->regmap; + config.driver_data = regulator; + for (i = 0; i < ARRAY_SIZE(regulators); i++) { + rdev = devm_regulator_register(&pdev->dev, + ®ulators[i].rdesc, + &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + regulator->rdesc.name); + return PTR_ERR(rdev); + } + } + return 0; +} + +static struct platform_driver hi655x_regulator_driver = { + .driver = { + .name = "hi655x-regulator", + }, + .probe = hi655x_regulator_probe, +}; +module_platform_driver(hi655x_regulator_driver); + +MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>"); +MODULE_DESCRIPTION("Hisilicon hi655x regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 0615f50a14cd..df37212a5cbd 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -13,3 +13,4 @@ menuconfig RESET_CONTROLLER If unsure, say no. source "drivers/reset/sti/Kconfig" +source "drivers/reset/hisilicon/Kconfig" diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 85d5904e5480..99e18c875e8a 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o obj-$(CONFIG_ARCH_STI) += sti/ +obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o obj-$(CONFIG_ATH79) += reset-ath79.o diff --git a/drivers/reset/hisilicon/Kconfig b/drivers/reset/hisilicon/Kconfig new file mode 100644 index 000000000000..26bf95a83a8e --- /dev/null +++ b/drivers/reset/hisilicon/Kconfig @@ -0,0 +1,5 @@ +config COMMON_RESET_HI6220 + tristate "Hi6220 Reset Driver" + depends on (ARCH_HISI && RESET_CONTROLLER) + help + Build the Hisilicon Hi6220 reset driver. diff --git a/drivers/reset/hisilicon/Makefile b/drivers/reset/hisilicon/Makefile new file mode 100644 index 000000000000..c932f86e2f10 --- /dev/null +++ b/drivers/reset/hisilicon/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_COMMON_RESET_HI6220) += hi6220_reset.o diff --git a/drivers/reset/hisilicon/hi6220_reset.c b/drivers/reset/hisilicon/hi6220_reset.c new file mode 100644 index 000000000000..ad708bdb2448 --- /dev/null +++ b/drivers/reset/hisilicon/hi6220_reset.c @@ -0,0 +1,164 @@ +/* + * Hisilicon Hi6220 reset controller driver + * + * Copyright (c) 2015-2016 Hisilicon Limited. + * + * Author: Feng Chen <puck.chen@hisilicon.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/io.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/bitops.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <linux/reset-controller.h> +#include <linux/reset.h> +#include <linux/platform_device.h> + +/* + * peripheral ctrl regs + */ +#define PERIPH_ASSERT_OFFSET 0x300 +#define PERIPH_DEASSERT_OFFSET 0x304 +#define PERIPH_MAX_INDEX 0x509 + +/* + * media ctrl regs + */ +#define SC_MEDIA_RSTEN 0x052C +#define SC_MEDIA_RSTDIS 0x0530 +#define MEDIA_MAX_INDEX 8 + +enum hi6220_reset_ctrl_type { + PERIPHERAL, + MEDIA, +}; + +#define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev) + +struct hi6220_reset_data { + struct reset_controller_dev rc_dev; + enum hi6220_reset_ctrl_type type; + struct regmap *regmap; +}; + +static int hi6220_media_assert(struct reset_controller_dev *rc_dev, + unsigned long idx) +{ + struct hi6220_reset_data *data = to_reset_data(rc_dev); + struct regmap *regmap = data->regmap; + + return regmap_write(regmap, SC_MEDIA_RSTEN, BIT(idx)); +} + +static int hi6220_media_deassert(struct reset_controller_dev *rc_dev, + unsigned long idx) +{ + struct hi6220_reset_data *data = to_reset_data(rc_dev); + struct regmap *regmap = data->regmap; + + return regmap_write(regmap, SC_MEDIA_RSTDIS, BIT(idx)); +} + +static int hi6220_peripheral_assert(struct reset_controller_dev *rc_dev, + unsigned long idx) +{ + struct hi6220_reset_data *data = to_reset_data(rc_dev); + struct regmap *regmap = data->regmap; + u32 bank = idx >> 8; + u32 offset = idx & 0xff; + u32 reg = PERIPH_ASSERT_OFFSET + bank * 0x10; + + return regmap_write(regmap, reg, BIT(offset)); +} + +static int hi6220_peripheral_deassert(struct reset_controller_dev *rc_dev, + unsigned long idx) +{ + struct hi6220_reset_data *data = to_reset_data(rc_dev); + struct regmap *regmap = data->regmap; + u32 bank = idx >> 8; + u32 offset = idx & 0xff; + u32 reg = PERIPH_DEASSERT_OFFSET + bank * 0x10; + + return regmap_write(regmap, reg, BIT(offset)); +} + +static const struct reset_control_ops hi6220_media_reset_ops = { + .assert = hi6220_media_assert, + .deassert = hi6220_media_deassert, +}; + +static const struct reset_control_ops hi6220_peripheral_reset_ops = { + .assert = hi6220_peripheral_assert, + .deassert = hi6220_peripheral_deassert, +}; + +static int hi6220_reset_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + enum hi6220_reset_ctrl_type type; + struct hi6220_reset_data *data; + struct regmap *regmap; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + type = (enum hi6220_reset_ctrl_type)of_device_get_match_data(dev); + + regmap = syscon_node_to_regmap(np); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to get reset controller regmap\n"); + return PTR_ERR(regmap); + } + + data->type = type; + data->regmap = regmap; + data->rc_dev.of_node = np; + if (type == MEDIA) { + data->rc_dev.ops = &hi6220_media_reset_ops; + data->rc_dev.nr_resets = MEDIA_MAX_INDEX; + } else { + data->rc_dev.ops = &hi6220_peripheral_reset_ops; + data->rc_dev.nr_resets = PERIPH_MAX_INDEX; + } + + return reset_controller_register(&data->rc_dev); +} + +static const struct of_device_id hi6220_reset_match[] = { + { + .compatible = "hisilicon,hi6220-sysctrl", + .data = (void *)PERIPHERAL, + }, + { + .compatible = "hisilicon,hi6220-mediactrl", + .data = (void *)MEDIA, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, hi6220_reset_match); + +static struct platform_driver hi6220_reset_driver = { + .probe = hi6220_reset_probe, + .driver = { + .name = "reset-hi6220", + .of_match_table = hi6220_reset_match, + }, +}; + +static int __init hi6220_reset_init(void) +{ + return platform_driver_register(&hi6220_reset_driver); +} + +postcore_initcall(hi6220_reset_init); |