From 4be78a86c5063a50782dd2f16bd76df6a1771d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Thu, 23 Feb 2017 19:27:12 +0100 Subject: clocksource: Add Owl timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Actions Semi S500 SoC provides four timers, 2Hz0/1 and 32-bit TIMER0/1. Use TIMER0 as clocksource and TIMER1 as clockevents. Based on LeMaker linux-actions tree. An S500 datasheet can be found on the LeMaker Guitar pages: http://www.lemaker.org/product-guitar-download-29.html Acked-by: Daniel Lezcano Signed-off-by: Andreas Färber --- drivers/clocksource/Kconfig | 7 ++ drivers/clocksource/Makefile | 1 + drivers/clocksource/owl-timer.c | 171 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 drivers/clocksource/owl-timer.c (limited to 'drivers') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 545d541ae20e..a1e4fc622569 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -109,6 +109,13 @@ config ORION_TIMER help Enables the support for the Orion timer driver +config OWL_TIMER + bool "Owl timer driver" if COMPILE_TEST + depends on GENERIC_CLOCKEVENTS + select CLKSRC_MMIO + help + Enables the support for the Actions Semi Owl timer driver. + config SUN4I_TIMER bool "Sun4i timer driver" if COMPILE_TEST depends on GENERIC_CLOCKEVENTS diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 2b5b56a6f00f..9cd12486483c 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_CLKSRC_PISTACHIO) += time-pistachio.o obj-$(CONFIG_CLKSRC_TI_32K) += timer-ti-32k.o obj-$(CONFIG_CLKSRC_NPS) += timer-nps.o obj-$(CONFIG_OXNAS_RPS_TIMER) += timer-oxnas-rps.o +obj-$(CONFIG_OWL_TIMER) += owl-timer.o obj-$(CONFIG_ARC_TIMERS) += arc_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o diff --git a/drivers/clocksource/owl-timer.c b/drivers/clocksource/owl-timer.c new file mode 100644 index 000000000000..4609363a79a6 --- /dev/null +++ b/drivers/clocksource/owl-timer.c @@ -0,0 +1,171 @@ +/* + * Actions Semi Owl timer + * + * Copyright 2012 Actions Semi Inc. + * Author: Actions Semi, Inc. + * + * Copyright (c) 2017 SUSE Linux GmbH + * Author: Andreas Färber + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define OWL_Tx_CTL 0x0 +#define OWL_Tx_CMP 0x4 +#define OWL_Tx_VAL 0x8 + +#define OWL_Tx_CTL_PD BIT(0) +#define OWL_Tx_CTL_INTEN BIT(1) +#define OWL_Tx_CTL_EN BIT(2) + +static void __iomem *owl_timer_base; +static void __iomem *owl_clksrc_base; +static void __iomem *owl_clkevt_base; + +static inline void owl_timer_reset(void __iomem *base) +{ + writel(0, base + OWL_Tx_CTL); + writel(0, base + OWL_Tx_VAL); + writel(0, base + OWL_Tx_CMP); +} + +static inline void owl_timer_set_enabled(void __iomem *base, bool enabled) +{ + u32 ctl = readl(base + OWL_Tx_CTL); + + /* PD bit is cleared when set */ + ctl &= ~OWL_Tx_CTL_PD; + + if (enabled) + ctl |= OWL_Tx_CTL_EN; + else + ctl &= ~OWL_Tx_CTL_EN; + + writel(ctl, base + OWL_Tx_CTL); +} + +static u64 notrace owl_timer_sched_read(void) +{ + return (u64)readl(owl_clksrc_base + OWL_Tx_VAL); +} + +static int owl_timer_set_state_shutdown(struct clock_event_device *evt) +{ + owl_timer_set_enabled(owl_clkevt_base, false); + + return 0; +} + +static int owl_timer_set_state_oneshot(struct clock_event_device *evt) +{ + owl_timer_reset(owl_clkevt_base); + + return 0; +} + +static int owl_timer_tick_resume(struct clock_event_device *evt) +{ + return 0; +} + +static int owl_timer_set_next_event(unsigned long evt, + struct clock_event_device *ev) +{ + void __iomem *base = owl_clkevt_base; + + owl_timer_set_enabled(base, false); + writel(OWL_Tx_CTL_INTEN, base + OWL_Tx_CTL); + writel(0, base + OWL_Tx_VAL); + writel(evt, base + OWL_Tx_CMP); + owl_timer_set_enabled(base, true); + + return 0; +} + +static struct clock_event_device owl_clockevent = { + .name = "owl_tick", + .rating = 200, + .features = CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_DYNIRQ, + .set_state_shutdown = owl_timer_set_state_shutdown, + .set_state_oneshot = owl_timer_set_state_oneshot, + .tick_resume = owl_timer_tick_resume, + .set_next_event = owl_timer_set_next_event, +}; + +static irqreturn_t owl_timer1_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = (struct clock_event_device *)dev_id; + + writel(OWL_Tx_CTL_PD, owl_clkevt_base + OWL_Tx_CTL); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static int __init owl_timer_init(struct device_node *node) +{ + struct clk *clk; + unsigned long rate; + int timer1_irq, ret; + + owl_timer_base = of_io_request_and_map(node, 0, "owl-timer"); + if (IS_ERR(owl_timer_base)) { + pr_err("Can't map timer registers"); + return PTR_ERR(owl_timer_base); + } + + owl_clksrc_base = owl_timer_base + 0x08; + owl_clkevt_base = owl_timer_base + 0x14; + + timer1_irq = of_irq_get_byname(node, "timer1"); + if (timer1_irq <= 0) { + pr_err("Can't parse timer1 IRQ"); + return -EINVAL; + } + + clk = of_clk_get(node, 0); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rate = clk_get_rate(clk); + + owl_timer_reset(owl_clksrc_base); + owl_timer_set_enabled(owl_clksrc_base, true); + + sched_clock_register(owl_timer_sched_read, 32, rate); + clocksource_mmio_init(owl_clksrc_base + OWL_Tx_VAL, node->name, + rate, 200, 32, clocksource_mmio_readl_up); + + owl_timer_reset(owl_clkevt_base); + + ret = request_irq(timer1_irq, owl_timer1_interrupt, IRQF_TIMER, + "owl-timer", &owl_clockevent); + if (ret) { + pr_err("failed to request irq %d\n", timer1_irq); + return ret; + } + + owl_clockevent.cpumask = cpumask_of(0); + owl_clockevent.irq = timer1_irq; + + clockevents_config_and_register(&owl_clockevent, rate, + 0xf, 0xffffffff); + + return 0; +} +CLOCKSOURCE_OF_DECLARE(owl_s500, "actions,s500-timer", owl_timer_init); -- cgit v1.2.3 From f35b0936151ed5711fb63e8c866f0202883600e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Mon, 27 Feb 2017 21:22:51 +0100 Subject: clocksource: owl: Add S900 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Actions Semi S900 SoC provides four 32-bit timers, TIMER0/1/2/3, but no 2Hz timers. An S900 datasheet can be found in 96Boards documentation: https://github.com/96boards/documentation/blob/master/ConsumerEdition/Bubblegum-96/HardwareDocs/SoC_bubblegum96.pdf Acked-by: Daniel Lezcano Signed-off-by: Andreas Färber --- drivers/clocksource/owl-timer.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/clocksource/owl-timer.c b/drivers/clocksource/owl-timer.c index 4609363a79a6..d19c53c11094 100644 --- a/drivers/clocksource/owl-timer.c +++ b/drivers/clocksource/owl-timer.c @@ -169,3 +169,4 @@ static int __init owl_timer_init(struct device_node *node) return 0; } CLOCKSOURCE_OF_DECLARE(owl_s500, "actions,s500-timer", owl_timer_init); +CLOCKSOURCE_OF_DECLARE(owl_s900, "actions,s900-timer", owl_timer_init); -- cgit v1.2.3 From aa9f800ded78d530bb07104a4745e95af723abf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Sun, 26 Feb 2017 04:09:57 +0100 Subject: soc: actions: Add Owl SPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement S500 Smart Power System power-gating. For now flag PD_CPU2 and PD_CPU3 as always-on. Based on LeMaker linux-actions tree. Signed-off-by: Andreas Färber --- drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/actions/Kconfig | 12 ++ drivers/soc/actions/Makefile | 1 + drivers/soc/actions/owl-sps.c | 252 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 267 insertions(+) create mode 100644 drivers/soc/actions/Kconfig create mode 100644 drivers/soc/actions/Makefile create mode 100644 drivers/soc/actions/owl-sps.c (limited to 'drivers') diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 309643fe35f9..a63eb0ffba98 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -1,5 +1,6 @@ menu "SOC (System On Chip) specific Drivers" +source "drivers/soc/actions/Kconfig" source "drivers/soc/atmel/Kconfig" source "drivers/soc/bcm/Kconfig" source "drivers/soc/fsl/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 824b44281efa..a3b27a33c309 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -2,6 +2,7 @@ # Makefile for the Linux Kernel SOC specific device drivers. # +obj-$(CONFIG_ARCH_ACTIONS) += actions/ obj-$(CONFIG_ARCH_AT91) += atmel/ obj-y += bcm/ obj-$(CONFIG_ARCH_DOVE) += dove/ diff --git a/drivers/soc/actions/Kconfig b/drivers/soc/actions/Kconfig new file mode 100644 index 000000000000..bdf827d5ce78 --- /dev/null +++ b/drivers/soc/actions/Kconfig @@ -0,0 +1,12 @@ +if ARCH_ACTIONS || COMPILE_TEST + +config OWL_PM_DOMAINS + bool "Actions Semi SPS power domains" + depends on PM + select PM_GENERIC_DOMAINS + help + Say 'y' here to enable support for Smart Power System (SPS) + power-gating on Actions Semiconductor S500 SoC. + If unsure, say 'n'. + +endif diff --git a/drivers/soc/actions/Makefile b/drivers/soc/actions/Makefile new file mode 100644 index 000000000000..720c34ed16e4 --- /dev/null +++ b/drivers/soc/actions/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_OWL_PM_DOMAINS) += owl-sps.o diff --git a/drivers/soc/actions/owl-sps.c b/drivers/soc/actions/owl-sps.c new file mode 100644 index 000000000000..8abb72f01929 --- /dev/null +++ b/drivers/soc/actions/owl-sps.c @@ -0,0 +1,252 @@ +/* + * Actions Semi Owl Smart Power System (SPS) + * + * Copyright 2012 Actions Semi Inc. + * Author: Actions Semi, Inc. + * + * Copyright (c) 2017 Andreas Färber + * + * 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 +#include +#include +#include +#include +#include + +#define OWL_SPS_PG_CTL 0x0 + +struct owl_sps_domain_info { + const char *name; + int pwr_bit; + int ack_bit; + unsigned int genpd_flags; +}; + +struct owl_sps_info { + unsigned num_domains; + const struct owl_sps_domain_info *domains; +}; + +struct owl_sps { + struct device *dev; + const struct owl_sps_info *info; + void __iomem *base; + struct genpd_onecell_data genpd_data; + struct generic_pm_domain *domains[]; +}; + +#define to_owl_pd(gpd) container_of(gpd, struct owl_sps_domain, genpd) + +struct owl_sps_domain { + struct generic_pm_domain genpd; + const struct owl_sps_domain_info *info; + struct owl_sps *sps; +}; + +static int owl_sps_set_power(struct owl_sps_domain *pd, bool enable) +{ + u32 val, pwr_mask, ack_mask; + int timeout; + bool ack; + + ack_mask = BIT(pd->info->ack_bit); + pwr_mask = BIT(pd->info->pwr_bit); + val = readl(pd->sps->base + OWL_SPS_PG_CTL); + ack = val & ack_mask; + + if (ack == enable) + return 0; + + if (enable) + val |= pwr_mask; + else + val &= ~pwr_mask; + + writel(val, pd->sps->base + OWL_SPS_PG_CTL); + + for (timeout = 5000; timeout > 0; timeout -= 50) { + val = readl(pd->sps->base + OWL_SPS_PG_CTL); + if ((val & ack_mask) == (enable ? ack_mask : 0)) + break; + udelay(50); + } + if (timeout <= 0) + return -ETIMEDOUT; + + udelay(10); + + return 0; +} + +static int owl_sps_power_on(struct generic_pm_domain *domain) +{ + struct owl_sps_domain *pd = to_owl_pd(domain); + + dev_dbg(pd->sps->dev, "%s power on", pd->info->name); + + return owl_sps_set_power(pd, true); +} + +static int owl_sps_power_off(struct generic_pm_domain *domain) +{ + struct owl_sps_domain *pd = to_owl_pd(domain); + + dev_dbg(pd->sps->dev, "%s power off", pd->info->name); + + return owl_sps_set_power(pd, false); +} + +static int owl_sps_init_domain(struct owl_sps *sps, int index) +{ + struct owl_sps_domain *pd; + + pd = devm_kzalloc(sps->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pd->info = &sps->info->domains[index]; + pd->sps = sps; + + pd->genpd.name = pd->info->name; + pd->genpd.power_on = owl_sps_power_on; + pd->genpd.power_off = owl_sps_power_off; + pd->genpd.flags = pd->info->genpd_flags; + pm_genpd_init(&pd->genpd, NULL, false); + + sps->genpd_data.domains[index] = &pd->genpd; + + return 0; +} + +static int owl_sps_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + const struct owl_sps_info *sps_info; + struct owl_sps *sps; + int i, ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "no device node\n"); + return -ENODEV; + } + + match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); + if (!match || !match->data) { + dev_err(&pdev->dev, "unknown compatible or missing data\n"); + return -EINVAL; + } + + sps_info = match->data; + + sps = devm_kzalloc(&pdev->dev, sizeof(*sps) + + sps_info->num_domains * sizeof(sps->domains[0]), + GFP_KERNEL); + if (!sps) + return -ENOMEM; + + sps->base = of_io_request_and_map(pdev->dev.of_node, 0, "owl-sps"); + if (IS_ERR(sps->base)) { + dev_err(&pdev->dev, "failed to map sps registers\n"); + return PTR_ERR(sps->base); + } + + sps->dev = &pdev->dev; + sps->info = sps_info; + sps->genpd_data.domains = sps->domains; + sps->genpd_data.num_domains = sps_info->num_domains; + + for (i = 0; i < sps_info->num_domains; i++) { + ret = owl_sps_init_domain(sps, i); + if (ret) + return ret; + } + + ret = of_genpd_add_provider_onecell(pdev->dev.of_node, &sps->genpd_data); + if (ret) { + dev_err(&pdev->dev, "failed to add provider (%d)", ret); + return ret; + } + + return 0; +} + +static const struct owl_sps_domain_info s500_sps_domains[] = { + [S500_PD_VDE] = { + .name = "VDE", + .pwr_bit = 0, + .ack_bit = 16, + }, + [S500_PD_VCE_SI] = { + .name = "VCE_SI", + .pwr_bit = 1, + .ack_bit = 17, + }, + [S500_PD_USB2_1] = { + .name = "USB2_1", + .pwr_bit = 2, + .ack_bit = 18, + }, + [S500_PD_CPU2] = { + .name = "CPU2", + .pwr_bit = 5, + .ack_bit = 21, + .genpd_flags = GENPD_FLAG_ALWAYS_ON, + }, + [S500_PD_CPU3] = { + .name = "CPU3", + .pwr_bit = 6, + .ack_bit = 22, + .genpd_flags = GENPD_FLAG_ALWAYS_ON, + }, + [S500_PD_DMA] = { + .name = "DMA", + .pwr_bit = 8, + .ack_bit = 12, + }, + [S500_PD_DS] = { + .name = "DS", + .pwr_bit = 9, + .ack_bit = 13, + }, + [S500_PD_USB3] = { + .name = "USB3", + .pwr_bit = 10, + .ack_bit = 14, + }, + [S500_PD_USB2_0] = { + .name = "USB2_0", + .pwr_bit = 11, + .ack_bit = 15, + }, +}; + +static const struct owl_sps_info s500_sps_info = { + .num_domains = ARRAY_SIZE(s500_sps_domains), + .domains = s500_sps_domains, +}; + +static const struct of_device_id owl_sps_of_matches[] = { + { .compatible = "actions,s500-sps", .data = &s500_sps_info }, + { } +}; + +static struct platform_driver owl_sps_platform_driver = { + .probe = owl_sps_probe, + .driver = { + .name = "owl-sps", + .of_match_table = owl_sps_of_matches, + .suppress_bind_attrs = true, + }, +}; + +static int __init owl_sps_init(void) +{ + return platform_driver_register(&owl_sps_platform_driver); +} +postcore_initcall(owl_sps_init); -- cgit v1.2.3 From 6932ec60cc0a71689150b16b71427cfdc6575602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Mon, 5 Jun 2017 21:04:21 +0200 Subject: soc: actions: owl-sps: Factor out owl_sps_set_pg() for power-gating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the SMP code to reuse PM domain code for CPU2/CPU3 wakeup. Signed-off-by: Andreas Färber --- drivers/soc/actions/Kconfig | 4 +++ drivers/soc/actions/Makefile | 1 + drivers/soc/actions/owl-sps-helper.c | 51 ++++++++++++++++++++++++++++++++++++ drivers/soc/actions/owl-sps.c | 34 +++--------------------- 4 files changed, 59 insertions(+), 31 deletions(-) create mode 100644 drivers/soc/actions/owl-sps-helper.c (limited to 'drivers') diff --git a/drivers/soc/actions/Kconfig b/drivers/soc/actions/Kconfig index bdf827d5ce78..9d68b5a771c3 100644 --- a/drivers/soc/actions/Kconfig +++ b/drivers/soc/actions/Kconfig @@ -1,8 +1,12 @@ if ARCH_ACTIONS || COMPILE_TEST +config OWL_PM_DOMAINS_HELPER + bool + config OWL_PM_DOMAINS bool "Actions Semi SPS power domains" depends on PM + select OWL_PM_DOMAINS_HELPER select PM_GENERIC_DOMAINS help Say 'y' here to enable support for Smart Power System (SPS) diff --git a/drivers/soc/actions/Makefile b/drivers/soc/actions/Makefile index 720c34ed16e4..1e101b06bab1 100644 --- a/drivers/soc/actions/Makefile +++ b/drivers/soc/actions/Makefile @@ -1 +1,2 @@ +obj-$(CONFIG_OWL_PM_DOMAINS_HELPER) += owl-sps-helper.o obj-$(CONFIG_OWL_PM_DOMAINS) += owl-sps.o diff --git a/drivers/soc/actions/owl-sps-helper.c b/drivers/soc/actions/owl-sps-helper.c new file mode 100644 index 000000000000..9d7a2c2b44ec --- /dev/null +++ b/drivers/soc/actions/owl-sps-helper.c @@ -0,0 +1,51 @@ +/* + * Actions Semi Owl Smart Power System (SPS) shared helpers + * + * Copyright 2012 Actions Semi Inc. + * Author: Actions Semi, Inc. + * + * Copyright (c) 2017 Andreas Färber + * + * 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 +#include + +#define OWL_SPS_PG_CTL 0x0 + +int owl_sps_set_pg(void __iomem *base, u32 pwr_mask, u32 ack_mask, bool enable) +{ + u32 val; + bool ack; + int timeout; + + val = readl(base + OWL_SPS_PG_CTL); + ack = val & ack_mask; + if (ack == enable) + return 0; + + if (enable) + val |= pwr_mask; + else + val &= ~pwr_mask; + + writel(val, base + OWL_SPS_PG_CTL); + + for (timeout = 5000; timeout > 0; timeout -= 50) { + val = readl(base + OWL_SPS_PG_CTL); + if ((val & ack_mask) == (enable ? ack_mask : 0)) + break; + udelay(50); + } + if (timeout <= 0) + return -ETIMEDOUT; + + udelay(10); + + return 0; +} +EXPORT_SYMBOL_GPL(owl_sps_set_pg); diff --git a/drivers/soc/actions/owl-sps.c b/drivers/soc/actions/owl-sps.c index 8abb72f01929..875225bfa21c 100644 --- a/drivers/soc/actions/owl-sps.c +++ b/drivers/soc/actions/owl-sps.c @@ -12,15 +12,12 @@ * option) any later version. */ -#include -#include #include #include #include +#include #include -#define OWL_SPS_PG_CTL 0x0 - struct owl_sps_domain_info { const char *name; int pwr_bit; @@ -51,37 +48,12 @@ struct owl_sps_domain { static int owl_sps_set_power(struct owl_sps_domain *pd, bool enable) { - u32 val, pwr_mask, ack_mask; - int timeout; - bool ack; + u32 pwr_mask, ack_mask; ack_mask = BIT(pd->info->ack_bit); pwr_mask = BIT(pd->info->pwr_bit); - val = readl(pd->sps->base + OWL_SPS_PG_CTL); - ack = val & ack_mask; - - if (ack == enable) - return 0; - - if (enable) - val |= pwr_mask; - else - val &= ~pwr_mask; - - writel(val, pd->sps->base + OWL_SPS_PG_CTL); - for (timeout = 5000; timeout > 0; timeout -= 50) { - val = readl(pd->sps->base + OWL_SPS_PG_CTL); - if ((val & ack_mask) == (enable ? ack_mask : 0)) - break; - udelay(50); - } - if (timeout <= 0) - return -ETIMEDOUT; - - udelay(10); - - return 0; + return owl_sps_set_pg(pd->sps->base, pwr_mask, ack_mask, enable); } static int owl_sps_power_on(struct generic_pm_domain *domain) -- cgit v1.2.3