diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-02 16:35:49 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-02 16:35:49 -0700 |
commit | a727eaf64ff084a50b983fc506810c7a576b7ce3 (patch) | |
tree | cb82642227ed590ebc43b12cfad285a2d7681d5d /arch | |
parent | 755a9ba7bf24a45b6dbf8bb15a5a56c8ed12461a (diff) | |
parent | 45e70b7d48d53d5eb193c6b3f012b31ca135fb4c (diff) |
Merge tag 'drivers-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc into next
Pull ARM SoC driver changes from Olof Johansson:
"SoC-near driver changes that we're merging through our tree. Mostly
because they depend on other changes we have staged, but in some cases
because the driver maintainers preferred that we did it this way.
This contains a largeish cleanup series of the omap_l3_noc bus driver,
cpuidle rework for Exynos, some reset driver conversions and a long
branch of TI EDMA fixes and cleanups, with more to come next release.
The TI EDMA cleanups is a shared branch with the dmaengine tree, with
a handful of Davinci-specific fixes on top.
After discussion at last year's KS (and some more on the mailing
lists), we are here adding a drivers/soc directory. The purpose of
this is to keep per-vendor shared code that's needed by different
drivers but that doesn't fit into the MFD (nor drivers/platform)
model. We expect to keep merging contents for this hierarchy through
arm-soc so we can keep an eye on what the vendors keep adding here and
not making it a free-for-all to shove in crazy stuff"
* tag 'drivers-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (101 commits)
cpufreq: exynos: Fix driver compilation with ARCH_MULTIPLATFORM
tty: serial: msm: Remove direct access to GSBI
power: reset: keystone-reset: introduce keystone reset driver
Documentation: dt: add bindings for keystone pll control controller
Documentation: dt: add bindings for keystone reset driver
soc: qcom: fix of_device_id table
ARM: EXYNOS: Fix kernel panic when unplugging CPU1 on exynos
ARM: EXYNOS: Move the driver to drivers/cpuidle directory
ARM: EXYNOS: Cleanup all unneeded headers from cpuidle.c
ARM: EXYNOS: Pass the AFTR callback to the platform_data
ARM: EXYNOS: Move S5P_CHECK_SLEEP into pm.c
ARM: EXYNOS: Move the power sequence call in the cpu_pm notifier
ARM: EXYNOS: Move the AFTR state function into pm.c
ARM: EXYNOS: Encapsulate the AFTR code into a function
ARM: EXYNOS: Disable cpuidle for exynos5440
ARM: EXYNOS: Encapsulate boot vector code into a function for cpuidle
ARM: EXYNOS: Pass wakeup mask parameter to function for cpuidle
ARM: EXYNOS: Remove ifdef for scu_enable in pm
ARM: EXYNOS: Move scu_enable in the cpu_pm notifier
ARM: EXYNOS: Use the cpu_pm notifier for pm
...
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/boot/dts/am33xx.dtsi | 3 | ||||
-rw-r--r-- | arch/arm/boot/dts/am4372.dtsi | 3 | ||||
-rw-r--r-- | arch/arm/common/edma.c | 197 | ||||
-rw-r--r-- | arch/arm/mach-davinci/devices-da8xx.c | 31 | ||||
-rw-r--r-- | arch/arm/mach-davinci/dm355.c | 14 | ||||
-rw-r--r-- | arch/arm/mach-davinci/dm365.c | 16 | ||||
-rw-r--r-- | arch/arm/mach-davinci/dm644x.c | 14 | ||||
-rw-r--r-- | arch/arm/mach-davinci/dm646x.c | 16 | ||||
-rw-r--r-- | arch/arm/mach-exynos/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-exynos/common.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-exynos/cpuidle.c | 255 | ||||
-rw-r--r-- | arch/arm/mach-exynos/exynos.c | 8 | ||||
-rw-r--r-- | arch/arm/mach-exynos/platsmp.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-exynos/pm.c | 152 | ||||
-rw-r--r-- | arch/arm/mach-exynos/regs-pmu.h | 2 |
15 files changed, 228 insertions, 488 deletions
diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 586397cf6e9..9f53e824b03 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -147,9 +147,6 @@ <0x44e10f90 0x40>; interrupts = <12 13 14>; #dma-cells = <1>; - dma-channels = <64>; - ti,edma-regions = <4>; - ti,edma-slots = <256>; }; gpio0: gpio@44e07000 { diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi index 1704e853f16..794c73e5c4e 100644 --- a/arch/arm/boot/dts/am4372.dtsi +++ b/arch/arm/boot/dts/am4372.dtsi @@ -112,9 +112,6 @@ <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>; #dma-cells = <1>; - dma-channels = <64>; - ti,edma-regions = <4>; - ti,edma-slots = <256>; }; uart0: serial@44e09000 { diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 5339009b3c0..485be42519b 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -102,7 +102,13 @@ #define PARM_OFFSET(param_no) (EDMA_PARM + ((param_no) << 5)) #define EDMA_DCHMAP 0x0100 /* 64 registers */ -#define CHMAP_EXIST BIT(24) + +/* CCCFG register */ +#define GET_NUM_DMACH(x) (x & 0x7) /* bits 0-2 */ +#define GET_NUM_PAENTRY(x) ((x & 0x7000) >> 12) /* bits 12-14 */ +#define GET_NUM_EVQUE(x) ((x & 0x70000) >> 16) /* bits 16-18 */ +#define GET_NUM_REGN(x) ((x & 0x300000) >> 20) /* bits 20-21 */ +#define CHMAP_EXIST BIT(24) #define EDMA_MAX_DMACH 64 #define EDMA_MAX_PARAMENTRY 512 @@ -233,7 +239,6 @@ struct edma { unsigned num_region; unsigned num_slots; unsigned num_tc; - unsigned num_cc; enum dma_event_q default_queue; /* list of channels with no even trigger; terminated by "-1" */ @@ -290,12 +295,6 @@ static void map_dmach_queue(unsigned ctlr, unsigned ch_no, ~(0x7 << bit), queue_no << bit); } -static void __init map_queue_tc(unsigned ctlr, int queue_no, int tc_no) -{ - int bit = queue_no * 4; - edma_modify(ctlr, EDMA_QUETCMAP, ~(0x7 << bit), ((tc_no & 0x7) << bit)); -} - static void __init assign_priority_to_queue(unsigned ctlr, int queue_no, int priority) { @@ -994,29 +993,23 @@ void edma_set_dest(unsigned slot, dma_addr_t dest_port, EXPORT_SYMBOL(edma_set_dest); /** - * edma_get_position - returns the current transfer points + * edma_get_position - returns the current transfer point * @slot: parameter RAM slot being examined - * @src: pointer to source port position - * @dst: pointer to destination port position + * @dst: true selects the dest position, false the source * - * Returns current source and destination addresses for a particular - * parameter RAM slot. Its channel should not be active when this is called. + * Returns the position of the current active slot */ -void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst) +dma_addr_t edma_get_position(unsigned slot, bool dst) { - struct edmacc_param temp; - unsigned ctlr; + u32 offs, ctlr = EDMA_CTLR(slot); - ctlr = EDMA_CTLR(slot); slot = EDMA_CHAN_SLOT(slot); - edma_read_slot(EDMA_CTLR_CHAN(ctlr, slot), &temp); - if (src != NULL) - *src = temp.src; - if (dst != NULL) - *dst = temp.dst; + offs = PARM_OFFSET(slot); + offs += dst ? PARM_DST : PARM_SRC; + + return edma_read(ctlr, offs); } -EXPORT_SYMBOL(edma_get_position); /** * edma_set_src_index - configure DMA source address indexing @@ -1421,6 +1414,67 @@ void edma_clear_event(unsigned channel) } EXPORT_SYMBOL(edma_clear_event); +static int edma_setup_from_hw(struct device *dev, struct edma_soc_info *pdata, + struct edma *edma_cc) +{ + int i; + u32 value, cccfg; + s8 (*queue_priority_map)[2]; + + /* Decode the eDMA3 configuration from CCCFG register */ + cccfg = edma_read(0, EDMA_CCCFG); + + value = GET_NUM_REGN(cccfg); + edma_cc->num_region = BIT(value); + + value = GET_NUM_DMACH(cccfg); + edma_cc->num_channels = BIT(value + 1); + + value = GET_NUM_PAENTRY(cccfg); + edma_cc->num_slots = BIT(value + 4); + + value = GET_NUM_EVQUE(cccfg); + edma_cc->num_tc = value + 1; + + dev_dbg(dev, "eDMA3 HW configuration (cccfg: 0x%08x):\n", cccfg); + dev_dbg(dev, "num_region: %u\n", edma_cc->num_region); + dev_dbg(dev, "num_channel: %u\n", edma_cc->num_channels); + dev_dbg(dev, "num_slot: %u\n", edma_cc->num_slots); + dev_dbg(dev, "num_tc: %u\n", edma_cc->num_tc); + + /* Nothing need to be done if queue priority is provided */ + if (pdata->queue_priority_mapping) + return 0; + + /* + * Configure TC/queue priority as follows: + * Q0 - priority 0 + * Q1 - priority 1 + * Q2 - priority 2 + * ... + * The meaning of priority numbers: 0 highest priority, 7 lowest + * priority. So Q0 is the highest priority queue and the last queue has + * the lowest priority. + */ + queue_priority_map = devm_kzalloc(dev, + (edma_cc->num_tc + 1) * sizeof(s8), + GFP_KERNEL); + if (!queue_priority_map) + return -ENOMEM; + + for (i = 0; i < edma_cc->num_tc; i++) { + queue_priority_map[i][0] = i; + queue_priority_map[i][1] = i; + } + queue_priority_map[i][0] = -1; + queue_priority_map[i][1] = -1; + + pdata->queue_priority_mapping = queue_priority_map; + pdata->default_queue = 0; + + return 0; +} + #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES) static int edma_xbar_event_map(struct device *dev, struct device_node *node, @@ -1471,65 +1525,16 @@ static int edma_of_parse_dt(struct device *dev, struct device_node *node, struct edma_soc_info *pdata) { - int ret = 0, i; - u32 value; + int ret = 0; struct property *prop; size_t sz; struct edma_rsv_info *rsv_info; - s8 (*queue_tc_map)[2], (*queue_priority_map)[2]; - - memset(pdata, 0, sizeof(struct edma_soc_info)); - - ret = of_property_read_u32(node, "dma-channels", &value); - if (ret < 0) - return ret; - pdata->n_channel = value; - - ret = of_property_read_u32(node, "ti,edma-regions", &value); - if (ret < 0) - return ret; - pdata->n_region = value; - - ret = of_property_read_u32(node, "ti,edma-slots", &value); - if (ret < 0) - return ret; - pdata->n_slot = value; - - pdata->n_cc = 1; rsv_info = devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL); if (!rsv_info) return -ENOMEM; pdata->rsv = rsv_info; - queue_tc_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); - if (!queue_tc_map) - return -ENOMEM; - - for (i = 0; i < 3; i++) { - queue_tc_map[i][0] = i; - queue_tc_map[i][1] = i; - } - queue_tc_map[i][0] = -1; - queue_tc_map[i][1] = -1; - - pdata->queue_tc_mapping = queue_tc_map; - - queue_priority_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); - if (!queue_priority_map) - return -ENOMEM; - - for (i = 0; i < 3; i++) { - queue_priority_map[i][0] = i; - queue_priority_map[i][1] = i; - } - queue_priority_map[i][0] = -1; - queue_priority_map[i][1] = -1; - - pdata->queue_priority_mapping = queue_priority_map; - - pdata->default_queue = 0; - prop = of_find_property(node, "ti,edma-xbar-event-map", &sz); if (prop) ret = edma_xbar_event_map(dev, node, pdata, sz); @@ -1556,6 +1561,7 @@ static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev, return ERR_PTR(ret); dma_cap_set(DMA_SLAVE, edma_filter_info.dma_cap); + dma_cap_set(DMA_CYCLIC, edma_filter_info.dma_cap); of_dma_controller_register(dev->of_node, of_dma_simple_xlate, &edma_filter_info); @@ -1574,7 +1580,6 @@ static int edma_probe(struct platform_device *pdev) struct edma_soc_info **info = pdev->dev.platform_data; struct edma_soc_info *ninfo[EDMA_MAX_CC] = {NULL}; s8 (*queue_priority_mapping)[2]; - s8 (*queue_tc_mapping)[2]; int i, j, off, ln, found = 0; int status = -1; const s16 (*rsv_chans)[2]; @@ -1585,7 +1590,6 @@ static int edma_probe(struct platform_device *pdev) struct resource *r[EDMA_MAX_CC] = {NULL}; struct resource res[EDMA_MAX_CC]; char res_name[10]; - char irq_name[10]; struct device_node *node = pdev->dev.of_node; struct device *dev = &pdev->dev; int ret; @@ -1650,12 +1654,10 @@ static int edma_probe(struct platform_device *pdev) if (!edma_cc[j]) return -ENOMEM; - edma_cc[j]->num_channels = min_t(unsigned, info[j]->n_channel, - EDMA_MAX_DMACH); - edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot, - EDMA_MAX_PARAMENTRY); - edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc, - EDMA_MAX_CC); + /* Get eDMA3 configuration from IP */ + ret = edma_setup_from_hw(dev, info[j], edma_cc[j]); + if (ret) + return ret; edma_cc[j]->default_queue = info[j]->default_queue; @@ -1707,14 +1709,21 @@ static int edma_probe(struct platform_device *pdev) if (node) { irq[j] = irq_of_parse_and_map(node, 0); + err_irq[j] = irq_of_parse_and_map(node, 2); } else { + char irq_name[10]; + sprintf(irq_name, "edma%d", j); irq[j] = platform_get_irq_byname(pdev, irq_name); + + sprintf(irq_name, "edma%d_err", j); + err_irq[j] = platform_get_irq_byname(pdev, irq_name); } edma_cc[j]->irq_res_start = irq[j]; - status = devm_request_irq(&pdev->dev, irq[j], - dma_irq_handler, 0, "edma", - &pdev->dev); + edma_cc[j]->irq_res_end = err_irq[j]; + + status = devm_request_irq(dev, irq[j], dma_irq_handler, 0, + "edma", dev); if (status < 0) { dev_dbg(&pdev->dev, "devm_request_irq %d failed --> %d\n", @@ -1722,16 +1731,8 @@ static int edma_probe(struct platform_device *pdev) return status; } - if (node) { - err_irq[j] = irq_of_parse_and_map(node, 2); - } else { - sprintf(irq_name, "edma%d_err", j); - err_irq[j] = platform_get_irq_byname(pdev, irq_name); - } - edma_cc[j]->irq_res_end = err_irq[j]; - status = devm_request_irq(&pdev->dev, err_irq[j], - dma_ccerr_handler, 0, - "edma_error", &pdev->dev); + status = devm_request_irq(dev, err_irq[j], dma_ccerr_handler, 0, + "edma_error", dev); if (status < 0) { dev_dbg(&pdev->dev, "devm_request_irq %d failed --> %d\n", @@ -1742,14 +1743,8 @@ static int edma_probe(struct platform_device *pdev) for (i = 0; i < edma_cc[j]->num_channels; i++) map_dmach_queue(j, i, info[j]->default_queue); - queue_tc_mapping = info[j]->queue_tc_mapping; queue_priority_mapping = info[j]->queue_priority_mapping; - /* Event queue to TC mapping */ - for (i = 0; queue_tc_mapping[i][0] != -1; i++) - map_queue_tc(j, queue_tc_mapping[i][0], - queue_tc_mapping[i][1]); - /* Event queue priority mapping */ for (i = 0; queue_priority_mapping[i][0] != -1; i++) assign_priority_to_queue(j, @@ -1762,7 +1757,7 @@ static int edma_probe(struct platform_device *pdev) if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST) map_dmach_param(j); - for (i = 0; i < info[j]->n_region; i++) { + for (i = 0; i < edma_cc[j]->num_region; i++) { edma_write_array2(j, EDMA_DRAE, i, 0, 0x0); edma_write_array2(j, EDMA_DRAE, i, 1, 0x0); edma_write_array(j, EDMA_QRAE, i, 0x0); diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 56ea41d5f84..b85b781b05f 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -134,13 +134,6 @@ struct platform_device da8xx_serial_device[] = { } }; -static s8 da8xx_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {-1, -1} -}; - static s8 da8xx_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, @@ -148,12 +141,6 @@ static s8 da8xx_queue_priority_mapping[][2] = { {-1, -1} }; -static s8 da850_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {-1, -1} -}; - static s8 da850_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, @@ -161,12 +148,6 @@ static s8 da850_queue_priority_mapping[][2] = { }; static struct edma_soc_info da830_edma_cc0_info = { - .n_channel = 32, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, - .queue_tc_mapping = da8xx_queue_tc_mapping, .queue_priority_mapping = da8xx_queue_priority_mapping, .default_queue = EVENTQ_1, }; @@ -177,22 +158,10 @@ static struct edma_soc_info *da830_edma_info[EDMA_MAX_CC] = { static struct edma_soc_info da850_edma_cc_info[] = { { - .n_channel = 32, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, - .queue_tc_mapping = da8xx_queue_tc_mapping, .queue_priority_mapping = da8xx_queue_priority_mapping, .default_queue = EVENTQ_1, }, { - .n_channel = 32, - .n_region = 4, - .n_slot = 128, - .n_tc = 1, - .n_cc = 1, - .queue_tc_mapping = da850_queue_tc_mapping, .queue_priority_mapping = da850_queue_priority_mapping, .default_queue = EVENTQ_0, }, diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 07381d8cea6..2f3ed3a58d5 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -569,14 +569,6 @@ static u8 dm355_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ static s8 -queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {-1, -1}, -}; - -static s8 queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, @@ -585,12 +577,6 @@ queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, - .queue_tc_mapping = queue_tc_mapping, .queue_priority_mapping = queue_priority_mapping, .default_queue = EVENTQ_1, }; diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 08a61b93833..0ae8114f5cc 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -853,16 +853,6 @@ static u8 dm365_default_priorities[DAVINCI_N_AINTC_IRQ] = { /* Four Transfer Controllers on DM365 */ static s8 -dm365_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {2, 2}, - {3, 3}, - {-1, -1}, -}; - -static s8 dm365_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 7}, @@ -873,12 +863,6 @@ dm365_queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 4, - .n_slot = 256, - .n_tc = 4, - .n_cc = 1, - .queue_tc_mapping = dm365_queue_tc_mapping, .queue_priority_mapping = dm365_queue_priority_mapping, .default_queue = EVENTQ_3, }; diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 5debffba4b2..dc52657909c 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -499,14 +499,6 @@ static u8 dm644x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ static s8 -queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {-1, -1}, -}; - -static s8 queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, @@ -515,12 +507,6 @@ queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, - .queue_tc_mapping = queue_tc_mapping, .queue_priority_mapping = queue_priority_mapping, .default_queue = EVENTQ_1, }; diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 332d00d24dc..6c3bbea7d77 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -533,16 +533,6 @@ static u8 dm646x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /* Four Transfer Controllers on DM646x */ static s8 -dm646x_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {2, 2}, - {3, 3}, - {-1, -1}, -}; - -static s8 dm646x_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 4}, @@ -553,12 +543,6 @@ dm646x_queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 6, /* 0-1, 4-7 */ - .n_slot = 512, - .n_tc = 4, - .n_cc = 1, - .queue_tc_mapping = dm646x_queue_tc_mapping, .queue_priority_mapping = dm646x_queue_priority_mapping, .default_queue = EVENTQ_1, }; diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 9f325888094..788f26d2114 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -18,7 +18,6 @@ obj-$(CONFIG_ARCH_EXYNOS) += exynos.o pmu.o exynos-smc.o firmware.o obj-$(CONFIG_PM_SLEEP) += pm.o sleep.o obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o -obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index 5dba5a1ee6c..80b90e346ca 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h @@ -115,6 +115,7 @@ void mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1); struct map_desc; extern void __iomem *sysram_ns_base_addr; +extern void __iomem *sysram_base_addr; void exynos_init_io(void); void exynos_restart(enum reboot_mode mode, const char *cmd); void exynos_cpuidle_init(void); @@ -165,6 +166,7 @@ extern int exynos_cpu_power_state(int cpu); extern void exynos_cluster_power_down(int cluster); extern void exynos_cluster_power_up(int cluster); extern int exynos_cluster_power_state(int cluster); +extern void exynos_enter_aftr(void); extern void s5p_init_cpu(void __iomem *cpuid_addr); extern unsigned int samsung_rev(void); diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c deleted file mode 100644 index 3dd385ebf19..00000000000 --- a/arch/arm/mach-exynos/cpuidle.c +++ /dev/null @@ -1,255 +0,0 @@ -/* linux/arch/arm/mach-exynos4/cpuidle.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * http://www.samsung.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/kernel.h> -#include <linux/init.h> -#include <linux/cpuidle.h> -#include <linux/cpu_pm.h> -#include <linux/io.h> -#include <linux/export.h> -#include <linux/module.h> -#include <linux/time.h> -#include <linux/platform_device.h> - -#include <asm/proc-fns.h> -#include <asm/smp_scu.h> -#include <asm/suspend.h> -#include <asm/unified.h> -#include <asm/cpuidle.h> - -#include <plat/pm.h> - -#include <mach/map.h> - -#include "common.h" -#include "regs-pmu.h" - -#define REG_DIRECTGO_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \ - S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ - (S5P_VA_SYSRAM + 0x24) : S5P_INFORM0)) -#define REG_DIRECTGO_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \ - S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ - (S5P_VA_SYSRAM + 0x20) : S5P_INFORM1)) - -#define S5P_CHECK_AFTR 0xFCBA0D10 - -#define EXYNOS5_PWR_CTRL1 (S5P_VA_CMU + 0x01020) -#define EXYNOS5_PWR_CTRL2 (S5P_VA_CMU + 0x01024) - -#define PWR_CTRL1_CORE2_DOWN_RATIO (7 << 28) -#define PWR_CTRL1_CORE1_DOWN_RATIO (7 << 16) -#define PWR_CTRL1_DIV2_DOWN_EN (1 << 9) -#define PWR_CTRL1_DIV1_DOWN_EN (1 << 8) -#define PWR_CTRL1_USE_CORE1_WFE (1 << 5) -#define PWR_CTRL1_USE_CORE0_WFE (1 << 4) -#define PWR_CTRL1_USE_CORE1_WFI (1 << 1) -#define PWR_CTRL1_USE_CORE0_WFI (1 << 0) - -#define PWR_CTRL2_DIV2_UP_EN (1 << 25) -#define PWR_CTRL2_DIV1_UP_EN (1 << 24) -#define PWR_CTRL2_DUR_STANDBY2_VAL (1 << 16) -#define PWR_CTRL2_DUR_STANDBY1_VAL (1 << 8) -#define PWR_CTRL2_CORE2_UP_RATIO (1 << 4) -#define PWR_CTRL2_CORE1_UP_RATIO (1 << 0) - -static int exynos4_enter_lowpower(struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index); - -static DEFINE_PER_CPU(struct cpuidle_device, exynos4_cpuidle_device); - -static struct cpuidle_driver exynos4_idle_driver = { - .name = "exynos4_idle", - .owner = THIS_MODULE, - .states = { - [0] = ARM_CPUIDLE_WFI_STATE, - [1] = { - .enter = exynos4_enter_lowpower, - .exit_latency = 300, - .target_residency = 100000, - .flags = CPUIDLE_FLAG_TIME_VALID, - .name = "C1", - .desc = "ARM power down", - }, - }, - .state_count = 2, - .safe_state_index = 0, -}; - -/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ -static void exynos4_set_wakeupmask(void) -{ - __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK); -} - -static unsigned int g_pwr_ctrl, g_diag_reg; - -static void save_cpu_arch_register(void) -{ - /*read power control register*/ - asm("mrc p15, 0, %0, c15, c0, 0" : "=r"(g_pwr_ctrl) : : "cc"); - /*read diagnostic register*/ - asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc"); - return; -} - -static void restore_cpu_arch_register(void) -{ - /*write power control register*/ - asm("mcr p15, 0, %0, c15, c0, 0" : : "r"(g_pwr_ctrl) : "cc"); - /*write diagnostic register*/ - asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc"); - return; -} - -static int idle_finisher(unsigned long flags) -{ - cpu_do_idle(); - return 1; -} - -static int exynos4_enter_core0_aftr(struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index) -{ - unsigned long tmp; - - exynos4_set_wakeupmask(); - - /* Set value of power down register for aftr mode */ - exynos_sys_powerdown_conf(SYS_AFTR); - - __raw_writel(virt_to_phys(exynos_cpu_resume), REG_DIRECTGO_ADDR); - __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG); - - save_cpu_arch_register(); - - /* Setting Central Sequence Register for power down mode */ - tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); - tmp &= ~S5P_CENTRAL_LOWPWR_CFG; - __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); - - cpu_pm_enter(); - cpu_suspend(0, idle_finisher); - -#ifdef CONFIG_SMP - if (!soc_is_exynos5250()) - scu_enable(S5P_VA_SCU); -#endif - cpu_pm_exit(); - - restore_cpu_arch_register(); - - /* - * If PMU failed while entering sleep mode, WFI will be - * ignored by PMU and then exiting cpu_do_idle(). - * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically - * in this situation. - */ - tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); - if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { - tmp |= S5P_CENTRAL_LOWPWR_CFG; - __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); - } - - /* Clear wakeup state register */ - __raw_writel(0x0, S5P_WAKEUP_STAT); - - return index; -} - -static int exynos4_enter_lowpower(struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index) -{ - int new_index = index; - - /* AFTR can only be entered when cores other than CPU0 are offline */ - if (num_online_cpus() > 1 || dev->cpu != 0) - new_index = drv->safe_state_index; - - if (new_index == 0) - return arm_cpuidle_simple_enter(dev, drv, new_index); - else - return exynos4_enter_core0_aftr(dev, drv, new_index); -} - -static void __init exynos5_core_down_clk(void) -{ - unsigned int tmp; - - /* - * Enable arm clock down (in idle) and set arm divider - * ratios in WFI/WFE state. - */ - tmp = PWR_CTRL1_CORE2_DOWN_RATIO | \ - PWR_CTRL1_CORE1_DOWN_RATIO | \ - PWR_CTRL1_DIV2_DOWN_EN | \ - PWR_CTRL1_DIV1_DOWN_EN | \ - PWR_CTRL1_USE_CORE1_WFE | \ - PWR_CTRL1_USE_CORE0_WFE | \ - PWR_CTRL1_USE_CORE1_WFI | \ - PWR_CTRL1_USE_CORE0_WFI; - __raw_writel(tmp, EXYNOS5_PWR_CTRL1); - - /* - * Enable arm clock up (on exiting idle). Set arm divider - * ratios when not in idle along with the standby duration - * ratios. - */ - tmp = PWR_CTRL2_DIV2_UP_EN | \ - PWR_CTRL2_DIV1_UP_EN | \ - PWR_CTRL2_DUR_STANDBY2_VAL | \ - PWR_CTRL2_DUR_STANDBY1_VAL | \ - PWR_CTRL2_CORE2_UP_RATIO | \ - PWR_CTRL2_CORE1_UP_RATIO; - __raw_writel(tmp, EXYNOS5_PWR_CTRL2); -} - -static int exynos_cpuidle_probe(struct platform_device *pdev) -{ - int cpu_id, ret; - struct cpuidle_device *device; - - if (soc_is_exynos5250()) - exynos5_core_down_clk(); - - if (soc_is_exynos5440()) - exynos4_idle_driver.state_count = 1; - - ret = cpuidle_register_driver(&exynos4_idle_driver); - if (ret) { - dev_err(&pdev->dev, "failed to register cpuidle driver\n"); - return ret; - } - - for_each_online_cpu(cpu_id) { - device = &per_cpu(exynos4_cpuidle_device, cpu_id); - device->cpu = cpu_id; - - ret = cpuidle_register_device(device); - if (ret) { - dev_err(&pdev->dev, "failed to register cpuidle device\n"); - return ret; - } - } - - return 0; -} - -static struct platform_driver exynos_cpuidle_driver = { - .probe = exynos_cpuidle_probe, - .driver = { - .name = "exynos_cpuidle", - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(exynos_cpuidle_driver); diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index 4800b1ce3d7..bc43e22693b 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -169,12 +169,16 @@ void exynos_restart(enum reboot_mode mode, const char *cmd) } static struct platform_device exynos_cpuidle = { - .name = "exynos_cpuidle", - .id = -1, + .name = "exynos_cpuidle", + .dev.platform_data = exynos_enter_aftr, + .id = -1, }; void __init exynos_cpuidle_init(void) { + if (soc_is_exynos5440()) + return; + platform_device_register(&exynos_cpuidle); } diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 112bc66927a..ec02422e849 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -32,7 +32,7 @@ extern void exynos4_secondary_startup(void); -static void __iomem *sysram_base_addr; +void __iomem *sysram_base_addr; void __iomem *sysram_ns_base_addr; static void __init exynos_smp_prepare_sysram(void) diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index aba2ff6e443..87c0d34c7fb 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/suspend.h> #include <linux/syscore_ops.h> +#include <linux/cpu_pm.h> #include <linux/io.h> #include <linux/irqchip/arm-gic.h> #include <linux/err.h> @@ -165,9 +166,75 @@ int exynos_cluster_power_state(int cluster) S5P_CORE_LOCAL_PWR_EN); } +#define EXYNOS_BOOT_VECTOR_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \ + S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ + (sysram_base_addr + 0x24) : S5P_INFORM0)) +#define EXYNOS_BOOT_VECTOR_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \ + S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ + (sysram_base_addr + 0x20) : S5P_INFORM1)) + +#define S5P_CHECK_AFTR 0xFCBA0D10 +#define S5P_CHECK_SLEEP 0x00000BAD + +/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ +static void exynos_set_wakeupmask(long mask) +{ + __raw_writel(mask, S5P_WAKEUP_MASK); +} + +static void exynos_cpu_set_boot_vector(long flags) +{ + __raw_writel(virt_to_phys(exynos_cpu_resume), EXYNOS_BOOT_VECTOR_ADDR); + __raw_writel(flags, EXYNOS_BOOT_VECTOR_FLAG); +} + +void exynos_enter_aftr(void) +{ + exynos_set_wakeupmask(0x0000ff3e); + exynos_cpu_set_boot_vector(S5P_CHECK_AFTR); + /* Set value of power down register for aftr mode */ + exynos_sys_powerdown_conf(SYS_AFTR); +} + /* For Cortex-A9 Diagnostic and Power control register */ static unsigned int save_arm_register[2]; +static void exynos_cpu_save_register(void) +{ + unsigned long tmp; + + /* Save Power control register */ + asm ("mrc p15, 0, %0, c15, c0, 0" + : "=r" (tmp) : : "cc"); + + save_arm_register[0] = tmp; + + /* Save Diagnostic register */ + asm ("mrc p15, 0, %0, c15, c0, 1" + : "=r" (tmp) : : "cc"); + + save_arm_register[1] = tmp; +} + +static void exynos_cpu_restore_register(void) +{ + unsigned long tmp; + + /* Restore Power control register */ + tmp = save_arm_register[0]; + + asm volatile ("mcr p15, 0, %0, c15, c0, 0" + : : "r" (tmp) + : "cc"); + + /* Restore Diagnostic register */ + tmp = save_arm_register[1]; + + asm volatile ("mcr p15, 0, %0, c15, c0, 1" + : : "r" (tmp) + : "cc"); +} + static int exynos_cpu_suspend(unsigned long arg) { #ifdef CONFIG_CACHE_L2X0 @@ -212,37 +279,34 @@ static void exynos_pm_prepare(void) __raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0); } -static int exynos_pm_suspend(void) +static void exynos_pm_central_suspend(void) { unsigned long tmp; /* Setting Central Sequence Register for power down mode */ - tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); tmp &= ~S5P_CENTRAL_LOWPWR_CFG; __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); +} + +static int exynos_pm_suspend(void) +{ + unsigned long tmp; + + exynos_pm_central_suspend(); /* Setting SEQ_OPTION register */ tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0); __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION); - if (!soc_is_exynos5250()) { - /* Save Power control register */ - asm ("mrc p15, 0, %0, c15, c0, 0" - : "=r" (tmp) : : "cc"); - save_arm_register[0] = tmp; - - /* Save Diagnostic register */ - asm ("mrc p15, 0, %0, c15, c0, 1" - : "=r" (tmp) : : "cc"); - save_arm_register[1] = tmp; - } + if (!soc_is_exynos5250()) + exynos_cpu_save_register(); return 0; } -static void exynos_pm_resume(void) +static int exynos_pm_central_resume(void) { unsigned long tmp; @@ -259,22 +323,20 @@ static void exynos_pm_resume(void) /* clear the wakeup state register */ __raw_writel(0x0, S5P_WAKEUP_STAT); /* No need to perform below restore code */ - goto early_wakeup; - } - if (!soc_is_exynos5250()) { - /* Restore Power control register */ - tmp = save_arm_register[0]; - asm volatile ("mcr p15, 0, %0, c15, c0, 0" - : : "r" (tmp) - : "cc"); - - /* Restore Diagnostic register */ - tmp = save_arm_register[1]; - asm volatile ("mcr p15, 0, %0, c15, c0, 1" - : : "r" (tmp) - : "cc"); + return -1; } + return 0; +} + +static void exynos_pm_resume(void) +{ + if (exynos_pm_central_resume()) + goto early_wakeup; + + if (!soc_is_exynos5250()) + exynos_cpu_restore_register(); + /* For release retention */ __raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION); @@ -291,7 +353,7 @@ static void exynos_pm_resume(void) s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save)); - if (IS_ENABLED(CONFIG_SMP) && !soc_is_exynos5250()) + if (!soc_is_exynos5250()) scu_enable(S5P_VA_SCU); early_wakeup: @@ -369,10 +431,42 @@ static const struct platform_suspend_ops exynos_suspend_ops = { .valid = suspend_valid_only_mem, }; +static int exynos_cpu_pm_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + int cpu = smp_processor_id(); + + switch (cmd) { + case CPU_PM_ENTER: + if (cpu == 0) { + exynos_pm_central_suspend(); + exynos_cpu_save_register(); + } + break; + + case CPU_PM_EXIT: + if (cpu == 0) { + if (!soc_is_exynos5250()) + scu_enable(S5P_VA_SCU); + exynos_cpu_restore_register(); + exynos_pm_central_resume(); + } + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block exynos_cpu_pm_notifier_block = { + .notifier_call = exynos_cpu_pm_notifier, +}; + void __init exynos_pm_init(void) { u32 tmp; + cpu_pm_register_notifier(&exynos_cpu_pm_notifier_block); + /* Platform-specific GIC callback */ gic_arch_extn.irq_set_wake = exynos_irq_set_wake; diff --git a/arch/arm/mach-exynos/regs-pmu.h b/arch/arm/mach-exynos/regs-pmu.h index 4179f6a6d59..1d13b08708f 100644 --- a/arch/arm/mach-exynos/regs-pmu.h +++ b/arch/arm/mach-exynos/regs-pmu.h @@ -129,8 +129,6 @@ #define S5P_CORE_LOCAL_PWR_EN 0x3 #define S5P_INT_LOCAL_PWR_EN 0x7 -#define S5P_CHECK_SLEEP 0x00000BAD - /* Only for EXYNOS4210 */ #define S5P_CMU_CLKSTOP_LCD1_LOWPWR S5P_PMUREG(0x1154) #define S5P_CMU_RESET_LCD1_LOWPWR S5P_PMUREG(0x1174) |